Dubbo的简单使用

一、架构演进   

发展演变

1.1 单一应用架构

当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。适用于小型网站,小型管理系统,将所有功能都部署到一个功能里,简单易用。

缺点

  • 性能扩展比较难
  • 协同开发问题
  • 不利于升级维护

1.2 垂直应用架构

当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。通过切分业务来实现各个模块独立部署,降低了维护和部署的难度,团队各司其职更易管理,性能扩展也更方便,更有针对性。

缺点

  • 公用模块无法重复利用,开发性的浪费

1.3 分布式服务架构

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的**分布式服务框架****(RPC)**是关键。

1.4 流动计算架构

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)[ Service Oriented Architecture]是关键。

1.5 RPC概念

概述

RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。

RPC两个核心模块

  • 通讯
  • 序列化

二、注册中心

1、概述

主流的注册中心:

  • Nacos
  • Zookeeper
  • Multicast
  • Redis
  • Simple
  • eureka

2、搭建zk集群

Zookeeper应用及原理_MG-net的博客-CSDN博客

三、RPC及Dubbo

1、什么是PRC

Rpc是一种协议,一种远程过程调用协议,规定了双方通信采用什么格式、以及数据如何传输。

  • 指明调用类或者接口
  • 指名调用的方法及参数

2、手写远程过程调用

  • 准备一个注册中心,保存服务的地址
  • 服务提供者
      • 注册到注册中心
      • 告诉注册中心服务实现类
      • 准备服务协议,根据不同情况,使用不同的协议如http等
      • 接受到参数后,通过参数进行类反射调用
      • 最后将结果返回
  • 服务消费者
      • 使用代理调用服务提供者的实现类
      1. 封装本次调用的参数,类名、方法名、参数名、参数
      2. 获取地址列表
      3. 负载均衡选择服务提供者的地址
      4. 使用指定协议进行调用

总结,RPC解决的问题:

  • 数据怎么封装
  • 数据怎么传输,适配不同的协议

3、什么的Dubbo

Apache Dubbo 是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。

4、Dubbo是怎么实现远程调用的

5、Dubbo的初体验

5.1、服务提供者配置

在zk中,可以看到相关的节点文件:

5.2 服务消费者 

服务代理的过程: 

可以看出,在dubbo中,服务提供者把提供的服务注册到注册中心中,消费者可以在像使用自己本地的Bean一样使用远程的提供者的服务。dubbo底层采用netty框架进行网络通信。 

6、Dubbo的内部结构

  • 服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
  • 服务消费者(Consumer): 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  • 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
  • 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
  1. 初始化提供者
  2. 提供者注册到注册中心
  3. 消费者订阅注册中心中的服务,并且把服务提供者的地址缓存到本地
  4. 当提供者下线的时候,注册中心需要消费者服务下线
  5. 消费者同步的调用服务
  6. 监控

四、Springboot中的dubbo

1、服务提供者

double的pom依赖:

 1   <dependency>
 2             <groupId>org.apache.dubbo</groupId>
 3             <artifactId>dubbo-spring-boot-starter</artifactId>
 4             <version>3.0.6</version>
 5         </dependency>
 6  
 7         <dependency>
 8             <groupId>org.apache.dubbo</groupId>
 9             <artifactId>dubbo-registry-zookeeper</artifactId>
10             <version>3.0.6</version>
11         </dependency>

在启动类上增加注解:@EnableDubbo

在实现类上增加注解:@DubboService

配置文件配置dubbo的相关信息:

server:
  port: 9001
dubbo:
  application:
    name: dubbo_provider
  protocol:
    name: dubbo
    port: 20882
  registry:
    address: zookeeper://127.0.0.1:2181

启动后,服务提供着就自动注册到zk中了。

2、服务消费者

引入pom依赖:

 1  <dependency>
 2             <groupId>org.apache.dubbo</groupId>
 3             <artifactId>dubbo-spring-boot-starter</artifactId>
 4             <version>3.0.6</version>
 5         </dependency>
 6  
 7         <dependency>
 8             <groupId>org.apache.dubbo</groupId>
 9             <artifactId>dubbo-registry-zookeeper</artifactId>
