powermock: 一个支持 gRPC 的 Mock Server

news2024/11/24 14:25:29

请添加图片描述

文章目录

    • 背景
    • 选型
    • 架构
    • 安装
    • 配置
    • 使用教程
      • 快速开始
      • 接口定义
      • 配置启动
    • Mock 规则
      • redis 插件
      • HTTP Mock
      • 高级配置
        • 前置准备
        • 场景一 特定 ID 返回特定用户信息
        • 场景二 通过脚本返回用户数据
    • 总结
    • 参考资料

本文介绍的是如何基于 bilibili 的开源方案 powermock 搭建一套通用的适用于自己公司的 MockServer。

背景

我所在公司正处在一个高速发展的阶段,各产品线齐头并进。而我所在的部门主要负责核心能力建设与增长类业务,属于所有产品线的最下游。

业务部门希望在新产品部署产线后,我们能快速的配合。这就导致了一个非常尴尬的局面,产品确定上线时间,处在产品上线的最后阶段的我们,如果有任何异常,都可能导致上线时间被压缩。

如何防止这类情况?简单来说,就是如何防止因依赖导致项目开发的不可预期。

一个常见拉新活动的业务图为例,如下:

请添加图片描述

用户场景是,假设一名用户通过我的邀请码完成平台注册,并且完成首次购物,他才能算作我的成功邀请用户,我才能得到我想要的奖励。

一个简单的拉新活动,因为服务拆分,需要同时依赖于两个服务才可以能完成这一个活动的开发。我们的核心诉求是测试我们自己开发的邀请活动。而用户和订单服务,一方面不在测试范围之内,另一方面,这些服务是其他团队开发,在测试环境的稳定性没人保证,会成为开发排期的瓶颈,而且如果是并行开发,这些服务可能还没有完成。

如果有一个服务,能够实现依赖服务协议,方便我们尽可能的穷举依赖服务的各种场景,让我们不需要时时刻刻的依赖上游服务,是不是就能解决这个问题?

选型

基于这些困惑和一段时间的摸索,团队成员提出了一套新的解决思路,基于 Mock 方式解决问题。确定了这个思想,接下来就是如何实施了。

通常服务间的依赖可分两类,一类是由被依赖方主动触发的消息,二类是由依赖方主动发起的调用。消息类依赖主要容易 mock,而服务间的调用 mock 相对复杂。

当前的微服务架构下,gRPC 是主流的服务间调用的协议,Mock Server 必然需要支持,经过一番寻找,在市面上发现了最近 bilibili 的开源实现方案 powermock。

这个工具的开源看时间在 2021 年 5 份刚刚开源,powermock 同时支持 HTTP 与 gRPC 协议接口的 Mock,提供灵活的插件功能。面向对象包括前后端(HTTP、gRPC)、测试等对 Mock 有需求的所有人员。

当前这个项目的 star 为 5,顺手 star 加到了 6。虽然 star 有点少,但鉴于其特性的确是我想要的功能,肯定是要尝试一下的。powermock 最吸引我的地方在于,代码简单,易于阅读,二次开发方便。而且,对于 gRPC 的支持是一个亮点。市面上的 Mock Server 主要都是面向 HTTP 的接口,面向前端。

架构

为便于针对 powermock 二次开发,通过阅读源码,我整理出 powermock 的主体架构,如下所示:

请添加图片描述

从上图可以看出, powermock 本身是一个 server,提供了 HTTP 和 gRPC 两种接入方式,即通过它可以 mock HTTP 和 gRPC 两种服务。

Mock 服务的对象是作为调用方的我们,我们希望 Mock Server 能模拟依赖服务的行为,如此才能帮助我们解决环境依赖的问题, ApiManager 提供的用于指定 Mock Server 行为规则的接口。

插件是 powermock 扩展功能不可少的能力,通过阅读源码,主要有三类插件,分别是:

MatchPlugin,用于解析请求到响应的匹配规则,如指定请求 id 为 1 的情况下该返回什么内容。当前支持插件有 simple (YAML 指定匹配规则) 和 script (Javascript 指定匹配规则)插件;

