1. 最简单的使用
开篇用一个最简单的例子,来介绍如何用Dubbo搭建一个简单的例子。
本例包括:
- 注册中心(用Zookeeper代替)
- 服务提供者(provider)
- 服务消费者(consumer)
- 接口(interface)
1.1 项目结构
创建项目,并创建三个Module,项目结构如下:
项目结构
1.2 项目依赖
整体pom.xml为:
代码语言:txt复制<?xml version="1.0" encoding="UTF-8"?>
代码语言:txt复制<project xmlns="http://maven.apache.org/POM/4.0.0"
代码语言:txt复制 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
代码语言:txt复制 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
代码语言:txt复制 <modelVersion>4.0.0</modelVersion>
代码语言:txt复制 <groupId>org.example</groupId>
代码语言:txt复制 <artifactId>Study</artifactId>
代码语言:txt复制 <packaging>pom</packaging>
代码语言:txt复制 <version>1.0-SNAPSHOT</version>
代码语言:txt复制 <modules>
代码语言:txt复制 <module>consumer</module>
代码语言:txt复制 <module>provider</module>
代码语言:txt复制 <module>interface</module>
代码语言:txt复制 </modules>
代码语言:txt复制 <properties>
代码语言:txt复制 <spring-boot.version>2.3.1.RELEASE</spring-boot.version>
代码语言:txt复制 <dubbo.version>2.7.5</dubbo.version>
代码语言:txt复制 </properties>
代码语言:txt复制 <dependencyManagement>
代码语言:txt复制 <dependencies>
代码语言:txt复制 <!-- Spring Boot -->
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.springframework.boot</groupId>
代码语言:txt复制 <artifactId>spring-boot-dependencies</artifactId>
代码语言:txt复制 <version>${spring-boot.version}</version>
代码语言:txt复制 <type>pom</type>
代码语言:txt复制 <scope>import</scope>
代码语言:txt复制 </dependency>
代码语言:txt复制 <!-- Apache Dubbo -->
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo-dependencies-bom</artifactId>
代码语言:txt复制 <version>${dubbo.version}</version>
代码语言:txt复制 <type>pom</type>
代码语言:txt复制 <scope>import</scope>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo</artifactId>
代码语言:txt复制 <version>${dubbo.version}</version>
代码语言:txt复制 <exclusions>
代码语言:txt复制 <exclusion>
代码语言:txt复制 <groupId>org.springframework</groupId>
代码语言:txt复制 <artifactId>spring</artifactId>
代码语言:txt复制 </exclusion>
代码语言:txt复制 <exclusion>
代码语言:txt复制 <groupId>javax.servlet</groupId>
代码语言:txt复制 <artifactId>servlet-api</artifactId>
代码语言:txt复制 </exclusion>
代码语言:txt复制 <exclusion>
代码语言:txt复制 <groupId>log4j</groupId>
代码语言:txt复制 <artifactId>log4j</artifactId>
代码语言:txt复制 </exclusion>
代码语言:txt复制 </exclusions>
代码语言:txt复制 </dependency>
代码语言:txt复制 </dependencies>
代码语言:txt复制 </dependencyManagement>
代码语言:txt复制</project>
consumer的依赖为:
代码语言:txt复制<?xml version="1.0" encoding="UTF-8"?>
代码语言:txt复制<project xmlns="http://maven.apache.org/POM/4.0.0"
代码语言:txt复制 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
代码语言:txt复制 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
代码语言:txt复制 <parent>
代码语言:txt复制 <artifactId>Study</artifactId>
代码语言:txt复制 <groupId>org.example</groupId>
代码语言:txt复制 <version>1.0-SNAPSHOT</version>
代码语言:txt复制 </parent>
代码语言:txt复制 <modelVersion>4.0.0</modelVersion>
代码语言:txt复制 <artifactId>consumer</artifactId>
代码语言:txt复制 <build>
代码语言:txt复制 <plugins>
代码语言:txt复制 <plugin>
代码语言:txt复制 <groupId>org.apache.maven.plugins</groupId>
代码语言:txt复制 <artifactId>maven-compiler-plugin</artifactId>
代码语言:txt复制 <configuration>
代码语言:txt复制 <source>8</source>
代码语言:txt复制 <target>8</target>
代码语言:txt复制 </configuration>
代码语言:txt复制 </plugin>
代码语言:txt复制 </plugins>
代码语言:txt复制 </build>
代码语言:txt复制 <dependencies>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.example</groupId>
代码语言:txt复制 <artifactId>interface</artifactId>
代码语言:txt复制 <version>1.0-SNAPSHOT</version>
代码语言:txt复制 </dependency>
代码语言:txt复制 <!-- Dubbo Spring Boot Starter -->
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo-spring-boot-starter</artifactId>
代码语言:txt复制 <version>2.7.5</version>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo</artifactId>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.springframework.boot</groupId>
代码语言:txt复制 <artifactId>spring-boot-starter</artifactId>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.springframework.boot</groupId>
代码语言:txt复制 <artifactId>spring-boot-starter-web</artifactId>
代码语言:txt复制 </dependency>
代码语言:txt复制 <!-- Zookeeper dependencies -->
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo-dependencies-zookeeper</artifactId>
代码语言:txt复制 <version>${dubbo.version}</version>
代码语言:txt复制 <type>pom</type>
代码语言:txt复制 <exclusions>
代码语言:txt复制 <exclusion>
代码语言:txt复制 <groupId>org.slf4j</groupId>
代码语言:txt复制 <artifactId>slf4j-log4j12</artifactId>
代码语言:txt复制 </exclusion>
代码语言:txt复制 </exclusions>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo-rpc-http</artifactId>
代码语言:txt复制 <version>${dubbo.version}</version>
代码语言:txt复制 </dependency>
代码语言:txt复制 </dependencies>
代码语言:txt复制</project>
interface的依赖为:
代码语言:txt复制<?xml version="1.0" encoding="UTF-8"?>
代码语言:txt复制<project xmlns="http://maven.apache.org/POM/4.0.0"
代码语言:txt复制 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
代码语言:txt复制 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
代码语言:txt复制 <parent>
代码语言:txt复制 <artifactId>Study</artifactId>
代码语言:txt复制 <groupId>org.example</groupId>
代码语言:txt复制 <version>1.0-SNAPSHOT</version>
代码语言:txt复制 </parent>
代码语言:txt复制 <modelVersion>4.0.0</modelVersion>
代码语言:txt复制 <artifactId>interface</artifactId>
代码语言:txt复制 <build>
代码语言:txt复制 <plugins>
代码语言:txt复制 <plugin>
代码语言:txt复制 <groupId>org.apache.maven.plugins</groupId>
代码语言:txt复制 <artifactId>maven-compiler-plugin</artifactId>
代码语言:txt复制 <configuration>
代码语言:txt复制 <source>8</source>
代码语言:txt复制 <target>8</target>
代码语言:txt复制 </configuration>
代码语言:txt复制 </plugin>
代码语言:txt复制 </plugins>
代码语言:txt复制 </build>
代码语言:txt复制</project>
provider的依赖为:
代码语言:txt复制<?xml version="1.0" encoding="UTF-8"?>
代码语言:txt复制<project xmlns="http://maven.apache.org/POM/4.0.0"
代码语言:txt复制 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
代码语言:txt复制 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
代码语言:txt复制 <parent>
代码语言:txt复制 <artifactId>Study</artifactId>
代码语言:txt复制 <groupId>org.example</groupId>
代码语言:txt复制 <version>1.0-SNAPSHOT</version>
代码语言:txt复制 </parent>
代码语言:txt复制 <modelVersion>4.0.0</modelVersion>
代码语言:txt复制 <artifactId>provider</artifactId>
代码语言:txt复制 <build>
代码语言:txt复制 <plugins>
代码语言:txt复制 <plugin>
代码语言:txt复制 <groupId>org.apache.maven.plugins</groupId>
代码语言:txt复制 <artifactId>maven-compiler-plugin</artifactId>
代码语言:txt复制 <configuration>
代码语言:txt复制 <source>8</source>
代码语言:txt复制 <target>8</target>
代码语言:txt复制 </configuration>
代码语言:txt复制 </plugin>
代码语言:txt复制 </plugins>
代码语言:txt复制 </build>
代码语言:txt复制 <dependencies>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.example</groupId>
代码语言:txt复制 <artifactId>interface</artifactId>
代码语言:txt复制 <version>1.0-SNAPSHOT</version>
代码语言:txt复制 </dependency>
代码语言:txt复制 <!-- Dubbo Spring Boot Starter -->
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo-spring-boot-starter</artifactId>
代码语言:txt复制 <version>2.7.5</version>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.jboss.resteasy</groupId>
代码语言:txt复制 <artifactId>resteasy-jaxrs</artifactId>
代码语言:txt复制 <version>3.0.19.Final</version>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>javax.validation</groupId>
代码语言:txt复制 <artifactId>validation-api</artifactId>
代码语言:txt复制 <version>1.1.0.Final</version>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.springframework.boot</groupId>
代码语言:txt复制 <artifactId>spring-boot-starter</artifactId>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.springframework.boot</groupId>
代码语言:txt复制 <artifactId>spring-boot-starter-web</artifactId>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo</artifactId>
代码语言:txt复制 </dependency>
代码语言:txt复制 <!-- Zookeeper dependencies -->
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo-dependencies-zookeeper</artifactId>
代码语言:txt复制 <version>${dubbo.version}</version>
代码语言:txt复制 <type>pom</type>
代码语言:txt复制 <exclusions>
代码语言:txt复制 <exclusion>
代码语言:txt复制 <groupId>org.slf4j</groupId>
代码语言:txt复制 <artifactId>slf4j-log4j12</artifactId>
代码语言:txt复制 </exclusion>
代码语言:txt复制 </exclusions>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo-rpc-http</artifactId>
代码语言:txt复制 <version>${dubbo.version}</version>
代码语言:txt复制 </dependency>
代码语言:txt复制 <dependency>
代码语言:txt复制 <groupId>org.apache.dubbo</groupId>
代码语言:txt复制 <artifactId>dubbo-metadata-report-zookeeper</artifactId>
代码语言:txt复制 <version>${dubbo.version}</version>
代码语言:txt复制 </dependency>
代码语言:txt复制 </dependencies>
代码语言:txt复制</project>
1.3 具体代码
1.3.1 服务提供者
@Service注解暴露服务
实现类
代码语言:txt复制package com.zyz.provider.service;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.common.URL;
代码语言:txt复制import org.apache.dubbo.config.annotation.Service;
代码语言:txt复制import org.apache.dubbo.rpc.RpcContext;
代码语言:txt复制@Service(version = "default")
代码语言:txt复制public class DefaultDemoService implements DemoService {
代码语言:txt复制 @Override
代码语言:txt复制 public String sayHello(String name) {
代码语言:txt复制 System.out.println("执行了服务" name);
代码语言:txt复制 URL url = RpcContext.getContext().getUrl();
代码语言:txt复制 return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name); // 正常访问
代码语言:txt复制 }
代码语言:txt复制}
启动类
代码语言:txt复制package com.zyz;
代码语言:txt复制import org.springframework.boot.SpringApplication;
代码语言:txt复制import org.springframework.boot.autoconfigure.SpringBootApplication;
代码语言:txt复制@SpringBootApplication
代码语言:txt复制public class DubboProviderDemo {
代码语言:txt复制 public static void main(String[] args) {
代码语言:txt复制 SpringApplication.run(DubboProviderDemo.class,args);
代码语言:txt复制 }
代码语言:txt复制}
配置文件application.properties:
代码语言:txt复制# Spring boot application
代码语言:txt复制spring.application.name=dubbo-provider-demo
代码语言:txt复制server.port=8081
代码语言:txt复制# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service
代码语言:txt复制dubbo.scan.base-packages=com.zyz.provider.service
代码语言:txt复制dubbo.application.name=${spring.application.name}
代码语言:txt复制## Dubbo Registry
代码语言:txt复制dubbo.registry.address=zookeeper://127.0.0.1:2181
代码语言:txt复制# Dubbo Protocol
代码语言:txt复制dubbo.protocol.name=dubbo
代码语言:txt复制dubbo.protocol.port=20880
日志文件log4j.properties:
代码语言:txt复制###set log levels###
代码语言:txt复制log4j.rootLogger=info, stdout
代码语言:txt复制###output to the console###
代码语言:txt复制log4j.appender.stdout=org.apache.log4j.ConsoleAppender
代码语言:txt复制log4j.appender.stdout.Target=System.out
代码语言:txt复制log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
代码语言:txt复制log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n
1.3.2 接口
接口类DemoService:
代码语言:txt复制package com.zyz;
代码语言:txt复制public interface DemoService {
代码语言:txt复制 String sayHello(String name);
代码语言:txt复制}
1.3.3 服务消费者
@Reference引入服务,加入版本表示具体引入哪一个实现类的实例
启动类:
代码语言:txt复制package com.zyz;
代码语言:txt复制import org.apache.dubbo.config.annotation.Reference;
代码语言:txt复制import org.springframework.boot.SpringApplication;
代码语言:txt复制import org.springframework.boot.autoconfigure.SpringBootApplication;
代码语言:txt复制import org.springframework.context.ConfigurableApplicationContext;
代码语言:txt复制@SpringBootApplication
代码语言:txt复制public class DubboConsumerDemo {
代码语言:txt复制 @Reference(version = "default")
代码语言:txt复制 private DemoService demoService;
代码语言:txt复制 public static void main(String[] args) {
代码语言:txt复制 ConfigurableApplicationContext context = SpringApplication.run(DubboConsumerDemo.class);
代码语言:txt复制 DemoService demoService = context.getBean(DemoService.class);
代码语言:txt复制 System.out.println((demoService.sayHello("zyz")));
代码语言:txt复制 }
代码语言:txt复制}
配置文件application.yml:
代码语言:txt复制spring:
代码语言:txt复制 application:
代码语言:txt复制 name: dubbo-consumer-demo
代码语言:txt复制server:
代码语言:txt复制 port: 8082
代码语言:txt复制dubbo:
代码语言:txt复制 registry:
代码语言:txt复制 address: zookeeper://127.0.0.1:2181
Zookeeper的配置参考
先启动Zookeeper,再启动provider的启动类,最后启动consumer的启动类
看到输出:
代码语言:txt复制dubbo:20880, Hello, zyz
大功告成。
2. 具体应用
2.1 负载均衡
Dubbo提供了四种负载均衡策略,也可以自定义。
- Random LoadBalance:随机,按权重设置随机概率。
- RoundRobin LoadBalance:轮询,按公约后的权重设置轮询比率。
- LeastActive LoadBalance:最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
- ConsistentHash LoadBalance:一致性 Hash,相同参数的请求总是发到同一提供者。
2.2 具体代码
修改provider的配置文件为:
代码语言:txt复制# Spring boot application
代码语言:txt复制spring.application.name=dubbo-provider-demo
代码语言:txt复制server.port=8081
代码语言:txt复制# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service
代码语言:txt复制dubbo.scan.base-packages=com.zyz.provider.service
代码语言:txt复制dubbo.application.name=${spring.application.name}
代码语言:txt复制## Dubbo Registry
代码语言:txt复制dubbo.registry.address=zookeeper://127.0.0.1:2181
代码语言:txt复制# Dubbo Protocol
代码语言:txt复制#dubbo.protocol.name=dubbo
代码语言:txt复制#dubbo.protocol.port=20880
代码语言:txt复制dubbo.protocols.p1.id=dubbo1
代码语言:txt复制dubbo.protocols.p1.name=dubbo
代码语言:txt复制dubbo.protocols.p1.port=20881
代码语言:txt复制dubbo.protocols.p1.host=0.0.0.0
代码语言:txt复制dubbo.protocols.p2.id=dubbo2
代码语言:txt复制dubbo.protocols.p2.name=dubbo
代码语言:txt复制dubbo.protocols.p2.port=20882
代码语言:txt复制dubbo.protocols.p2.host=0.0.0.0
代码语言:txt复制dubbo.protocols.p3.id=dubbo3
代码语言:txt复制dubbo.protocols.p3.name=dubbo
代码语言:txt复制dubbo.protocols.p3.port=20883
代码语言:txt复制dubbo.protocols.p3.host=0.0.0.0
服务消费端新增类:
代码语言:txt复制package com.zyz.consumer;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.config.annotation.Reference;
代码语言:txt复制import org.springframework.boot.SpringApplication;
代码语言:txt复制import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
代码语言:txt复制import org.springframework.context.ConfigurableApplicationContext;
代码语言:txt复制@EnableAutoConfiguration
代码语言:txt复制public class LoadBalanceDubboConsumerDemo {
代码语言:txt复制 @Reference(version = "default", loadbalance = "roundrobin")
代码语言:txt复制 private DemoService demoService;
代码语言:txt复制 public static void main(String[] args) {
代码语言:txt复制 ConfigurableApplicationContext context = SpringApplication.run(LoadBalanceDubboConsumerDemo.class);
代码语言:txt复制 DemoService demoService = context.getBean(DemoService.class);
代码语言:txt复制 roundRobin(demoService);
代码语言:txt复制 }
代码语言:txt复制 /**
代码语言:txt复制 * 负载均衡(轮询)
* @param demoService
*/
public static void roundRobin(DemoService demoService){
代码语言:txt复制 for (int i = 0; i < 1000; i ) {
代码语言:txt复制 System.out.println((demoService.sayHello("zyz")));
代码语言:txt复制 try {
代码语言:txt复制 Thread.sleep(1 * 1000);
代码语言:txt复制 } catch (InterruptedException e) {
代码语言:txt复制 e.printStackTrace();
代码语言:txt复制 }
代码语言:txt复制 }
代码语言:txt复制 }
代码语言:txt复制}
最终输出为:
代码语言:txt复制dubbo:20882, Hello, zyz
代码语言:txt复制dubbo:20883, Hello, zyz
代码语言:txt复制dubbo:20881, Hello, zyz
代码语言:txt复制dubbo:20882, Hello, zyz
代码语言:txt复制dubbo:20883, Hello, zyz
代码语言:txt复制dubbo:20881, Hello, zyz
2.2 服务超时
2.2.1 说明
在服务提供者和服务消费者上都可以配置服务超时时间,这两者是不⼀样的。
消费者调⽤⼀个服务,分为三步:
- 消费者发送请求(⽹络传输)
- 服务端执⾏服务
- 服务端返回响应(⽹络传输)
如果在服务端和消费端只在其中⼀⽅配置了timeout,那么没有歧义,表示消费端调⽤服务的超时时间,消
费端如果超过时间还没有收到响应结果,则消费端会抛超时异常,但,服务端不会抛异常,服务端在执⾏
服务后,会检查执⾏该服务的时间,如果超过timeout,则会打印⼀个超时⽇志。服务会正常的执⾏完。
如果在服务端和消费端各配了⼀个timeout,那就⽐较复杂了,假设
- 服务执⾏为5s
- 消费端timeout=3s
- 服务端timeout=6s
那么消费端调⽤服务时,消费端会收到超时异常(因为消费端超时了),服务端⼀切正常(服务端没有超时)。
2.2.2 具体代码
服务提供方新增两个类:
代码语言:txt复制package com.zyz.provider.service;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.common.URL;
代码语言:txt复制import org.apache.dubbo.config.annotation.Service;
代码语言:txt复制import org.apache.dubbo.rpc.RpcContext;
代码语言:txt复制import java.util.concurrent.TimeUnit;
代码语言:txt复制@Service(version = "timeout", timeout = 6000)
代码语言:txt复制public class TimeoutDemoService implements DemoService {
代码语言:txt复制 @Override
代码语言:txt复制 public String sayHello(String name) {
代码语言:txt复制 System.out.println("执行了timeout服务" name);
代码语言:txt复制 // 服务执行5秒
代码语言:txt复制 // 服务超时时间为3秒,但是执行了5秒,服务端会把任务执行完的
代码语言:txt复制 // 服务的超时时间,是指如果服务执行时间超过了指定的超时时间则会抛一个warn
代码语言:txt复制 try {
代码语言:txt复制 TimeUnit.SECONDS.sleep(5);
代码语言:txt复制 } catch (InterruptedException e) {
代码语言:txt复制 e.printStackTrace();
代码语言:txt复制 }
代码语言:txt复制 System.out.println("执行结束" name);
代码语言:txt复制 URL url = RpcContext.getContext().getUrl();
代码语言:txt复制 return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name); // 正常访问
代码语言:txt复制 }
代码语言:txt复制}
代码语言:txt复制package com.zyz;
代码语言:txt复制import org.apache.dubbo.config.spring.ServiceBean;
代码语言:txt复制import org.springframework.beans.BeansException;
代码语言:txt复制import org.springframework.beans.factory.config.BeanPostProcessor;
代码语言:txt复制import org.springframework.stereotype.Component;
代码语言:txt复制@Component
代码语言:txt复制public class UpdateBeanPostProcessors implements BeanPostProcessor {
代码语言:txt复制 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
代码语言:txt复制 if (beanName.contains("ServiceBean")) {
代码语言:txt复制 //这里设置id,否则会造成同一个Service有多个group时,只能注入第一个service
代码语言:txt复制 ServiceBean serviceBean = (ServiceBean) bean;
代码语言:txt复制 serviceBean.setId(beanName);
代码语言:txt复制 }
代码语言:txt复制 return bean;
代码语言:txt复制 }
代码语言:txt复制 @Override
代码语言:txt复制 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
代码语言:txt复制 return bean;
代码语言:txt复制 }
代码语言:txt复制}
消费方新增一个启动类:
代码语言:txt复制package com.zyz.consumer;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.config.annotation.Reference;
代码语言:txt复制import org.springframework.boot.SpringApplication;
代码语言:txt复制import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
代码语言:txt复制import org.springframework.context.ConfigurableApplicationContext;
代码语言:txt复制import java.io.IOException;
代码语言:txt复制@EnableAutoConfiguration
代码语言:txt复制public class TimeoutDubboConsumerDemo {
代码语言:txt复制 @Reference(version = "timeout", timeout = 3000)
代码语言:txt复制 private DemoService demoService;
代码语言:txt复制 public static void main(String[] args) throws IOException {
代码语言:txt复制 ConfigurableApplicationContext context = SpringApplication.run(TimeoutDubboConsumerDemo.class);
代码语言:txt复制 DemoService demoService = context.getBean(DemoService.class);
代码语言:txt复制 // 服务调用超时时间为1秒,默认为3秒
代码语言:txt复制 // 如果这1秒内没有收到服务结果,则会报错
代码语言:txt复制 System.out.println((demoService.sayHello("周瑜"))); //xxservestub
代码语言:txt复制 }
代码语言:txt复制}
2.3 集群容错
集群容错表示:服务消费者在调⽤某个服务时,这个服务有多个服务提供者,在经过负载均衡后选出其中
⼀个服务提供者之后进⾏调⽤,但调⽤报错后,Dubbo所采取的后续处理策略。
2.3.1 设置
- Failover Cluster:默认配置,重试两次,一共请求三次
- Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录
- Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作
- Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作
- Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
- Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
2.3.2 具体代码
cousumer新增一个类:
代码语言:txt复制package com.zyz.consumer;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.config.annotation.Reference;
代码语言:txt复制import org.springframework.boot.SpringApplication;
代码语言:txt复制import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
代码语言:txt复制import org.springframework.context.ConfigurableApplicationContext;
代码语言:txt复制import java.io.IOException;
代码语言:txt复制@EnableAutoConfiguration
代码语言:txt复制public class ClusterDubboConsumerDemo {
代码语言:txt复制 @Reference(version = "cluster", timeout = 1000, cluster = "failfast")
代码语言:txt复制 private DemoService demoService;
代码语言:txt复制 public static void main(String[] args) throws IOException {
代码语言:txt复制 ConfigurableApplicationContext context = SpringApplication.run(ClusterDubboConsumerDemo.class);
代码语言:txt复制 DemoService demoService = context.getBean(DemoService.class);
代码语言:txt复制 System.out.println((demoService.sayHello("zyz")));
代码语言:txt复制 }
代码语言:txt复制}
provider新增一个实现类:
代码语言:txt复制package com.zyz.provider.service;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.common.URL;
代码语言:txt复制import org.apache.dubbo.config.annotation.Service;
代码语言:txt复制import org.apache.dubbo.rpc.RpcContext;
代码语言:txt复制import java.util.concurrent.TimeUnit;
代码语言:txt复制@Service(version = "cluster", timeout = 3000)
代码语言:txt复制public class ClusterDemoService implements DemoService {
代码语言:txt复制 @Override
代码语言:txt复制 public String sayHello(String name) {
代码语言:txt复制 System.out.println("执行了cluster服务" name);
代码语言:txt复制 try {
代码语言:txt复制 TimeUnit.SECONDS.sleep(5);
代码语言:txt复制 } catch (InterruptedException e) {
代码语言:txt复制 e.printStackTrace();
代码语言:txt复制 }
代码语言:txt复制 System.out.println("执行结束" name);
代码语言:txt复制 URL url = RpcContext.getContext().getUrl();
代码语言:txt复制 return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name); // 正常访问
代码语言:txt复制 }
代码语言:txt复制}
2.4 服务降级
服务降级表示:服务消费者在调⽤某个服务提供者时,如果该服务提供者报错了,所采取的措施。
集群容错和服务降级的区别在于:
- 集群容错是整个集群范围内的容错
- 服务降级是单个服务提供者的⾃身容错
2.4.1 具体代码
consumer新增一个类:
代码语言:txt复制package com.zyz.consumer;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.config.annotation.Reference;
代码语言:txt复制import org.springframework.boot.SpringApplication;
代码语言:txt复制import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
代码语言:txt复制import org.springframework.context.ConfigurableApplicationContext;
代码语言:txt复制import java.io.IOException;
代码语言:txt复制@EnableAutoConfiguration
代码语言:txt复制public class MockDubboConsumerDemo {
代码语言:txt复制 @Reference(version = "timeout", timeout = 1000, mock = "fail: return 123")
代码语言:txt复制 private DemoService demoService;
代码语言:txt复制 public static void main(String[] args) throws IOException {
代码语言:txt复制 ConfigurableApplicationContext context = SpringApplication.run(MockDubboConsumerDemo.class);
代码语言:txt复制 DemoService demoService = context.getBean(DemoService.class);
代码语言:txt复制 System.out.println((demoService.sayHello("zyz")));
代码语言:txt复制 }
代码语言:txt复制}
2.5 本地存根
本地存根就是⼀段逻辑,这段逻辑是在服务消费端执⾏的,
这段逻辑⼀般都是由服务提供者提供,服务提供者可以利⽤这种机制在服务消费者远程调⽤服务提供者之前或之后再做⼀些其他事情,⽐如结果缓存,请求参数验证等等。(AOP功能)
2.5.1 具体代码
interface新增一个类:
代码语言:txt复制package com.zyz;
代码语言:txt复制public class DemoServiceStub implements DemoService {
代码语言:txt复制 private final DemoService demoService;
代码语言:txt复制 // 构造函数传入真正的远程代理对象
代码语言:txt复制 public DemoServiceStub(DemoService demoService){
代码语言:txt复制 this.demoService = demoService;
代码语言:txt复制 }
代码语言:txt复制 @Override
代码语言:txt复制 public String sayHello(String name) {
代码语言:txt复制 // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
代码语言:txt复制 try {
代码语言:txt复制 System.out.println("校验逻辑");
代码语言:txt复制 return demoService.sayHello(name); // safe null
代码语言:txt复制 } catch (Exception e) {
代码语言:txt复制 // 你可以容错,可以做任何AOP拦截事项
代码语言:txt复制 return "容错数据";
代码语言:txt复制 }
代码语言:txt复制 }
代码语言:txt复制}
consumer新增一个类:
代码语言:txt复制package com.zyz.consumer;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.config.annotation.Reference;
代码语言:txt复制import org.springframework.boot.SpringApplication;
代码语言:txt复制import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
代码语言:txt复制import org.springframework.context.ConfigurableApplicationContext;
代码语言:txt复制import java.io.IOException;
代码语言:txt复制@EnableAutoConfiguration
代码语言:txt复制public class StubDubboConsumerDemo {
代码语言:txt复制 @Reference(version = "timeout", timeout = 1000, stub = "com.zyz.DemoServiceStub")
代码语言:txt复制 private DemoService demoService;
代码语言:txt复制 public static void main(String[] args) throws IOException {
代码语言:txt复制 ConfigurableApplicationContext context = SpringApplication.run(StubDubboConsumerDemo.class);
代码语言:txt复制 DemoService demoService = context.getBean(DemoService.class);
代码语言:txt复制 System.out.println((demoService.sayHello("周瑜")));
代码语言:txt复制 }
代码语言:txt复制}
2.6 本地伪装
就是Mock功能,Mock其实就是Dubbo中的服务容错的解决⽅案。
2.7 参数回调
⾸先,如果当前服务⽀持参数回调,意思就是:对于某个服务接⼝中的某个⽅法,如果想⽀持消费者在调
⽤这个⽅法时能设置回调逻辑,那么该⽅法就需要提供⼀个⼊参⽤来表示回调逻辑。
因为Dubbo协议是基于⻓连接的,所以消费端在两次调⽤同⼀个⽅法时想指定不同的回调逻辑,那么就需 要在调⽤时在指定⼀定key进⾏区分。
2.7.1 参数回调
consumer新增两个类
代码语言:txt复制package com.zyz.consumer;
代码语言:txt复制import com.zyz.DemoServiceListener;
代码语言:txt复制public class DemoServiceListenerImpl implements DemoServiceListener {
代码语言:txt复制 @Override
代码语言:txt复制 public void changed(String msg) {
代码语言:txt复制 System.out.println("被回调了:" msg);
代码语言:txt复制 }
代码语言:txt复制}
代码语言:txt复制package com.zyz.consumer;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.config.annotation.Reference;
代码语言:txt复制import org.springframework.boot.SpringApplication;
代码语言:txt复制import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
代码语言:txt复制import org.springframework.context.ConfigurableApplicationContext;
代码语言:txt复制import java.io.IOException;
代码语言:txt复制@EnableAutoConfiguration
代码语言:txt复制public class CallbackDubboConsumerDemo {
代码语言:txt复制 @Reference(version = "callback")
代码语言:txt复制 private DemoService demoService;
代码语言:txt复制 public static void main(String[] args) throws IOException {
代码语言:txt复制 ConfigurableApplicationContext context = SpringApplication.run(CallbackDubboConsumerDemo.class);
代码语言:txt复制 DemoService demoService = context.getBean(DemoService.class);
代码语言:txt复制 // 用来进行callback
代码语言:txt复制 System.out.println(demoService.sayHello("zyz", "d1", new DemoServiceListenerImpl()));
代码语言:txt复制 System.out.println(demoService.sayHello("zyz", "d2", new DemoServiceListenerImpl()));
代码语言:txt复制 System.out.println(demoService.sayHello("zyz", "d3", new DemoServiceListenerImpl()));
代码语言:txt复制 }
代码语言:txt复制}
interface新增一个类,并在DemoService中新增方法:
代码语言:txt复制package com.zyz;
代码语言:txt复制public interface DemoService {
代码语言:txt复制 String sayHello(String name);
代码语言:txt复制 // 添加回调
代码语言:txt复制 default String sayHello(String name, String key, DemoServiceListener listener) {
代码语言:txt复制 return null;
代码语言:txt复制 };
代码语言:txt复制}
代码语言:txt复制package com.zyz;
代码语言:txt复制public interface DemoServiceListener {
代码语言:txt复制 void changed(String msg);
代码语言:txt复制}
provider新增类:
代码语言:txt复制package com.zyz.provider.service;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import com.zyz.DemoServiceListener;
代码语言:txt复制import org.apache.dubbo.common.URL;
代码语言:txt复制import org.apache.dubbo.config.annotation.Argument;
代码语言:txt复制import org.apache.dubbo.config.annotation.Method;
代码语言:txt复制import org.apache.dubbo.config.annotation.Service;
代码语言:txt复制import org.apache.dubbo.rpc.RpcContext;
代码语言:txt复制import java.text.SimpleDateFormat;
代码语言:txt复制import java.util.Date;
代码语言:txt复制import java.util.Map;
代码语言:txt复制import java.util.concurrent.ConcurrentHashMap;
代码语言:txt复制// DemoService的sayHello方法的index=1的参数是回调对象,服务消费者可以调用addListener方法来添加回调对象,服务提供者一旦执行回调对象的方法就会通知给服务消费者
代码语言:txt复制@Service(version = "callback", methods = {@Method(name = "sayHello", arguments = {@Argument(index = 2, callback = true)})}, callbacks = 3)
代码语言:txt复制public class CallBackDemoService implements DemoService {
代码语言:txt复制 private final Map<String, DemoServiceListener> listeners = new ConcurrentHashMap<String, DemoServiceListener>();
代码语言:txt复制 public CallBackDemoService() {
代码语言:txt复制 Thread t = new Thread(new Runnable() {
代码语言:txt复制 @Override
代码语言:txt复制 public void run() {
代码语言:txt复制 while (true) {
代码语言:txt复制 for (Map.Entry<String, DemoServiceListener> entry : listeners.entrySet()) {
代码语言:txt复制 entry.getValue().changed(getChanged(entry.getKey()));
代码语言:txt复制 }
代码语言:txt复制 try {
代码语言:txt复制 Thread.sleep(5000);
代码语言:txt复制 } catch (InterruptedException e) {
代码语言:txt复制 e.printStackTrace();
代码语言:txt复制 }
代码语言:txt复制 }
代码语言:txt复制 }
代码语言:txt复制 });
代码语言:txt复制 t.start();
代码语言:txt复制 }
代码语言:txt复制 private String getChanged(String key) {
代码语言:txt复制 return "Changed: " new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
代码语言:txt复制 }
代码语言:txt复制 @Override
代码语言:txt复制 public String sayHello(String name) {
代码语言:txt复制 return null;
代码语言:txt复制 }
代码语言:txt复制 @Override
代码语言:txt复制 public String sayHello(String name, String key, DemoServiceListener callback) {
代码语言:txt复制 System.out.println("执行了回调服务" name);
代码语言:txt复制 listeners.put(key, callback);
代码语言:txt复制 URL url = RpcContext.getContext().getUrl();
代码语言:txt复制 return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name); // 正常访问
代码语言:txt复制 }
代码语言:txt复制}
2.8 异步调用
从 2.7.0 开始,Dubbo 的所有异步编程接口开始以 CompletableFuture 为基础
基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小。
2.8.1 具体代码
consumer新增类:
代码语言:txt复制package com.zyz.consumer;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.config.annotation.Reference;
代码语言:txt复制import org.springframework.boot.SpringApplication;
代码语言:txt复制import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
代码语言:txt复制import org.springframework.context.ConfigurableApplicationContext;
代码语言:txt复制import java.io.IOException;
代码语言:txt复制import java.util.concurrent.CompletableFuture;
代码语言:txt复制@EnableAutoConfiguration
代码语言:txt复制public class AsyncDubboConsumerDemo {
代码语言:txt复制 @Reference(version = "async")
代码语言:txt复制 private DemoService demoService;
代码语言:txt复制 public static void main(String[] args) throws IOException {
代码语言:txt复制 ConfigurableApplicationContext context = SpringApplication.run(AsyncDubboConsumerDemo.class);
代码语言:txt复制 DemoService demoService = context.getBean(DemoService.class);
代码语言:txt复制 // 调用直接返回CompletableFuture
代码语言:txt复制 CompletableFuture<String> future = demoService.sayHelloAsync("异步调用"); // 5
代码语言:txt复制 future.whenComplete((v, t) -> {
代码语言:txt复制 if (t != null) {
代码语言:txt复制 t.printStackTrace();
代码语言:txt复制 } else {
代码语言:txt复制 System.out.println("Response: " v);
代码语言:txt复制 }
代码语言:txt复制 });
代码语言:txt复制 System.out.println("结束了");
代码语言:txt复制 }
代码语言:txt复制}
interface中DemoService新增方法:
代码语言:txt复制package com.zyz;
代码语言:txt复制import java.util.concurrent.CompletableFuture;
代码语言:txt复制public interface DemoService {
代码语言:txt复制 String sayHello(String name);
代码语言:txt复制 // 异步调用方法
代码语言:txt复制 default CompletableFuture<String> sayHelloAsync(String name) {
代码语言:txt复制 return null;
代码语言:txt复制 };
代码语言:txt复制 // 添加回调
代码语言:txt复制 default String sayHello(String name, String key, DemoServiceListener listener) {
代码语言:txt复制 return null;
代码语言:txt复制 };
代码语言:txt复制}
consumer新增一个类:
代码语言:txt复制package com.zyz.provider.service;
代码语言:txt复制import com.zyz.DemoService;
代码语言:txt复制import org.apache.dubbo.common.URL;
代码语言:txt复制import org.apache.dubbo.config.annotation.Service;
代码语言:txt复制import org.apache.dubbo.rpc.RpcContext;
代码语言:txt复制import java.util.concurrent.CompletableFuture;
代码语言:txt复制@Service(version = "async")
代码语言:txt复制public class AsyncDemoService implements DemoService {
代码语言:txt复制 @Override
代码语言:txt复制 public String sayHello(String name) {
代码语言:txt复制 System.out.println("执行了同步服务" name);
代码语言:txt复制 URL url = RpcContext.getContext().getUrl();
代码语言:txt复制 return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name); // 正常访问
代码语言:txt复制 }
代码语言:txt复制 @Override
代码语言:txt复制 public CompletableFuture<String> sayHelloAsync(String name) {
代码语言:txt复制 System.out.println("执行了异步服务" name);
代码语言:txt复制 return CompletableFuture.supplyAsync(() -> {
代码语言:txt复制 return sayHello(name);
代码语言:txt复制 });
代码语言:txt复制 }
代码语言:txt复制}
2.9 泛化调用
泛化调⽤可以⽤来做服务测试。
在Dubbo中,如果某个服务想要⽀持泛化调⽤,就可以将该服务的generic属性设置为true,那对于服务消费者来说,就可以不⽤依赖该服务的接⼝,直接利⽤GenericService接⼝来进⾏服务调⽤。
2.9.1 具体代码
consumer新增一个类:
代码语言:txt复制package com.zyz.consumer;
代码语言:txt复制import org.apache.dubbo.config.annotation.Reference;
代码语言:txt复制import org.apache.dubbo.rpc.service.GenericService;
代码语言:txt复制import org.springframework.boot.SpringApplication;
代码语言:txt复制import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
代码语言:txt复制import org.springframework.context.ConfigurableApplicationContext;
代码语言:txt复制import java.io.IOException;
代码语言:txt复制@EnableAutoConfiguration
代码语言:txt复制public class GenericDubboConsumerDemo {
代码语言:txt复制 @Reference(id = "demoService", version = "default", interfaceName = "com.zyz.DemoService", generic = true)
代码语言:txt复制 private GenericService genericService;
代码语言:txt复制 public static void main(String[] args) throws IOException {
代码语言:txt复制 ConfigurableApplicationContext context = SpringApplication.run(GenericDubboConsumerDemo.class);
代码语言:txt复制 GenericService genericService = (GenericService) context.getBean("demoService");
代码语言:txt复制 Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"zyz"});
代码语言:txt复制 System.out.println(result);
代码语言:txt复制 }
代码语言:txt复制}
provider新增一个类:
代码语言:txt复制package com.zyz.provider.service;
代码语言:txt复制import org.apache.dubbo.config.annotation.Service;
代码语言:txt复制import org.apache.dubbo.rpc.service.GenericException;
代码语言:txt复制import org.apache.dubbo.rpc.service.GenericService;
代码语言:txt复制@Service(interfaceName = "com.zyz.DemoService", version = "generic")
代码语言:txt复制public class GenericDemoService implements GenericService {
代码语言:txt复制 @Override
代码语言:txt复制 public Object $invoke(String s, String[] strings, Object[] objects) throws GenericException {
代码语言:txt复制 System.out.println("执行了generic服务");
代码语言:txt复制 return "执行的方法是" s;
代码语言:txt复制 }
代码语言:txt复制}
3. 相关工具
Zookeeper可视化客户端
管理台(https://links.jianshu.com/go?to=https://github.com/apache/dubbo-
admin)