10             <version>3.0.6</version>
11         </dependency>

在启动类上增加注解:@EnableDubbo

注入类的时候,使用注解:@DubboReference,这个注解做了2件事

  • 订阅服务
  • 建立代理对象

配置文件中配置dubbo信息:

server:
  port: 8001
dubbo:
  application:
    name: consumer
  registry:
    address: zookeeper://127.0.0.1:2181

3、@EnableDubbo的用法

注解流程:

@EnableDubbo->@EnableDubboConfig、@DubboCompentScan

五、dubbo的用法示例

1、version版本号

服务提供者可以指定版本号,这样消费者可以调用需要指定的版本号即可。

2、指定protocol协议

dubbo协议可支持的协议:

  • rest:
  • http
  • dubbo(netty实现)
  • 自定义协议

在配置文件:

在配置文件中,可以使用 protocols 进行多协议配置。

在提供者或者消费者端配置需要使用的协议即可:

如果配置服务未指定协议,但是配置文件配置了多个协议,那么就会生成配置个数的服务,例如配置了2种协议dubbo、rest,那么就会生成2个服务一个是dubbo协议、一个是rest协议。

3、使用rest访问dubbo的服务

需要注意依赖的引入。使用到jboss的依赖

 1        <!-- Dubbo rest protocol -->
 2         <dependency>
 3             <groupId>org.jboss.resteasy</groupId>
 4             <artifactId>resteasy-jaxrs</artifactId>
 5             <version>3.15.1.Final</version>
 6         </dependency>
 7  
 8         <dependency>
 9             <groupId>javax.validation</groupId>
10             <artifactId>validation-api</artifactId>
11             <version>1.1.0.Final</version>
12         </dependency>

同时需要注意,使用服务器,tomcat 

4、消费者通过制定url连接制定服务提供着

如果服务提供者配置了多个协议,那么在启动服务的时候,就会根据不同的协议提供多个服务。

如果在消费者这边 指明消费某个服务,需要配置:

 1 @RestController
 2 @RequestMapping("site")
 3 public class SiteContorller {
 4  
 5     //订阅服务
 6     //生成代理对象
 7     @DubboReference(version = "v2", url = "dubbo://127.0.0.1:20882/com.mg.boot.api.SiteService")
 8     private SiteService siteService;
 9  
10     @GetMapping("getName")
11     public String getName(String name){
12         return siteService.getName(name);
13     }
14  
15 }

5、服务超时

服务超时,就是调用接口超过服务消费者设置的最长时间。

在dubbo中,直接使用timeout,设置服务的超时时间。

服务提供者:如果超时,会打印超时日志,并且会执行完毕。

 1 @DubboService(version = "timeout", timeout = 4000)
 2 public class TimeOutSiteSerivce implements SiteService {
 3  
 4     @Override
 5     public String getName(String name) {
 6         try {
 7             Thread.sleep(5000);
 8         }catch (Exception e){}
 9         System.out.println("服务。。。。");
10         return "timeout service " + name;
11     }
12 }

服务消费者:如果超时,则进行重试,重试失败抛出异常。

1     @DubboReference(version = "timeout", timeout = 6000)
2     private SiteService siteService;

服务提供者在任何情况下,都会执行业务逻辑。重试2次,就会执行3次(第一次是正常调用)。 

6、集群容错

@DubboReference(version = "timeout", timeout = 3000, cluster = "failover", retries = 1)

dubbo提供的集群容错方案:

  • failover(默认、推荐):当出现失败的时候,会进行重试其他的服务器,默认重试2次,会出现幂等性问题,但是可以在业务方面进行解决:
  • 方案1:把数据的业务ID作为数据库的联合主键,此时业务ID不能重复
  • 方案2:使用分布式锁来控制重复消费问题
  • failfast:当出现问题的时候,立即放回错误,不重试,通常用于非幂等操作,如新增记录
  • failsafe:当出现问题的时候,记录日志
  • failback:失败就失败,开启定时任务重发,通常用于通知操作
  • forking:并行访问多个服务器,获取一个结果既视为成功,forks=2,会浪费很多服务器资源
  • broadcast:广播所有服务提供者,逐个调用,任意一台报错则报错