StoragePlugin,用于保存 Mock 规则,支持的插件有 redis 和 rediscluster 三类插件。默认规则保存在内存里,重启即丢失。redis 可永久保存规则,但没有将数据结构化存储,大规划构造测试数据不是很方便;

MockPlugin,用于生成 Mock 数据,包含三个插件,分别是有 HTTP 和 gRPC,负责将 response 按 gRPC 或者 HTTP 格式返回;

整体的架构大概如此,理解了它们,对于接下来的无论是安装配置,还是具体的使用都会有大有裨益。

安装

powermock 提供了 powermock 和 powermock-v8(支持 javascript 脚本解析规则) 两个命令。

安装方式如下:

$ go get github.com/bilibili-base/powermock/cmd/powermock
$ go get github.com/bilibili-base/powermock/cmd/powermock-v8 // 支持 javascript 的 server

配置

powermock 的配置分为两类,一个 mockserver 本身的配置,另外则是对于 Mock 规则的配置。

这小节介绍的内容主要是 Mock Server 自身的配置。如下是一个示例配置:

log:
    pretty: true
    level: debug
grpcmockserver:
    enable: true
    address: 0.0.0.0:30002
    protomanager:
        protoimportpaths: [ ]
        protodir: ./apis
httpmockserver:
    enable: true
    address: 0.0.0.0:30003
apimanager:
    grpcaddress: 0.0.0.0:30000
    httpaddress: 0.0.0.0:30001
pluginregistry: { }
plugin:
    simple: { }
    grpc: { }
    http: { }
    script: { }
    redis:
        enable: false
        addr: 127.0.0.1:6379
        password: ""
        db: 0
        prefix: /powermock/

如果已经了解前面的架构,这里的配置就比较容易理解了。

包括:

  • mock server 配置,主要是 gRPC 和 HTTP 两类服务的地址与端口配置;
  • apimanager 配置,指定 grpc 和 http 的 mock API 规则管理服务的接入地址和端口;
  • plugin 插件配置,插件的管理配置,这里吐槽一下, powermock 将所有类别的插件在一个 plugin 配置项下,类别区分不明晰,如果增加二级目录,如 mock、match、storage,配置会更加易懂;

使用教程

接下来,我将通过几个真实的案例带大家一起看下 powermock 的使用方法,如何实现 proto 和 http 服务的 Mock。

首先,创建目录 examples/,将所有的 proto 文件定义整理到这个文件下。

快速开始

先来介绍一个官方的快速接入的案例,介绍如何通过内存和 redis 存储 Mock 规则。

接口定义

首选,创建 greeter/apis/greeter.proto 文件,内容如下:


syntax = "proto3";

package examples.hello.api;
option go_package = "github.com/poloxue/mock/examples/greeter";

service Greeter {
    rpc Hello(HelloRequest) returns (HelloResponse);
}

message HelloRequest {
    string message = 2;
}

message HelloResponse {
    string message = 2;
}

在 greeter 目录下执行使用 protoc 命令编译 proto 文件,如下:

$ protoc -I. --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. ./apis/*.proto

编译生成文件包括 greeter/apis/greeter.pb.go 和 greeter/apis/greeter_grpc.pb.go。

配置启动

首先是一个基本的内存版本的 mockserver,创建 greeter/config.yaml,配置如下:

log:
  pretty: true
  level: debug
grpcmockserver:
  enable: true
  address: 0.0.0.0:30002
  protomanager:
    protoimportpaths: []
    protodir: ./apis
httpmockserver:
  enable: true
  address: 0.0.0.0:30003
apimanager:
  grpcaddress: 0.0.0.0:30000
  httpaddress: 0.0.0.0:30001
pluginregistry: { }
plugin:
  simple:
    enable: true

配置的具体含义参考前面的介绍,主要的是启用的 plugin 是 simple,最简单的内存版本。执行如下命令启动服务:

$ powermock serve --config.file config.yaml
9:18PM INF * start to create pluginRegistry component=main file=setup.go:40
9:18PM INF * start to create apiManager component=main file=setup.go:46
9:18PM INF * start to create httpMockServer component=main file=setup.go:63
...
9:18PM INF starting service component=main.gRPCMockServer.gRPC file=service.go:29
9:18PM INF starting service component=main.apiManager.http file=service.go:29
9:18PM INF starting service component=main.httpMockServer file=service.go:29

输出日志,成功启动服务。

Mock 规则

成功启动服务之后,如何设定配置 Mock 数据的生产规则呢?即什么输入对应输出的规则。

先来一个简单的配置目标,无论输入什么都返回固定的 {“message”: “hello world!”}。

创建文件 greeter/apis.yaml,配置如下:

uniqueKey: "hello_example_gRPC"
path: "/examples.greeter.api.Greeter/Hello"
method: "POST"
cases:
  - response:
        simple:
        header:
          x-unit-id: "3"
          x-unit-region: "sh"
        trailer:
          x-api-version: "1.3.2"
        body: |
          {"message": "hello world!"}

上述不仅设置了响应的 body,还设置了 x-unit-id 等 header 和 grpc trailer 信息。

如何将规则加载到 mock server 中,命令如下:

$ powermock load --address=127.0.0.1:30000 apis.yaml

加载完成后,我们可以直接通过 grpcurl 命令验证下 Mock 的结果是否是我们想要的。

$ protoc --proto_path=. --descriptor_set_out=greeter.protoset --include_imports apis/*.proto
$ grpcurl -plaintext -protoset greeter.protoset 127.0.0.1:30002 examples.greeter.api.Greeter/Hello
{
  "message": "hello world!"
}

通过 powermock 的 load 将 apis 配置加载到 mock server 的内存中,但这种方式,如果一旦我们重启了 mock server,先前定义的配置就会失效。

有没有持久化的方案呢?有,就是接下来要介绍的 redis 方案。

redis 插件

通过将 config.yaml 的 simple 配置更新为 redis,如下:

redis:
  enable: true
  addr: 127.0.0.1:6379
  password: ""
  db: 0
  prefix: /mockserver/

重启 Mock 服务之后,再次 load apis 配置信息。

127.0.0.1:6379> keys *
1) "/mockserver/hello_example_gRPC"
2) "/mockserver/__REVISION__"

通过 redis 持久化存储的 Mock 规则的好处不言而喻。

不断补充依赖服务在正式场景下的规则,逐步完成一个与真实场景几近相同的 Mock Server,无论是开发人员效率,还是自动化测试的场景覆盖都有极大的提升。

HTTP Mock

前面介绍的都是 gRPC 接口的 Mock,作为后端开发的我肯定更加关心的这个。如果是前端更关心的肯定是 HTTP 接口的 Mock。apis 的 Mock 也非常简单。

配置目标,无论什么情况下,都返回消息 “hello world”,在 apis.yaml 中新增如下配置:

uniqueKey: "hello_example_http"
path: "/hello"
method: "GET"
cases:
  - response:
      simple:
        header:
          x-unit-id: "3"
          x-unit-region: "sh"
        trailer:
          x-api-vesion: "1.3.2"
        body:
          hello world!

通过 curl 命令测试下 mock 结果,如下:


$ curl -v http://localhost:30003/hello
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 30003 (#0)
> GET /hello HTTP/1.1
> Host: localhost:30003
> User-Agent: curl/7.64.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: application/json
< X-Unit-Id: 3
< X-Unit-Region: sh
< Date: Sun, 11 Jul 2021 08:06:57 GMT
< Content-Length: 12
< 
* Connection #0 to host localhost left intact
hello world!* Closing connection 0

至此,一个 HTTP 接口的 Mock 已经完成。

高级配置

前面的内容主要介绍如何快速上手这个新的 Mock Server。但在真实场景下,伪造数据场景复杂,只返回固定的信息肯定不满足我们的要求。

如何处理呢?这就需要 apis.yaml 支持更多的高级 Mock 规则。

前置准备

还是以一个真实的场景为例吧,一个简单的用户服务的 proto 接口定义。

syntax = "proto3";

package examples.user.api;
option go_package = "github.com/poloxue/mock/examples/user/apis";

service User {
  rpc Register(GetUserRequest) returns(GetUserResponse);
  rpc SetUserDetail(SetUserDetailRequest) returns(SetUserDetailRequest);
  rpc GetUser(GetUserRequest) returns(GetUserResponse);
}

message RegisterRequest {
  string mobile = 1;
  string password = 2;
}

message RegisterResponse {
  int32 status = 1;
}

message SetUserDetailRequest {
  int64 id = 1;
  string mobile = 2;
  string username = 3;
  string email = 4;
}

message GetUserRequest {
  int64 id = 1;
  string mobile = 2;
}

message GetUserResponse {
  int64 id = 1;
  string mobile = 2;
    string username = 3;
    string email = 4;
}

共三个接口,分别是用户注册、设置详情以及获取用户信息。我们按 快速开始 中的步骤把 proto 的 go 文件编译,配置等准备完成。

场景一 特定 ID 返回特定用户信息

如果 id 为 1000 的情况,返回用户信息如下:

{
    "id": 1000,
    "mobile": "15300000001",
    "username": "poloxue",
    "email": "[poloxue123@gmail.com](mailto:poloxue123@gmail.com)"
}

配置 Mock 规则如下:

uniqueKey: "get_user_gRPC"
path: "/examples.user.api.User/GetUser"
method: "POST"
cases:
  - condition:
      simple:
        items:
          - operandX: "$request.body.id"
            operator: "=="
            operandY: "1000"
    response:
      simple:
        body: |
          {"id": 1000, "mobile": "15300000001", "username": "poloxue", "email": "poloxue123@gmail.com"}

将配置加载到 Mock Server 中,通过 grpcurl 请求数据,将得到如下结果:

$ grpcurl -d '{"id": 1000}' -plaintext -protoset user.protoset 127.0.0.1:30002 examples.user.api.User/GetUser
{
  "id": "1000",
  "mobile": "15300000001",
  "username": "poloxue",
  "email": "poloxue123@gmail.com"
}

上述通过 condition 的判断,如果 $request.body.id 是 1000,则返回特定的用户数据。

场景二 通过脚本返回用户数据

上述的场景按用户 ID 返回用户,如果需要返回多种场景用户,要写大量的映射规则。如果制定通用的生成规则呢?

指定一个目标,如果用户 ID 在 500 以内,返回规则为 email 为空,mobile 规则为 1530000+4 位用户 ID,用户名为 poloxue+用户ID。

新增的 condition 配置规则如下:

- condition:
      simple:
        items:
          - operandX: "$request.body.id"
            operator: "<"
            operandY: "500"
    response:
      script:
        lang: "javascript"
        content: |
          (function () {
              function pad(num, size) {
                  num = num.toString();
                  while (num.length < size) num = "0" + num;
                  return num;
              }
              return {
                code: 0,
                body: {
                    id: request.body.id,
                    username: 'username' + request.body.id,
                    mobile: '1530000' + pad(request.body.id, 4),
                },
              }
          })()

使用 powermock load 命令加载新的配置到 server 中。至此,如果我们使用 grpcurl 访问这个服务,得到的信息如下:

$ grpcurl -d '{"id": 100}' -plaintext -protoset user.protoset 127.0.0.1:30002 examples.user.api.User/GetUser
ERROR:
  Code: Internal
  Message: plugin(grpc): failed to unmarshal: EOF

因为,如果希望通过脚本生成 mock 数据,要将 powermock 替换为 powermock-v8 命令才可执行javascript 脚本。

$ grpcurl -d '{"id": 1}' -plaintext -protoset user.protoset 127.0.0.1:30002 examples.user.api.User/GetUser 
{
  "id": "1",
  "mobile": "15300000001",
  "username": "username1"
}
$ grpcurl -d '{"id": 100}' -plaintext -protoset user.protoset 127.0.0.1:30002 examples.user.api.User/GetUser
{
  "id": "100",
  "mobile": "15300000100",
  "username": "username100"
}

一个批量 Mock 用户数据的规则就写好了。

总结

整体而言, powermock 易于使用,但刚刚开源,还有些待完善的地方。但鉴于代码易读,容易扩展,与我们而言,当前的确是一个不错的选择。

参考资料

  • Mock Server 实践
  • 阿里Mock平台使用方法揭秘!
  • powermock 代码仓库

博客地址:powermock: 一个支持 gRPC 的 Mock Server

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1415616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11. 双目视觉之立体视觉基础

目录 1. 深度恢复1.1 单目相机缺少深度信息1.2 如何恢复场景深度&#xff1f;1.3 深度恢复的思路 2. 对极几何约束2.1 直观感受2.2 数学上的描述 1. 深度恢复 1.1 单目相机缺少深度信息 之前学习过相机模型&#xff0c;最经典的就是小孔成像模型。我们知道相机通过小孔成像模…

uniapp scroll-view用法[下拉刷新,触底事件等等...](4)

前言:可滚动视图区域。用于区域滚动 话不多说 直接上官网属性 官网示例 讲一下常用的几个 scroll 滚动时触发 scrolltoupper 滚动到顶部或左边&#xff0c;会触发 scrolltoupper 事件 scrolltolower 滚动到底部或右边&#xff0c;会触发 scrolltolower 事件 1.纵向滚动…

【揭秘】RecursiveAction全面解析

内容概要 RecursiveAction是Java中一个强大的工具&#xff0c;它允许将复杂任务分解为更小的子任务&#xff0c;这些子任务可以并行执行&#xff0c;从而提高整体性能&#xff0c;其主要优点在于能够有效地利用多核处理器&#xff0c;减少任务执行时间&#xff0c;并简化并行编…

SQL注入:盲注

SQL注入系列文章&#xff1a; 初识SQL注入-CSDN博客 SQL注入&#xff1a;联合查询的三个绕过技巧-CSDN博客 SQL注入&#xff1a;报错注入-CSDN博客 目录 什么是盲注&#xff1f; 布尔盲注 手工注入 使用python脚本 使用sqlmap 时间盲注 手工注入 使用python脚本 使…

SpringBoot项目配置SSL后,WebSocket连接失败的解决方案

SpringBoot项目配置SSL后&#xff0c;WebSocket连接应使用wss协议&#xff0c;而不是ws协议。在前端配置WebSocket时&#xff0c;URL以wss://开头。

【计算机网络】中小型校园网构建与配置

拓扑图配置文件传送门 Packet Tracer-中小型校园网配置布局文件文件 相关文章 【计算机网络】IP协议及动态路由算法 【计算机网络】Socket通信编程与传输协议分析 【计算机网络】网络应用通信基本原理 原理 1. Network 广域网&#xff0c;WAN Wide Area Network&#xff…

记录浏览器能打开github.com,android studio无法拉取github项目,并且ping github.com也拼不通的问题

问题&#xff1a; Android studio编译flutter工程突然碰上如下问题&#xff1a; 在浏览器打开该地址能正常打开&#xff0c;尝试ping&#xff1a; 解决方式 通过搜索&#xff0c;查到如下办法&#xff1a; 1、首先在ipaddress.com中查询github.com域名的固定ip地址&#xff…

nginx使用stream模块代理端口

今天原本准备将本地金仓的54321端口进行代理的&#xff0c;但是发现代理后总是无法访问。后来突然发现之前使用nginx代理的是一个地址而不是端口。因此做以下笔记用来做记录。 一、stream安装与配置 假设nginx在usr/local/nginx中 1. 测试当前nginx是否有stream模块 ./usr/…

【数据结构1-1】线性表

线性表是最简单、最基本的一种数据结构&#xff0c;线性表示多个具有相同类型数据“串在一起”&#xff0c;每个元素有前驱&#xff08;前一个元素&#xff09;和后继&#xff08;后一个元素&#xff09;。根据不同的特性&#xff0c;线性表也分为数组&#xff08;vector&#…

JVM基础知识汇总篇

☆* o(≧▽≦)o *☆嗨~我是小奥&#x1f379; &#x1f4c4;&#x1f4c4;&#x1f4c4;个人博客&#xff1a;小奥的博客 &#x1f4c4;&#x1f4c4;&#x1f4c4;CSDN&#xff1a;个人CSDN &#x1f4d9;&#x1f4d9;&#x1f4d9;Github&#xff1a;传送门 &#x1f4c5;&a…

【Docker】附录二:热门镜像介绍

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; 热门镜像介绍 本章将介绍一些热门镜像的功能&#xff0c;使用方法等。包括 Ubuntu、CentOS、MySQL、MongoDB、Redis、Nginx、Wordpress、No…

k8s学习-DaemonSet和Job

1.1DaemonSet是什么 Deployment部署的副本Pod会分布在各个Node上&#xff0c;每个Node都可能运行好几个副本。DaemonSet的不同之处在于&#xff1a;每个Node上最多只能运行⼀个副本。DaemonSet的典型应用场景有&#xff1a; &#xff08;1&#xff09;在集群的每个节点上运⾏存…

Adobe ColdFusion 反序列化漏洞复现(CVE-2023-38203)

0x01 产品简介 Adobe ColdFusion是美国奥多比(Adobe)公司的一套快速应用程序开发平台。该平台包括集成开发环境和脚本语言。 0x02 漏洞概述 Adobe ColdFusion存在代码问题漏洞,该漏洞源于受到不受信任数据反序列化漏洞的影响,攻击者通过漏洞可以代码执行,可导致服务器失…

6.小时表天分区和小时分区顺序错误,怎么办?

目录 一、解决思路二、修复表结构2.1 建表2.2查询数据2.3查询数据路径2.4删表重建2.5查询表结构2.6查询数据 三、恢复数据3.1方案1 直接移动原分区数据到新分区下3.2方案2 将分区路径指导原数据路径3.3方案3 通过临时表恢复数据 一、解决思路 对于这个问题&#xff0c;主要分为…

C++类和对象——构造函数与解析函数介绍

目录 1.构造函数和析构函数 1.构造函数&#xff0c;进行初始化 2.析构函数&#xff0c;进行清理 2.构造函数的分类及调用 1.括号法 注意&#xff1a; 2.显示法 3.隐式转化法 匿名对象 3.拷贝构造函数调用时机 4.构造函数调用规则 1.定义有参构造函数&#xff0c;不…

微信小程序之页面导航、生命周期和WXS脚本

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

机器学习的数据库积累........

https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf1_detection_zoo.md ​​​​​​​ 另一个database:&#xff08;网址:Object Detection Made Easy with TensorFlow Hub: Tutorial&#xff09; Object Detection Made Easy with Ten…

【Java Kubernates】Java调用kubernates提交Yaml到SparkOperator

背景 目前查询框架使用的是trino&#xff0c;但是trino也有其局限性&#xff0c;需要准备一个备用的查询框架。考虑使用spark&#xff0c;spark operator也已经部署到k8s&#xff0c;现在需要定向提交spark sql到k8s的sparkoperator上&#xff0c;使用k8s资源执行sql。 对比 …

RCC——使用HSE/HSI配置时钟

RCC 文章目录 前言一、背景二、仿真计算周期 2.1 2.2 三、MCO引脚输出时钟总结 前言 前期疑问&#xff1a;1、RCC是什么意思。 2、最终配好的72M是系统时钟吗&#xff1f; 3、一共有哪些时钟 本文目标&#xff1a;将PLL时钟配置成72M 疑问解答&#xff1a;最终配好的时钟是…

GitLab16.8配置webhooks、Jenkins2.4配置GitLab插件实现持续集成

看本篇文章的前提是已经部署完GItlab和Jenkins服务器&#xff0c;已经可以手动构建成功&#xff0c;并且经过了很多次实践&#xff0c;对这两款软件基本熟悉。 希望大家都能够厚积薄发。 ~~~道理都懂&#xff0c;但有时候做不到&#xff0c;所以还是没真正懂。 前端自动化&am…