dubbo 应用示例

本文将创建3个工程:
dubbo-provider 服务提供者工程
dubbo-consumer 服务消费者工程
dubbo-api 服务接口

1 api 方式使用

1.1 注册中心方式

创建dubbo-api工程,提供者和消费者均需要依赖
在这里插入图片描述
提供一个接口

public interface HelloService {
    /**
     * 返回一句话
     * @param name
     * @return: java.lang.String
     * @Author: jt-ape
     * @Date: 2021/2/1 12:32
     */
    public String sayHello(String name);
}

创建dubbo-provider工程
在这里插入图片描述
提供一个实现

public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        return "dubbo say hello:"+ name;
    }
}

创建测试类,使用api方式暴露服务,注册中心使用zookeeper

public class DubboProviderApiTest {

    @Test
    public void test() throws IOException {
        // 创建应用
        ApplicationConfig applicationConfig = new ApplicationConfig("provider");

        // 创建协议
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName("dubbo");
        // port 设置为-1表示随机端口
        protocolConfig.setPort(20880);

        // 创建注册中心
        RegistryConfig registryConfig = new RegistryConfig();
        // zookeeper集群
        registryConfig.setAddress("121.36.168.192:2181,121.36.168.192:2182,121.36.168.192:2183");
        registryConfig.setProtocol("zookeeper");

        // 注册服务
        ServiceConfig<HelloService> serviceServiceConfig = new ServiceConfig<>();
        serviceServiceConfig.setApplication(applicationConfig);
        serviceServiceConfig.setProtocol(protocolConfig);
        serviceServiceConfig.setRegistry(registryConfig);
        serviceServiceConfig.setInterface(HelloService.class);
        serviceServiceConfig.setRef(new HelloServiceImpl());

        // 暴露服务
        serviceServiceConfig.export();

        System.in.read();
    }

pom.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>dubbo-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dubbo-provider</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.0.0</version>
        </dependency>
        <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>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

创建consumer工程
在这里插入图片描述
创建测试类,使用api方式调用服务

public class DubboConsumerApiTest {

    @Test
    public void test() {
        // 创建应用
        ApplicationConfig applicationConfig = new ApplicationConfig("consumer");

        // 创建注册中心
        RegistryConfig registryConfig = new RegistryConfig();
        // zookeeper集群
        registryConfig.setAddress("121.36.168.192:2181,121.36.168.192:2182,121.36.168.192:2183");
        registryConfig.setProtocol("zookeeper");

        ReferenceConfig<HelloService> referenceConfig = new ReferenceConfig<>();
        referenceConfig.setApplication(applicationConfig);
        referenceConfig.setRegistry(registryConfig);
        referenceConfig.setInterface(HelloService.class);

        HelloService helloService = referenceConfig.get();

        System.out.println(helloService.sayHello("dubbo api"));
    }
}

1.2 使用广播的方式

修改注册中心地址
固定写法:multicast://224.
在这里插入图片描述

1.3 使用随机端口

将协议的端口设置为-1,则会随机使用一个端口.
在这里插入图片描述
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>dubbo-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dubbo-consumer</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.0.0</version>
        </dependency>
        <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>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2 与springboot 整合使用

同样使用上面创建的三个工程,从上面的pom.xml中可以看出创建的是springboot工程。

2.1 dubbo-provider工程配置

application.yml

dubbo:
  application:
    name: dubbo-application-provider
  registry:
    address: 121.36.168.192:2181,121.36.168.192:2182,121.36.168.192:2183
    protocol: zookeeper
  scan:
    base-packages: com.example.dubboprovider.service.impl
  protocol:
    name: dubbo
    port: 20880

HelloServiceImpl 加上dubbo的@Service注解注册服务

@Service
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        return "dubbo say hello:"+ name;
    }
}

启动类:

@SpringBootApplication
public class DubboProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubboProviderApplication.class, args);
    }

}

2.2 dubbo-consumer配置

application.yml

dubbo:
  application:
    name: dubbo-application-consumer
  registry:
    address: 121.36.168.192:2181,121.36.168.192:2182,121.36.168.192:2183
    protocol: zookeeper

启动类使用@Reference订阅服务:

@SpringBootApplication
public class DubboConsumerApplication implements InitializingBean {

    @Reference
    private HelloService helloService;

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println(helloService.sayHello("hhahhahh"));
    }

    public static void main(String[] args) {
        SpringApplication.run(DubboConsumerApplication.class, args);
    }

}

4 高级应用

4.1 负载均衡

dubbo默认的负载均衡策略是随机取一个服务。

首先配置多个协议,相当于注册多个服务,修改提供者配置,增加一个dubbo协议端口为20881,现在注册了两个服务,20880、20881
修改dubbo-provider工程

多协议配置

application.yml

dubbo:
  application:
    name: dubbo-application-provider
  registry:
    address: 121.36.168.192:2181,121.36.168.192:2182,121.36.168.192:2183
    protocol: zookeeper
  scan:
    base-packages: com.example.dubboprovider.service.impl
  protocol:
    name: dubbo
    port: 20880
  protocols:
# key1为任意协议的唯一键
    key1:
      id: dubbo1
      # 同样也配置dubbo协议,使用不同的端口
      name: dubbo
      port: 20881


修改服务实现类HelloServiceImpl,将端口号打印出来

@Service
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        int port =RpcContext.getContext().getUrl().getPort();
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }
}

修改dubbo-consumer工程
修改启动类DubboConsumerApplication

@SpringBootApplication
public class DubboConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubboConsumerApplication.class, args);
    }

}

修改pom.xml文件,增加web包starter

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

新增controller

@RestController
public class HelloController {
    @Reference
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {
        String result = helloService.sayHello("hahahaha");
        System.out.println(result);
        return result;
    }
}

启动dubbo-provider、dubbo-consumer
浏览器访问:http://localhost:8080/say
在这里插入图片描述
可以看到会随机选一个服务。

4.1.1 四种负载均衡策略

random 随机
roundrobin 轮询
leastactive 最少活跃数,是在消费端统计的,每个服务都有一个属性active,选出一个服务,就会对其active属性+1,服务返回就对active属性-1,如果有相同的最小活跃数,则随机。
consistenthash 一致性hash,根据传入的参数不同,匹配到一个服务

4.1.2 负载均衡配置

修改服务实现类,@Service注解添加loadbalance属性,配置为轮询

@Service(loadbalance = "roundrobin")
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        int port =RpcContext.getContext().getUrl().getPort();
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }
}

在这里插入图片描述
可以看出结果为轮询获取服务。

4.1.3 优先级别

负载均衡的配置可以配置在服务端,也可配置在消费端,其中消费端的优先级比服务端高。
修改dubbo-consumer工程
修改HelloController,在@Reference注解加入loadbalance属性

@RestController
public class HelloController {
    @Reference(loadbalance = "random")
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {
        String result = helloService.sayHello("hahahaha");
        System.out.println(result);
        return result;
    }
}

在这里插入图片描述
可以看到结果又变为随机获取服务。

4.2 服务超时

服务端超时时间既可以在服务端配置,也可以在消费端配置,但是二者表示的含义是不一样的,且造成的结果也是不一样的。

一方配置超时时间
指仅在消费者或提供者一方单独配置,只在一方配置超时时候,消费端如果超出时间还未收到返回结果,则会抛出异常,服务端在执行服务时,会检查服务执行时间,如果超时,会打印一个错误日志,服务会正常执行完成。
两方同时配置超时时间
消费者和提供者同时配置超时时间

4.2.1 一方配置

修改dubbo-provider工程
修改服务实现类,@Service加上timeout属性,并且在sayHello方法中睡3s

@Service(loadbalance = "roundrobin",timeout = 2000)
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }
}

修改pom.xml在引入的dubbo包下排除log4j,否则日志不会打印。

<dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.3</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

日志配置,在resources下增加log4j.properties文件

log4j.rootLogger=info, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n

分别启动doubble-provider、dubbo-consumer,浏览器访问http://localhost:8080/say
消费端结果如下:
在这里插入图片描述
提供者结果如下:
在这里插入图片描述
可以看出服务依然执行完成,会打印timeout日志

4.3.2 两方配置

现在在以上代码基础上,修改服务实现类,将timeout修改未7000ms

@Service(loadbalance = "roundrobin",timeout = 7000)
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }
}

修改dubbo-consumer在HelloController的@Reference上配置timeout为4000ms

@RestController
public class HelloController {
    @Reference(loadbalance = "random",timeout = 4000)
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {
        String result = helloService.sayHello("hahahaha");
        System.out.println(result);
        return result;
    }
}

分别启动doubble-provider、dubbo-consumer,浏览器访问http://localhost:8080/say
消费端结果如下:
在这里插入图片描述
提供者结果如下:
在这里插入图片描述
可以看到服务消费端超时报错,服务提供端一切正常。

4.3 集群容错

集群容错就是集群调用失败时,Dubbo提供的一种后续处理方案。可在消费端或服务端配置,如果同时配置将会使容错失效。
dubbo提供了6种种容错方案:
failover Cluster :失败自动切换,出现失败后会重新调用其它服务器,一般用于读操作,但是重试会带来更长延迟。可通过 retries=“2” 来设置重试次数(不含第一次)。这种方式是dubbo的默认配置
failfast Cluster:快速失败,即失败后就报错,一般用于新增等操作。
failsafe Cluster:失败安全,指失败后忽略,一般用于写日志等
failback Cluster:失败自动恢复,指后台记录失败的请求,定时重发,一般用于消息发送。
forking Cluster:并行调用多个服务,一个成功即返回,一般用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。
broadcast Cluster:广播调用所有提供者,依次调用,有一个报错了就报错,一般用于通知所有提供者更新缓存或日志等本地资源信息。

4.3.1 示例应用

4.3.1.1 默认容错方案

通过章节4.2.2可以看出dubbo默认的容错方案失败自动切换,重试了两次,加上第一次调用所以共调用了三次,所以输出了3次字符串。
在这里插入图片描述