结论:使用dubbo的时候,不推荐把重试关闭,在非幂等操作场景下,服务提供者需要提供幂等解决方案。

7、服务降级

所谓的服务降级,就是当达到流量高峰的时候,需要保留核心业务的使用。

mock:接口提供假数据的功能。

  • mock=force:return+null 表示消费方对该服务的方法调用都直接返回null值,不发起远程调用。用来屏蔽不重要服务不可用时,对调用放的影响。
  • mock=fail:return+null 表示消费方对该服务方法在调用失败后,在返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
1  @DubboReference(version = "timeout", timeout = 3000, mock = "fail:return timeout")
2     private SiteService siteService;

同时在接口的相同包下建立存根类,这样就会在注入接口的时候,在封装一层存根类的逻辑:

 1 public class SiteServiceStub implements SiteService {
 2  
 3     private final SiteService siteService;
 4  
 5     public SiteServiceStub(SiteService siteService) {
 6         this.siteService = siteService;
 7     }
 8  
 9  
10     @Override
11     public String getName(String name) {
12         try {
13             return siteService.getName(name);
14         }catch (Exception e){
15             return "stub" + name;
16         }
17     }
18 }

9、参数回调

消费者为服务提供者提供一个回调接口,也就是参数回调。

  • 在接口层中增加方法,增加回调参数
 1 package com.mg.boot.api;
 2  
 3 public interface SiteServiceCallBack {
 4  
 5     String siteName(String name);
 6  
 7     default String siteName(String name , String key, SiteServiceCallBackListener siteServiceCallBackListener){
 8         return null;
 9     }
10 }
  • 增加回调接口和实现类
 1  
 2 import java.io.Serializable;
 3  
 4 public class SiteServiceCallBackListenerImpl implements SiteServiceCallBackListener, Serializable {
 5     @Override
 6     public String change(String data) {
 7         System.out.println("change:" + data);
 8         return "change:" + data;
 9     }
10 }
  • 服务提供者,指明了哪个方法、第几个参数是回调参数
 1 @DubboService(version = "callback", methods = {@Method(name="siteName", arguments = {@Argument(index = 2, callback = true)})}, callbacks = 3)
 2 public class SiteServiceCallBackImpl implements SiteServiceCallBack {
 3  
 4  
 5     @Override
 6     public String siteName(String name) {
 7         return null;
 8     }
 9  
10     @Override
11     public String siteName(String name , String key, SiteServiceCallBackListener siteServiceCallBackListener){
12         siteServiceCallBackListener.change("p data");
13         return "cc:" + name;
14     }
15  
16 }
  • 服务消费者
1     @DubboReference(version = "callback")
2     private SiteServiceCallBack siteServiceCallBack;
3  
4     @GetMapping("getName")
5     public String getName(String name){
6         return siteServiceCallBack.siteName(name, "key",  new SiteServiceCallBackListenerImpl());
7     }

10、异步调用

主要使用 CompletableFuture 接口进行一步数据的返回。

1  @Override
2     public CompletableFuture<String> siteNameAsync(String name){
3         System.out.println("data:" + name);
4         return CompletableFuture.supplyAsync(()->{
5             return siteName(name);
6         });
7     }

六、dubbo的负载均衡策略

官方提供的集中负载均衡的策略:

  • 随机
  • 轮训
  • 一致性hash:相同参数的请求,总是发送到一个提供者
  • 最少活跃调用数:就是处理慢的提供者(服务配置低)接收到少的请求,根据调用的计数差
    • 消费者在本地缓存所有的服务提供者
    • 调用的时候,会选择本地所有服务提供者中active最小的
    • 选定该服务提供者和后,并对其active+1
    • 完成服务后,对其active-1
    • 当active越大,则证明该服务提供者处理性能越差

七、安装Dubbo admin监管平台

可以使用本地安装,也可以使用docker安装。这里介绍受用docker安装方式。

docker run --name dubbo-admin -d -p 7001:7001 dubbo-admin:1.0

在admin中,可以配置相关权重、方法路由规则等等。 

八、Dubbo的SPI可扩展机制

1、java中的sip机制

