微服务框架方案对比

一、什么是微服务

微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。

系统中的各个微服务可被独立部署,各个微服务之间是松耦合。

二、微服务的特点

  • 复杂度可控

    每一个微服务专注于单一功能

  • 独立部署

    当某个微服务发生变更时无需编译、部署整个应用

  • 技术选型灵活

    技术选型是去中心化的

  • 容错

    故障会被隔离在单个服务中

  • 扩展

    每个服务可以根据实际需求独立进行扩

三、主流的解决方案

  • Dubbo
  • SpringCloud

四、Dubbo VS SpringCloud

4.1、总体架构

  • Dubbo


    image.png
  • Provider
    暴露服务的提供方,可通过jar或者容器的方式启动服务

  • Consumer
    调用远程服务的服务消费方

  • Registry
    服务注册中心和发现中心

  • Monitor
    统计服务和调用次数,调用时间监控中心

  • Container
    服务运行的容器

  • SpringCloud


    image.png
  • Service Provider
    暴露服务的提供方

  • Service Consumer
    调用远程服务的服务消费方

  • EureKa Server
    服务注册中心和服务发现中心

结论:
整体架构上来看,二者模式接近,都需要需要服务提供方,注册中心,服务消费方。

4.2、功能扩展

功能要素 Dubbo SpringCloud
服务注册中心 Zookeeper、Redis Netflix Eureka
服务调用方式 RPC Rest API
服务网关 暂无 Netflix Zuul
断路(熔断)器 暂不完善 Netflix Hystrix
配置中心 暂无 Spring Cloud Config
调用链追踪 暂无 Spring Cloud Sleuth
消息总线 暂无 Spring Cloud Bus
数据流 暂无 Spring Cloud Stream 封装了与Redis,Rabbit、Kafka等发送接收消息
批量任务 暂无 Spring Cloud Task
  1. Dubbo只是实现了服务治理

  2. SpringCloud子项目分别覆盖了微服务架构体系下的方方面面,服务治理只是其中的一个方面

  3. Dubbo额外提供了Filter扩展,对于上述“暂无”的部分,都可以通过扩展Filter来完善

  • 配置中心

可以使用淘宝的diamond、百度的disconf来实现分布式配置管理

  • 服务跟踪

可以使用京东开源的Hydra,或者扩展Filter用Zipkin来做服务跟踪

  • 批量任务

可以使用当当开源的Elastic-Job、tbschedule

结论: 从功能扩展上来看,Spring Cloud 更胜一筹,在开发过程中只要整合Spring Cloud的子项目就可以顺利的完成各种组件的融合,而Dubbo缺需要通过实现各种Filter来做定制,开发成本以及技术难度略高。

4.3、通讯协议

  • Dubbo
  1. Dubbo使用RPC通讯协议

  2. Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况

  • SpringCloud

Spring Cloud 使用HTTP协议的REST API

结论: dubbo支持各种通信协议,而且消费方和服务方使用长链接方式交互,通信速度上略胜Spring Cloud,如果对于系统的响应时间有严格要求,长链接更合适。

4.4、服务依赖方式

比较维度 Dubbo SpringCloud
交互方式 定义DTO JSON方式
调用方式 RPC HTTP
代码入侵 配置xml,无代码入侵 注解配置,有代码入侵
依赖情况 调用方与提供方强依赖 无依赖,可跨平台
版本管理 需要制定完善的版本管理机制 省略了版本管理的问题,但是具体字段含义需要统一管理
  • Dubbo


    image.png
  1. 服务提供方与消费方通过接口的方式依赖,服务调用设计如下
  • Interface层:
    服务接口层,定义了服务对外提供的所有接口

  • Molel层:
    服务的DTO对象层

  • business层:
    业务实现层,实现interface接口并且和DB数据库进行交互

  1. 通过maven的install & deploy命令把Interface和Model层发布到仓库中,服务调用方只需要依赖Interface和model层即可,然后通过xml配置方式即可很方便地接入dubbo,对代码无入侵
  • SpringCloud
    image.png

服务提供方和服务消费方通过json方式交互,因此只需要定义好相关json字段即可,消费方和提供方无接口依赖。通过注解方式来实现服务配置,对于程序有一定入侵

结论:

  1. Dubbo服务依赖略重,需要有完善的版本管理机制,但是程序入侵少。

  2. Spring Cloud通过Json交互,省略了版本管理的问题,但是具体字段含义需要统一管理,自身Rest API方式交互,为跨平台调用奠定了基础。

4.5、组件运行流程

  • Dubbo


    image.png
  • gateWay
    前置网关,具体业务操作,gateWay通过dubbo提供的负载均衡机制自动完成

  • Service
    原子服务,只提供该业务相关的原子服务

  • Zookeeper
    原子服务注册到zk上

  • SpringCloud


    image.png
  1. 所有请求都统一通过API网关(Zuul)来访问内部服务

  2. 网关接收到请求后,从注册中心(Eureka)获取可用服务

  3. 由Ribbon进行均衡负载后,分发到后端的具体实例

  4. 微服务之间通过Feign进行通信处理业务

  5. Hystrix负责处理服务超时熔断

  6. Turbine监控服务间的调用和熔断相关指标

