高手过招:用SQL解决环环相扣的刑侦推理问题(苏旭辉版本)

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介:

SOFARPC 是近期蚂蚁金服开源的一个高可扩展性、高性能、生产级的 Java RPC 框架。在蚂蚁金服 SOFARPC 已经经历了十多年及五代版本的发展。SOFARPC 致力于简化应用之间的 RPC 调用,为应用提供方便透明、稳定高效的点对点远程服务调用方案。为了用户和开发者方便的进行功能扩展,SOFARPC 提供了丰富的模型抽象和可扩展接口,包括过滤器、路由、负载均衡等等。

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

现在这个最佳实践来了,它就是:

egg-sofa-rpc 插件

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

egg-rpc-generator 工具

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

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

准备工作

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

d47e62d2b349aca45e42305ed6714efbe5ed61d9安装 nodejs >= 8.0.0

    • 下载安装包:

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

    • 执行安装

d47e62d2b349aca45e42305ed6714efbe5ed61d9安装 zookeeper

 

$ brew install zookeeper

d47e62d2b349aca45e42305ed6714efbe5ed61d9启动 zookeeper 服务

 

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

d47e62d2b349aca45e42305ed6714efbe5ed61d9克隆 SOFARPC Java 的示例仓库

SOFARPC 的更多信息可以参考官方文档

 

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

d47e62d2b349aca45e42305ed6714efbe5ed61d9安装 egg-init

 

$ npm i egg-init -g

创建工程

d47e62d2b349aca45e42305ed6714efbe5ed61d9通过 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

d47e62d2b349aca45e42305ed6714efbe5ed61d9进入生成好的项目目录,并安装依赖

 

$ cd /rpc-demo$ npm i

d47e62d2b349aca45e42305ed6714efbe5ed61d9安装 egg-sofa-rpc 插件和 egg-rpc-generator 工具

 

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

d47e62d2b349aca45e42305ed6714efbe5ed61d9配置 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"  }
}

d47e62d2b349aca45e42305ed6714efbe5ed61d9配置 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',    }  }]
};

d47e62d2b349aca45e42305ed6714efbe5ed61d9appName(必选): 服务提供方的应用名,如果没有可以任意起一个
d47e62d2b349aca45e42305ed6714efbe5ed61d9api(必选): 接口列表,是一个 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"

运行成功以后,会发现生成了两个文件

d47e62d2b349aca45e42305ed6714efbe5ed61d9app/proxy/ProtoService.js - 调用服务的代理文件
d47e62d2b349aca45e42305ed6714efbe5ed61d9run/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/,得到下面的结果,说明成功了

a1dbef727dba143dfa4c7f467413c9946aee91fe

暴露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,其他配置都可以缺省:

d47e62d2b349aca45e42305ed6714efbe5ed61d9namespace(必选): 接口的命名空间,所有的暴露的接口默认都在该命名空间下
d47e62d2b349aca45e42305ed6714efbe5ed61d9selfPublish(可选): 是否每个 worker 进程独立暴露服务。nodejs 多进程模式下,如果多个进程共享一个端口,在 RPC 这种场景可能造成负载不均,所以 selfPublish 默认为 true,代表每个进程独立监听端口和发布服务
d47e62d2b349aca45e42305ed6714efbe5ed61d9port(可选): 服务监听的端口(注意:在 selfPublish=true 时,监听的端口是基于这个配置生成的)
d47e62d2b349aca45e42305ed6714efbe5ed61d9maxIdleTime(可选): 客户端连接如果在该配置时长内没有任何流量,则主动断开连接
d47e62d2b349aca45e42305ed6714efbe5ed61d9responseTimeout(可选): 服务端建议的超时时长,具体的超时还是以客户端配置为准
d47e62d2b349aca45e42305ed6714efbe5ed61d9codecType(可选): 推荐的序列化方式,默认为 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-21

本文作者:宗羽

本文来自云栖社区合作伙伴“中生代技术”,了解相关信息可以关注“中生代技术”。

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
相关文章
|
6月前
|
SQL 流计算
Flink CDC 1.12版本引入了对SQL Server的支持
【1月更文挑战第26天】【1月更文挑战第124篇】Flink CDC 1.12版本引入了对SQL Server的支持
77 1
|
1月前
|
SQL 监控 测试技术
SQL现在到哪个版本及版本更新技巧与方法
SQL(Structured Query Language)作为数据库管理和操作的标准语言,随着技术的不断进步和数据库管理系统(DBMS)的持续发展,其版本也在不断更新和完善
|
1月前
|
SQL Oracle 关系型数据库
SQL数据库当前版本概览与更新趋势
在探讨SQL(Structured Query Language)数据库的当前版本时,我们首先要明确的是,SQL本身是一种查询语言标准,而并非特指某一个具体的数据库产品
|
18天前
|
SQL 关系型数据库 MySQL
MySql5.6版本开启慢SQL功能-本次采用永久生效方式
MySql5.6版本开启慢SQL功能-本次采用永久生效方式
32 0
|
2月前
|
SQL 数据管理 BI
SQL 有哪些版本?
SQL 有哪些版本?
268 4
|
2月前
|
SQL 数据管理 BI
SQL Server 有哪些版本?
SQL Server 有哪些版本?
246 3
|
5月前
|
SQL 关系型数据库 数据库
nacos 2.2.3版本 查看配置文件的历史版本的接口 是针对MySQL数据库的sql 改成postgresql后 sql语句报错 该怎么解决
在Nacos 2.2.3中切换到PostgreSQL后,执行配置文件历史版本分页查询出错,因`LIMIT 0, 10`语法不被PostgreSQL支持,需改为`LIMIT 10 OFFSET 0`。仅当存在历史版本时报错。解决方案是调整查询SQL以兼容PostgreSQL语法。
|
6月前
|
SQL XML Linux
SQL Server的版本
【5月更文挑战第14天】SQL Server的版本
75 3
|
6月前
|
SQL 关系型数据库 MySQL
查询mysql版本sql - 蓝易云
执行这个命令后,MySQL将返回当前正在运行的版本信息。
65 0
|
6月前
|
SQL 安全 大数据
SQL Server主流版本生命周期管理
SQL Server主流版本提供至少10年的支持周期,包括5年主要支持期(含功能更新、性能改进等)和5年扩展支持期(仅限安全更新),直至终止支持为止。
226 0