典型的使用sip,就是jdbc。java内部之约定了jdbc的接口,并没有提供具体实现。当需要连接mysql数据库的时候,就需要mysql实现这套jdbc的接口。这就是java中的sip机制。

如mysql jdbc:

java的DriverManager就会读取这个文件,获取实现类的全路径。

简单理解就是定义一套规范,实现来自不同的供应者,当需要使用哪个供应者的相关内容,引入即可。 

2、sip机制的缺点

自己实现的时候,就使用SeviceLoader 进行加载 配置文件中配置的实现类。 

可以看出java中sip存在,当配置多个实现类的时候,使用ServiceLoader可以获取多个,没有办法指定某一个,不够灵活。

3、dubbo中的sip机制

可以看到,在dubbo中,可以指明需要哪个实现类。

在dubbo中,有很多地方使用spi,例如服务调用协议的部分,根据配置文件中的配置,进行协议的使用。

同时,还可以在spi实现类中实现AOP的效果,其实就是在实现代码的时候,在前面或者后面增加相关功能。

九、Dubbo源码分析

1、Dubbo服务调用过程

a、服务消费者获取代理对象

b、 在注册中心获取服务集群(缓存在消费者本地的地址列表),使用ZookeeperRegistry

c、使用服务器集群中的Invoker调用器,Invoker封装了一次调用流程的整体内容

d、参数、方法名称等封装在RpcInvcation中,使用DubboInvoker进行调用

2、关于DubboInvoker的装饰

通过源码可以看出,Invoker调用采用的是装饰着模式,每一层封装不同的功能。

  • DubboInvoker:使用dubbo协议调用提供者
  • AsyncToSyncInvoker:把异步转换成同步
  • ProtocolFilterInvocker:在调用过程中,提供了大量的功能如监控中心发送调用数据、TSP限流等等,所以主要负责维护过滤器链
  • ListenerInvocker:负责监听链,提供业务回调窗口

3、权重轮训算法

权重轮训就是根据权重去轮训相关内容,例如A、B、C三台权重6、2、2,那调用应该是ABACA的顺序。

 

十、多种配置方式

在入门实例的时候,我们使用的是 xml 配置的方式,对 dubbo 的环境进行了配置,但是,官方还提供了其他的配置方式,这里我们也一一分解。

10.1 API配置方式

这种方式其实官方是不太推荐的,官方推荐使用 xml 配置的方式,但是,在有的时候测试的时候,还是可以用的到的,另外,为了保证完整性,这些内容还是有必要讲讲的。

首先还是回到服务端工程。

服务端

这里我们使用 api 的方式配置,所以,provider.xml 这个配置文件就暂时不需要了,我们只需要在上面的 AppApi 这个类中的 main 方法中用 api配置及启动即可。

 1 package com.sihai.dubbo.provider;
 2 
 3 import com.alibaba.dubbo.config.ApplicationConfig;
 4 import com.alibaba.dubbo.config.ProtocolConfig;
 5 import com.alibaba.dubbo.config.RegistryConfig;
 6 import com.alibaba.dubbo.config.ServiceConfig;
 7 import com.sihai.dubbo.provider.service.ProviderService;
 8 import com.sihai.dubbo.provider.service.ProviderServiceImpl;
 9 