结论:

  1. 业务部署方式相同,都需要前置一个网关来隔绝外部直接调用原子服务的风险

  2. Dubbo需要自己开发一套API网关,技术难度稍大

  3. 而Spring Cloud则可以通过Zuul配置即可完成网关定制,比较便捷

  4. 使用方式上Spring Cloud略胜一筹

五、目前的现状

  • Dubbo
  1. 目前以来,一直使用的dubbo,对我们团队而言,dubbo相对比较熟悉

  2. 公司架构师大部分出自阿里系,对dubbo比较熟悉,出现问题比较好解决

  3. 公司其他小组也是使用的dubbo,日后对接比较方便

  • SpringCloud
  1. 团队成员对于此技术栈比较陌生,少数队员熟悉

  2. 后面上线倘若出现问题,不太容易解决

  3. 技术栈不统一,后面与其他团队对接不太方便

六、Dubbo与SpringCloud共存方案

首先,我们需要对相应的工程进行调整,并接入SpringCloud,使得服务在保持原有工作的基础上,也同时要能够接到Eureka进行服务提供,主要包含两部分:

  1. 使原有的dubbo服务提供Restful规范接口

  2. 服务调用方接入调整

6.1、共存实现方式

  1. 独立共存

保持原dubbo的service实现不变,额外基于service改造提供Restful接口

  1. 基于Feign进行切换

2.1 在原有dubbo的service接口上配置feign支持

2.2 修改原dubbo的service实现类,对外提供Restful接口

6.2、独立共存方案

  • 初始状态结构

服务提供者(provider)往注册中心(registry)注册服务,服务消费者(consumer)从注册中心订阅服务

image.png
  • 共存状态结构

保持原dubbo的service实现不变,额外基于service改造提供Restful接口

image.png

6.3、基于Feign进行切换

1、修改原dubbo provider,同时向dubbo registry和eureka注册相同服务

2、修改原dubbo consumer,根据配置方式支持dubbo或http调用

image.png
具体实现思路
  1. 修改原dubbo服务定义的api接口,支持feign调用
@FeignClient("demo")
public interface DemoService {

    @RequestMapping(value = "/{version}/pt/demos/{appId}", method = RequestMethod.GET)
    Response<Integer> post(@PathVariable("appId") String appId);
}
  1. 修改原dubbo服务的实现类,添加@RestController对外提供Restful接口
@Service
@RestController
public class DemoServiceImpl implements DemoService {

    @Override
    public Response<Integer> post(String appId) {
        return null;
    }
}
  1. 消费者需要切换调用方式,如果原来的dubbo消费者使用了@Refrence注解,直接将其更改为@Autowoired即可。如果使用的是xml配置refrence则直接注解即可。
@Reference
private DemoService demoService;

//更改为
@Autowired
private DemoService demoService;

七、RPC调用的痛点

1. 服务提供方与调用方接口依赖方式太强

  • Dubbo

1、每个微服务都定义了抽象接口,并且每次并更之后都需要发布到仓库

2、调用方与提供方存在强依赖,需要严格地进行版本管理,否则容易出现调用与服务方版本不一致而导致无法编译成功

3、本地开发环境也会受到影响,往往一个应用可能需要依赖一系列的上游应用,每当上游应用发生修改,则需要经常更新代码并且install之后才能进行后续开发

总结:
需要严格的版本管理制度或者开发一些自动化工具否则,依赖关系会成为一大噩梦

  • SpringCloud

1、REST接口相比RPC更为轻量化,服务提供方和调用方的依赖只是依靠一纸契约,不存在代码级别的强依赖

2、然而REST接口也有痛点,因为接口定义过轻,很容易导致定义文档与实际实现不一致导致服务集成时的问题

3、可以通过每个服务都整合swagger,使得每个服务的代码与文档一体化,从而解决上述问题

总结:
在分布式环境下,REST方式的服务依赖要比RPC方式的依赖更为灵活

2. 服务对平台敏感,难以简单复用

  1. 通常我们在提供对外服务时,都会以REST的方式提供出去,这样可以实现跨平台的特点,任何一个语言的调用方都可以根据接口定义来实现

  2. 在Dubbo中我们要提供REST接口时,不得不实现一层代理,用来将RPC接口转换成REST接口进行对外发布

  3. 若我们每个服务本身就以REST接口方式存在,当要对外提供服务时,主要在API网关中配置映射关系和权限控制就可实现服务的复用了

八、Zuul服务网关

1、针对某个功能,客户端在微服务架构的情况下需要请求多个模块接口

2、针对于身份认证、日志、流量控制等公共模块每个微服务都需要做一遍,不利于业务与非业务的拆分

针对以上这些问题,Zuul可完美解决

  • 所有的微服务对外只有一个接口,我们只需访问一个网关地址,即可由网关将所有的请求代理到不同的服务中
  • 客户端只需要知道网关而不需要知道具体模块的地址,所有服务由网关对外提供
  • 身份认证类的东西单独抽象出来,业务模块只做业务

九、后续思考

  1. 目前主要存在什么问题?
  2. 是否有必要引进SpringCloud?
  3. 如果引进了SpringCloud,后续可能会出现哪些问题?

十、原文参考

  1. https://blog.csdn.net/qq_41587754/article/details/80133775
  2. http://springcloud.cn/view/254

推荐阅读更多精彩内容