欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Cloud和Dubbo整合开发笔记(1)

程序员文章站 2022-05-14 11:00:56
一、需求背景: 二、Dubbo和Spring Cloud 的比较 首先Dubbo是一个分布式服务框架,以及SOA治理方案。它的功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等,它是著名的阿里服务治理的核心框架。Spring Cloud更加关心为开发人 ......

一、需求背景:

  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。:

Spring Cloud和Dubbo整合开发笔记(1)

相比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控制台:

Spring Cloud和Dubbo整合开发笔记(1)

 

测试服务消费正常使用postman:

Spring Cloud和Dubbo整合开发笔记(1)

 下一篇中我将继续展示dubbo服务创建和spring cloud 互相调用。