10 import java.io.IOException;
11 
12 /**
13  * Api方式启动
14  * api的方式调用不需要其他的配置,只需要下面的代码即可。
15  * 但是需要注意,官方建议:
16  * Api方式用于测试用例使用,推荐xml的方式
17  */
18 public class AppApi
19 {
20     public static void main( String[] args ) throws IOException {
21 
22         // 服务实现
23         ProviderService providerService = new ProviderServiceImpl();
24 
25         // 当前应用配置
26         ApplicationConfig application = new ApplicationConfig();
27         application.setName("provider");
28         application.setOwner("sihai");
29 
30         // 连接注册中心配置
31         RegistryConfig registry = new RegistryConfig();
32         registry.setAddress("zookeeper://localhost:2181");
33 //        registry.setUsername("aaa");
34 //        registry.setPassword("bbb");
35 
36         // 服务提供者协议配置
37         ProtocolConfig protocol = new ProtocolConfig();
38         protocol.setName("dubbo");
39         protocol.setPort(20880);
40         //protocol.setThreads(200);
41 
42         // 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口
43 
44         // 服务提供者暴露服务配置
45         ServiceConfig<ProviderService> service = new ServiceConfig<ProviderService>(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
46         service.setApplication(application);
47         service.setRegistry(registry); // 多个注册中心可以用setRegistries()
48         service.setProtocol(protocol); // 多个协议可以用setProtocols()
49         service.setInterface(ProviderService.class);
50         service.setRef(providerService);
51         service.setVersion("1.0.0");
52 
53         // 暴露及注册服务
54         service.export();
55     }
56 }

分析

看到上面的代码是不是云里雾里,不要慌,我们通过对照 xml 的方式分析一下。

registry 的 xml 方式
<dubbo:registry protocol="zookeeper" address="localhost:2181"/>
API 的方式
1 RegistryConfig registry = new RegistryConfig();
2 registry.setAddress("zookeeper://localhost:2181");

dubbo:registry节点对应RegistryConfig ,xml 的属性对应 API 方式用 set 方法就可以了。对比之下,你就会发现,如果 API 的方式不熟悉,可以对照 xml 配置方式就可以。

其他 API

org.apache.dubbo.config.ServiceConfig
org.apache.dubbo.config.ReferenceConfig
org.apache.dubbo.config.ProtocolConfig
org.apache.dubbo.config.RegistryConfig
org.apache.dubbo.config.MonitorConfig
org.apache.dubbo.config.ApplicationConfig
org.apache.dubbo.config.ModuleConfig
org.apache.dubbo.config.ProviderConfig
org.apache.dubbo.config.ConsumerConfig
org.apache.dubbo.config.MethodConfig
org.apache.dubbo.config.ArgumentConfig

我们再看看我配置的消费端的 Api 方式。

消费端

同样,我们不需要 consumer.xml 配置文件了,只需要在 main 方法中启动即可。

 1 package com.sihai.dubbo.consumer;
 2 
 3 import com.alibaba.dubbo.config.ApplicationConfig;
 4 import com.alibaba.dubbo.config.ReferenceConfig;
 5 import com.alibaba.dubbo.config.RegistryConfig;
 6 import com.sihai.dubbo.provider.service.ProviderService;
 7 
 8 /**
 9  * api的方式调用
10  * api的方式调用不需要其他的配置,只需要下面的代码即可。
11  * 但是需要注意,官方建议:
12  * Api方式用于测试用例使用,推荐xml的方式
13  */
14 public class AppApi {
15 
16     public static void main(String[] args) {
17         // 当前应用配置
18         ApplicationConfig application = new ApplicationConfig();
19         application.setName("consumer");
20         application.setOwner("sihai");
21 
22         // 连接注册中心配置
23         RegistryConfig registry = new RegistryConfig();
24         registry.setAddress("zookeeper://localhost:2181");
25 
26         // 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接
27 
28         // 引用远程服务
29         ReferenceConfig<ProviderService> reference = new ReferenceConfig<ProviderService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
30         reference.setApplication(application);
31         reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
32         reference.setInterface(ProviderService.class);
33 
34         // 和本地bean一样使用xxxService
35         ProviderService providerService = reference.get(); // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用
36         providerService.SayHello("hello dubbo! I am sihai!");
37     }
38 }

这部分的 API 配置的方式就到这了,注意:官方推荐 xml 的配置方法。

5.2 注解配置方式

注解配置方式还是需要了解一下的,现在微服务都倾向于这种方式,这也是以后发展的趋势,0 配置应该是这几年的趋势。

那么如何对 dubbo 使用注解的方式呢?我们先看服务端。

服务端

 

第一步:定义接口及实现类,在上面的截图中的 annotation 包下

1 package com.sihai.dubbo.provider.service.annotation;
2 
3 /**
4  * 注解方式接口
5  */
6 public interface ProviderServiceAnnotation {
7     String SayHelloAnnotation(String word);
8 }
 1 package com.sihai.dubbo.provider.service.annotation;
 2 
 3 import com.alibaba.dubbo.config.annotation.Service;
 4 
 5 /**
 6  * 注解方式实现类
 7  */
 8 @Service(timeout = 5000)
 9 public class ProviderServiceImplAnnotation implements ProviderServiceAnnotation{
10 
11     public String SayHelloAnnotation(String word) {
12         return word;
13     }
14 }

@Service

@Service 用来配置 Dubbo 的服务提供方。

第二步:组装服务提供方。通过 Spring 中 Java Config 的技术(@Configuration)和 annotation 扫描(@EnableDubbo)来发现、组装、并向外提供 Dubbo 的服务。

 1 package com.sihai.dubbo.provider.configuration;
 2 
 3 import com.alibaba.dubbo.config.ApplicationConfig;
 4 import com.alibaba.dubbo.config.ProtocolConfig;
 5 import com.alibaba.dubbo.config.ProviderConfig;
 6 import com.alibaba.dubbo.config.RegistryConfig;
 7 import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
 8 import org.springframework.context.annotation.Bean;
 9 import org.springframework.context.annotation.Configuration;
10 
11 /**
12  * 注解方式配置
13  */
14 @Configuration
15 @EnableDubbo(scanBasePackages = "com.sihai.dubbo.provider.service.annotation")
16 public class DubboConfiguration {
17 
18     @Bean // #1 服务提供者信息配置
19     public ProviderConfig providerConfig() {
20         ProviderConfig providerConfig = new ProviderConfig();
21         providerConfig.setTimeout(1000);
22         return providerConfig;
23     }
24 
25     @Bean // #2 分布式应用信息配置
26     public ApplicationConfig applicationConfig() {
27         ApplicationConfig applicationConfig = new ApplicationConfig();
28         applicationConfig.setName("dubbo-annotation-provider");
29         return applicationConfig;
30     }
31 
32     @Bean // #3 注册中心信息配置
33     public RegistryConfig registryConfig() {
34         RegistryConfig registryConfig = new RegistryConfig();
35         registryConfig.setProtocol("zookeeper");
36         registryConfig.setAddress("localhost");
37         registryConfig.setPort(2181);
38         return registryConfig;
39     }
40 
41     @Bean // #4 使用协议配置,这里使用 dubbo
42     public ProtocolConfig protocolConfig() {
43         ProtocolConfig protocolConfig = new ProtocolConfig();
44         protocolConfig.setName("dubbo");
45         protocolConfig.setPort(20880);
46         return protocolConfig;
47     }
48 }

分析

  • 通过 @EnableDubbo 指定在com.sihai.dubbo.provider.service.annotation 下扫描所有标注有 @Service 的类
  • 通过 @Configuration 将 DubboConfiguration 中所有的 @Bean 通过 Java Config 的方式组装出来并注入给 Dubbo 服务,也就是标注有 @Service 的类。这其中就包括了:

    • ProviderConfig:服务提供方配置
    • ApplicationConfig:应用配置
    • RegistryConfig:注册中心配置
    • ProtocolConfig:协议配置

看起来很复杂,其实。。。

第三步:启动服务

 1 package com.sihai.dubbo.provider;
 2 
 3 import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan;
 4 import com.sihai.dubbo.provider.configuration.DubboConfiguration;
 5 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 6 import sun.applet.Main;
 7 
 8 import java.io.IOException;
 9 
10 /**
11  * 注解启动方式
12  */
13 public class AppAnnotation {
14 
15     public static void main(String[] args) throws IOException {
16         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DubboConfiguration.class); 
17         context.start();
18         System.in.read(); 
19     }
20 }