4.3.1.2 配置容错方案

本示例配置一个forking Cluster 并行调用,其它方案可自行测试。
修改dubbo-provider工程
修改application.yml,注释掉20881的端口

dubbo:
  application:
    name: dubbo-application-provider
  registry:
    address: 121.36.168.192:2181,121.36.168.192:2182,121.36.168.192:2183
    protocol: zookeeper
  scan:
    base-packages: com.example.dubboprovider.service.impl
  protocol:
    name: dubbo
    port: 20880
#  protocols:
#    key1:
#      id: dubbo1
#      name: dubbo
#      port: 20881


修改服务实现类HelloServiceImpl,@Service注解新增属性cluster,并修改睡眠时间,也可注释,以免浪费时间。

@Service(loadbalance = "roundrobin",timeout = 7000, cluster = "forking")
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }
}

启动dubbo-provider 工程,本次启动后只注册了一个服务。

然后再修改dubbo-provider工程的服务实现类HelloServiceImpl,将实现方法上抛出异常,然后再次启动,注意不要停掉上面已经启动的应用。注意需要在idea启动配置上勾选上运行多个实例启动,配置如下:
修改HelloServiceImpl

@Service(loadbalance = "roundrobin",timeout = 7000, cluster = "forking")
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
        throw new RuntimeException("测试集群容错。。。");
//        return "["+ port + "]" + "dubbo say hello:"+ name;
    }
}

修改application.properties,修改端口为20881

dubbo:
  application:
    name: dubbo-application-provider
  registry:
    address: 121.36.168.192:2181,121.36.168.192:2182,121.36.168.192:2183
    protocol: zookeeper
  scan:
    base-packages: com.example.dubboprovider.service.impl
  protocol:
    name: dubbo
    port: 20881
#  protocols:
#    key1:
#      id: dubbo1
#      name: dubbo
#      port: 20881


idea配置多个实例运行
在这里插入图片描述
在这里插入图片描述
启动两个实例后即注册了两个服务。
下一步启动dubbo-consumer工程,访问http://localhost:8080/say
在这里插入图片描述
查看20880后台,返回成功
在这里插入图片描述
查看20881后台,返回失败
在这里插入图片描述
查看客户端后台,返回成功
在这里插入图片描述

4.3.2 自定义容错方案

自定义容错方法需要实现org.apache.dubbo.rpc.cluster.Cluster接口
修改dubbo-api工程
创建MyCluster.java,自定义容错逻辑为,如果选取的服务异常,那么再重复调用这个服务五次,如果还是异常则返回错误消息

public class MyCluster implements Cluster {
    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new AbstractClusterInvoker<T>(directory) {
            @Override
            protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadBalance) throws RpcException {

                List<Invoker<T>> copyInvokers = invokers;
                // 检查服务列表
                this.checkInvokers(invokers, invocation);

                List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size());
                Result result = null;
                // 通过负载均衡策略选择一个服务
                Invoker<T> invoker = this.select(loadBalance, invocation, copyInvokers, invoked);

                // 自定义容错方案,
                for (int i=0;i<5;i++) {
                        result = invoker.invoke(invocation);
                        if (result.hasException()) {
                            result = new AsyncRpcResult(invocation);
                            result.setValue(invoker.getUrl().getPort()+":大哥我尽力了,尝试了五次都失败了");
                        }
                }


                return result;
            }
        };
    }
}

配置MyCluster
resources目录下面新建META-INF/dubbo/org.apache.dubbo.rpc.cluster.Cluster配置文件,内容如下,其中mycluster为自定义容错策略的名称。

mycluster=com.example.dubboapi.service.cluster.MyCluster

项目结果如下:
在这里插入图片描述
修改dubbo-provider
applicaton.yml,将协议的端口号配置为-1,启动多个实例会分配一个端口从20880依次+1。

dubbo:
  application:
    name: dubbo-application-provider
  registry:
    address: 121.36.168.192:2181,121.36.168.192:2182,121.36.168.192:2183
    protocol: zookeeper
  scan:
    base-packages: com.example.dubboprovider.service.impl
  protocol:
    name: dubbo
    port: -1
#  protocols:
#    key1:
#      id: dubbo1
#      name: dubbo
#      port: 20881


修改实现类HelloServiceImpl ,配置cluster的值为mycluster自定义容错策略。

@Service(loadbalance = "roundrobin",timeout = 7000, cluster = "mycluster")
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
        throw new RuntimeException("测试集群容错。。。");
//        return "["+ port + "]" + "dubbo say hello:"+ name;
    }
}

现在启动3个提供者,再启动消费者,浏览器访问http://localhost:8080/say
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
可以看到返回了我们自定义的错误消息,且只有20882的提供者有被调用。自定义容错策略成功。

4.4 服务降级

通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。
mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
且配置只能再消费端,配置再服务端,服务启动会报错。并且返回信息如果是中文会导致mock失效。

修改dubbo-provider 工程
修改服务实现类HelloServiceImpl ,去掉@Service注解的cluste属性r

