Eggjs 和 SOFA 的跨語言互調

Eggjs 和 SOFA 的跨語言互調

來自專欄 Node.js

推薦訪問語雀地址以獲得更好的閱讀體驗

伴隨 SOFARPC 的開源,我們也開源了 sofa-bolt-node 和 sofa-rpc-node 兩個 Nodejs RPC 基礎模塊。但細心的用戶可能注意了我們在文檔裡面寫到並不希望大家直接使用它們,並預告會在 Eggjs 里提供 RPC 最佳實踐。現在這個最佳實踐來了,它就是:

  • egg-sofa-rpc 插件
  • egg-rpc-generator 工具

本文通過 Step by Step 的形式介紹了 Eggjs 和 SOFA(Java)是如何進行互聯互通的,涵蓋了 RPC 的服務發現、介面定義、本地代理生成、服務端實現等各方面,期望展現給你一個相對完整的 Nodejs RPC 解決方案。考慮到社區的接受度、多語言友好性等因素,接下來的示例採用 protobuf 作為 RPC 的序列化方式。

一、準備工作

注意: 本文以 macOS 為例,其他操作系統的安裝、使用方法請自行 google。

  • 安裝 nodejs >= 8.0.0

    • 下載安裝包:nodejs.org/en/download/
    • 執行安裝
  • 安裝 zookeeper

$ brew install zookeeper

  • 啟動 zookeeper 服務

$ zkServer startZooKeeper JMX enabled by defaultUsing config: /usr/local/etc/zookeeper/zoo.cfgStarting zookeeper ... STARTED

  • 克隆 SOFARPC Java 的示例倉庫

SOFARPC 的更多信息可以參考官方文檔

git clone git@github.com:gxcsoccer/sofa-rpc-java-demo.git

  • 安裝 egg-init

$ npm i egg-init -g

二、創建工程

  • 通過 egg-init 初始化項目腳手架,選擇 simple 模板,接下來根據實際情況填寫必要信息

$ egg-init? Please select a boilerplate type (Use arrow keys) ──────────────? simple - Simple egg app boilerplate ts - Simple egg && typescript app boilerplate empty - Empty egg app boilerplate plugin - egg plugin boilerplate framework - egg framework boilerplate

  • 進入生成好的項目目錄,並安裝依賴

$ cd /rpc-demo$ npm i

  • 安裝 egg-sofa-rpc 插件和 egg-rpc-generator 工具

$ npm i egg-sofa-rpc --save$ npm i egg-rpc-generator --save-dev

  • 配置 package.json 的 scripts 節點,增加一個命令 rpc 如下

{ "scripts": { "start": "egg-scripts start --daemon --title=egg-server-rpc-demo", "stop": "egg-scripts stop --title=egg-server-rpc-demo", "dev": "egg-bin dev", "debug": "egg-bin debug", "test": "npm run lint -- --fix && npm run test-local", "test-local": "egg-bin test", "cov": "egg-bin cov", "lint": "eslint .", "ci": "npm run lint && npm run cov", "autod": "autod", "rpc": "egg-rpc-generator" }}

  • 配置 config/plugin.js 開啟 egg-sofa-rpc 插件

// config/plugin.jsexports.sofaRpc = { enable: true, package: egg-sofa-rpc,};

三、定義介面

protobuf 有自己的介面定義語言,詳細可以參考官方文檔。

# ProtoService.proto syntax = "proto3";package com.alipay.sofa.rpc.protobuf;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;}

上面這個 ProtoService.proto 文件定義了一個服務:com.alipay.sofa.rpc.protobuf.ProtoService,它有一個叫 echoObj 的方法,入口參數類型是 EchoRequest,返回值類型是 EchoResponse

四、調用 Java 暴露的 RPC 服務

1、啟動 Java 服務端

進入上面克隆的 Java 示例倉庫,運行 ProtobufServiceServerMain

2、配置服務發現參數