发现输出下面信息就表示 success 了。

消费端

同样我们下看看消费端的工程,有一个感性认识。

第一步:引用服务

 1 package com.sihai.dubbo.consumer.Annotation;
 2 
 3 import com.alibaba.dubbo.config.annotation.Reference;
 4 import com.sihai.dubbo.provider.service.annotation.ProviderServiceAnnotation;
 5 import org.springframework.stereotype.Component;
 6 
 7 /**
 8  * 注解方式的service
 9  */
10 @Component("annotatedConsumer")
11 public class ConsumerAnnotationService {
12 
13     @Reference
14     private ProviderServiceAnnotation providerServiceAnnotation;
15 
16     public String doSayHello(String name) {
17         return providerServiceAnnotation.SayHelloAnnotation(name);
18     }
19 }

在 ConsumerAnnotationService 类中,通过 @Reference 引用服务端提供的类,然后通过方法调用这个类的方式,给消费端提供接口。
注意:如果这里找不到 ProviderServiceAnnotation 类,请在服务端先把服务端工程用 Maven intall 一下,然后将服务端的依赖放到消费端的 pom 中。如下:

<dependency>
          <groupId>com.ouyangsihai</groupId>
            <artifactId>dubbo-provider</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

第二步:组装服务消费者
这一步和服务端是类似的,这里就不在重复了。

 1 package com.sihai.dubbo.consumer.configuration;
 2 
 3 import com.alibaba.dubbo.config.ApplicationConfig;
 4 import com.alibaba.dubbo.config.ConsumerConfig;
 5 import com.alibaba.dubbo.config.RegistryConfig;
 6 import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
 7 import org.springframework.context.annotation.Bean;
 8 import org.springframework.context.annotation.ComponentScan;
 9 import org.springframework.context.annotation.Configuration;
