NodeJS
首发于NodeJS
Eggjs 和 SOFA 的跨语言互调

Eggjs 和 SOFA 的跨语言互调

推荐访问语雀地址以获得更好的阅读体验


伴随 SOFARPC 的开源,我们也开源了 sofa-bolt-nodesofa-rpc-node 两个 Nodejs RPC 基础模块。但细心的用户可能注意了我们在文档里面写到并不希望大家直接使用它们,并预告会在 Eggjs 里提供 RPC 最佳实践。现在这个最佳实践来了,它就是:


本文通过 Step by Step 的形式介绍了 Eggjs 和 SOFA(Java)是如何进行互联互通的,涵盖了 RPC 的服务发现、接口定义、本地代理生成、服务端实现等各方面,期望展现给你一个相对完整的 Nodejs RPC 解决方案。考虑到社区的接受度、多语言友好性等因素,接下来的示例采用 protobuf 作为 RPC 的序列化方式。


一、准备工作

注意: 本文以 macOS 为例,其他操作系统的安装、使用方法请自行 google。



  • 安装 zookeeper
$ brew install zookeeper
  • 启动 zookeeper 服务
$ zkServer start
ZooKeeper JMX enabled by default
Using config: /usr/local/etc/zookeeper/zoo.cfg
Starting zookeeper ... STARTED

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.js

exports.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.js
'use 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 文件内容如下(注意:不要手动去改这个文件):

// Don't modified this file, it's auto created by egg-rpc-generator

'use 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.js
'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;

7、启动应用,调试

$ npm run dev


在浏览器中访问 http://127.0.0.1:7001/,得到下面的结果,说明成功了

五、暴露 RPC 服务给 Java 调用

这回换做 Nodejs 来暴露同样的服务,Java 端作为消费者

1、配置服务发现参数

和上面作为调用者的配置一样

// config/config.default.js
'use 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.js
'use 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 - Starting
2018-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 GMT
2018-06-11 18:08:00,432 INFO  [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:host.name=30.23.232.6
2018-06-11 18:08:00,432 INFO  [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:java.version=1.8.0_171
2018-06-11 18:08:00,432 INFO  [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:java.vendor=Oracle Corporation
2018-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/jre
2018-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.jar
2018-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 X
2018-06-11 18:08:00,433 INFO  [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:os.arch=x86_64
2018-06-11 18:08:00,433 INFO  [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:os.version=10.13.4
2018-06-11 18:08:00,433 INFO  [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:user.name=gaoxiaochen
2018-06-11 18:08:00,433 INFO  [main] org.apache.zookeeper.ZooKeeper:logEnv:100 - Client environment:user.home=/Users/gaoxiaochen
2018-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-demo
2018-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@2e005c4b
2018-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 session
2018-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 = 40000
2018-06-11 18:08:00,558 INFO  [main-EventThread] org.apache.curator.framework.state.ConnectionStateManager:postState:228 - State change: CONNECTED
2018-06-11 18:08:00,594 WARN  [main] org.apache.curator.utils.ZKPaths:<clinit>:76 - The version of ZooKeeper being used doesn't 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-09
200: hello zhang, you are in 0
200: hello zhang, you are in 0
200: hello zhang, you are in 0
200: hello zhang, you are in 0
200: hello zhang, you are in 0
200: hello zhang, you are in 0
200: hello zhang, you are in 0
200: hello zhang, you are in 0


六、示例仓库

编辑于 2018-06-12

文章被以下专栏收录

    在 eggjs 团队的日常协作中,遵循「基于 GitLab 的硬盘式异步协作模式」。 先通过 issue 发起 RFC 召集讨论,再提交 Pull Request 和 Code Review,这样便于沉淀,即使是当时没有参与讨论的开发者,事后也能通过 issue 了解某个功能设计的前因后果。 因此,本专栏用于汇总近期值得关注的 Egg.js 和 Node.js 相关动态,将不定期发布。

    SOFAStack是致力于打造一流的分布式技术在金融场景应用实践的技术交流平台,专注于交流金融科技行业内最前沿、可供参考的技术方案与实施路线。微信公众号:金融级分布式架构(Antfin_SOFA)