Spring Cloud和Dubbo整合開發筆記(1)


一、需求背景:

  1. 公司內部老項目微服務技術棧使用Dubbo, 新項目技術棧使用主流的Spring Cloud相關組件開發,新舊項目涉及交互調用,無法直接通信數據傳遞。
  2. 老項目基於Dubbo,重構代碼升級使用Spring Cloud,改造升級要求成本最低,不影響現有系統運行。

二、Dubbo和Spring Cloud 的比較

  首先Dubbo是一個分布式服務框架,以及SOA治理方案。它的功能主要包括:高性能NIO通訊及多協議集成,服務動態尋址與路由,軟負載均衡與容錯,依賴分析與降級等,它是著名的阿里服務治理的核心框架。Spring Cloud更加關心為開發人員提供開箱即用的一系列常見的分布式工具(例如配置管理,服務發現,斷路器,智能路由,微代理,控制總線),它是基於輕量級框架Spring家族,維護版本速度相對較快。

想深入了解的朋友,可以參考這篇文章專門分析了兩者的區別:聽聽八年阿里架構師怎樣講述Dubbo和Spring Cloud微服務架構  

改造思路:Spring Cloud和Dubbo用於協調服務組件需要進行統一,使得Dubbo服務和Spring Cloud 服務能夠相互感知。其次統一微服務之間的通信協議,Spring Cloud使用Http協議,Dubbo支持Dubbo,Hessian,rmi,http,webservice,thrift,redis,rest,memcached協議;數據傳輸載體或者說格式在網絡中也是統一的,和調用的服務架構無關。改造前需要明確的是兩種架構Spring Cloud和Dubbo在項目中的主次,不然容易造成開發人員使用API困惑,代碼接口混亂,其次不要急於期望短期將架構統一,改造完整,特別是Spring Cloud帶來了項目開發更多的環節,更多的組件(意味着有更多的坑)。

改造方案:

  • 傳統方案:保留完整的Dubbo老系統,Dubbo服務不需要向SpringCloud組件注冊服務,通過Ribbon/Fegin調用Dubbo服務暴露的Restful Api.缺點也明顯,需要人工維護Dubbo服務和Spring Cloud服務的關系,當Dubbo服務較多時,不同的環境配置太多。
  • 傳統方案:借助SideCar支持多語言的特性,連接Dubbo和Spring Cloud底層使用Sidecar交互,同時Dubbo也可以將信息傳播到Eureka上面。缺點明顯,需要每個Dubbo服務節點額外配置Sidecar服務節點,同時增加了鏈路的長度。

我的方案:Spring Cloud和Dubbo的服務中心選擇阿里的Nacos,它是一個動態服務發現、配置管理和服務管理平台,為什么不選擇使用Zookeeper,因為zookeeper是個CP系統,強一致性。如果其中master掛掉,此時zookeeper集群會進行重新選舉,不能提供服務列表信息的服務,其次zookeeper通過tcp不能准確判斷服務此時是否可用,數據庫掛了,數據庫連接池滿了等也能提供TCP信息。通信協議我選擇Http協議,Dubbo2.6之后支持http協議,數據格式使用Json,前端無需根據服務區分數據格式解析。

Nacos支持部署的模式有單機,集群,多集群模式,Nacos 0.8之后支持數據庫持久化,可以方便看見服務信息的前后變化。單機模式很簡單啟動,下載最新版Nacos:https://github.com/alibaba/nacos/releases ,解壓直接運行bin/startup.sh或者startup.cmd即可。

Nacos不僅提供服務發現和服務健康監測,它也提供控制台可視化頁面進行動態配置服務,動態 DNS 服務,服務及其元數據管理等功能,Nacos0.8版本支持簡單登錄功能,默認用戶名/密碼為 nacos/nacos。:

相比Eureka提供的單一的看板頁面,提供的管理功能可以說沒得比,具體使用手冊參考官方:https://nacos.io/zh-cn/docs/console-guide.html,這里不再贅述。

首先開發基於Spring Cloud+Nacos架構的微服務,nacos-discovery-provider為服務提供者,nacos-discovery-consumer為服務消費方, 

nacos-discovery-provider的pom.xml加入相關依賴:

 1 <dependencies>
 2         <dependency>
 3             <groupId>org.springframework.cloud</groupId>
 4             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
 5             <version>0.2.1.RELEASE</version>
 6         </dependency>
 7         <dependency>
 8             <groupId>org.springframework.boot</groupId>
 9             <artifactId>spring-boot-starter-web</artifactId>
