release dubbo2.js@2.1.5
2 files changed
tree: 5dbe3cb1a45f6bcfb2c60b32b204e927423e3683
  1. examples/
  2. java/
  3. packages/
  4. perf/
  5. resources/
  6. .gitignore
  7. .yarnrc
  8. _config.yml
  9. CODE_OF_CONDUCT.md
  10. CONTRIBUTING.md
  11. dubbo.json
  12. lerna.json
  13. LICENSE
  14. Makefile
  15. package.json
  16. README.md
  17. tsconfig.json
  18. yarn.lock
README.md

dubbo2.js

love dubbo

NPM

dubbo2.js = Nodejs connected Java Dubbo service by dubbo protocol (dubbo head + hessian body)

多年期盼,一朝梦圆! We love dubbo 👏

感谢 js-to-java,hessian.js 两大核心模块, 感谢fengmk2dead-horse老师。

Features

  1. Keep it Simple

  2. Support zookeeper as register center

  3. TCP Dubbo Native protocol (Dubbo Header + Hessian Body)

  4. Socket Pool (ServerAgent -> SocketPool -> SocketWorker)

  5. Support Directly Dubbo (const Dubbo = DirectlyDubbo({..}))

  6. Middleware, Easy to extend.

  7. Tracing

  8. Supported Dubbox

  9. Typescript type definition

  10. Convert java dubbo interface to typescript module

  11. socket-worker auto retry

Getting Started

yarn add dubbo2.js

How to Usage?

dubbo2.js@2.0.4+

//=====================service.ts==================
//generated by interpret tools
import {BasicTypeProvider} from './providers/com/alibaba/dubbo/demo/BasicTypeProvider';
import {DemoProvider} from './providers/com/alibaba/dubbo/demo/DemoProvider';
import {ErrorProvider} from './providers/com/alibaba/dubbo/demo/ErrorProvider';

export default {
  BasicTypeProvider,
  DemoProvider,
  ErrorProvider,
};

//===============dubbo.ts========================
import {Dubbo} from 'dubbo2.js';
import service from './service';

//创建dubbo对象
const dubbo = new Dubbo<typeof service>({
  application: {name: 'node-dubbo'},
  //zookeeper address
  register: 'localhost:2181',
  service,
});

//main method
(async () => {
  const result1 = await dubbo.service.DemoProvider.sayHello('node');
  //print {err: null, res:'hello node from dubbo service'}
  const res = await dubbo.service.DemoProvider.echo();
  //print {err: null, res: 'pang'}
  const res = await dubbo.service.DemoProvider.getUserInfo();
  //status: 'ok', info: { id: '1', name: 'test' }
})();

如果不是从 java 自动生成的 typescript 代码怎么注入到 dubbo 对象中?

//创建要注入的service
import {Dubbo} from 'dubbo2.js';
const demoProvider = dubbo =>
  dubbo.proxyService({
    dubboInterface: 'com.alibaba.dubbo.demo.DemoProvider',
    version: '1.0.0',
    methods: {
      sayHello(name) {
        return [java.String(name)];
      },

      echo() {},

      test() {},

      getUserInfo() {
        return [
          java.combine('com.alibaba.dubbo.demo.UserRequest', {
            id: 1,
            name: 'nodejs',
            email: 'node@qianmi.com',
          }),
        ];
      },
    },
  });

//将该service合入dubbo对象构造函数的service对象中
const service = {
  demoProvider,
};

const dubbo = new Dubbo<typeof service>({
  //  ....other parameters
  service,
});

dubbo2.js@1.xxx

import {Dubbo, java, TDubboCallResult} from 'dubbo2.js';
//generated by interpret tools
import {BasicTypeProvider} from './providers/com/alibaba/dubbo/demo/BasicTypeProvider';
import {DemoProvider} from './providers/com/alibaba/dubbo/demo/DemoProvider';
import {ErrorProvider} from './providers/com/alibaba/dubbo/demo/ErrorProvider';

//创建dubbo对象
const dubbo = new Dubbo({
  application: {name: 'node-dubbo'},
  //zookeeper address
  register: 'localhost:2181',
  interfaces: [
    'com.alibaba.dubbo.demo.DemoProvider',
    'com.alibaba.dubbo.demo.BasicTypeProvider',
    'com.alibaba.dubbo.demo.ErrorProvider',
  ],
});

const basicTypeProvider = BasicTypeProvider(dubbo);
const demoProvider = DemoProvider(dubbo);
const errorProvider = ErrorProvider(dubbo);

