背景
我跳到国企之后,第一个接触到的项目就是简单开放平台,公司搭这个平台的原因是由于业务规模的扩张,之前架构出现比如数据孤岛、定制化逻辑代码难维护等问题。为解决这些痛点,团队在搭建新平台时,借鉴了开源项目的设计思想,而我在读开源项目的源码时觉得里面有些技术设计值得学习,就整理记录下来。
开源项目地址:gitee.com/durcframewo…
什么是SOP?
SOP 简单开放平台核心功能是提供标准化接口与集成工具,实现多系统快速对接、数据互通及功能扩展,大幅降低跨系统开发与协作门槛。
标准化接口:通过统一平台接入的接口规范,实现标准化。
集成工具:封装接口调用中复杂的技术细节,例如验签、参数校验、密钥管理等。
数据互通:确保数据传递过程的一致性,内置的异常处理机制保障数据传递的准确性。
核心架构图
除了最核心的sop-gateway网关、sop-admin后台管理能力之外,SOP项目还提供了以下能力
| 模块 | 用途 |
|---|---|
| sop-example | 服务端接入网关示例 |
| sop-registry | 内置本地环境用的zookeeper注册中心 |
| sop-support | 为服务端接入提供依赖jar包 |
| sop-sdk | 支持多语言服务请求网关api |
| sop-website | 业务api文档可视化网站 |
在核心架构图基础上,添加上述模块可得完整架构图
如上图所示,流量经过负载均衡后会流向sop的不同模块,admin模块负责接收并处理sop后台管理类相关的请求,website模块负责接收api文档网站的相关请求,而 gateway 模块的作用是为平台提供业务服务的流量转发能力,负责接收上游请求并精准转发至下游业务服务。
在承接上述模块的数据层上平台使用了mysql+redis,主要用于存储服务密钥、向平台网关注册的接口信息、接口文档等内容。
核心模块:sop-gateway网关
sop-gateway属于RPC网关架构,承接Http流量并以Dubbo请求的方式转发到下游业务服务中,下面介绍下游业务服务怎样将接口注册到网关中,网关流量是如何进行Http->Dubbo的协议转换以及如何实现高效精准的流量分发。
一、业务服务接口注册
整体流程如图所示:
1、网关服务启动,将注册接口以及对应的地址ip、port上报至注册中心zookeeper(项目里用的zk)
2、服务侧引入网关侧提供的support包,包中提供了@Open注解,在服务端的dubbo接口上添加此注解,标识该接口需要被注册到网关中,此外supoort包中还提供了步骤1中提到的注册接口,服务侧项目启动时,会把dubbo接口及对应的地址上报至注册中心zk
3、服务侧从zk获取网关侧注册接口的地址
4、服务侧拿到地址后调用注册接口,将添加@Open的接口注册到网关中
5、网关会持久化接口信息至api_info表中
api_info表结构:
create table api_info
(
id bigint unsigned auto_increment comment 'id' primary key,
application varchar(64) default '' not null comment '应用名称',
api_name varchar(128) default '' not null comment '接口名称',
api_version varchar(16) default '1.0' not null comment '版本号',
description varchar(64) default '' null comment '接口描述',
remark text null comment '备注',
interface_class_name varchar(128) default '' not null comment '接口class',
method_name varchar(128) default '' not null comment '方法名称',
param_info text null comment '参数信息',
is_permission tinyint default 0 not null comment '接口是否需要授权访问',
is_need_Token tinyint default 0 not null comment '是否需要appAuthToken',
has_common_response tinyint default 1 null comment '是否有公共响应参数',
reg_source tinyint default 1 not null comment '注册来源,1-系统注册,2-手动注册',
api_mode tinyint default 1 null comment '接口模式,1-open接口,2-Restful模式',
status tinyint default 1 not null comment '1启用,0禁用',
add_time datetime default CURRENT_TIMESTAMP not null comment '添加时间',
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '修改时间',
add_by bigint default 0 null comment '创建人id',
update_by bigint default 0 null comment '修改人id',
constraint uk_apiname_version unique (api_name, api_version)
) comment '接口信息表';
其中,api_name 名称与api_version将作为用户调用网关接口时的入参,而interface_class_name、method_name、param_info 参数则会作为网关泛化调用业务服务时的参数。当用户调用接口时,网关会根据api_name和api_version查到interface_class_name、method_name、param_info后泛化调用服务侧dubbo接口,完成从http到dubbo调用的协议转换。
这种技术设计确保了SOP网关服务在运行过程中,也能够顺利完成新服务接口的接入工作。
二、网关侧流量转发
当用户以HTTP方式请求网关接口时,SOP网关内部基于上述api_info表内容将HTTP请求转化为Dubbo请求,并以Dubbo泛化调用的方式请求业务服务,获取结果后以统一响应的结构返回给用户。
整体流程
三、技术点:Dubbo泛化调用
RPC 网关实现精准流量转发依靠Dubbo泛化调用能力:在 RPC 网关架构中,网关作为统一 RPC 调用消费者,需遵循不依赖服务生产者接口 API 的原则,否则业务服务新增接口时,网关需频繁改码重部署,严重制约系统灵活性与迭代效率。
借助 Dubbo 泛化调用,网关可在运行时动态调用 Dubbo 接口,无需改码或重部署,既解决接口依赖问题,又大幅提升系统扩展性与迭代效率。
RPC网关基于Dubbo泛化调用实现流量转发流程图
实现原理:动态代理+反射
客户端请求服务端:
服务端处理完请求,将结果返回客户端:
Dubbo泛化调用demo:
@Slf4j
public class GenericServiceInvoker{
private String applicationName = "dubbo-generic-client";
private String registryAddress = "zookeeper://127.0.0.1:2181";
private final ApplicationConfig applicationConfig;
private final ReferenceCache referenceCache;
public GenericServiceInvoker(){
//初始化应用配置与缓存
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress(registryAddress);
applicationConfig = new ApplicationConfig();
applicationConfig.setName(applicationName);
applicationConfig.setRegistry(registryConfig);
applicationConfig.setQosPort(22224);
referenceCache = SimpleReferenceCache.getCache();
}
public Object invoke(String interfaceName, String method, String[] parameterTypes, Object[] params) {
ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
reference.setGeneric(true); // 开启泛化调用
reference.setApplication(applicationConfig);
reference.setInterface(interfaceName); // 动态指定接口名
reference.setTimeout(5000);
// 从缓存获取泛化服务(避免重复创建ReferenceConfig)
GenericService genericService = referenceCache.get(reference);
return genericService.$invoke(method, parameterTypes, params);
}
public static void main(String[] args) {
GenericServiceInvoker genericServiceInvoker = new GenericServiceInvoker();
Object resObj = genericServiceInvoker.invoke(
"com.example.dubbodemo.service.DemoService",
"sayHello",
new String[]{"java.lang.String"},
new Object[]{"Dubbo"}
);
log.info("获取泛化调用的结果 resObj={}", resObj);
}
}
//仅展示相关代码
public interface DemoService {
String sayHello(String name);
}
@DubboService
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "hello " + name;
}
}
四、RPC网关与HTTP网关优劣势分析
相比与HTTP网关,RPC网关的性能要更优秀,Dubbo基于长连接传输二进制数据,避免了HTTP建立连接的过程,二进制数据流在传输时也更加高效,依托 Zookeeper、Nacos 等注册中心,Dubbo 原生支持服务注册 / 发现、动态负载均衡等能力,在服务治理上表现也更加优秀。
反之,在兼容性和开发学习成本上,HTTP网关有很大的优势,内部能兼容多语言服务,开发和调试更加方便,同时接口语义设计上也更加清晰。
这里仅基于公司业务场景做技术选型的对比,http网关中Srping cloud Gateway很优秀但不符合公司的实际情况,据我了解服务在一定规模下用RPC+HTTP网关的方式效果更好。
SOP的功能拓展
简单开放平台的基本功能已经具备,但还有一些可扩展空间,包括但不限于:
1、接口维度的熔断限流 —— 常见方式有固定窗口(感兴趣的可以在开源的RuoYi项目中找到)、滑动窗口、令牌桶、漏桶等算法
2、链路追溯 —— 雪花算法/UUID+logback 或 skyWalking等开源组件
3、接口回调
感谢阅读!