org.apache.dubbo 2.7.x 再聚首

Dubbo 版本 :

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

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

  Apache dubbo 官网已经出现3.0版本。目前官网展示版本为 2.7 与3.0.如若下列官网地址打不开,可能是项目更新了,进入 http://dubbo.apache.org/ 自行查阅

  版本更多信息请参考官网。相信小伙伴们对于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 常用功能简介:

多协议支持:

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

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

集群容错:

  在集群调用失败时,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/2.7/user/references/xml/dubbo-provider/
  2. consumerhttp://dubbo.apache.org/zh-cn/docs/2.7/user/references/xml/dubbo-consumer/

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

 

  然后我们打开它:

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

  更多特性请参考官网

posted @ 2020-09-28 12:42  吴振照  阅读(1827)  评论(0编辑  收藏  举报