10             <version>2.0.6.RELEASE</version>
11         </dependency>
12         <dependency>
13             <groupId>org.springframework.boot</groupId>
14             <artifactId>spring-boot-starter-actuator</artifactId>
15             <version>2.0.6.RELEASE</version>
16         </dependency>
17     </dependencies>

 

 

application.properties的配置為:

1 server.port=18082
2 spring.application.name=service-provider
3 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848   
4 management.endpoints.web.exposure.include=*

 

其中spring.cloud.nacos.discovery.server-addr為配置的Nacos地址,management.endpoints.web.exposure.include=*表示對外暴露所有項目信息。

啟動類的代碼:

 1 package org.springframework.cloud.alibaba.cloud.examples;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 6 import org.springframework.web.bind.annotation.PathVariable;
 7 import org.springframework.web.bind.annotation.RequestMapping;
 8 import org.springframework.web.bind.annotation.RequestMethod;
 9 import org.springframework.web.bind.annotation.RequestParam;
10 import org.springframework.web.bind.annotation.RestController;
11 
12 @SpringBootApplication
13 @EnableDiscoveryClient
14 public class ProviderApplication {
15 
16     public static void main(String[] args) {
17         SpringApplication.run(ProviderApplication.class, args);
18     }
19 
20     @RestController
21     class EchoController {
22         @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
23         public String echo(@PathVariable String string) {
24             return "hello Nacos Discovery " + string;
25         }
26 
27         @RequestMapping(value = "/divide", method = RequestMethod.GET)
28         public String divide(@RequestParam Integer a, @RequestParam Integer b) {
29             return String.valueOf(a / b);
30         }
31     }
32 }

 

使用SpringCloud的原生注解@EnableDiscoveryClient 開啟服務注冊發現功能。

接下來創建服務消費方nacos-discovery-consumer進行服務消費:

pom.xml:

 1 <dependencies>
 2         <dependency>
 3             <groupId>org.springframework.boot</groupId>
 4             <artifactId>spring-boot-starter-web</artifactId>
 5             <version>2.0.6.RELEASE</version>
 6         </dependency>
 7         <dependency>
 8             <groupId>org.springframework.cloud</groupId>
 9             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
10             <version>0.2.1.RELEASE</version>
11         </dependency>
12         <dependency>
13             <groupId>org.springframework.boot</groupId>
14             <artifactId>spring-boot-starter-actuator</artifactId>
15             <version>2.0.6.RELEASE</version>
16         </dependency>
17         <dependency>
18             <groupId>org.springframework.cloud</groupId>
19             <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
20             <version>2.0.2.RELEASE</version>
21         </dependency>
22         <dependency>
23             <groupId>org.springframework.cloud</groupId>
24             <artifactId>spring-cloud-starter-openfeign</artifactId>
25             <version>2.0.2.RELEASE</version>
26         </dependency>
27         <dependency>
28             <groupId>org.springframework.cloud</groupId>
29             <artifactId>spring-cloud-alibaba-sentinel</artifactId>
30             <version>0.2.1.RELEASE</version>
31         </dependency>
32     </dependencies>

 

其中sentinel和傳統的Spring Cloud組件Hystrix類似,提供熔斷降級,系統負載保護等功能。application.properties的配置為:

 1 spring.application.name=service-consumer
 2 server.port=18083
 3 management.endpoints.web.exposure.include=*
 4 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
 5 
 6 feign.sentinel.enabled=true
 7 
 8 spring.cloud.sentinel.transport.dashboard=localhost:8080
 9 spring.cloud.sentinel.eager=true
10 
11 spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json
12 spring.cloud.sentinel.datasource.ds1.file.data-type=json
13 spring.cloud.sentinel.datasource.ds1.file.rule-type=flow

 

其中flowrule.json配置了限流降級的規則:

 1 [
 2   {
 3     "resource": "GET:http://service-provider/echo/{str}",
 4     "controlBehavior": 0,
 5     "count": 1,
 6     "grade": 1,
 7     "limitApp": "default",
 8     "strategy": 0
 9   }
10 ]

 

消費啟動類ConsumerApplication.java:

 1 package org.springframework.cloud.alibaba.cloud.examples;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.alibaba.cloud.examples.ConsumerApplication.EchoService;
 6 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 7 import org.springframework.cloud.client.loadbalancer.LoadBalanced;
 8 import org.springframework.cloud.openfeign.EnableFeignClients;
 9 import org.springframework.cloud.openfeign.FeignClient;