@Service(loadbalance = "roundrobin",timeout = 7000)
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
        throw new RuntimeException("测试集群容错。。。");
//        return "["+ port + "]" + "dubbo say hello:"+ name;
    }
}

修改dubbo-consumer工程
修改HelloController,给@Reference 添加mock属性配置。

@RestController
public class HelloController {
    @Reference(loadbalance = "roundrobin",timeout = 4000,mock = "force: return hhh")
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {
        String result = helloService.sayHello("hahahaha");
        System.out.println(result);
        return result;
    }
}

修改pom文件,使用mock会依赖fastjson包

 <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>

启动服务端和消费端,浏览器访问http://localhost:8080/say

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
通过提供方的控制台可以看出此时根本未调用服务。

4.5 本地存根

在客户端执行部分逻辑,这段逻辑一般由服务提供者提供。
本地存根实现:
1.需要配置stub属性,可配置再服务端,可配置再消费端,以消费端为准。
提供端配置如下

Service(stub = "com.example.dubboapi.service.HelloServiceStub")Service(stub = "true")

如果stub的值未true,那么包名必须和服务接口一致,类名必须是接口名+Stub。
2.需要提供stub实现
Stub 类必须要提供传入代理对象的构造函数。 必须实现代理对象的接口。

修改dubbo-api
提供stub实现

public class HelloServiceStub implements HelloService{
    private HelloService helloService;
    public HelloServiceStub(HelloService helloService) {
        this.helloService = helloService;
    }

    @Override
    public String sayHello(String name){
        System.out.println("提供者调用服务之前执行....");
        return helloService.sayHello(name);
    }

}

修改dubbo-provider
修改接口实现类HelloServiceImpl ,@Service添加stub属性

@Service(loadbalance = "roundrobin",timeout = 7000,stub = "com.example.dubboapi.service.HelloServiceStub")
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
//        throw new RuntimeException("测试集群容错。。。");
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }
}

启动服务端、消费端,浏览器访问http://localhost:8080/say

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
可以看到消费端的控制台打印了加入的逻辑,所以这段逻辑是运行再客户端。

4.6 本地伪装

本地伪装和服务降级是一样的都是,服务提供方全部挂掉后,注意是服务挂掉,就是说如果方法抛出的是业务异常如RuntimeException,那么不会走mock的逻辑,必须是服务级别的异常比如挂掉、超时,与业务方法抛出的异常无关,客户端不抛出异常,而是通过 Mock 数据返回。下面介绍几种mock用法

4.6.1 使用简介

// 如果mock的值是true,那么需要在服务接口包下面提供一个名为“接口名+Mock” 的实现。
@Reference(mock = "true")
public class HelloServiceMock implements HelloService{
    @Override
    public String sayHello(String name) {
        return "额 我是一个伪装者";
    }
}
// mock 也可指定一个伪装类。
@Reference(mock = "com.example.dubboapi.service.HelloServiceMock")
// mock 也可指定一个伪装类。
@Reference(mock = "com.example.dubboapi.service.HelloServiceMock")

return使用:使用 return 来返回一个字符串表示的对象,作为 Mock 的返回值。
empty: 代表空,基本类型的默认值,或者集合类的空值
null: null
true: true
false: false
JSON 格式: 反序列化 JSON 所得到的对象

// 
@Reference(mock = "return {name:\"我是一个小mock\",age:\"20\"}")

throw使用:
使用 throw 来返回一个 Exception 对象,作为 Mock 的返回值。

// 抛出RPCException
@Reference(mock = "throw")
// 抛出指定异常
@Reference(mock = "throw java.lang.RuntimeException")

fail本文4.4已经演示,这里再次强调下:
fail失败返回,接口调用失败了会返回,会发起远程调用,这里的失败不是业务级别的异常,比如你服务方法里抛出的RuntimeException并不会走mock,而是服务级别的异常,比如挂掉,超时。

force本文4.4已经演示,这里再次强调下:
force强制返回,不在意服务端怎么样,强制走mock逻辑。

这里增加一点也与 throw连用

// 强制抛出指定异常
@Reference(mock = "force:throw java.lang.RuntimeException")

4.6.2 演示return json

修改dubbo-api工程
新增User.java
在这里插入图片描述

public class User implements Serializable {
    private String name;
    private String age;

    public User(String name, String age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}

修改接口HelloService,新增一个方法

public interface HelloService {
    /**
     * 返回一句话
     * @param name
     * @return: java.lang.String
     * @Author: jt-ape
     * @Date: 2021/2/1 12:32
     */
    public String sayHello(String name);

    public User sayHello();
}

修改dubbo-provider工程
修改实现类HelloServiceImpl,实现新增的方法,注意实现方法抛出的是RpcException

@Service(loadbalance = "roundrobin",timeout = 7000)
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
//        throw new RuntimeException("测试集群容错。。。");
        throw new RpcException("我看看这个异常");
//        return "["+ port + "]" + "dubbo say hello:"+ name;
    }

    @Override
    public User sayHello()  {
        System.out.println("22222222222222222222");
//        return new User("jack", "12");
        throw new RpcException("一定要抛RpcException,或者将服务挂掉");
    }
}

