一、准备工作

安装和启动 nacos

  1. 从 Github 下载 nacos 最新的 release 包

  2. 解压后,进入 nacos/bin 目录

  1. $ tar -xvf nacos-server-1.0.0.tar.gz
  2. $ cd nacos/bin
  1. 启动 nacos
  1. $ sh startup.sh -m standalone

下载 java 服务端工程

  1. $ git clone git@github.com:gxcsoccer/dubbo-demo.git
  2. $ cd dubbo-demo && mvn clean install

运行 Provider main

  1. package org.eggjs.dubbo.provider;
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;
  3. public class Provider {
  4. public static void main(String[] args) throws Exception {
  5. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"dubbo-demo-provider.xml"});
  6. context.start();
  7. System.in.read(); // press any key to exit
  8. }
  9. }

二、创建 egg-cloud 工程

使用 egg microservice 脚手架初始化工程

  1. $ npm init egg --type microservice dubbo-consumer

初始化好以后,结构如下:

  1. .
  2. └── dubbo-consumer
  3. ├── README.md
  4. ├── app
  5. ├── controller
  6. └── home.js
  7. └── router.js
  8. ├── assembly
  9. ├── dubbo-demo-api-1.0-SNAPSHOT-sources.jar
  10. └── dubbo-demo-api-1.0-SNAPSHOT.jar
  11. ├── config
  12. ├── config.default.js
  13. ├── plugin.js
  14. └── proxy.js
  15. ├── package.json
  16. └── test
  17. └── app
  18. └── controller
  19. └── home.test.js

执行 npm i 安装依赖

  1. $ npm i

三、配置调用的接口

将 java 工程编译好的接口 jar 包放置到 egg-cloud 工程根目录下的 assembly 子目录下(脚手架已经将该示例的 jar 包放到指定位置)

配置 $app_root/config/proxy.js 文件

  1. 'use strict';
  2. module.exports = {
  3. group: 'HSF',
  4. version: '1.0.0',
  5. services: [{
  6. appName: 'dubbo',
  7. api: {
  8. UserService: {
  9. interfaceName: 'org.eggjs.dubbo.UserService',
  10. },
  11. },
  12. dependency: [{
  13. groupId: 'eggjs',
  14. artifactId: 'dubbo-demo-api',
  15. version: '1.0-SNAPSHOT',
  16. }],
  17. }],
  18. };

配置详细信息:

  • errorAsNull (可选): 是否将后台服务错误以 null 返回, 默认为 false, 不处理, 直接抛异常。

  • tpl (可选): 指定 proxy 的模板,如不指定则使用默认模板

  • services (必须): RPC 服务详细配置

    • appName (必须): 提供此 RPC 服务的应用名。

    • api (必须): jar 包里提供了很多服务(服务通常是类级别的), 用于指明为哪些服务生成 proxy 文件, 其中的 key 生成 proxy 文件的名字. value 是服务接口名。如果需要进行更加细粒度的配置, value 也支持以下对象格式:

      • interfaceName (可选): 服务接口.

      • version (可选): 服务的版本号,如:1.0.0; 1.0.0.daily。默认是 1.0。

      • group (可选): 服务组别,默认是 SOFA。

    • dependency (必须): 对应 Maven pom 中配置的 dependency。

    • tpl (可选): 指定当前 proxy 的模板,覆盖全局模板配置。

四、配置 RPC 参数

通过 $app_root/config/config.${env}.js 文件可以配置 rpc 的一些信息,比如这里我们配置 registry 的地址为 127.0.0.1:8848 nacos 的地址

  1. 'use strict';
  2. exports.rpc = {
  3. registry: {
  4. type: 'nacos',
  5. address: '127.0.0.1:8848',
  6. },
  7. };

五、生成 Proxy

运行 npm run rpc 命令来生成调用代理。运行完毕以后,会生成两个文件夹

  • $app_root/app/proxy:存放调用代理的目录,下面会有一个 userService.js 文件

  • $app_root/app/proxy_class:从 jar 包导出的接口元数据会存放在这里

userService.js 长下面这个样子,它会自动帮你导出接口暴露的方法,并且帮你处理好 js 到 java 之间的类型映射问题。另外,proxy 文件还会将 java 接口定义的注释也导出,尽量做到和 java 调用 java 的体验一致

  1. 'use strict';
  2. const path = require('path');
  3. module.exports = function (app) {
  4. const appName = 'dubbo';
  5. let version = '1.0.0';
  6. if (app.config.proxy && app.config.proxy.envVersion) {
  7. version = app.config.proxy.envVersion[appName] || version;
  8. }
  9. const rpcClient = app.rpcClient;
  10. if (!rpcClient) return;
  11. const consumer = rpcClient.createConsumer({
  12. interfaceName: 'org.eggjs.dubbo.UserService',
  13. version,
  14. targetAppName: appName,
  15. group: 'HSF',
  16. proxyName: 'userService',
  17. responseTimeout: 3000,
  18. });
  19. /**
  20. * 用户服务
  21. */
  22. class UserService extends app.Proxy {
  23. constructor(ctx) {
  24. super(ctx, consumer);
  25. }
  26. // java source code: String sayHello(String name);
  27. // returnType: java.lang.String
  28. /**
  29. * 打招呼
  30. * @param name
  31. * @return
  32. */
  33. async sayHello(name) {
  34. const args = [
  35. {
  36. $class: 'java.lang.String',
  37. $: name,
  38. }
  39. ];
  40. return await consumer.invoke('sayHello', args, {
  41. ctx: this.ctx,
  42. });
  43. }
  44. // java source code: User echoUser(User user);
  45. // returnType: org.eggjs.dubbo.User
  46. /**
  47. * 返回 user
  48. * @param user
  49. * @return
  50. */
  51. async echoUser(user) {
  52. const args = [
  53. {
  54. $class: 'org.eggjs.dubbo.User',
  55. $: user,
  56. }
  57. ];
  58. return await consumer.invoke('echoUser', args, {
  59. ctx: this.ctx,
  60. });
  61. }
  62. }
  63. return UserService;
  64. };

$app_root/app/proxy_class 目录下导出了接口依赖的类型、枚举等元数据,比如 userService 接口依赖一个 User 类会被导出成下面形式,包含其属性和类型、缺省值等信息

  1. module.exports = {
  2. 'org.eggjs.dubbo.User': {
  3. 'id': {
  4. 'type': 'int',
  5. 'defaultValue': 0,
  6. },
  7. 'name': {
  8. 'type': 'java.lang.String',
  9. },
  10. 'address': {
  11. 'type': 'java.lang.String',
  12. },
  13. 'salary': {
  14. 'type': 'int',
  15. 'defaultValue': 0,
  16. },
  17. },
  18. };

六、调用 Proxy

生成的 proxy 在运行时会被挂载在 ctx.proxy 对象上,你可以通过下面方式调用接口提供的方法,一般情况下你只需要传递 JSON 对象就好,就像调用本地函数一样

  1. const result = await ctx.proxy.userService.echoUser({
  2. id: 123456,
  3. name: '宗羽',
  4. address: '蚂蚁 C 空间',
  5. salary: 100000000,
  6. });

七、运行 & 调试

执行 npm run dev 启动应用

  1. $ npm run dev

然后在浏览器里访问 http://127.0.0.1:7001 ,可以看到下面效果,说明调用已经成功了

使用 egg-cloud 调用 dubbo 服务 - 图1