//main method
(async () => {
  const result1 = await demoProvider.sayHello('node');
  //print {err: null, res:'hello node from dubbo service'}
  const res = await demoProvider.echo();
  //print {err: null, res: 'pang'}

  const res = await demoProvider.getUserInfo();
  //status: 'ok', info: { id: '1', name: 'test' }
})();

如果不是从 java 自动生成的 typescript 代码怎么注入到 dubbo 对象中?

//创建要注入的service
import {Dubbo} from 'dubbo2.js';
const demoProvider = dubbo =>
  dubbo.proxyService({
    dubboInterface: 'com.alibaba.dubbo.demo.DemoProvider',
    version: '1.0.0',
    methods: {
      sayHello(name) {
        return [java.String(name)];
      },

      echo() {},

      test() {},

      getUserInfo() {
        return [
          java.combine('com.alibaba.dubbo.demo.UserRequest', {
            id: 1,
            name: 'nodejs',
            email: 'node@qianmi.com',
          }),
        ];
      },
    },
  });

//将该service合入dubbo对象构造函数的service对象中
const service = {
  demoProvider,
};

const dubbo = new Dubbo({
  //  ....other parameters
  interfaces: [
    //....
    'com.alibaba.dubbo.demo.DemoProvider',
  ],
});

const demoProvider = demoProvider(dubbo);

as developer

brew install zookeeper
brew services start zookeeper

#运行java/dubbo-demo-provider下面的例子

yarn run test

# 全链路日志跟踪
DEBUG=dubbo* yarn run test

dubbo flow graph

dubbo-flow

API

create dubbo object

const dubbo = new Dubbo({
  isSupportedDubbox     //是不是支持dubbox (boolean类型); 可选,默认false
  application           //记录应用的名称,zookeeper的调用时候写入consumer 类型:({name: string};) 可选
  dubboInvokeTimeout    //设置dubbo调用超时时间默认10s 可选 类型number
  dubboSocketPool       //设置dubbo创建socket的pool大小,默认4 可选 类型number
  register              //设置zookeeper注册中心地址 必填 类型string
  zkRoot                //zk的默认根路径,默认/dubbo 类型string
  interfaces            //设置zk监听的接口名称 类型 Array<string> 必填, 在dubbo2.js@2.0.4+版本中不在使用这个参数
  service               //注入到dubbo容器的dubbo服务,类型Object, 在dubbo2.js@2.0.4+使用
});

// Or
const dubbo = Dubbo.from({
  isSupportedDubbox     //是不是支持dubbox (boolean类型); 可选,默认false
  application           //记录应用的名称,zookeeper的调用时候写入consumer 类型:({name: string};) 可选
  dubboInvokeTimeout    //设置dubbo调用超时时间默认10s 可选 类型number
  dubboSocketPool       //设置dubbo创建socket的pool大小,默认4 可选 类型number
  register              //设置zookeeper注册中心地址 必填 类型string
  zkRoot                //zk的默认根路径,默认/dubbo 类型string
  interfaces            //设置zk监听的接口名称 类型 Array<string> 必填, 在dubbo2.js@2.0.4+版本中不在使用这个参数
  service               //注入到dubbo容器的dubbo服务,类型Object, 在dubbo2.js@2.0.4+使用
})


//dubbo的代理服务
const demoSerivce = dubbo.proxService({
  //代理的服务接口 - string 必传
  dubboInterface: 'com.alibaba.dubbo.demo.DemoService',
  //服务接口的版本 - string 可选
  version: '1.0.0',
  //超时时间 number 可选
  timeout: 10
  //所属组 string 可选
  group: 'qianmi',
  //接口内的方法 - Array<Function> 必传
  methods: {
    //method name
    sayHello(name) {
      //仅仅做参数hessian化转换
      return [java.String(name)];
    },
    //method name
    getUserInfo() {
      //仅仅做参数hessian化转换
      return [
        java.combine('com.alibaba.dubbo.demo.UserRequest', {
          id: 1,
          name: 'nodejs',
          email: 'node@qianmi.com',
        }),
      ];
    },
  },
})

connect dubbo directly

import {DirectlyDubbo, java} from 'dubbo2.js';
import {
  DemoProvider,
  DemoProviderWrapper,
  IDemoProvider,
} from './providers/com/alibaba/dubbo/demo/DemoProvider';
import {UserRequest} from './providers/com/alibaba/dubbo/demo/UserRequest';

const dubbo = DirectlyDubbo.from({
  dubboAddress: 'localhost:20880',
  dubboVersion: '2.0.0',
  dubboInvokeTimeout: 10,
});

const demoService = dubbo.proxyService<IDemoProvider>({
  dubboInterface: 'com.alibaba.dubbo.demo.DemoProvider',
  methods: DemoProviderWrapper,
  version: '1.0.0',
});