我們默認的服務發現依賴於 zookeeper,所以需要配置一個 zk 的地址。在 config/config.{env}.js 中配置如下:

// config/config.default.jsuse strict;exports.sofaRpc = { registry: { address: 127.0.0.1:2181, // zk 地址指向本地 2181 埠 },};

3、獲取介面定義

在 egg 項目根目錄下創建 proto 目錄,然後將上面定義的 ProtoService.proto 文件放到里

.├── app│ ├── controller│ │ └── home.js│ └── router.js├── config│ ├── config.default.js│ └── plugin.js├── package.json└── proto └── ProtoService.proto

4、配置要調用的介面

config/proxy.js 中配置要調用的服務信息

use strict;module.exports = { services: [{ appName: sofarpc, api: { ProtoService: com.alipay.sofa.rpc.protobuf.ProtoService, }, }],};

  • appName(必選): 服務提供方的應用名,如果沒有可以任意起一個
  • api(必選): 介面列表,是一個 key-value 鍵值對,key 是生成的 proxy 文件名,value 是介面名(如果要跟精細的配置也可以是一個對象)

config/proxy.js 詳細的配置說明可以參考文檔

5、生成調用代理

在根目錄下運行 npm run rpc,生成調用的 proxy 文件

$ npm run rpc> rpc-demo@1.0.0 rpc /egg-rpc-demo> egg-rpc-generator[EggRpcGenerator] framework: /egg-rpc-demo/node_modules/egg, baseDir: /egg-rpc-demo[ProtoRPCPlugin] found "com.alipay.sofa.rpc.protobuf.ProtoService" in proto file[ProtoRPCPlugin] save all proto info into "/egg-rpc-demo/run/proto.json"

運行成功以後,會發現生成了兩個文件

  • app/proxy/ProtoService.js - 調用服務的代理文件
  • run/proto.json - 從 .proto 文件中導出的介面信息,是一個 json 格式文件

.├── app│ ├── controller│ │ └── home.js│ ├── proxy│ │ └── ProtoService.js│ └── router.js├── config│ ├── config.default.js│ ├── plugin.js│ └── proxy.js├── package.json├── proto│ └── ProtoService.proto└── run └── proto.json

生成的 app/proxy/ProtoService.js 文件內容如下(注意:不要手動去改這個文件):

// Dont modified this file, its auto created by egg-rpc-generatoruse strict;const path = require(path);/* eslint-disable *//* istanbul ignore next */module.exports = app => { const consumer = app.sofaRpcClient.createConsumer({ interfaceName: com.alipay.sofa.rpc.protobuf.ProtoService, targetAppName: sofarpc, version: 1.0, group: SOFA, proxyName: ProtoService, responseTimeout: 3000, }); if (!consumer) { // `app.config[sofarpc.rpc.service.enable] = false` will disable this consumer return; } app.beforeStart(async() => { await consumer.ready(); }); class ProtoService extends app.Proxy { constructor(ctx) { super(ctx, consumer); } async echoObj(req) { return await consumer.invoke(echoObj, [ req ], { ctx: this.ctx, codecType: protobuf, }); } } return ProtoService;};/* eslint-enable */

6、調用代理類,實現業務邏輯

上面定義的這個 ProtoService 這個類,會掛載在 app.proxyClasses 上。通過 ctx.proxy.protoService(注意這裡是小駝峰)可以訪問它的實例,這樣我們就可以在業務中調用 RPC 的服務了,例如:下面我們在 home controller 調用 ProtoService 的 echoObj 方法

// app/controller/home.jsuse 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;

7、啟動應用,調試

$ npm run dev

在瀏覽器中訪問 http://127.0.0.1:7001/,得到下面的結果,說明成功了

五、暴露 RPC 服務給 Java 調用

這回換做 Nodejs 來暴露同樣的服務,Java 端作為消費者

1、配置服務發現參數

和上面作為調用者的配置一樣

// config/config.default.jsuse strict;exports.sofaRpc = { registry: { address: 127.0.0.1:2181, // zk 地址指向本地 2181 埠 },};