10 
11 import java.util.HashMap;
12 import java.util.Map;
13 
14 /**
15  * 注解配置类
16  */
17 @Configuration
18 @EnableDubbo(scanBasePackages = "com.sihai.dubbo.consumer.Annotation")
19 @ComponentScan(value = {"com.sihai.dubbo.consumer.Annotation"})
20 public class ConsumerConfiguration {
21     @Bean // 应用配置
22     public ApplicationConfig applicationConfig() {
23         ApplicationConfig applicationConfig = new ApplicationConfig();
24         applicationConfig.setName("dubbo-annotation-consumer");
25         Map<String, String> stringStringMap = new HashMap<String, String>();
26         stringStringMap.put("qos.enable","true");
27         stringStringMap.put("qos.accept.foreign.ip","false");
28         stringStringMap.put("qos.port","33333");
29         applicationConfig.setParameters(stringStringMap);
30         return applicationConfig;
31     }
32 
33     @Bean // 服务消费者配置
34     public ConsumerConfig consumerConfig() {
35         ConsumerConfig consumerConfig = new ConsumerConfig();
36         consumerConfig.setTimeout(3000);
37         return consumerConfig;
38     }
39 
40     @Bean // 配置注册中心
41     public RegistryConfig registryConfig() {
42         RegistryConfig registryConfig = new RegistryConfig();
43         registryConfig.setProtocol("zookeeper");
44         registryConfig.setAddress("localhost");
45         registryConfig.setPort(2181);
46         return registryConfig;
47     }
48 }

第三步:发起远程调用

在 main 方法中通过启动一个 Spring Context,从其中查找到组装好的 Dubbo 的服务消费者,并发起一次远程调用。

 1 package com.sihai.dubbo.consumer;
 2 
 3 import com.sihai.dubbo.consumer.Annotation.ConsumerAnnotationService;
 4 import com.sihai.dubbo.consumer.configuration.ConsumerConfiguration;
 5 import com.sihai.dubbo.provider.service.ProviderService;
 6 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 7 import org.springframework.context.support.ClassPathXmlApplicationContext;
 8 
 9 import java.io.IOException;
10 
11 /**
12  * 注解方式启动
13  *
14  */
15 public class AppAnnotation
16 {
17     public static void main( String[] args ) throws IOException {
18 
19         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); 
20         context.start(); // 启动
21         ConsumerAnnotationService consumerAnnotationService = context.getBean(ConsumerAnnotationService.class); 
22         String hello = consumerAnnotationService.doSayHello("annotation"); // 调用方法
23         System.out.println("result: " + hello); // 输出结果
24 
25     }
26 }

结果

 

 

参考:

https://blog.csdn.net/liming0025/article/details/123785898

https://blog.csdn.net/qq_44292366/article/details/124744901

https://segmentfault.com/a/1190000019896723

https://juejin.cn/post/6949558189733969951

posted @ 2022-10-21 16:44  Boblim  阅读(229)  评论(0编辑  收藏  举报