一、什么是微服務
微服務架構是一種架構模式,它提倡將單一應用程序劃分成一組小的服務,服務之間互相協調、互相配合,為用戶提供最終價值。
系統中的各個微服務可被獨立部署,各個微服務之間是松耦合。
二、微服務的特點
-
復雜度可控
每一個微服務專注于單一功能
-
獨立部署
當某個微服務發生變更時無需編譯、部署整個應用
-
技術選型靈活
技術選型是去中心化的
-
容錯
故障會被隔離在單個服務中
-
擴展
每個服務可以根據實際需求獨立進行擴
三、主流的解決方案
- 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 |
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
image.png
- 服務提供方與消費方通過接口的方式依賴,服務調用設計如下
Interface層:
服務接口層,定義了服務對外提供的所有接口Molel層:
服務的DTO對象層business層:
業務實現層,實現interface接口并且和DB數據庫進行交互
- 通過maven的install & deploy命令把Interface和Model層發布到倉庫中,服務調用方只需要依賴Interface和model層即可,然后通過xml配置方式即可很方便地接入dubbo,對代碼無入侵
- SpringCloud
image.png
服務提供方和服務消費方通過json方式交互,因此只需要定義好相關json字段即可,消費方和提供方無接口依賴。通過注解方式來實現服務配置,對于程序有一定入侵
結論:
Dubbo服務依賴略重,需要有完善的版本管理機制,但是程序入侵少。
Spring Cloud通過Json交互,省略了版本管理的問題,但是具體字段含義需要統一管理,自身Rest API方式交互,為跨平臺調用奠定了基礎。
4.5、組件運行流程
-
Dubbo
image.png
gateWay
前置網關,具體業務操作,gateWay通過dubbo提供的負載均衡機制自動完成Service
原子服務,只提供該業務相關的原子服務Zookeeper
原子服務注冊到zk上
-
SpringCloud
image.png
所有請求都統一通過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,后續可能會出現哪些問題?