kd-微服务简介
功能介绍
微服务是一种开发软件的架构和组织方法。在苍穹中,通过明确定义的API 将平台业务依据应用进行微服务拆分。微服务调用kd.bos.servicehelper.DispatchServiceHelper为微服务上层封装,起到简化集群中不同微服务之间的调用操作的作用。
对于平台应用而言,平台的功能为通用型,jar包会在每一个节点上进行部署;而对于业务而言,其功能jar包只会部署在自身所在的应用节点上,因此需要调用到正确的节点才能的到正确的调用返回。因此微服务调用区分了以下类型:
- 平台微服务调用,该类别的微服务调用会依据负载均衡算法调用到集群中任一节点;
- 业务微服务调用,该类别的微服务调用会调用到指定的业务集群的节点上;
- 二开微服务调用,该类别的微服务调用会调用到指定二开注册的微服务节点上。
说明:二开微服务必须由服务工厂注册定义才能使用,因此必须要有服务工厂类,服务工厂路由规则为:{isv标识|公司标识}.{云id}.{应用id}.ServiceFactory
例如:
- 新建微服务接口示例:isv.ti.bo.mservice.xxxService
- 新建服务工厂示例:isv.ti.bo.ServiceFactory
目前苍穹使用的负载均衡算法为健康度负载算法。
API概览
方法 | 说明 |
---|---|
static T getBOSService (String appId, String serviceName, Class bizCls) | 通过应用Id获取平台提供的微服务 |
static Object invokeBOSService (String appId, String serviceName, String methodName, Object… paras) | 平台微服务调用 |
static T invokeBizService (String cloudId, String appId, String serviceName, String methodName,Object… paras) | 业务微服务调用 |
static T invokeService (String factoryQualifiedPrefix, String appId, String serviceName, String methodName, Object… paras) | 二开业务调用 |
|
|
传入参数:
参数名称 | 参数 | 参数类型 | 是否必传 |
---|---|---|---|
云Id | cloudId | String | 是 |
应用Id | appId | String | 是 |
注册的服务名称 | serviceName | String | 是 |
调用服务方法 | methodName | String | 是 |
方法入参 | paras | Object[] | 是 |
|
|
框架解析
|
|
|
|
|
|
|
|
|
|
|
|
微服务框架分析
1. 核心架构与组件
该框架是一个多协议支持、可扩展的微服务调用框架,核心组件如下:
组件 | 作用 |
---|---|
ServiceLookup | 服务查找接口,抽象了服务实例的获取逻辑,支持多协议(Dubbo、Feign、Assembly)。 |
DubboServiceLookup | Dubbo 协议的具体实现,通过动态代理和缓存管理服务实例。 |
FeignServiceLookup | Feign 协议的具体实现,支持 HTTP 调用和 RESTful 接口。 |
AssemblyServiceLookup | 组合模式实现,集成多个服务查找策略(如内部服务、外部服务)。 |
ProxyFactory | 代理工厂,生成动态代理对象,封装远程调用逻辑(如协议选择、编解码)。 |
DispatchServiceHelper | 微服务调用入口,提供统一接口(invokeBizService /invokeService )调用服务。 |
AssemblyServiceManager | 管理服务注册、查找、启动的组件,负责组合多个服务实现。 |
2. 核心流程与协作
(1) 服务调用流程
|
|
(2) 关键步骤分析
-
服务查找
ServiceLookup
通过MServiceLookup
接口抽象,支持多种实现(Dubbo、Feign、Assembly)。DubboServiceLookup
使用ConcurrentHashMap
缓存服务实例(appIdInstanceMap
/hostInstanceMap
),避免重复创建。AssemblyServiceLookup
通过组合模式调用内部/外部服务查找实现(getInsideServiceLookup()
/getOutsideServiceLookup()
)。
-
动态代理
ProxyFactory.createProxy(...)
使用 Java 动态代理生成服务接口的代理对象。ProxyHandler
在invoke(...)
方法中封装远程调用逻辑,支持协议选择(prototol
)和数据编解码(dataCodecType
)。
-
协议支持
- Dubbo:通过
ReferenceConfig
配置服务引用,支持 RPC 协议。 - Feign:通过
FeignProxyFactory
生成 HTTP 客户端,支持 RESTful 接口。 - Assembly:组合多个服务查找策略,适配不同场景(如内部服务、外部服务)。
- Dubbo:通过
3. 设计模式与实现
设计模式 | 应用位置 | 作用与优势 |
---|---|---|
代理模式 | ProxyFactory.createProxy(...) |
封装远程调用细节,对调用方透明。支持统一处理超时、重试、链路追踪等横切关注点。 |
工厂模式 | ProxyFactory 、AssemblyServiceManager |
隐藏代理对象的创建逻辑,提供统一接口生成不同类型的代理实例(如 Dubbo、Feign),提高扩展性。 |
策略模式 | ServiceLookup.setImpl(MServiceLookup lookup) |
允许运行时切换不同的服务查找实现(如 Dubbo、Feign),实现框架与具体 RPC 实现的解耦,提升灵活性。 |
单例模式 | ConcurrentHashMap 缓存服务实例(appIdInstanceMap /hostInstanceMap ) |
通过缓存避免重复创建代理对象,减少资源消耗,提升性能。 |
模板方法模式 | DubboServiceLookup.doLookupImpl(...) |
抽象通用逻辑(如本地调试判断、缓存 key 生成),子类按需覆盖具体实现(如 lookupNotAppSplit(...) )。 |
组合模式 | AssemblyServiceLookup 调用 getInsideServiceLookup() /getOutsideServiceLookup() |
集成多个服务查找策略,支持复杂场景(如内部服务 + 外部服务组合)。 |
4. 关键功能与特性
-
多协议支持
- Dubbo:通过
ReferenceConfig
配置服务引用,支持 RPC 协议和本地调试(LocalDebugProxy
)。 - Feign:通过
FeignProxyFactory
生成 HTTP 客户端,适配 RESTful 接口。 - Assembly:组合多个服务查找策略,适配不同业务场景。
- Dubbo:通过
-
服务缓存与复用
- 使用
ConcurrentHashMap
缓存服务实例,避免重复创建代理对象,提升性能。 - 根据
appId
、协议(prototol
)、编解码方式(dataCodecType
)生成唯一 key,确保缓存精确。
- 使用
-
链路追踪与可观测性
EntityTracer.create(...)
创建调用链路上下文,支持分布式追踪。ProxyHandler
中处理超时和异常(如RpcTimeoutEvent
),增强系统可观测性。
-
本地调试与热部署
LocalDebugProxy.debugProxy(...)
支持本地调试,直接调用本地服务实例。DubboServiceLookup
中的canlooluplocal
标志控制是否启用本地调试。
-
应用拆分与多租户支持
Instance.isAppSplit()
判断是否启用应用拆分,适配多租户场景。AppUtils.isDeployAloneApp(appId)
判断服务是否独立部署,动态调整查找逻辑。
5. 优势与适用场景
优势 | 说明 |
---|---|
解耦性 | 业务代码无需感知 Dubbo/Feign 原生 API,通过统一接口调用服务。 |
扩展性 | 支持多协议(Dubbo/Feign/Assembly),可扩展新的服务查找策略。 |
性能优化 | 通过缓存和复用代理实例减少资源消耗,适配高并发场景。 |
可观测性 | 集成链路追踪(EntityTracer )和调试代理(LocalDebugProxy ),便于问题定位。 |
灵活性 | 支持应用拆分、多租户、本地调试,适配复杂业务场景。 |
适用场景
- 企业级微服务架构:支持 Dubbo/Feign 协议,适配传统和云原生场景。
- 多租户系统:通过
appId
和应用拆分策略管理不同租户的服务。 - 混合部署环境:组合内部服务(Dubbo)和外部服务(Feign)调用。
- 高并发系统:通过缓存和动态代理优化性能,减少资源消耗。
6. 代码结构与依赖关系
(1) 包结构
|
|
(2) 依赖关系
- ServiceLookup 依赖
MServiceLookup
接口,实现多协议支持。 - DubboServiceLookup 依赖
ReferenceConfig
(Dubbo 原生 API)。 - FeignServiceLookup 依赖
FeignProxyFactory
和 HTTP 客户端。 - ProxyFactory 依赖 Java 动态代理和
InvocationHandler
。 - AssemblyServiceManager 依赖
ExtensionFactory
和 SPI 扩展机制。
7. 潜在优化点
-
协议自动选择
可根据服务注册信息自动选择最优协议(如优先使用 Dubbo,降级到 Feign)。 -
负载均衡策略
在ProxyHandler
中集成负载均衡算法(如轮询、权重),提升服务调用的可用性。 -
服务降级与熔断
在代理调用中集成 Hystrix 或 Resilience4j,实现服务降级和熔断机制。 -
配置中心集成
将ReferenceConfig
和 Feign 配置迁移到配置中心(如 Nacos),支持动态配置更新。 -
服务注册与发现
集成服务注册中心(如 Nacos/Eureka),实现服务的自动注册与发现。
8. 总结
该微服务框架通过 抽象层设计(如 ServiceLookup
接口)和 动态代理模式 实现了多协议支持(Dubbo/Feign)和灵活扩展。其核心优势在于:
- 解耦业务与 RPC 实现,提升代码可维护性。
- 通过缓存和复用优化性能,适配高并发场景。
- 支持本地调试与链路追踪,增强开发效率和系统可观测性。
适用于企业级微服务架构、多租户系统以及混合部署环境,具备良好的扩展性和灵活性。
调用链路解析:以dubbo为例
微服务调用分析
-
调用入口
DispatchServiceHelper.invokeBizService(...)
是微服务调用的入口方法,其核心流程如下:- 通过
EntityTracer.create(...)
创建调用链路追踪上下文(用于分布式追踪)。 - 调用
serviceLookup(appId)
获取DispatchService
实例。 - 通过
service.invoke(...)
执行远程调用,参数包含服务工厂类名、服务名、方法名和参数。
- 通过
-
Dubbo 封装实现
- 动态代理:
ProxyFactory.createProxy(...)
使用 Java 动态代理生成服务接口的代理对象。代理对象在invoke
方法中封装了 Dubbo 的ReferenceConfig
配置和远程调用逻辑。 - 协议与编解码:
ProxyFactory.Handler
中通过prototol
(如rpc
/http
)和dataCodecType
(如javaobj
)指定通信协议和数据编解码方式,实现多协议支持。 - 本地调试支持:
DubboServiceLookup.lookup(...)
中通过LocalDebugProxy.debugProxy(...)
实现代理对象的本地调试能力,支持开发环境直接调用本地服务。
- 动态代理:
-
服务查找与缓存
DubboServiceLookup
使用ConcurrentHashMap
缓存服务实例(appIdInstanceMap
/hostInstanceMap
),避免重复创建代理对象。- 根据
appId
和应用拆分策略(Instance.isAppSplit()
)决定服务查找逻辑,支持多租户和微服务拆分场景。
使用的设计模式及好处
设计模式 | 实现位置 | 作用与好处 |
---|---|---|
代理模式 | ProxyFactory.createProxy(...) |
通过动态代理封装远程调用细节,对调用方透明,实现解耦。支持统一处理超时、重试、链路追踪等横切关注点。 |
工厂模式 | ProxyFactory 类 |
隐藏代理对象的创建逻辑(如协议选择、编解码配置),提供统一接口生成不同类型的代理实例,提高扩展性。 |
策略模式 | ServiceLookup.setImpl(MServiceLookup lookup) |
允许运行时切换不同的服务查找实现(如 Dubbo、gRPC),实现框架与具体 RPC 实现的解耦,提升灵活性。 |
单例模式 | ConcurrentHashMap 缓存服务实例 |
通过缓存避免重复创建代理对象,减少资源消耗,提升性能。 |
模板方法模式 | DubboServiceLookup.doLookupImpl(...) |
抽象通用逻辑(如本地调试判断、缓存 key 生成),子类按需覆盖具体实现(如 lookupNotAppSplit(...) )。 |
典型调用链路图
|
|
优势总结
- 解耦性:业务代码无需感知 Dubbo 原生 API,通过统一服务查找接口抽象实现解耦。
- 扩展性:支持多协议(RPC/HTTP)、多编解码方式,且可扩展新的服务查找策略。
- 可观测性:集成链路追踪(
EntityTracer
)和调试代理(LocalDebugProxy
),便于问题定位。 - 性能优化:通过缓存和复用代理实例减少重复创建开销,适应高并发场景。
Feign VS Dubbo
技术定位与通信协议
Feign
-
定位:HTTP客户端工具,本质是对HTTP调用的封装,简化RESTful API的调用。
-
协议:基于HTTP协议,支持JSON/XML等通用数据格式,天然跨语言(需API规范一致)。
-
场景:适用于基于HTTP的微服务架构,尤其是云原生场景(如Kubernetes原生支持HTTP)。
Dubbo
-
定位:高性能RPC框架,提供服务发现、负载均衡、集群容错等完整的微服务解决方案。
-
协议:默认使用Dubbo协议(二进制序列化),也支持HTTP、gRPC等协议,强调高性能和低延迟。
-
场景:适用于高并发、低延迟的内部服务调用,尤其是Java技术栈主导的大型分布式系统。
服务发现与注册中心
Feign
依赖外部服务发现组件(如Spring Cloud Eureka、Consul、Nacos),需手动集成。
服务调用通过服务名解析为具体URL,需配合负载均衡器(如Ribbon)。
Dubbo
内置多种注册中心(ZooKeeper、Nacos、Etcd等),支持服务自动注册与发现。
服务发现与负载均衡由框架自动处理,提供更细粒度的流量控制(如权重、标签路由)。
负载均衡与集群容错
Feign
依赖Spring Cloud LoadBalancer或Ribbon实现负载均衡,支持轮询、随机等策略。
容错需集成Hystrix或Resilience4j实现熔断、降级。
Dubbo
内置丰富的负载均衡策略(随机、加权轮询、最少活跃调用等)和集群容错模式(failover、failfast等)。
提供更细粒度的服务治理能力,如动态权重调整、流量隔离、自适应负载均衡。
序列化与性能
Feign
-
基于HTTP协议,默认使用JSON序列化,性能受限于文本格式的开销。
-
适合对吞吐量要求不极端、API兼容性要求高的场景。
Dubbo
-
默认使用Hessian2二进制序列化,性能显著优于JSON(尤其对于复杂对象)。
-
支持多种序列化协议(如Kryo、Protobuf),并可通过TCP长连接复用提升效率。
服务治理与扩展性
Feign
-
服务治理能力较弱,需依赖Spring Cloud生态组件(如Gateway、Sentinel)。
-
扩展性主要通过拦截器(RequestInterceptor)和解码器(ErrorDecoder)实现。
Dubbo
提供全方位的服务治理能力:
-
动态配置(如参数路由、流量控制)。
-
服务降级与熔断(支持自适应熔断)。
-
服务监控与链路追踪(集成Zipkin、Skywalking等)。
-
支持SPI扩展机制,可自定义协议、序列化、负载均衡策略等。
多语言支持
Feign
-
基于标准HTTP协议,天然支持跨语言调用(只要API规范一致)。
-
其他语言需自行实现HTTP客户端。
Dubbo
-
原生支持Java,对其他语言(如Go、Python)的支持需依赖Dubbo3的Triple协议或REST扩展。
-
跨语言场景需额外开发适配层。
开发体验与生态
Feign
简单易用,通过接口+注解即可定义服务调用(如@FeignClient)。
强依赖Spring Cloud生态,适合Spring Boot项目快速集成。
Dubbo
配置相对复杂,需理解服务提供者/消费者、注册中心等概念。
提供丰富的可视化管理工具(如Dubbo Admin),生态逐渐完善(尤其在阿里系技术栈中)。
适用场景对比
总结
选Feign:若项目基于Spring Cloud,追求简单集成HTTP接口,且对性能要求不是极端苛刻。
选Dubbo:若项目需要高性能RPC、丰富的服务治理能力,且以Java技术栈为主,尤其适合大型分布式系统。
两者并非完全互斥,在混合架构中也可结合使用(如外部API用Feign,内部服务用Dubbo)。