修改dubbo-consumer
修改HelloController ,新增一个方法,并在@Reference注解中添加mock属性

@RestController
public class HelloController {
    @Reference(loadbalance = "roundrobin",timeout = 4000,mock = "return {name:\"我是一个小mock\",age:\"11111\"}")
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {
        String result = helloService.sayHello("hahahaha");
        System.out.println(result);
        return result;
    }

    @RequestMapping("/user")
    public String say1() {
        User result = helloService.sayHello();
        System.out.println(result);
        return result.getName();
    }

}

启动dubbo-provider,dubbo-consumer,启动成功后再关掉dubbo-provider浏览器访问:http://localhost:8080/user
在这里插入图片描述

4.7 参数回调

从服务器端调用客户端逻辑,只需要声明服务方法中哪个参数是 callback 类型即可。
修改dubbo-api工程
新增一个回调接口CallBackListener

public interface CallBackListener {

    public String callBack();
}

在这里插入图片描述
修改接口HelloService,重载一个sayHello参数为CallBackListener

public interface HelloService {
    /**
     * 返回一句话
     * @param name
     * @return: java.lang.String
     * @Author: jt-ape
     * @Date: 2021/2/1 12:32
     */
    public String sayHello(String name);

    public User sayHello();

    public String sayHello(CallBackListener callBackListener);
}

修改dubbo-provider工程
修改服务实现类,实现接口新增的方法,@Service注解增加methods和callbacks属性,
以下注解表示,methods属性的配置表示sayHello方法下标为0的参数是回调参数,callbacks表示回调实例最多有3个。

@Service(loadbalance = "roundrobin",timeout = 7000, methods = {@Method(name = "sayHello",arguments = {@Argument(index = 0, callback = true)})},callbacks = 3)
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
//        throw new RuntimeException("测试集群容错。。。");
//        throw new RpcException("我看看这个异常");
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }

    @Override
    public User sayHello()  {
        System.out.println("22222222222222222222");
//        return new User("jack", "12");
        throw new RpcException("我看看这个异常一定要抛出这个异常RemotingException");
    }

    @Override
    public String sayHello(CallBackListener callBackListener) {
        return callBackListener.callBack();
    }
}

修改dubbo-consumer工程
修改HelloController,新增一个方法say2

@RestController
public class HelloController {
    @Reference(loadbalance = "roundrobin",timeout = 4000,mock = "fail: return 111")
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {
        String result = helloService.sayHello("hahahaha");
        System.out.println(result);
        return result;
    }

    @RequestMapping("/user")
    public String say1() {
        User result = helloService.sayHello();
        System.out.println(result);
        return result.getName();
    }

    @RequestMapping("/callback")
    public String say2() {
        return helloService.sayHello(new CallBackListener() {
            @Override
            public String callBack() {
                return "通过回调方法返回客户端的东西";
            }
        });
    }

}

启动dubbo-provider,启动dubbo-consumer,浏览器访问http://localhost:8080/callback,这里遇到一个非常坑的问题,我dubbo版本是用的2.7.3,结果这个版本使用注解添加methods 属性会报错,xml方式没有试过,后将dubbo版本改为了2.7.5,然后正常了
修改dubbo-provider,dubbo-consumer工程的dubbo版本,将dubbo和dubbo-spring-boot-starter的版本都要改成2.7.5

<dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.5</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

然后启动访问浏览器返回正常。
在这里插入图片描述

4.8 异步调用

Dubbo 的异步调用时使用 CompletableFuture 为基础, 通过NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小。

4.8.1 RpcContest

修改dubbo-provider 工程
修改接口实现类上,将@Servcie上的method属性去掉。

@Service(loadbalance = "roundrobin",timeout = 7000)
public class HelloServiceImpl implements HelloService {

    private final Map<String, CallBackListener> listeners = new ConcurrentHashMap<String, CallBackListener>();

    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
//        throw new RuntimeException("测试集群容错。。。");
//        throw new RpcException("我看看这个异常");
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }

    @Override
    public User sayHello()  {
        System.out.println("22222222222222222222");
//        return new User("jack", "12");
        throw new RpcException("我看看这个异常一定要抛出这个异常RemotingException");
    }

    @Override
    public String sayHello(CallBackListener callBackListener) {
        return callBackListener.callBack();
    }
}

修改dubbo-consumer工程
修改HelloController,使用RpcContext.getContext().asyncCall() 来调用服务,asyncCall方法有两个实现,其中传入Runable接口表示不关心返回,这时是没有返回值的,传入Callable时候会返回CompletableFuture。

