美文网首页
Dubbo基础(未完待续)

Dubbo基础(未完待续)

作者: 笔记本一号 | 来源:发表于2020-09-29 03:52 被阅读0次

    不多BB,开门见山
    这是阿里巴巴的官方中文文档:https://dubbo.gitbooks.io/dubbo-user-book/content/preface/background.html

    image.png
    节点说明
    image.png

    调用关系说明

    1、Container负责启动,加载,运行Provider。
    2、Provider在启动时,向注册中心注册自己提供的服务。
    3、Consumer在启动时,向注册中心订阅自己所需的服务。
    4、注册中心返回服务提供者地址列表给Consumer,如果有变更,注册中心将基于长连接推送变更数据给Consumer。
    5、Consumer,从Provider地址列表中,基于软负载均衡算法,选一台Provider进行调用,如果调用失败,再选另一台调用。
    6、Consumer和Provider,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到Monitor。

    Dubbo的特性

    Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。

    连通性

    • 注册中心负责服务地址的注册与查找,相当于目录服务,Provider和Consumer只在启动时与注册中心交互,注册中心不转发请求,压力较小
    • 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
    • Provider向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销
    • Consumer向注册中心获取Provider地址列表,并根据负载算法直接调用Provider,同时汇报调用时间到监控中心,此时间包含网络开销
    • 注册中心,Provider,Consumer三者之间均为长连接,监控中心除外
    • 注册中心通过长连接感知Provider的存在,Provider宕机,注册中心将立即推送事件通知Consumer
    • 注册中心和监控中心全部宕机,不影响已运行的Provider和Consumer,Consumer在本地缓存了Provider列表
    • 注册中心和监控中心都是可选的,Consumer可以直连Provider

    健状性

    • 监控中心宕掉不影响使用,只是丢失部分采样数据
    • 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
    • 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
    • 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
    • Provider无状态,任意一台宕掉后,不影响使用
    • Provider全部宕掉后,Consumer应用将无法使用,并无限次重连等待Provider恢复

    伸缩性

    • 注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心
    • Provider无状态,可动态增加机器部署实例,注册中心将推送新的Provider信息给Consumer

    演示:

    Spring版本的配置

    image.png

    Provider

    image.png
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:aop="http://www.springframework.org/schema/aop"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
            xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
            http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
            ">
    
        <!-- 提供方应用信息,用于计算依赖关系 -->
        <dubbo:application name="hello-world-app"  />
    
        <!-- 用dubbo协议在20880端口暴露服务 -->
        <dubbo:protocol name="dubbo" port="20880" />
    
        <!-- 使用zookeeper进行注册中心化 -->
    <!-- 
    protocol:协议,这里是zookeeper协议
         -->
        <dubbo:registry protocol="zookeeper"
                        address="192.168.200.131:2181,192.168.200.130:2181,192.168.200.132:2181"/>
    
    
        <!-- 和本地bean一样实现服务 -->
        <bean id="quickStartService" class="com.learn.test.dubbo.quickstart.QuickStartServiceImpl" />
    
        <!-- 声明需要暴露的服务接口 -->
     <!-- 声明需要暴露的服务接口  写操作可以设置
             retries :被调用接口超时时重复调用次数,默认是三次
             interface:接口
             ref:实现类
             group:当接口多实现时,可区分具体是哪个实现类
             loadbalance:负载均衡算法
             async:异步调用
            register:需注册到哪一个注册中心,如果缺省,则默认向所有注册中心注册
         -->
        <dubbo:service
                timeout="10000"
                async="true"
                interface="com.learn.test.dubbo.ServiceAPI"
                ref="quickStartService"/>
    <!-- N/A代表不需要注册中,采用直连方式 -->
        <!-- registry="N/A" -->
    
    </beans>
    

    Consumer

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:aop="http://www.springframework.org/schema/aop"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
            xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
            http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
            ">
    
        <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
        <dubbo:application name="consumer-of-helloworld-app"  />
    
        <!-- 使用zookeeper进行注册中心化 -->
        <dubbo:registry protocol="zookeeper"
                        address="192.168.200.131:2181,192.168.200.130:2181,192.168.200.132:2181"/>
        <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
        <dubbo:reference
                id="consumerService"
                interface="com.learn.test.dubbo.ServiceAPI"/>
    </beans>
    

    调用

    public class ConsumerClient {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-hello-consumer.xml");
            context.start();
            while (true){
                Scanner scanner = new Scanner(System.in);
                String message = scanner.next();
                // 获取接口
                ServiceAPI serviceAPI = (ServiceAPI)context.getBean("consumerService");
                // 测试异步调用
                long beginTime = System.currentTimeMillis();
                serviceAPI.sendMessage(message);
         
            }
        }
    
    }
    

    SpringBoot版本的配置

    Provider

    image.png
    spring:
      application:
        name: dubbo-spring-boot-starter
      dubbo:
        server: true
        registry:
          protocol: zookeeper
          address: 192.168.200.131:2181,192.168.200.130:2181,192.168.200.132:2181
    

    提供的接口

    import com.alibaba.dubbo.config.annotation.Service;
    @Component
    @Service(interfaceClass = ServiceAPI.class)
    public class QuickstartServiceImpl implements ServiceAPI {
    
        @Override
        public String sendMessage(String message) {
            System.out.println(message);
            return "quickstart-provider-message="+message;
        }
    }
    

    启动

    @SpringBootApplication
    @EnableDubboConfiguration
    public class PrivoderApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(PrivoderApplication.class, args);
        }
    }
    

    Consumer

    image.png
    spring:
      application:
        name: dubbo-spring-boot-starter
      dubbo:
        server: true
        registry:
          protocol: zookeeper
          address: 192.168.200.131:2181,192.168.200.130:2181,192.168.200.132:2181
    

    调用的接口

    @Component
    public class QuickstartConsumer {
    
        @Reference(interfaceClass = ServiceAPI.class)
        ServiceAPI serviceAPI;
    
        public void sendMessage(String message){
            System.out.println(serviceAPI.sendMessage(message));
        }
    
    }
    

    启动

    @SpringBootApplication
    @EnableDubboConfiguration
    public class ConsumerApplication {
    
        public static void main(String[] args) {
    
            ConfigurableApplicationContext run =
                    SpringApplication.run(ConsumerApplication.class, args);
    
            QuickstartConsumer quickstartConsumer = (QuickstartConsumer)run.getBean("quickstartConsumer");
    
            quickstartConsumer.sendMessage("dubbo是好东私");
    
        }
    }
    

    启动项目就可以看到注册中心相应的节点了

    image.png

    启动检查check

    Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"
    可以通过 check="false" 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动的情况下可以关闭了。

    另外,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果 check="false",总是会返回引用,当服务恢复时,能自动连上。
    spring

    <dubbo:reference  check="false"
                id="consumerService"
                interface="com.learn.test.dubbo.ServiceAPI"/>
    

    或者是springboot

      @Reference(interfaceClass = ServiceAPI.class,check = false)
        ServiceAPI serviceAPI;
    

    负载均衡策略

    在集群负载均衡时,Dubbo 提供了多种均衡策略,默认为 Random 随机调用。

    Random

    随机,按权重设置随机概率。
    在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

    RoundRobin

    轮循,按公约后的权重设置轮循比率。
    存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

    LeastActive

    最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
    使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

    ConsistentHash

    一致性 Hash,相同参数的请求总是发到同一提供者。
    当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

    配置

    配置Spring版的:

    Provider

    <dubbo:service interface="..." loadbalance="roundrobin" />
    

    Consumer

    <dubbo:reference interface="..." loadbalance="roundrobin" />
    

    Provider

    <dubbo:service interface="...">
        <dubbo:method name="..." loadbalance="roundrobin"/>
    </dubbo:service>
    

    Consumer

    <dubbo:reference interface="...">
        <dubbo:method name="..." loadbalance="roundrobin"/>
    </dubbo:reference>
    
    配置SpringBoot版

    Consumer

        @Reference(interfaceClass = ServiceAPI.class,loadbalance = "roundrobin")
        ServiceAPI serviceAPI;
    

    协议

    Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。

    列举几个 Dubbo多协议

    SpringBoot版

    配置

    spring:
      application:
        name: dubbo-spring-boot-starter
      dubbo:
        server: true
        registry:
          protocol: rmi/*协议在这里改*/
          address: 192.168.200.131:2181,192.168.200.130:2181,192.168.200.132:2181
    

    本地存根

    远程服务后,客户端通常只是调用一下生产端的接口,而接口实现全都在生产者端,但生产者端有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后服务降级等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub 然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。

    本地存根需要Stub类的有参构造函数,把Provider端提供的接口传入,本地存根的原理就是强制让Consumer先走Stub类对应的实现接口进行逻辑处理

    image.png

    演示

    Provider

    创建Stub类

    public class Mystub implements ServiceAPI {
    
        private ServiceAPI serviceAPI;
    
        public Mystub(ServiceAPI serviceAPI) {
            this.serviceAPI = serviceAPI;
        }
    
        @Override
        public String sendMessage(String message) {
            if ("tzb".equals(message)){
                return this.serviceAPI.sendMessage(message);
            }else {
                System.out.println("符合规则,降级");
                return "符合规则,降级";
            }
        }
    }
    
    
    public class QuickStartServiceImpl implements ServiceAPI {
    
        @Override
        public String sendMessage(String message) {
            System.out.println("message="+message);
            return "quickstart-provider-message="+message;
        }
    }
    
    <!-- 提供方应用信息,用于计算依赖关系 -->
        <dubbo:application name="hello-world-app"  />
    
        <!-- 用dubbo协议在20880端口暴露服务 -->
        <dubbo:protocol name="dubbo" port="20883" />
    
        <!-- 使用zookeeper进行注册中心化 -->
        <dubbo:registry protocol="zookeeper"
                        address="192.168.200.131:2181,192.168.200.130:2181,192.168.200.132:2181"/>
        <!-- 和本地bean一样实现服务 -->
        <bean id="quickStartService" class="com.learn.test.dubbo.quickstart.QuickStartServiceImpl" />
    
        <!-- 声明需要暴露的服务接口 -->
        <dubbo:service
                stub="com.learn.test.dubbo.stub.Mystub" 
                interface="com.learn.test.dubbo.ServiceAPI"
                ref="quickStartService"
        />
    
    Consumer

    在消费者端需要从生产者端拷贝一份Stub类,然后修改一下逻辑,其他不变

    public class Mystub implements ServiceAPI {
        private ServiceAPI serviceAPI;
        public Mystub(ServiceAPI serviceAPI) {
            this.serviceAPI = serviceAPI;
        }
        @Override
        public String sendMessage(String message) {
            if ("tzb".equals(message)){
                System.out.println("这里是Consumer端");
                return this.serviceAPI.sendMessage(message);
            }else {
                System.out.println("这里是Consumer端");
                System.out.println("符合规则,降级或者是参数验证");
            }
            return null;
        }
    }
    

    我们看到本地存根,虽然是远程调用dubbo的接口,但是先走的是消费者的Stub类,通过Stub类进行一些逻辑处理在调用远程调用dubbo的接口,起到了

    consumer image.png

    本地伪装(服务降级神器)

    这个是本地存根的子集,调用这个需要Mock类的空参构造函数,如果Provider的接口出现异常就会走Consumer端的Mock类,本地伪装通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。

    本地伪装的原理就是在发生Provider的接口发生异常时,强制使Consumer端的走Mock类对应的实现接口,从而达到服务器的降级

    本地伪装和本地存根不同的地方就在于,本地存根是会拦截所有的对应接口对应的方法,而本地伪装只是在异常时才去拦截对应接口对应的方法

    演示

    Provider

    mock类(说白了就是服务降级的处理类)

    //关于ServiceAPI的服务降级
    public class Mymock implements ServiceAPI {
    //ServiceAPI类的sendMessage方法异常或者超时就触发服务降级
        @Override
        public String sendMessage(String message) {
            System.out.println("服务降级被触发了");
            return "mock服务降级";
        }
    
        @Override
        public String sendMessage02(String message) {
            return null;
        }
    }
    

    我给被调用的方法设置一个超时,2秒肯定超时了的,看看超时会不会触发降级

    public class QuickStartServiceImpl implements ServiceAPI {
    
        @Override
        public String sendMessage(String message) {
            System.out.println("message="+message);
    
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            return "quickstart-provider-message="+message;
        }
    ........
    }
    
    <!-- 提供方应用信息,用于计算依赖关系 -->
        <dubbo:application name="hello-world-app"  />
    
        <!-- 用dubbo协议在20880端口暴露服务 -->
        <dubbo:protocol name="dubbo" port="20883" />
    
        <!-- 使用zookeeper进行注册中心化 -->
        <dubbo:registry protocol="zookeeper"
                        address="192.168.200.131:2181,192.168.200.130:2181,192.168.200.132:2181"/>
        <!-- 和本地bean一样实现服务 -->
        <bean id="quickStartService" class="com.learn.test.dubbo.quickstart.QuickStartServiceImpl" />
    
        <!-- 配置服务降级 -->
        <dubbo:service
                mock="com.learn.test.dubbo.mock.Mymock"
                interface="com.learn.test.dubbo.ServiceAPI"
                ref="quickStartService"
        />
    
    Consumer

    在消费者端需要从生产者端拷贝一份Mock类

    public class Mymock implements ServiceAPI {
        @Override
        public String sendMessage(String message) {
            System.out.println("这里是Consumer端");
            System.out.println("Provider端出现异常,降级!!!");
            return message;
        }
    }
    
        <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
        <dubbo:application name="consumer-of-helloworld-app"  />
    
        <!-- 使用zookeeper进行注册中心化 -->
        <dubbo:registry protocol="zookeeper"
                        address="192.168.200.131:2181,192.168.200.130:2181,192.168.200.132:2181"/>
        <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
        <dubbo:reference
                id="consumerService"
                interface="com.learn.test.dubbo.ServiceAPI"
                mock="com.learn.test.dubbo.mock.Mymock"
        />
    
    image.png

    相关文章

      网友评论

          本文标题:Dubbo基础(未完待续)

          本文链接:https://www.haomeiwen.com/subject/pajjuktx.html