zoukankan      html  css  js  c++  java
  • 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 互相调用。

  • 相关阅读:
    斗鱼扩展--localStorage备份与导出(九)
    斗鱼扩展--管理移除房间(八)
    斗鱼扩展--让你看到更多内容(七)
    Ubuntu18.04 安装水星1300M无线网卡
    Course1_Week1_ProgrammingHomeWork
    找出3个数中不为-1的最小数
    马拉车算法
    偏差-方差分解
    决策树如何防止过拟合
    可视化数据集两个类别变量的关系
  • 原文地址:https://www.cnblogs.com/liujie037/p/10311840.html
Copyright © 2011-2022 走看看