@RestController
public class HelloController {
    @Reference
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {
        
        CompletableFuture<String> completableFuture = RpcContext.getContext().asyncCall(() -> {
            return helloService.sayHello("异步调用");
        });
		String result = helloService.sayHello("hahahaha");
        System.out.println(result);
        try {
            String s = completableFuture.get();
            System.out.println("异步返回结果:"+s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return result;
    }

    @RequestMapping("/user")
    public String say1() {
        User result = helloService.sayHello();
        System.out.println(result);
        return result.getName();
    }

    @RequestMapping("/callback")
    public String say2() {
        return helloService.sayHello(new CallBackListener() {
            @Override
            public String callBack() {
                return "通过回调方法返回客户端的东西";
            }
        });
    }

}

启动服务,访问http://localhost:8080/say
在这里插入图片描述
可以看到异步调用没有阻塞,先输出了同步调用结果。

4.8.2 RpcContext + @Method

修改dubbo-consumer工程
修改HelloController,给@Reference注解加上methods属性,配置如下:
其中,@Method还有属性return 如果等于fasle则不会生成CompletableFuture对象,这里不做演示。

@RestController
public class HelloController {
    @Reference(methods = {@Method(name = "sayHello",async = true)})
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {
        String result = helloService.sayHello("hahahaha");
        System.out.println(result);
        return result;
    }

    @RequestMapping("/user")
    public String say1() {
        User result = helloService.sayHello();
        System.out.println(result);
        return result.getName();
    }

    @RequestMapping("/callback")
    public String say2() {
        return helloService.sayHello(new CallBackListener() {
            @Override
            public String callBack() {
                return "通过回调方法返回客户端的东西";
            }
        });
    }

}

这时sayHello方法已经时异步方法了,直接调用会返回null,比如我们现在直接启动服务端和消费端,访问http://localhost:8080/say
在这里插入图片描述
我们可以通过使用RpcContext来接收异步返回结果
修改dubbo-consumer工程
修改HelloController,修改第一个say方法,使用RpcContext来获取一个CompletableFuture,通过它的get()方法获取返回结果,get()会阻塞。

@RestController
public class HelloController {
    @Reference(methods = {@Method(name = "sayHello",async = true)})
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {
        String result = helloService.sayHello("hahahaha");
        System.out.println(result);
        CompletableFuture<String> completableFuture = RpcContext.getContext().getCompletableFuture();
        try {
            String s = completableFuture.get();
            System.out.println("异步返回结果:"+s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return result;
    }

    @RequestMapping("/user")
    public String say1() {
        User result = helloService.sayHello();
        System.out.println(result);
        return result.getName();
    }

    @RequestMapping("/callback")
    public String say2() {
        return helloService.sayHello(new CallBackListener() {
            @Override
            public String callBack() {
                return "通过回调方法返回客户端的东西";
            }
        });
    }

}

启动服务,浏览器访问http://localhost:8080/say
在这里插入图片描述

4.8.3 CompletableFuture

可以直接修改方法签名,返回CompletableFuture,则可以不使用RpcContext。
修改dubbo-api工程
修改接口HelloService,新增一个asyncCall方法

public interface HelloService {
    /**
     * 返回一句话
     * @param name
     * @return: java.lang.String
     * @Author: jt-ape
     * @Date: 2021/2/1 12:32
     */
    public String sayHello(String name);

    public User sayHello();

    public String sayHello(CallBackListener callBackListener);

    public CompletableFuture<String> asyncCall();
}

修改dubbo-provider工程
修改服务实现类,HelloServiceImpl,实现接口方法,注意这里方法里的逻辑需要放到CompletableFuture里执行。

@Service(loadbalance = "roundrobin",timeout = 7000)
public class HelloServiceImpl implements HelloService {

    private final Map<String, CallBackListener> listeners = new ConcurrentHashMap<String, CallBackListener>();

    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
//        throw new RuntimeException("测试集群容错。。。");
//        throw new RpcException("我看看这个异常");
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }

    @Override
    public User sayHello()  {
        System.out.println("22222222222222222222");
//        return new User("jack", "12");
        throw new RpcException("我看看这个异常一定要抛出这个异常RemotingException");
    }

    @Override
    public String sayHello(CallBackListener callBackListener) {
        return callBackListener.callBack();
    }

    @Override
    public CompletableFuture<String> asyncCall() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "我是一个异步方法";
        });
    }
}

修改dubbo-consumer工程
修改HelloController,去掉@Rerence中的methods属性,新增一个方法say3