When dubbo was ready?

const dubbo = Dubbo.from(/*...*/);

(async () => {
  await dubbo.ready();
  //TODO dubbo was ready
})();

//for example, in egg.js
app.beforeStart(async () => {
  await dubbo.ready();
  app.logger.info('dubbo was ready...');
});

dubbo's subscriber

const dubbo = Dubbo.from(/*...*/);

dubbo.subcribe({
  onReady: () => {
    //dubbo was ready.
    //TODO for example logger
  },
  onSysError: err => {
    //dubbo occur error
    //TODO dingTalkRobot.send('error')
  },
  onStatistics: stat => {
    //get invoke time statistics info
    //in order to know load whether balance
  },
});

middleware

通过对调用链路的抽象使用和 koa 相同的 middleware 机制,方便自定义拦截器,比如 logger,

//cost-time middleware
dubbo.use(async (ctx, next) => {
  const startTime = Date.now();
  await next();
  const endTime = Date.now();
  console.log('invoke cost time->', endTime - startTime);
});

dubbo-invoker

在 dubbo 的接口调用中,需要设置一些动态的参数如,version, group, timeout, retry 等常常

这些参数需要在 consumer 调用方才精确设定值,之前是在 interpret 翻译生成 ts 的代码里面进行设置这个不够灵活,所以这里面我就抽象一个 dubbo-invoker 作为设置参数的 middleware,这样可以很方便的动态设置各种 runtime 参数

import {dubboInvoker, matcher} from 'dubbo-invoker';

//init
const dubbo = Dubbo.from(/*....*/);

const matchRuler = matcher
  //精确匹配接口
  .match('com.alibaba.demo.UserProvider', {
    version: '1.0.0',
    group: 'user',
  })
  //match thunk
  .match(ctx => {
    if (ctx.dubboInterface === 'com.alibaba.demo.ProductProvider') {
      ctx.version = '2.0.0';
      ctx.group = 'product-center';
      //通知dubboInvoker匹配成功
      return true;
    }
  })
  //正则匹配
  .match(/^com.alibaba.dubbo/, {
    version: '2.0.0',
    group: '',
  });

dubbo.use(dubboInvoke(matchRuler));

Translator => Cool.

我们坚定的认为开发体验同用户的体验同等重要,我们做了一些创新,一些很酷的实践。

为了使 node 和 dubbo 之间的调用像 java 调用 dubbo 一样简单透明,我们设计和实现了 translator.

通过分析 java 的 jar 包中的 bytecode 提取 dubbo 调用的接口信息,自动生成 typescript 类型定义文件以及调用的代码。

在 packages/dubbo/src/tests/provider 就是根据 java 目录下的 demo 翻译而来。

我们希望整个 dubbo 调用的代码都可以无缝生成。

职责

  1. 翻译 Interface 代码,生成 node 端可调用代码;
  2. 自动将参数转换为 hessian.js 能识别的对象;
  3. 接口方法及参数类型提示;

translator 详细介绍

Performance

 loadtest -t 20 -c 200 http://localhost:3000/dubbo -k
[Wed Jun 20 2018 15:10:16 GMT+0800 (CST)] INFO Requests: 0, requests per second: 0, mean latency: 0 ms
[Wed Jun 20 2018 15:10:21 GMT+0800 (CST)] INFO Requests: 37305, requests per second: 7484, mean latency: 26.9 ms
[Wed Jun 20 2018 15:10:26 GMT+0800 (CST)] INFO Requests: 79187, requests per second: 8371, mean latency: 23.9 ms
[Wed Jun 20 2018 15:10:31 GMT+0800 (CST)] INFO Requests: 120374, requests per second: 8247, mean latency: 24.2 ms
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO Target URL:          http://localhost:3000/dubbo
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO Max time (s):        20
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO Concurrency level:   200
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO Agent:               keepalive
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO Completed requests:  161828
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO Total errors:        0
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO Total time:          20.000902374000002 s
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO Requests per second: 8091
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO Mean latency:        24.7 ms
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO Percentage of the requests served within a certain time
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO   50%      22 ms
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO   90%      30 ms
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO   95%      34 ms
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO   99%      49 ms
[Wed Jun 20 2018 15:10:36 GMT+0800 (CST)] INFO  100%      134 ms (longest request)

FAQ

import {Dubbo} from 'dubbo2.js';

默认导入的 dubbo2.js 是按照 es2017 进行编译的,支持 node7.10 以上。

如果更低的 node 版本,可以使用

import {Dubbo} from 'dubbo2.js/es6';

怎么参与开发?

1.欢迎 pr

2.欢迎 issue

分享直戳

talk