一、什么是微服务
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。
系统中的各个微服务可被独立部署,各个微服务之间是松耦合。
二、微服务的特点
-
复杂度可控
每一个微服务专注于单一功能
-
独立部署
当某个微服务发生变更时无需编译、部署整个应用
-
技术选型灵活
技术选型是去中心化的
-
容错
故障会被隔离在单个服务中
-
扩展
每个服务可以根据实际需求独立进行扩
三、主流的解决方案
- Dubbo
- SpringCloud
四、Dubbo VS SpringCloud
4.1、总体架构
-
Dubbo
Provider
暴露服务的提供方,可通过jar或者容器的方式启动服务Consumer
调用远程服务的服务消费方Registry
服务注册中心和发现中心Monitor
统计服务和调用次数,调用时间监控中心Container
服务运行的容器
-
SpringCloud
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 |
Dubbo只是实现了服务治理
SpringCloud子项目分别覆盖了微服务架构体系下的方方面面,服务治理只是其中的一个方面
Dubbo额外提供了Filter扩展,对于上述“暂无”的部分,都可以通过扩展Filter来完善
- 配置中心
可以使用淘宝的diamond、百度的disconf来实现分布式配置管理
- 服务跟踪
可以使用京东开源的Hydra,或者扩展Filter用Zipkin来做服务跟踪
- 批量任务
可以使用当当开源的Elastic-Job、tbschedule
结论: 从功能扩展上来看,Spring Cloud 更胜一筹,在开发过程中只要整合Spring Cloud的子项目就可以顺利的完成各种组件的融合,而Dubbo缺需要通过实现各种Filter来做定制,开发成本以及技术难度略高。
4.3、通讯协议
- Dubbo
Dubbo使用RPC通讯协议
Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况
- SpringCloud
Spring Cloud 使用HTTP协议的REST API
结论: dubbo支持各种通信协议,而且消费方和服务方使用长链接方式交互,通信速度上略胜Spring Cloud,如果对于系统的响应时间有严格要求,长链接更合适。
4.4、服务依赖方式
比较维度 | Dubbo | SpringCloud |
---|---|---|
交互方式 | 定义DTO | JSON方式 |
调用方式 | RPC | HTTP |
代码入侵 | 配置xml,无代码入侵 | 注解配置,有代码入侵 |
依赖情况 | 调用方与提供方强依赖 | 无依赖,可跨平台 |
版本管理 | 需要制定完善的版本管理机制 | 省略了版本管理的问题,但是具体字段含义需要统一管理 |
-
Dubbo
- 服务提供方与消费方通过接口的方式依赖,服务调用设计如下
Interface层:
服务接口层,定义了服务对外提供的所有接口Molel层:
服务的DTO对象层business层:
业务实现层,实现interface接口并且和DB数据库进行交互
- 通过maven的install & deploy命令把Interface和Model层发布到仓库中,服务调用方只需要依赖Interface和model层即可,然后通过xml配置方式即可很方便地接入dubbo,对代码无入侵
- SpringCloud
服务提供方和服务消费方通过json方式交互,因此只需要定义好相关json字段即可,消费方和提供方无接口依赖。通过注解方式来实现服务配置,对于程序有一定入侵
结论:
Dubbo服务依赖略重,需要有完善的版本管理机制,但是程序入侵少。
Spring Cloud通过Json交互,省略了版本管理的问题,但是具体字段含义需要统一管理,自身Rest API方式交互,为跨平台调用奠定了基础。
4.5、组件运行流程
-
Dubbo
gateWay
前置网关,具体业务操作,gateWay通过dubbo提供的负载均衡机制自动完成Service
原子服务,只提供该业务相关的原子服务Zookeeper
原子服务注册到zk上
-
SpringCloud
所有请求都统一通过API网关(Zuul)来访问内部服务
网关接收到请求后,从注册中心(Eureka)获取可用服务
由Ribbon进行均衡负载后,分发到后端的具体实例
微服务之间通过Feign进行通信处理业务
Hystrix负责处理服务超时熔断
Turbine监控服务间的调用和熔断相关指标
结论:
业务部署方式相同,都需要前置一个网关来隔绝外部直接调用原子服务的风险
Dubbo需要自己开发一套API网关,技术难度稍大
而Spring Cloud则可以通过Zuul配置即可完成网关定制,比较便捷
使用方式上Spring Cloud略胜一筹
五、目前的现状
- Dubbo
目前以来,一直使用的dubbo,对我们团队而言,dubbo相对比较熟悉
公司架构师大部分出自阿里系,对dubbo比较熟悉,出现问题比较好解决
公司其他小组也是使用的dubbo,日后对接比较方便
- SpringCloud
团队成员对于此技术栈比较陌生,少数队员熟悉
后面上线倘若出现问题,不太容易解决
技术栈不统一,后面与其他团队对接不太方便
六、Dubbo与SpringCloud共存方案
首先,我们需要对相应的工程进行调整,并接入SpringCloud,使得服务在保持原有工作的基础上,也同时要能够接到Eureka进行服务提供,主要包含两部分:
使原有的dubbo服务提供Restful规范接口
服务调用方接入调整
6.1、共存实现方式
- 独立共存
保持原dubbo的service实现不变,额外基于service改造提供Restful接口
- 基于Feign进行切换
2.1 在原有dubbo的service接口上配置feign支持
2.2 修改原dubbo的service实现类,对外提供Restful接口
6.2、独立共存方案
- 初始状态结构
服务提供者(provider)往注册中心(registry)注册服务,服务消费者(consumer)从注册中心订阅服务
- 共存状态结构
保持原dubbo的service实现不变,额外基于service改造提供Restful接口
6.3、基于Feign进行切换
1、修改原dubbo provider,同时向dubbo registry和eureka注册相同服务
2、修改原dubbo consumer,根据配置方式支持dubbo或http调用
具体实现思路
- 修改原dubbo服务定义的api接口,支持feign调用
@FeignClient("demo")
public interface DemoService {
@RequestMapping(value = "/{version}/pt/demos/{appId}", method = RequestMethod.GET)
Response<Integer> post(@PathVariable("appId") String appId);
}
- 修改原dubbo服务的实现类,添加@RestController对外提供Restful接口
@Service
@RestController
public class DemoServiceImpl implements DemoService {
@Override
public Response<Integer> post(String appId) {
return null;
}
}
- 消费者需要切换调用方式,如果原来的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. 服务对平台敏感,难以简单复用
通常我们在提供对外服务时,都会以REST的方式提供出去,这样可以实现跨平台的特点,任何一个语言的调用方都可以根据接口定义来实现
在Dubbo中我们要提供REST接口时,不得不实现一层代理,用来将RPC接口转换成REST接口进行对外发布
若我们每个服务本身就以REST接口方式存在,当要对外提供服务时,主要在API网关中配置映射关系和权限控制就可实现服务的复用了
八、Zuul服务网关
1、针对某个功能,客户端在微服务架构的情况下需要请求多个模块接口
2、针对于身份认证、日志、流量控制等公共模块每个微服务都需要做一遍,不利于业务与非业务的拆分
针对以上这些问题,Zuul可完美解决
- 所有的微服务对外只有一个接口,我们只需访问一个网关地址,即可由网关将所有的请求代理到不同的服务中
- 客户端只需要知道网关而不需要知道具体模块的地址,所有服务由网关对外提供
- 身份认证类的东西单独抽象出来,业务模块只做业务
九、后续思考
- 目前主要存在什么问题?
- 是否有必要引进SpringCloud?
- 如果引进了SpringCloud,后续可能会出现哪些问题?