10 import org.springframework.context.annotation.Bean;
11 import org.springframework.web.bind.annotation.PathVariable;
12 import org.springframework.web.bind.annotation.RequestMapping;
13 import org.springframework.web.bind.annotation.RequestMethod;
14 import org.springframework.web.bind.annotation.RequestParam;
15 import org.springframework.web.client.RestTemplate;
16 
17 /**
18  * @author liujie037
19  */
20 @SpringBootApplication
21 @EnableDiscoveryClient
22 @EnableFeignClients
23 public class ConsumerApplication {
24 
25     @LoadBalanced
26     @Bean
27     public RestTemplate restTemplate() {
28         return new RestTemplate();
29     }
30 
31     public static void main(String[] args) {
32         SpringApplication.run(ConsumerApplication.class, args);
33     }
34 
35     @FeignClient(name = "service-provider", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class)
36     public interface EchoService {
37         @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
38         String echo(@PathVariable("str") String str);
39 
40         @RequestMapping(value = "/divide", method = RequestMethod.GET)
41         String divide(@RequestParam("a") Integer a, @RequestParam("b") Integer b);
42 
43         @RequestMapping(value = "/notFound", method = RequestMethod.GET)
44         String notFound();
45     }
46 
47 }
48 
49 class FeignConfiguration {
50     @Bean
51     public EchoServiceFallback echoServiceFallback() {
52         return new EchoServiceFallback();
53     }
54 }
55 
56 class EchoServiceFallback implements EchoService {
57     @Override
58     public String echo(@PathVariable("str") String str) {
59         return "echo fallback";
60     }
61 
62     @Override
63     public String divide(@RequestParam Integer a, @RequestParam Integer b) {
64         return "divide fallback";
65     }
66 
67     @Override
68     public String notFound() {
69         return "notFound fallback";
70     }
71 }

 

通過 Spring Cloud 原生注解 @EnableDiscoveryClient 開啟服務注冊發現功能。給 RestTemplate 實例添加 @LoadBalanced 注解,開啟 @LoadBalanced 與 Ribbon 的集成:

消費的接口TestController.java:

 1 package org.springframework.cloud.alibaba.cloud.examples;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.cloud.alibaba.cloud.examples.ConsumerApplication.EchoService;
 5 import org.springframework.cloud.client.discovery.DiscoveryClient;
 6 import org.springframework.web.bind.annotation.PathVariable;
 7 import org.springframework.web.bind.annotation.RequestMapping;
 8 import org.springframework.web.bind.annotation.RequestMethod;
 9 import org.springframework.web.bind.annotation.RequestParam;
10 import org.springframework.web.bind.annotation.RestController;
11 import org.springframework.web.client.RestTemplate;
12 
13 /**
14  * @author liujie037
15  */
16 @RestController
17 public class TestController {
18 
19     @Autowired
20     private RestTemplate restTemplate;
21     @Autowired
22     private EchoService echoService;
23 
24     @Autowired
25     private DiscoveryClient discoveryClient;
26 
27     @RequestMapping(value = "/echo-rest/{str}", method = RequestMethod.GET)
28     public String rest(@PathVariable String str) {
29         return restTemplate.getForObject("http://service-provider/echo/" + str,
30                 String.class);
31     }
32 
33     @RequestMapping(value = "/notFound-feign", method = RequestMethod.GET)
34     public String notFound() {
35         return echoService.notFound();
36     }
37 
38     @RequestMapping(value = "/divide-feign", method = RequestMethod.GET)
39     public String divide(@RequestParam Integer a, @RequestParam Integer b) {
40         return echoService.divide(a, b);
41     }
42 
43     @RequestMapping(value = "/echo-feign/{str}", method = RequestMethod.GET)
44     public String feign(@PathVariable String str) {
45         return echoService.echo(str);
46     }
47 
48     @RequestMapping(value = "/services/{service}", method = RequestMethod.GET)
49     public Object client(@PathVariable String service) {
50         return discoveryClient.getInstances(service);
51     }
52 
53     @RequestMapping(value = "/services", method = RequestMethod.GET)
54     public Object services() {
55         return discoveryClient.getServices();
56     }
57 }

 

訪問Nacos控制台:

 

測試服務消費正常使用postman:

 下一篇中我將繼續展示Dubbo服務創建和Spring Cloud 互相調用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM