zoukankan      html  css  js  c++  java
  • org.apache.dubbo 2.7.x 再聚首

    Dubbo 版本 :

      Dubbo 社区目前主力维护的有 2.6.x 和 2.7.x 两大版本,其中,

    • 2.6.x 主要以 bugfix 和少量 enhancements 为主,因此能完全保证稳定性
    • 2.7.x 作为社区的主要开发版本,得到持续更新并增加了大量新 feature 和优化,同时也带来了一些稳定性挑战

      版本更多信息请参考官网。相信小伙伴们对于Dubbo 都有一定的了解。相关基础知识点可以参考 https://www.cnblogs.com/wuzhenzhao/p/10008824.html .

    Dubbo 再聚首之自动化配置:

    dubbo-spring-boot-starter(org.apache.dubbo:2.7.7):

      基于目前的 Spring Boot 自动化配置的盛行,我们在使用 Dubbo的时候不再像以前集成 spring 的时候那样的繁琐,需要进行很多的配置。接下来来体验一下 dubbo-spring-boot-starter 带来的便捷。

      本文注册中心采用 Spring  Cloud  Alibaba  Nacos ,不熟悉的小伙伴可以参考 https://www.cnblogs.com/wuzhenzhao/category/1530796.html

    项目目录:

      springboot-dubbo-api 模块:

    1.构建服务接口,api模块,导入 Rest 协议支持依赖:

    <dependencies>
            <!--添加REST支持-->
            <!--Rest协议-->
            <dependency>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-jaxrs</artifactId>
                <version>3.8.1.Final</version>
            </dependency>
            <dependency>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-client</artifactId>
                <version>4.0.0.Final</version>
            </dependency>
            <dependency>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-server</artifactId>
                <version>9.4.12.RC2</version>
            </dependency>
            <dependency>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-servlet</artifactId>
                <version>9.4.12.RC2</version>
            </dependency>
        </dependencies>

    2. 创建接口 :

    @Path("/")
    public interface HelloService {
        
        @GET
        @Path("/sayRest")
        String sayHello() throws Exception;
    }

      springboot-dubbo-provider 模块:

    1.导入依赖:

    <dependencies>
            <!--基于spring-boot-dependencies 2.3.0RELEASE 版本 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!-- dubbo 依赖-->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>2.7.7</version>
            </dependency>
            <!--nacos注册中心依赖-->
            <dependency>
                <groupId>com.alibaba.nacos</groupId>
                <artifactId>nacos-client</artifactId>
                <version>1.2.1</version>
            </dependency>
            <!--zk注册中心依赖-->
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-recipes</artifactId>
                <version>4.0.1</version>
            </dependency>
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-framework</artifactId>
                <version>4.0.1</version>
            </dependency>
            <dependency>
                <artifactId>springboot-dubbo-api</artifactId>
                <groupId>com.wuzz.demo</groupId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>

    3. 创建服务实现类 :

    @DubboService(loadbalance = "random", // 负载均衡
            timeout = 50000, //超时
            cluster = "failsafe", // 服务容错
            protocol = {"dubbo", "rest"}, //多协议支持
            registry = {"hangzhou", "wenzhou"} //多注册中心
    )
    public class HelloServiceImpl implements HelloService {
        @Override
        public String sayHello() throws Exception {
            return "Hello Dubbo";
        }
    }

    4. 配置文件配置

    spring.application.name=springboot-dubbo
    
    # Netty ->
    dubbo.protocols.dubbo.name=dubbo
    dubbo.protocols.dubbo.port=-1
    
    # jetty (配置了rest协议)
    dubbo.protocols.rest.name=rest
    dubbo.protocols.rest.port=-1
    dubbo.protocols.rest.server=jetty
    
    # zk注册中心
    dubbo.registries.hangzhou.address=zookeeper://192.168.1.101:2181
    dubbo.registries.hangzhou.timeout=10000
    dubbo.registries.hangzhou.default=true
    ## 服务启动的时候,如果注册中心有问题,那么服务就启动失败
    dubbo.registries.hangzhou.check=false
    # nacos 注册中心
    dubbo.registries.wenzhou.address=nacos://localhost:8848

    5. 服务启动类,配置扫描路径

    @DubboComponentScan(basePackages = "com.wuzz.demo") //dubbo服务扫描
    @SpringBootApplication
    public class SpringBootDubboProviderApp {
    
        private final static Logger log = LoggerFactory.getLogger(SpringBootDubboProviderApp.class);
    
        public static void main(String[] args) {
            SpringApplication.run(SpringBootDubboProviderApp.class, args);
            log.info("服务启动成功");
    
        }
    }

      springboot-dubbo-client 模块:

    1.导入相关依赖:

    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>2.7.7</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.nacos</groupId>
                <artifactId>nacos-client</artifactId>
                <version>1.2.1</version>
            </dependency>
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-recipes</artifactId>
                <version>4.0.1</version>
            </dependency>
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-framework</artifactId>
                <version>4.0.1</version>
            </dependency>
            <dependency>
                <artifactId>springboot-dubbo-api</artifactId>
                <groupId>com.wuzz.demo</groupId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>

    2. 创建测试类 :

    @RestController
    public class DubboController {
        //Dubbo提供的注解
        @DubboReference(loadbalance = "roundrobin",
                  timeout = 9000, cluster = "failfast",
                  mock = "com.wuzz.demo.mock.HelloServiceMock", check = false)
        HelloService helloService;
    
        @GetMapping("/sayhello")
        public String sayHello() throws Exception {
            return helloService.sayHello(); //我调用这个服务可能失败,如果失败了,我要怎么处理
        }
    
        // dubbo 泛化调用 
        @DubboReference(interfaceName = "com.wuzz.demo.api.HelloService",generic = true,check = false)
        GenericService genericService;
    
        @GetMapping("/demo")
        public String demo(){
            return genericService.$invoke("sayHello",new String[0],null).toString();
        }
    }

      mock 实现类:

    public class HelloServiceMock implements HelloService {
        @Override
        public String sayHello() {
            return "服务端发生异常, 被降解了。返回兜底数据。。。";
        }
    }

    3.配置文件,启动类无需任何配置

    spring.application.name=springboot-dubbo-client
    dubbo.registry.address=nacos://localhost:8848
    server.port=8889

      然后先后启动 服务提供者、服务消费者模块。可以看到 注册中心应当有两个服务的相关注册信息:

      然后就可以访问对应的接口进行测试。

    spring-cloud-starter-dubbo(org.apache.dubbo:2.7.6):

      与springboot 集成不同,spring-cloud-alibaba 自成生态,在多注册中心的用法上有兼容问题。

    项目目录

      spring-cloud-alibaba-dubbo-api 模块:

    1.添加相关接口

    public interface HelloService {
    
        String sayHello() throws Exception;
    }

      spring-cloud-alibaba-dubbo-provider 模块:

    1.添加相关依赖:

    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <!--dubbo 依赖-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-dubbo</artifactId>
                <version>2.2.1.RELEASE</version>
            </dependency>
            <!--nacos 注册中心依赖-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
                <version>2.2.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.wuzz.demo</groupId>
                <artifactId>spring-cloud-alibaba-dubbo-api</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>

    2.实现类:

    @Service(loadbalance = "random",timeout = 50000,cluster = "failsafe")
    public class HelloServiceImpl implements HelloService {
        @Override
        public String sayHello() throws Exception {
            return "Hello Dubbo";
        }
    }

    3. 配置文件配置:

    spring.application.name=springboot-dubbo
    dubbo.scan.base-packages=com.wuzz.demo
    dubbo.protocol.port=20882
    dubbo.protocol.name=dubbo
    spring.cloud.nacos.discovery.server-addr=localhost:8848

    4.启动类:

    @EnableDiscoveryClient
    @SpringBootApplication
    public class SpringCloudAlibabaDubboProviderApp {
    
        private final static Logger log = LoggerFactory.getLogger(SpringCloudAlibabaDubboProviderApp.class);
    
        public static void main(String[] args) {
            SpringApplication.run(SpringCloudAlibabaDubboProviderApp.class, args);
            log.info("服务启动成功");
    
        }
    }

      spring-cloud-alibaba-dubbo-client 模块:

    1.导入依赖:

    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-dubbo</artifactId>
                <version>2.2.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
                <version>2.2.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.wuzz.demo</groupId>
                <artifactId>spring-cloud-alibaba-dubbo-api</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>

    3.测试类编写:

    @RestController
    public class DubboController {
        //Dubbo提供的注解
        @Reference(loadbalance = "roundrobin",
                timeout = 1, cluster = "failfast",
                mock = "com.wuzz.demo.mock.HelloServiceMock", check = false)
        HelloService helloService;
    
        @GetMapping("/sayhello")
        public String sayHello() throws Exception {
            return helloService.sayHello(); //我调用这个服务可能失败,如果失败了,我要怎么处理
        }
    }

      mock 实现类:

    public class HelloServiceMock implements HelloService {
        @Override
        public String sayHello() {
            return "服务端发生异常, 被降解了。返回兜底数据。。。";
        }
    }

    4.配置文件:

    spring.application.name=springboot-dubbo-client
    server.port=8889
    spring.cloud.nacos.discovery.server-addr=localhost:8848

    5.启动类:

    @EnableDiscoveryClient
    @SpringBootApplication
    public class SpringCloudAlibabaDubboClientApp {
    
        private final static Logger log = LoggerFactory.getLogger(SpringCloudAlibabaDubboClientApp.class);
    
        public static void main(String[] args) {
            SpringApplication.run(SpringCloudAlibabaDubboClientApp.class, args);
            log.info("服务启动成功");
        }
    }

      先后启动服务提供者、服务消费者进行测试即可。

    Dubbo 常用功能简介:

    多协议支持:

      官网协议参考手册 :http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html

    • dubbo://  :Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
    • rmi://      : RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式。
    • hessian://  : Hessian 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。
    • http://     :基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现 
    • webservice://    :基于 WebService 的远程调用协议,基于 Apache CXF  的 frontend-simple 和 transports-http 实现 。
    • thrift://    :当前 dubbo 支持 的 thrift 协议是对 thrift 原生协议 的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。
    • memcached://   :基于 memcached  实现的 RPC 协议 。
    • redis://   :基于 Redis  实现的 RPC 协议 。
    • rest://    :基于标准的Java REST API——JAX-RS 2.0(Java API for RESTful Web Services的简写)实现的REST调用支持
    • grpc://    :Dubbo 自 2.7.5 版本开始支持 gRPC 协议,对于计划使用 HTTP/2 通信,或者想利用 gRPC 带来的 Stream、反压、Reactive 编程等能力的开发者来说, 都可以考虑启用 gRPC 协议。

    Dubbo 的负载均衡:

      在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。

      1.Random LoadBalance:

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

      2.RoundRobin LoadBalance:

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

      3.LeastActive LoadBalance:

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

      4.ConsistentHash LoadBalance:

    • 一致性 Hash,相同参数的请求总是发到同一提供者。
    • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
    • 算法参见:http://en.wikipedia.org/wiki/Consistent_hashing
    • 缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key="hash.arguments" value="0,1" />
    • 缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key="hash.nodes" value="320" />

      5.ShortestResponse LoadBalance:

      最短响应时间负载均衡算法,筛选成功调用响应时间最短的调用程序的数量,并计算这些调用程序的权重和数量。然后根据响应时间的长短来分配目标服务的路由权重。

    集群容错:

      官方文档 :http://dubbo.apache.org/zh-cn/docs/user/demos/fault-tolerent-strategy.html

      在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。

    • Failover Cluster  :失败自动切换,当出现失败,重试其它服务器 。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

    • Failfast Cluster  :快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

    • Failsafe Cluster   :失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

    • Failback Cluster   :失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

    • Forking Cluster   :并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

    • Broadcast Cluster   :广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息。

    服务降级:

      dubbo的降级方式: Mock。上文代码中已给出示例实现步骤:

    1. 在client端创建一个 HelloServiceMock 类,实现对应的接口(需要对哪个接口进行mock,就实现哪个),名称必须以Mock结尾
    2. 在client端的服务调用的注解配置中,添加 mock 配置,增加一个mock属性指向创建的HelloServiceMock 
    3. 模拟错误(设置timeout),模拟超时异常,运行测试代码即可访问到HelloServiceMock 这个类。当服务端故障解除以后,调用过程将恢复正常,

    Dubbo泛化:

      泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。 上文已给出示例。

      更多的泛化配置可以参考官网 :http://dubbo.apache.org/zh-cn/docs/user/demos/generic-reference.html

    主机绑定:

      关于主机绑定的源码实现位于  org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol

    String host = findConfigedHosts(protocolConfig, registryURLs, map);
    Integer port = findConfigedPorts(protocolConfig, name, map);

      主机绑定的步骤主要有以下几个步骤:

    •  查找环境变量中是否存在启动参数 [DUBBO_IP_TO_BIND] =服务注册的ip
    • 读取配置文件, dubbo.protocols.dubbo.host= 服务注册的ip
    • InetAddress.getLocalHost().getHostAddress() 获得本机ip地址
    • 通过Socket去连接注册中心,从而获取本机IP
    • 会轮询本机的网卡,直到找到合适的IP地址
    • 上面获取到的ip地址是bindip,如果需要作为服务注册中心的ip, DUBBO_IP_TO_REGISTRY -dDUBBO_IP_TO_REGISTRY=ip

    配置优先级:

    • 方法层面的配置要优先于接口层面的配置, 接口层面的配置要优先于全局配置.
    • 如果级别一样,以客户端的配置优先,服务端次之.

    性能调优的参数:

      dubbo 提供了针对服务端/客户端的相关参数调优,以下列举了一些比较重要的参数。

    @Configuration
    public class DubboConfig {
    
        //服务端相关调优参数
        @Bean
        public ProviderConfig providerConfig() {
            ProviderConfig config = new ProviderConfig();
            //默认200 服务线程池大小(固定大小)
            config.setThreads(200);
            //默认CPU + 1
            //IO线程池,接收网络读写中断,以及序列化和反序列化,
            // 不处理业务,业务线程池参见threads配置,此线程池和CPU相关,不建议配置。
            config.setIothreads(Runtime.getRuntime().availableProcessors() + 1);
            //线程池类型,可选:fixed/cached/limit(2.5.3以上)/eager(2.6.x以上)
            config.setThreadpool("fixed");
            //对每个提供者的最大连接数,rmi、http、hessian
            //等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数
            config.setConnections(0);
            //线程池队列大小,当线程池满时,排队等待执行的队列大小,
            //建议不要设置,当线程池满时应立即失败,重试其它服务提供机器,
            //而不是排队,除非有特殊需求。
            config.setQueues(0);
            //每服务消费者每服务每方法最大并发调用数
            config.setAccepts(0);
            //服务提供者每服务每方法最大可并行执行请求数
            config.setExecutes(0);
            return config;
    
        }
        //客户端相关调优参数
        @Bean
        public ConsumerConfig consumerConfig() {
            ConsumerConfig config = new ConsumerConfig();
            //每个服务对每个提供者的最大连接数,
            //rmi、http、hessian等短连接协议支持此配置,dubbo协议长连接不支持此配置
            config.setConnections(100);
            //每服务消费者每服务每方法最大并发调用数
            config.setActives(0);
            return config;
        }
    }

      参数调优可以参考以下dubbo的处理流程

      更多参数请参考官网:

    1. providerhttp://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-provider.html
    2. consumerhttp://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-consumer.html

    Dubbo缓存文件:

      配置服务地址的缓存,避免注册中心挂了之后对于服务通信的影响,客户端做以下配置 :

    spring.application.name=springboot-dubbo-client
    server.port=8889
    
    # nacos 注册中心
    dubbo.registries.wenzhou.address=nacos://localhost:8848
    # 配置服务地址的缓存,避免注册中心挂了之后对于服务通信的影响
    dubbo.registries.wenzhou.file=${user.home}/dubbo.cache

      然后启动服务提供者/服务消费者,可以到用户目录下看到一个文件,dubbo.cache

     

      然后我们打开它:

      我们会发现服务信息已经被缓存下来了。这个时候,把注册中心关了,再去访问接口 ,发现也是没问题的。

      更多特性请参考官网

  • 相关阅读:
    【翻译/介绍】jump consistent hash:零内存消耗,均匀,快速,简洁,来自Google的一致性哈希算法 [2015-03-13]
    现代密码学实践指南[2015年]
    本博客迁走了
    高性能web系统的架构和系统优化
    vs 2013调试的时候重启的解决方案
    年会与项目管理
    javascript 关闭窗口,弹出新窗口并带有确认关闭对话框解决办法
    成长
    POCO exception
    通过OpenGL ES在iOS平台实践增强现实(二)
  • 原文地址:https://www.cnblogs.com/wuzhenzhao/p/13744165.html
Copyright © 2011-2022 走看看