2、定義介面

同樣需要先定義介面,然後將 .proto 文件放到 proto 目錄下,然後運行 npm run rpc,這些和上面作為調用者時都一樣

3、配置 RPC 服務端的參數

通過 config/config.{env}.js 配置 RPC 服務端的參數

// config/config.default.jsuse strict;exports.sofaRpc = { server: { namespace: com.alipay.sofa.rpc.protobuf, },};

其中最主要的配置就是 namespace,其他配置都可以預設:

  • namespace(必選): 介面的命名空間,所有的暴露的介面默認都在該命名空間下
  • selfPublish(可選): 是否每個 worker 進程獨立暴露服務。nodejs 多進程模式下,如果多個進程共享一個埠,在 RPC 這種場景可能造成負載不均,所以 selfPublish 默認為 true,代表每個進程獨立監聽埠和發布服務
  • port(可選): 服務監聽的埠(注意:在 selfPublish=true 時,監聽的埠是基於這個配置生成的)
  • maxIdleTime(可選): 客戶端連接如果在該配置時長內沒有任何流量,則主動斷開連接
  • responseTimeout(可選): 服務端建議的超時時長,具體的超時還是以客戶端配置為準
  • codecType(可選): 推薦的序列化方式,默認為 protobuf

4、實現介面邏輯

app/rpc 目錄下創建 ProtoService.js 文件,用於實現介面邏輯

use strict;exports.echoObj = async function(req) { return { code: 200, message: hello + req.name + , you are in + req.group, };};

5、啟動應用,發布服務

$ npm run dev

6、Java 作為客戶端調用服務

進入上面克隆的 Java 示例倉庫,運行 ProtobufServiceClientMain

