Skip to content

Latest commit

 

History

History
294 lines (218 loc) · 8.72 KB

README.zh_CN.md

File metadata and controls

294 lines (218 loc) · 8.72 KB

egg-rpc

NPM version build status Test coverage David deps Known Vulnerabilities npm download

egg-rpc 插件是为 egg 提供调用和发布 RPC 服务的能力

安装

$ npm i egg-rpc-base --save

用法

开启插件

通过 ${app_root}/config/plugin.js 配置启动 egg-rpc 插件:

exports.rpc = {
  enable: true,
  package: 'egg-rpc-base',
};

RPC 服务发现

默认的服务发现依赖于 zookeeper,所以你需要配置一个 zk 的地址

// ${app_root}/config/config.${env}.js
config.rpc = {
  registry: {
    address: '127.0.0.1:2181', // 根据实际情况配置
  },
};

后续我们还会提供更多的服务发现实现,你也可以根据自己的需求实现自己的 registry,详细可以参考:自定义服务发现实现

RPC 客户端

该插件提供调用其他系统暴露的 rpc 接口的能力

1. 获取接口定义

以 protobuf 为例,将 *.proto 文件放置到 ${app_root}/proto 目录下

2. 全局配置

可以在 ${app_root}/config/config.${env}.js 做一些全局性的配置

// ${app_root}config/config.${env}.js
exports.rpc = {
  client: {
    responseTimeout: 3000,
  },
};
  • responseTimeout(可选): RPC 的超时时长,默认为 3 秒

3. 申明要调用的接口

RPC 客户端还有一个重要的配置文件是:${app_root}/config/proxy.js,你需要把你调用的服务配置到里面,然后通过 egg-rpc-generator 工具帮你生成本地调用代码。

让我们看一个最简单的配置,它的基本含义是:我需要调用 rpc-demo 应用暴露的 org.eggjs.rpc.test.ProtoService 这个服务。

'use strict';

module.exports = {
  services: [{
    appName: 'rpc-demo',
    api: {
      ProtoService: 'org.eggjs.rpc.test.ProtoService',
    },
  }],
};

详细的配置可以参考 RPC 代理(Proxy)配置

4. 生成本地调用代理

配置好了,运行 egg-rpc-generator 生成本地调用代理文件。运行成功后,会在 ${app_root}/app/proxy 目录下生成一个 ProtoSerivce.js 文件

$ egg-rpc-generator

[EggRpcGenerator] framework: /egg-rpc-demo/node_modules/egg, baseDir: /egg-rpc-demo
[ProtoRPCPlugin] found "org.eggjs.rpc.test.ProtoService" in proto file
[ProtoRPCPlugin] save all proto info into "/egg-rpc-demo/run/proto.json"

5. 调用服务

通过 ctx.proxy.proxyName 来访问生成的 proxy 代码,proxyName 就是上面 proxy.js 配置的 api 键值对中的 key。例如:上面配置的 ProtoService,但是需要特别注意的是 proxyName 会自动转成小驼峰形式,所以就是 ctx.proxy.protoService

'use strict';

const Controller = require('egg').Controller;

class HomeController extends Controller {
  async index() {
    const { ctx } = this;
    const res = await ctx.proxy.protoService.echoObj({
      name: 'gxcsoccer',
	    group: 'A',
    });
    ctx.body = res;
  }
}

module.exports = HomeController;

和调用本地方法体验一模一样。

RPC 服务端

该插件还可以暴露 SOFARPC 接口给其他应用调用

1. 定义接口

同样以 protobuf 为例,将接口定义放置到 ${app_root}/proto 目录下。

# ProtoService.proto
syntax = "proto3";

package org.eggjs.rpc.test;
option java_multiple_files = true; // 可选
option java_outer_classname = "ProtoServiceModels"; // 可选

service ProtoService {
    rpc echoObj (EchoRequest) returns (EchoResponse) {}
}

message EchoRequest {
    string name = 1;
    Group group = 2;
}

message EchoResponse {
    int32 code = 1;
    string message = 2;
}

enum Group {
    A = 0;
    B = 1;
}

2. 全局配置

${app_root}/config/config.${env}.js 做一些配置

module.exports = {
	rpc: {
    server: {
      namespace: 'org.eggjs.rpc.test',
    },
	},
},
  • namespace(必选): 接口的命名空间,所有的暴露的接口默认都在该命名空间下
  • selfPublish(可选): 是否每个 worker 进程独立暴露服务。nodejs 多进程模式下,如果多个进程共享一个端口,在 RPC 这种场景可能造成负载不均,所以 selfPublish 默认为 true,代表每个进程独立监听端口和发布服务
  • port(可选): 服务监听的端口(注意:在 selfPublish=true 时,监听的端口是基于这个配置生成的)
  • maxIdleTime(可选): 客户端连接如果在该配置时长内没有任何流量,则主动断开连接
  • responseTimeout(可选): 服务端建议的超时时长,具体的超时还是以客户端配置为准
  • codecType(可选): 推荐的序列化方式,默认为 protobuf

3. 接口实现

${app_root}/app/rpc 目录下放置接口的具体实现代码

// ${app_root}/app/rpc/ProtoService.js
exports.echoObj = async function(req) {
  return {
    code: 200,
    message: 'hello ' + req.name + ', you are in ' + req.group,
  };
};

4. 测试 RPC 接口

调用服务的单元测试方式,使用 app.mockProxy API 来 mock 服务

'use strict';

const mm = require('egg-mock');
const assert = require('assert');

describe('test/mock.test.js', () => {
  let app;
  before(async function() {
    app = mm.app({
      baseDir: 'apps/mock',
    });
    await app.ready();
  });
  afterEach(mm.restore);
  after(async function() {
    await app.close();
  });

  it('should app.mockProxy ok', async function() {
    app.mockProxy('DemoService', 'sayHello', async function(name) {
      await sleep(1000);

      return 'hello ' + name + ' from mock';
    });

    const ctx = app.createAnonymousContext();
    const res = await ctx.proxy.demoService.sayHello('gxcsoccer');
    assert(res === 'hello gxcsoccer from mock');
  });
});

在单元测试中,我们还可以通过 app.rpcRequest 接口来方便的测试我们自己暴露的 RPC 服务,例如:

'use strict';

const mm = require('egg-mock');

describe('test/index.test.js', () => {
  let app;
  before(async function() {
    app = mm.app({
      baseDir: 'apps/rpcserver',
    });
    await app.ready();
  });
  after(async function() {
    await app.close();
  });

  it('should invoke HelloService', done => {
    app.rpcRequest('org.eggjs.rpc.test.HelloService')
      .invoke('hello')
      .send([ 'gxcsoccer' ])
      .expect('hello gxcsoccer', done);
  });
});

详细 app.rpcRequest 的 api 可以参考:单元测试 RPC 服务的方法

相关文档

贡献代码

请告知我们可以为你做些什么,不过在此之前,请检查一下是否有已经存在的Bug或者意见

如果你是一个代码贡献者,请参考代码贡献规范

开源协议

MIT