SOFARPC is a highly scalable, high-performance, production-level Java RPC framework recently opened by Ant Financial. Ant Financial SOFARPC has gone through more than a decade and five generations of version development. SOFARPC is committed to simplifying RPC calls between applications and providing convenient, transparent, stable and efficient point-to-point remote service invocation solutions for applications. For users and developers to easily extend functionality, SOFARPC provides a rich model abstraction and extensible interface, including filters, routing, load balancing, and more.

With the open source of SOFARPC, we also open source sofa- Bolt-Node and SOFA – RPC-Node two Nodejs RPC base modules. But careful users may have noticed that we wrote in the documentation that we didn’t want you to use them directly and that we were going to provide RPC best practices in Eggjs.

Here comes the best practice, which is:

An egg – sofa xml-rpc plug-in:

https://github.com/eggjs/egg-sofa-rpc

An egg xml-rpc – generator tools:

https://github.com/eggjs/egg-rpc-generator

This paper introduces how Eggjs and SOFA (Java) interconnect with each other in the form of Step by Step, covering the service discovery, interface definition, local proxy generation, server implementation and other aspects of RPC, hoping to show you a relatively complete Nodejs RPC solution. The following example uses Protobuf as the serialization method for RPC in consideration of community acceptance, multilingual friendliness, and other factors.

The preparatory work

Note: This article uses macOS as an example. Please install and use other operating systems on Google.

The installationNodejs > = 8.0.0

    • Download the installation package:

        https://nodejs.org/en/download/

    • Perform the installation

The installationzookeeper

Copy the code

$ brew install zookeeper

Start thezookeeperservice

Copy the code

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

Sample repository for cloning SOFARPC Java

More information about SOFARPC can be found in the official documentation

Copy the code

git clone [email protected]:gxcsoccer/sofa-rpc-java-demo.git

The installationegg-init

Copy the code

$ npm i egg-init -g

Create a project

throughegg-initInitialize the project scaffolding, select the Simple template, and fill in the necessary information as required

Copy the code

$ 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

Go to the generated project directory and install the dependencies

Copy the code

$ cd /rpc-demo$ npm i

The installationegg-sofa-rpcPlug-in andegg-rpc-generatortool

Copy the code

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

configurationpackage.jsonTo the scripts node, add a command RPC as follows

Copy the code

{  "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"  }

}

configurationconfig/plugin.jsopenegg-sofa-rpcThe plug-in

Copy the code

// config/plugin.js



exports.sofaRpc = {  enable: true,  package: 'egg-sofa-rpc'

};

Defines the interface

Protobuf has its own interface definition language, see official documentation for details.

Copy the code

# ProtoService.proto syntax = "proto3"; package com.alipay.sofa.rpc.protobuf; option java_multiple_files = true; // Optional option JAVA_outer_className = "ProtoServiceModels"; // Optional 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; }

The above protoservice. proto file defines a service: Com. Alipay. Sofa. RPC. Protobuf. ProtoService, it has a method named echoObj entrance parameter type is EchoRequest, the return value type is EchoResponse.

Invoke the Java exposed RPC service

1. Start the Java server

Enter Java example above cloning warehouse, run ProtobufServiceServerMain

2. Set service discovery parameters

Our default service discovery relies on ZooKeeper, so we need to configure a ZK address. In config/config.{env}.js:

Copy the code

// config/config.default.js

'use strict';



Exports. sofaRpc = {registry: {address: '127.0.0.1:2181', // zk address to local port 2181}

};

3. Get the interface definition

Create a proto directory in the root of the egg project and place the protoservice.proto file defined above in it

Copy the code

. ├ ─ ─ app │ ├ ─ ─ controller │ │ └ ─ ─ home. Js │ └ ─ ─ the router. The js ├ ─ ─ the config │ ├ ─ ─ config. The default. The js │ └ ─ ─ plugin. Js ├ ─ ─ Package. The json └ ─ ─ proto └ ─ ─ ProtoService. The proto

4. Configure the interface to be invoked

Configure the service information to be invoked in config/proxy.js

Copy the code

'use strict';



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

};

appName(Mandatory): Specifies the application name of the service provider

api(Mandatory): interface list, which is a key-value key-value pair. Key is the generated proxy file name, and value is the interface name (it can also be an object for more detailed configuration).

Config /proxy.js For details about the configuration, see the documentation

5. Generate the invocation proxy

Run NPM run RPC in the root directory to generate the proxy file for the call

Copy the code

$ npm run rpc

> [email protected] RPC/egg xml-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"

After a successful run, you will see that two files have been generated

app/proxy/ProtoService.js– Proxy file to invoke the service

run/proto.json– The interface information exported from the.proto file is a JSON file

Copy the code

├ ─ ─ app │ ├ ─ ─ controller │ │ └ ─ ─ home. Js │ ├ ─ ─ the proxy │ │ └ ─ ─ ProtoService. Js │ └ ─ ─ the router. The js ├ ─ ─ the config │ ├ ─ ─ │ ├─ ├─ proto ├─ proto, proto, proto, proto, proto, proto, proto

Generated app/proxy/ProtoService js file content is as follows (note: do not hand to change the file) :

Copy the code

// 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. Invoke proxy classes to realize business logic

The ProtoService class defined above will be mounted to app.proxyclasses. An instance of ctx.proxy.protoService can be accessed through ctx.proxy.protoService (note the small hump here), so we can call the RPC service in the business, for example: here we call the echoObj method of the protoService in the Home Controller

Copy the code

// 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. Start the application and debug it

Copy the code

$ npm run dev

Visit http://127.0.0.1:7001/ in your browser and get the following result, indicating success



Expose RPC services to Java calls

_____

This time switch to Nodejs to expose the same service, with the Java side as the consumer

1. Set service discovery parameters

Same configuration as above as caller

Copy the code

// config/config.default.js



'use strict';



Exports. sofaRpc = {registry: {address: '127.0.0.1:2181', // zk address to local port 2181}

};

2. Define interfaces

Again, you need to define the interface, then place the.proto file in the proto directory, and then run NPM run RPC, just as you did above as the caller

3. Set the parameters of the RPC server

Run config/config.{env}. Js to set RPC server parameters

Copy the code

// config/config.default.js



'use strict';



exports.sofaRpc = {  server: {    namespace: 'com.alipay.sofa.rpc.protobuf'  }

};

The most important configuration is namespace. Other configurations can be default:

namespace(Mandatory): Namespace of the interface. All exposed interfaces reside in this namespace by default

selfPublish(Optional): Whether each worker process exposes services independently. In nodeJS multi-process mode, if multiple processes share the same port, load may be uneven in RPC scenarios. Therefore, selfPublish is set to true by default, indicating that each process independently listens on the port and publishes the service

port(Optional): Port on which the service listens (note: when selfPublish=true, the listening port is generated based on this configuration)

maxIdleTime(Optional): Client connection If there is no traffic within the specified period, the client disconnects

responseTimeout(Optional): Indicates the recommended timeout period for the server. The specific timeout period is subject to the client configuration

codecType(Optional): Recommended serialization method. The default is Protobuf

4. Realize interface logic

Protoservice. js file is created in app/ RPC directory to implement interface logic

Copy the code

'use strict';



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

};

5. Start the application and publish the service

Copy the code

$ npm run dev

Java invokes the service as a client

Enter Java example above cloning warehouse, run ProtobufServiceClientMain

The result is as follows:

Copy the code

Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.rpc Log4j2 ]



The original article was published on June 21, 2018

Author: Zong Yu

This article is from The cloud community partner “Mesozoic Technology”. For related information, you can follow “Mesozoic Technology”.