執行的結果如下:

Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.rpc Log4j2 ]2018-06-11 18:07:59,977 INFO [main] com.alipay.sofa.common.log:report:30 - Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.rpc Log4j2 ]2018-06-11 18:08:00,419 INFO [main] org.apache.curator.framework.imps.CuratorFrameworkImpl:start:234 - Starting2018-06-11 18:08:00,432 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT2018-06-11 18:08:00,432 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:host.name=30.23.232.62018-06-11 18:08:00,432 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:java.version=1.8.0_1712018-06-11 18:08:00,432 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:java.vendor=Oracle Corporation2018-06-11 18:08:00,432 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre2018-06-11 18:08:00,432 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:java.class.path=/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/lib/tools.jar:/Users/gaoxiaochen/projj/github.com/gxcsoccer/sofa-rpc-java-demo/target/classes:/Users/gaoxiaochen/.m2/repository/com/alipay/sofa/sofa-rpc-all/5.4.0/sofa-rpc-all-5.4.0.jar:/Users/gaoxiaochen/.m2/repository/com/alipay/sofa/bolt/1.4.1/bolt-1.4.1.jar:/Users/gaoxiaochen/.m2/repository/com/alipay/sofa/common/sofa-common-tools/1.0.12/sofa-common-tools-1.0.12.jar:/Users/gaoxiaochen/.m2/repository/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar:/Users/gaoxiaochen/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/Users/gaoxiaochen/.m2/repository/io/netty/netty-all/4.1.25.Final/netty-all-4.1.25.Final.jar:/Users/gaoxiaochen/.m2/repository/com/alipay/sofa/hessian/3.3.0/hessian-3.3.0.jar:/Users/gaoxiaochen/.m2/repository/com/alipay/sofa/tracer-core/2.1.1/tracer-core-2.1.1.jar:/Users/gaoxiaochen/.m2/repository/io/opentracing/opentracing-api/0.22.0/opentracing-api-0.22.0.jar:/Users/gaoxiaochen/.m2/repository/io/opentracing/opentracing-noop/0.22.0/opentracing-noop-0.22.0.jar:/Users/gaoxiaochen/.m2/repository/io/opentracing/opentracing-mock/0.22.0/opentracing-mock-0.22.0.jar:/Users/gaoxiaochen/.m2/repository/io/opentracing/opentracing-util/0.22.0/opentracing-util-0.22.0.jar:/Users/gaoxiaochen/.m2/repository/org/jboss/resteasy/resteasy-netty4/3.0.12.Final/resteasy-netty4-3.0.12.Final.jar:/Users/gaoxiaochen/.m2/repository/org/jboss/resteasy/resteasy-jaxrs/3.0.12.Final/resteasy-jaxrs-3.0.12.Final.jar:/Users/gaoxiaochen/.m2/repository/org/jboss/resteasy/jaxrs-api/3.0.12.Final/jaxrs-api-3.0.12.Final.jar:/Users/gaoxiaochen/.m2/repository/org/jboss/spec/javax/annotation/jboss-annotations-api_1.1_spec/1.0.1.Final/jboss-annotations-api_1.1_spec-1.0.1.Final.jar:/Users/gaoxiaochen/.m2/repository/javax/activation/activation/1.1.1/activation-1.1.1.jar:/Users/gaoxiaochen/.m2/repository/org/apache/httpcomponents/httpclient/4.3.6/httpclient-4.3.6.jar:/Users/gaoxiaochen/.m2/repository/org/apache/httpcomponents/httpcore/4.3.3/httpcore-4.3.3.jar:/Users/gaoxiaochen/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/Users/gaoxiaochen/.m2/repository/commons-codec/commons-codec/1.6/commons-codec-1.6.jar:/Users/gaoxiaochen/.m2/repository/commons-io/commons-io/2.1/commons-io-2.1.jar:/Users/gaoxiaochen/.m2/repository/net/jcip/jcip-annotations/1.0/jcip-annotations-1.0.jar:/Users/gaoxiaochen/.m2/repository/org/jboss/resteasy/resteasy-client/3.0.12.Final/resteasy-client-3.0.12.Final.jar:/Users/gaoxiaochen/.m2/repository/org/jboss/resteasy/resteasy-jackson-provider/3.0.12.Final/resteasy-jackson-provider-3.0.12.Final.jar:/Users/gaoxiaochen/.m2/repository/org/codehaus/jackson/jackson-core-asl/1.9.12/jackson-core-asl-1.9.12.jar:/Users/gaoxiaochen/.m2/repository/org/codehaus/jackson/jackson-mapper-asl/1.9.12/jackson-mapper-asl-1.9.12.jar:/Users/gaoxiaochen/.m2/repository/org/codehaus/jackson/jackson-jaxrs/1.9.12/jackson-jaxrs-1.9.12.jar:/Users/gaoxiaochen/.m2/repository/org/codehaus/jackson/jackson-xc/1.9.12/jackson-xc-1.9.12.jar:/Users/gaoxiaochen/.m2/repository/com/alipay/sofa/lookout/lookout-api/1.4.0/lookout-api-1.4.0.jar:/Users/gaoxiaochen/.m2/repository/org/apache/curator/curator-recipes/2.9.1/curator-recipes-2.9.1.jar:/Users/gaoxiaochen/.m2/repository/org/apache/curator/curator-framework/2.9.1/curator-framework-2.9.1.jar:/Users/gaoxiaochen/.m2/repository/org/apache/curator/curator-client/2.9.1/curator-client-2.9.1.jar:/Users/gaoxiaochen/.m2/repository/org/apache/zookeeper/zookeeper/3.4.6/zookeeper-3.4.6.jar:/Users/gaoxiaochen/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar:/Users/gaoxiaochen/.m2/repository/jline/jline/0.9.94/jline-0.9.94.jar:/Users/gaoxiaochen/.m2/repository/io/netty/netty/3.7.0.Final/netty-3.7.0.Final.jar:/Users/gaoxiaochen/.m2/repository/com/google/guava/guava/16.0.1/guava-16.0.1.jar:/Users/gaoxiaochen/.m2/repository/com/google/protobuf/protobuf-java/3.1.0/protobuf-java-3.1.0.jar:/Users/gaoxiaochen/.m2/repository/org/apache/logging/log4j/log4j-core/2.3/log4j-core-2.3.jar:/Users/gaoxiaochen/.m2/repository/org/apache/logging/log4j/log4j-api/2.3/log4j-api-2.3.jar:/Users/gaoxiaochen/.m2/repository/com/lmax/disruptor/3.3.7/disruptor-3.3.7.jar:/Users/gaoxiaochen/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.3/log4j-slf4j-impl-2.3.jar:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar2018-06-11 18:08:00,432 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:java.library.path=/Users/gaoxiaochen/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.2018-06-11 18:08:00,433 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:java.io.tmpdir=/var/folders/q4/4nwl16wn32ndm69rzh1zyvhh0000gn/T/2018-06-11 18:08:00,433 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:java.compiler=<NA>2018-06-11 18:08:00,433 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:os.name=Mac OS X2018-06-11 18:08:00,433 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:os.arch=x86_642018-06-11 18:08:00,433 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:os.version=10.13.42018-06-11 18:08:00,433 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:user.name=gaoxiaochen2018-06-11 18:08:00,433 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:user.home=/Users/gaoxiaochen2018-06-11 18:08:00,433 INFO [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:user.dir=/Users/gaoxiaochen/projj/github.com/gxcsoccer/sofa-rpc-java-demo2018-06-11 18:08:00,434 INFO [main] org.apache.zookeeper.ZooKeeper:<init>:438 - Initiating client connection, connectString=127.0.0.1:2181 sessionTimeout=60000 watcher=org.apache.curator.ConnectionState@2e005c4b2018-06-11 18:08:00,459 INFO [main-SendThread(127.0.0.1:2181)] org.apache.zookeeper.ClientCnxn:logStartConnect:975 - Opening socket connection to server 127.0.0.1/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)2018-06-11 18:08:00,547 INFO [main-SendThread(127.0.0.1:2181)] org.apache.zookeeper.ClientCnxn:primeConnection:852 - Socket connection established to 127.0.0.1/127.0.0.1:2181, initiating session2018-06-11 18:08:00,555 INFO [main-SendThread(127.0.0.1:2181)] org.apache.zookeeper.ClientCnxn:onConnected:1235 - Session establishment complete on server 127.0.0.1/127.0.0.1:2181, sessionid = 0x1000894bb75004f, negotiated timeout = 400002018-06-11 18:08:00,558 INFO [main-EventThread] org.apache.curator.framework.state.ConnectionStateManager:postState:228 - State change: CONNECTED2018-06-11 18:08:00,594 WARN [main] org.apache.curator.utils.ZKPaths:<clinit>:76 - The version of ZooKeeper being used doesnt support Container nodes. CreateMode.PERSISTENT will be used instead.Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.remoting Log4j2 ]2018-06-11 18:08:00,645 INFO [SOFA-CLI-CONN-com.alipay.sofa.rpc.protobuf.ProtoService-3-T1] com.alipay.sofa.common.log:report:30 - Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.remoting Log4j2 ]2018-06-11 18:08:00,703 WARN No Root logger was configured, creating default ERROR-level Root logger with Console appender/Users/gaoxiaochen/logs/tracelog/rpc-client-digest.log -> /Users/gaoxiaochen/logs/tracelog/rpc-client-digest.log.2018-06-09200: hello zhang, you are in 0200: hello zhang, you are in 0200: hello zhang, you are in 0200: hello zhang, you are in 0200: hello zhang, you are in 0200: hello zhang, you are in 0200: hello zhang, you are in 0200: hello zhang, you are in 0

六、示例倉庫

  • SOFA Java Demo
  • Eggjs RPC Example

推薦閱讀:

babel: yet another rpc, but far beyond rpc(中)
RPC的原理
SpringBoot+Hessian實例
FPNN Technology Ecosystem 介紹

TAG:eggjs | 開源 | RPC框架 |