@RestController
public class HelloController {
    @Reference
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {

        CompletableFuture<String> completableFuture = RpcContext.getContext().asyncCall(() -> {
            return helloService.sayHello("异步调用");
        });

        String result = helloService.sayHello("hahahaha");
        System.out.println(result);

        try {
            String s = completableFuture.get();
            System.out.println("异步返回结果:"+s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return result;
    }

    @RequestMapping("/user")
    public String say1() {
        User result = helloService.sayHello();
        System.out.println(result);
        return result.getName();
    }

    @RequestMapping("/callback")
    public String say2() {
        return helloService.sayHello(new CallBackListener() {
            @Override
            public String callBack() {
                return "通过回调方法返回客户端的东西";
            }
        });
    }
    @RequestMapping("/sync")
    public String say3() throws ExecutionException, InterruptedException {
        CompletableFuture<String> stringCompletableFuture = helloService.asyncCall();
        
        return  stringCompletableFuture.get();
    }

}

启动服务,访问http://localhost:8080/sync
在这里插入图片描述

4.8.4 CompletableFuture 不阻塞获取结果的方式

修改dubbo-consumer 工程
修改HelloController,给CompletableFuture对象添加回调

@RestController
public class HelloController {
    @Reference
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {

        CompletableFuture<String> completableFuture = RpcContext.getContext().asyncCall(() -> {
            return helloService.sayHello("异步调用");
        });

        String result = helloService.sayHello("hahahaha");
        System.out.println(result);

        try {
            String s = completableFuture.get();
            System.out.println("异步返回结果:"+s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return result;
    }

    @RequestMapping("/user")
    public String say1() {
        User result = helloService.sayHello();
        System.out.println(result);
        return result.getName();
    }

    @RequestMapping("/callback")
    public String say2() {
        return helloService.sayHello(new CallBackListener() {
            @Override
            public String callBack() {
                return "通过回调方法返回客户端的东西";
            }
        });
    }
    @RequestMapping("/sync")
    public String say3() throws ExecutionException, InterruptedException {
        CompletableFuture<String> stringCompletableFuture = helloService.asyncCall();

        // 添加回调
        stringCompletableFuture.whenComplete((result, exception) -> {
           if (exception == null) {
               System.out.println("异步返回结果:"+result);
           } 
        });
        System.out.println("输出一段话,打印再异步返回结果前面");
        return "success";
    }

}

启动服务,访问http://localhost:8080/sync
在这里插入图片描述

4.9 泛化调用

泛化调用可以不依赖服务端提供的接口,直接使用GenericService完成接口调用。
修改dubbo-api工程
修改接口HelloService,新增一个方法GenericCall。

public interface HelloService {
    /**
     * 返回一句话
     * @param name
     * @return: java.lang.String
     * @Author: jt-ape
     * @Date: 2021/2/1 12:32
     */
    public String sayHello(String name);

    public User sayHello();

    public String sayHello(CallBackListener callBackListener);

    public CompletableFuture<String> asyncCall();

    public String genericCall(User user);
}

修改dubbo-provide 工程
修改服务实现类HelloServiceImpl,实现接口新增的genericCall方法

@Service(loadbalance = "roundrobin",timeout = 7000)
public class HelloServiceImpl implements HelloService {

    private final Map<String, CallBackListener> listeners = new ConcurrentHashMap<String, CallBackListener>();

    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
//        throw new RuntimeException("测试集群容错。。。");
//        throw new RpcException("我看看这个异常");
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }

    @Override
    public User sayHello()  {
        System.out.println("22222222222222222222");
//        return new User("jack", "12");
        throw new RpcException("我看看这个异常一定要抛出这个异常RemotingException");
    }

    @Override
    public String sayHello(CallBackListener callBackListener) {
        return callBackListener.callBack();
    }

    @Override
    public CompletableFuture<String> asyncCall() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "我是一个异步方法";
        });
    }

    @Override
    public String GenericCall(User user) {
        System.out.println(user);
        return "泛化返回成,接收到的用户名:"+user.getName();
    }
}

修改dubbo-consumer工程
修改HelloController,使用@Reference注入一个GenericService ,新增一个方法say4,注意调用方法的参数如果是POJO,可用map传递参数。

