commit | 983547158fbf491bd7c75b4f3094e59b51c94b7a | [log] [tgz] |
---|---|---|
author | hufeng <hufeng@qianmi.com> | Wed Jul 18 12:18:51 2018 +0800 |
committer | hufeng <hufeng@qianmi.com> | Wed Jul 18 12:18:51 2018 +0800 |
tree | a40897d4c733bde7b4bed7205c21af994b967a6b | |
parent | a51984cc18f2abb9fcb4cfcf02d704acf54817c0 [diff] |
细化scheduler错误处理,server-agent=> dubbo-agent增加运行期间追踪能力,fixed zookeeper 自动重连不需要判等 bug
dubbo2.js = Nodejs connected Java Dubbo service by dubbo protocol (dubbo head + hessian body)
多年期盼,一朝梦圆! We love dubbo 👏
感谢 js-to-java,hessian.js 两大核心模块, 感谢fengmk2和dead-horse老师。
Keep it Simple (build tools for humans).
Support zookeeper as register center
TCP Dubbo Native protocol (Dubbo Header + Hessian Body)
Socket Pool (ServerAgent -> SocketPool -> SocketWorker)
Support Directly Dubbo (const Dubbo = DirectlyDubbo({..}))
Middleware the same as Koa middleware, Easy to extend.
Tracing (runtime info, call stack)
Supported Dubbox
Typescript type definition
Convert java dubbo interface to typescript module
SocketWorker auto retry
yarn add dubbo2.js
//=====================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' } })();
//创建要注入的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, });
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' } })();
//创建要注入的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);
brew install zookeeper brew services start zookeeper #运行java/dubbo-demo-provider下面的例子 yarn run test # 全链路日志跟踪 DEBUG=dubbo* yarn run test
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', }), ]; }, }, })
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', });
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...'); });
const dubbo = Dubbo.from(/*...*/); //通过subscribe dubbo.subcribe({ onTrace(msg: ITrace) { //logger msg }, });
You will get all runtim system info just like this.
{ type: 'INFO', msg: 'dubbo:bootstrap version => 2.1.5' } { type: 'INFO', msg: 'connected to zkserver localhost:2181' } { type: 'INFO', msg: 'ServerAgent create socket-pool: 172.19.6.203:20880' } { type: 'INFO', msg: 'socket-pool: 172.19.6.203:20880 poolSize: 1' } { type: 'INFO', msg: 'new SocketWorker#1 |> 172.19.6.203:20880' } { type: 'INFO', msg: 'SocketWorker#1 =connecting=> 172.19.6.203:20880' } { type: 'INFO', msg: 'SocketWorker#1 <=connected=> 172.19.6.203:20880' } { type: 'INFO', msg: 'scheduler is ready' } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.DemoProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.ErrorProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.BasicTypeProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'ERR', msg: Error: Can not be found any agents at Object.Scheduler._handleZkClientOnData [as onData] (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/scheduler.js:68:29) at EventEmitter.<anonymous> (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/zookeeper.js:275:30) at <anonymous> at process._tickCallback (internal/process/next_tick.js:118:7) } { type: 'ERR', msg: Error: SocketWorker#1 <=closed=> 172.19.6.203:20880 retry: 6 at SocketWorker._onClose (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/socket-worker.js:78:29) at Socket.emit (events.js:180:13) at TCP._handle.close [as _onclose] (net.js:541:12) } { type: 'INFO', msg: 'SocketWorker#1 =connecting=> 172.19.6.203:20880' } { type: 'ERR', msg: { Error: connect ECONNREFUSED 172.19.6.203:20880 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1173:14) errno: 'ECONNREFUSED', code: 'ECONNREFUSED', syscall: 'connect', address: '172.19.6.203', port: 20880 } } { type: 'ERR', msg: Error: SocketWorker#1 <=closed=> 172.19.6.203:20880 retry: 5 at SocketWorker._onClose (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/socket-worker.js:78:29) at Socket.emit (events.js:180:13) at TCP._handle.close [as _onclose] (net.js:541:12) } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.DemoProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.BasicTypeProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.ErrorProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.ErrorProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.DemoProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.BasicTypeProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'ERR', msg: Error: Can not be found any agents at Object.Scheduler._handleZkClientOnData [as onData] (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/scheduler.js:68:29) at EventEmitter.<anonymous> (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/zookeeper.js:275:30) at <anonymous> at process._tickCallback (internal/process/next_tick.js:118:7) } { type: 'INFO', msg: 'SocketWorker#1 =connecting=> 172.19.6.203:20880' } { type: 'ERR', msg: { Error: connect ECONNREFUSED 172.19.6.203:20880 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1173:14) errno: 'ECONNREFUSED', code: 'ECONNREFUSED', syscall: 'connect', address: '172.19.6.203', port: 20880 } } { type: 'ERR', msg: Error: SocketWorker#1 <=closed=> 172.19.6.203:20880 retry: 4 at SocketWorker._onClose (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/socket-worker.js:78:29) at Socket.emit (events.js:180:13) at TCP._handle.close [as _onclose] (net.js:541:12) } { type: 'INFO', msg: 'SocketWorker#1 =connecting=> 172.19.6.203:20880' } { type: 'ERR', msg: { Error: connect ECONNREFUSED 172.19.6.203:20880 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1173:14) errno: 'ECONNREFUSED', code: 'ECONNREFUSED', syscall: 'connect', address: '172.19.6.203', port: 20880 } } { type: 'ERR', msg: Error: SocketWorker#1 <=closed=> 172.19.6.203:20880 retry: 3 at SocketWorker._onClose (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/socket-worker.js:78:29) at Socket.emit (events.js:180:13) at TCP._handle.close [as _onclose] (net.js:541:12) } { type: 'INFO', msg: 'SocketWorker#1 =connecting=> 172.19.6.203:20880' } { type: 'ERR', msg: { Error: connect ECONNREFUSED 172.19.6.203:20880 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1173:14) errno: 'ECONNREFUSED', code: 'ECONNREFUSED', syscall: 'connect', address: '172.19.6.203', port: 20880 } } { type: 'ERR', msg: Error: SocketWorker#1 <=closed=> 172.19.6.203:20880 retry: 2 at SocketWorker._onClose (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/socket-worker.js:78:29) at Socket.emit (events.js:180:13) at TCP._handle.close [as _onclose] (net.js:541:12) } { type: 'INFO', msg: 'SocketWorker#1 =connecting=> 172.19.6.203:20880' } { type: 'ERR', msg: { Error: connect ECONNREFUSED 172.19.6.203:20880 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1173:14) errno: 'ECONNREFUSED', code: 'ECONNREFUSED', syscall: 'connect', address: '172.19.6.203', port: 20880 } } { type: 'ERR', msg: Error: SocketWorker#1 <=closed=> 172.19.6.203:20880 retry: 1 at SocketWorker._onClose (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/socket-worker.js:78:29) at Socket.emit (events.js:180:13) at TCP._handle.close [as _onclose] (net.js:541:12) } { type: 'INFO', msg: 'SocketWorker#1 =connecting=> 172.19.6.203:20880' } { type: 'ERR', msg: { Error: connect ECONNREFUSED 172.19.6.203:20880 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1173:14) errno: 'ECONNREFUSED', code: 'ECONNREFUSED', syscall: 'connect', address: '172.19.6.203', port: 20880 } } { type: 'ERR', msg: Error: SocketWorker#1 <=closed=> 172.19.6.203:20880 retry: 0 at SocketWorker._onClose (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/socket-worker.js:78:29) at Socket.emit (events.js:180:13) at TCP._handle.close [as _onclose] (net.js:541:12) } { type: 'ERR', msg: Error: 172.19.6.203:20880's pool socket-worker had all closed. delete 172.19.6.203:20880 at ServerAgent._clearClosedPool (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/server-agent.js:66:33) at Object.onClose (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/server-agent.js:51:34) at SocketWorker._onClose (/Users/hufeng/Github/dubbo2.js/packages/dubbo/es7/socket-worker.js:97:34) at Socket.emit (events.js:180:13) at TCP._handle.close [as _onclose] (net.js:541:12) } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.DemoProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'INFO', msg: 'ServerAgent create socket-pool: 172.19.6.203:20880' } { type: 'INFO', msg: 'socket-pool: 172.19.6.203:20880 poolSize: 1' } { type: 'INFO', msg: 'new SocketWorker#2 |> 172.19.6.203:20880' } { type: 'INFO', msg: 'SocketWorker#2 =connecting=> 172.19.6.203:20880' } { type: 'INFO', msg: 'SocketWorker#2 <=connected=> 172.19.6.203:20880' } { type: 'INFO', msg: 'scheduler is ready' } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.BasicTypeProvider/providers, type: NODE_CHILDREN_CHANGED' } { type: 'INFO', msg: 'trigger watch /dubbo/com.alibaba.dubbo.demo.ErrorProvider/providers, type: NODE_CHILDREN_CHANGED' }
通过对调用链路的抽象使用和 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 的接口调用中,需要设置一些动态的参数如,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));
我们坚定的认为开发体验同用户的体验同等重要,我们做了一些创新,一些很酷的实践。
为了使 node 和 dubbo 之间的调用像 java 调用 dubbo 一样简单透明,我们设计和实现了 translator.
通过分析 java 的 jar 包中的 bytecode 提取 dubbo 调用的接口信息,自动生成 typescript 类型定义文件以及调用的代码。
在 packages/dubbo/src/tests/provider 就是根据 java 目录下的 demo 翻译而来。
我们希望整个 dubbo 调用的代码都可以无缝生成。
职责
❯ 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)
import {Dubbo} from 'dubbo2.js';
默认导入的 dubbo2.js 是按照 es2017 进行编译的,支持 node7.10 以上。
如果更低的 node 版本,可以使用
import {Dubbo} from 'dubbo2.js/es6';
1.欢迎 pr
2.欢迎 issue