@RestController
public class HelloController {
    @Reference(interfaceName = "com.example.dubboapi.service.HelloService",generic = true)
    private GenericService genericService;
    @Reference
    private HelloService helloService;
    @RequestMapping("/say")
    public String say() {

        CompletableFuture<String> completableFuture = RpcContext.getContext().asyncCall(() -> {
            return helloService.sayHello("异步调用");
        });

        String result = helloService.sayHello("hahahaha");
        System.out.println(result);

        try {
            String s = completableFuture.get();
            System.out.println("异步返回结果:"+s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return result;
    }

    @RequestMapping("/user")
    public String say1() {
        User result = helloService.sayHello();
        System.out.println(result);
        return result.getName();
    }

    @RequestMapping("/callback")
    public String say2() {
        return helloService.sayHello(new CallBackListener() {
            @Override
            public String callBack() {
                return "通过回调方法返回客户端的东西";
            }
        });
    }
    @RequestMapping("/sync")
    public String say3() throws ExecutionException, InterruptedException {
        CompletableFuture<String> stringCompletableFuture = helloService.asyncCall();

        // 添加回调
        stringCompletableFuture.whenComplete((result, exception) -> {
           if (exception == null) {
               System.out.println("异步返回结果:"+result);
           }
        });
        System.out.println("输出一段话,打印再异步返回结果前面");
        return "success";
    }

    @RequestMapping("/generic")
    public String say4() {

       Map<String,Object> user = new HashMap<String,Object>();
        user.put("class", "com.example.dubboapi.model.User");
        user.put("name","泛化用户");
        user.put("age", "23");

        return genericService.$invoke("genericCall", new String[] {"com.example.dubboapi.model.User"}, new Object[]{user}).toString();
    }

}

启动服务,浏览器访问http://localhost:8080/generic
在这里插入图片描述

4.10 泛化服务

通过实现 GenericService 接口处理所有服务请求。
修改dubbo-provider工程
新增一个GenericService的实现GenericServiceImpl

@Service(interfaceName = "com.example.dubboapi.service.HelloService",version = "generic")
public class GenericServiceImpl implements GenericService {
    @Override
    public Object $invoke(String s, String[] strings, Object[] objects) throws GenericException {
        return "这是一个泛化服务啊哈哈哈";
    }
}

修改dubbo-consumer工程
修改HelloController,将之前的引入服务都注释,只保留

@RestController
public class HelloController {


    @Reference(version = "generic")
    private HelloService genericHelloService;

    
    @RequestMapping("/genericService")
    public String say5() {
        return genericHelloService.sayHello("11111");
    }

}

启动服务端和消费端,浏览器访问http://localhost:8080/say
在这里插入图片描述

4.11 rest调用

修改dubbo-provider工程
修改pom.xml,新增4个依赖

<dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
            <version>3.0.19.Final</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-rpc-http</artifactId>
            <version>2.7.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>

修改application.yml,配置rest协议

dubbo:
  application:
    name: dubbo-application-provider
  registry:
    address: 121.36.168.192:2181,121.36.168.192:2182,121.36.168.192:2183
    protocol: zookeeper
  scan:
    base-packages: com.example.dubboprovider.service.impl
  protocol:
    name: rest
    port: 8083

修改HelloServiceImpl ,类上增加@Path注解配置路径,在sayHello方法添加@Get注解表示get请求,@Path配置路径,@Consumers配置传入数据格式,@Produces 指定返回数据格式,

@PathParam 指定访问路径上的参数。

@Service(loadbalance = "roundrobin",timeout = 7000)
@Path("hello")
public class HelloServiceImpl implements HelloService {

    private final Map<String, CallBackListener> listeners = new ConcurrentHashMap<String, CallBackListener>();

    @GET
    @Path("sayHello/{name}")
    @Consumes({}) // 指定传入数据格式
    @Produces({"application/json; charset=UTF-8", "text/xml; charset=UTF-8"}) // 指定返回数据格式
    @Override
    public String sayHello(@PathParam("name") String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
//        throw new RuntimeException("测试集群容错。。。");
//        throw new RpcException("我看看这个异常");
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }

    @Override
    public User sayHello()  {
        System.out.println("22222222222222222222");
//        return new User("jack", "12");
        throw new RpcException("我看看这个异常一定要抛出这个异常RemotingException");
    }

    @Override
    public String sayHello(CallBackListener callBackListener) {
        return callBackListener.callBack();
    }

    @Override
    public CompletableFuture<String> asyncCall() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "我是一个异步方法";
        });
    }

    @Override
    public String genericCall(User user) {
        System.out.println(user);
        return "泛化返回成,接收到的用户名:"+user.getName();
    }
}

修改GenericServiceImpl ,注释调@Service注解,因为配置了rest协议,如果不注释会使用rest协议,但是GenericServiceImpl 上没有rest协议的配置。

//@Service(interfaceName = "com.example.dubboapi.service.HelloService",version = "generic")
public class GenericServiceImpl implements GenericService {
    @Override
    public Object $invoke(String s, String[] strings, Object[] objects) throws GenericException {
        return "这是一个泛化服务啊哈哈哈";
    }
}

启动dubbo-provider,浏览器访问http://localhost:8083/hello/sayHello/hahah
在这里插入图片描述

@QueryParam 指定url中的参数

修改dubbo-provider
修改HelloServiceImpl,在sayHello的参数上使用@QueryParam注解

@Service(loadbalance = "roundrobin",timeout = 7000)
@Path("hello")
public class HelloServiceImpl implements HelloService {

    private final Map<String, CallBackListener> listeners = new ConcurrentHashMap<String, CallBackListener>();

    @GET
    @Path("sayHello")
    @Consumes({}) // 指定传入数据格式
    @Produces({"application/json; charset=UTF-8", "text/xml; charset=UTF-8"}) // 指定返回数据格式
    @Override
    public String sayHello(@QueryParam("name") String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int port =RpcContext.getContext().getUrl().getPort();
        System.out.println("11111111111111111111111111111111111");
//        throw new RuntimeException("测试集群容错。。。");
//        throw new RpcException("我看看这个异常");
        return "["+ port + "]" + "dubbo say hello:"+ name;
    }

    @Override
    public User sayHello()  {
        System.out.println("22222222222222222222");
//        return new User("jack", "12");
        throw new RpcException("我看看这个异常一定要抛出这个异常RemotingException");
    }

    @Override
    public String sayHello(CallBackListener callBackListener) {
        return callBackListener.callBack();
    }

    @Override
    public CompletableFuture<String> asyncCall() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "我是一个异步方法";
        });
    }

    @Override
    public String genericCall(User user) {
        System.out.println(user);
        return "泛化返回成,接收到的用户名:"+user.getName();
    }
}

启动dubbo-provider,浏览器访问http://localhost:8083/hello/sayHello?name=123123
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值