Dubbo的基本应用与进阶应用


前言

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。

本文将通过三个方面进行dubbo的应用,包括基础调用、异步调用、泛化调用


zookeeper安装与使用

注册中心我们选择zookeeper,我们下载好zookeeper后,以windows方式启动zookeeper为例
1、下载zookeeper,下载地址,官网,下载带bin的压缩包,如apache-zookeeper-3.6.4-bin.tar.gz
2、下载完后解压,在conf目录下的zoo_sample.cfg文件复制一份为zoo.cfg,修改dataDir配置,例如dataDir=D:\java\apache-zookeeper-3.6.4-bin\data
完整配置如下

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=D:\java\apache-zookeeper-3.6.4-bin\data
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1

## Metrics Providers
#
# https://prometheus.io Metrics Exporter
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true


3、打开bin目录下的zkServer.cmd,启动
启动成功如图

在这里插入图片描述

一、基础调用

1、基于dubbo api调用

引入maven依赖

        <dubbo.version>3.1.0</dubbo.version>

		<dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>${dubbo.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-x-discovery</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.13</version>
        </dependency>

定义服务接口
服务接口 Dubbo 中沟通消费端和服务端的桥梁。

public interface GreetingsService {

    String sayHi(String name);

}

定义服务端的实现
定义了服务接口之后,可以在服务端这一侧定义对应的实现,这部分的实现相对于消费端来说是远端的实现,本地没有相关的信息。

public class GreetingsServiceImpl implements GreetingsService {
    @Override
    public String sayHi(String name) {
        return "hi, " + name;
    }
}

服务端发布服务
在实现了服务之后,本小节将通过 Dubbo 的 API 在网络上发布这个服务。

public class ProviderApplication {
    public static void main(String[] args) throws IOException {
        // 设置应用名称
        ApplicationConfig config = new ApplicationConfig();
        config.setName("ProviderApplication");

        // 连接注册中心
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("127.0.0.1:2181");
        registryConfig.setProtocol("zookeeper");

        // 设置协议
        ProtocolConfig protocolConfigDubbo = new ProtocolConfig();
        protocolConfigDubbo.setPort(12881);
        // 设置dubbo的协议
        protocolConfigDubbo.setName("dubbo");

        // 服务提供者暴露服务
        ServiceConfig<GreetingsService> serviceServiceConfig = new ServiceConfig<>();
        serviceServiceConfig.setApplication(config);                    // 设置应用名称
        serviceServiceConfig.setRegistry(registryConfig);               // 设置注册中心
        serviceServiceConfig.setProtocol(protocolConfigDubbo);          // 设置DUBBO协议
        serviceServiceConfig.setInterface(GreetingsService.class);      // 设置接口
        serviceServiceConfig.setRef(new GreetingsServiceImpl());        // 设置具体实现类
        serviceServiceConfig.export();                                  // 暴露和注册服务

        System.in.read();
    }
}

消费端订阅并调用
对于消费端,可以通过 Dubbo 的 API 可以进行消费端订阅。

public class ConsumerApplication {

    public static void main(String[] args) throws IOException {
        //设置应用名称
        ApplicationConfig config = new ApplicationConfig();
        config.setName("ConsumerApplication");

        //连接注册中心
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("127.0.0.1:2181");  //本地虚拟机的地址
        registryConfig.setProtocol("zookeeper");

        ReferenceConfig<GreetingsService> referenceConfig = new ReferenceConfig<>();
        referenceConfig.setApplication(config);
        referenceConfig.setRegistry(registryConfig);
        referenceConfig.setInterface(GreetingsService.class);
        referenceConfig.setProtocol("dubbo");

        GreetingsService greetingsService = referenceConfig.get();
        String msg = greetingsService.sayHi("dubbo");
        System.out.println("Receive result ======> " + msg);   //具体的调用
        System.in.read();

    }
}

启动完成后,打印如下,表示消费成功
在这里插入图片描述

2、集成 Springboot 调用

maven 依赖,除了以上依赖,还需引用spring的相关依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
    </parent>
    
        <nacos-spring-boot.version>0.2.12</nacos-spring-boot.version>
        <nacos.client.version>2.1.0</nacos.client.version>
        
<!--   spring tarter     -->
        <!--Spring Boot核心启动器,包含了自动配置、日志和YAML-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!--   第三方starter     -->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-config-spring-boot-starter</artifactId>
            <version>${nacos-spring-boot.version}</version>
        </dependency>
          <!--   第三方依赖包     -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>${dubbo.version}</version>
        </dependency>

定义实体类


public class Student implements Serializable {
    private static final long serialVersionUID = -1L;

    private String name;
    private Integer age;

    public Student() {
    }

    public String getName() {
        return this.name;
    }

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

    public Integer getAge() {
        return this.age;
    }

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

    public String toString() {
        return "Student{name='" + this.name + '\'' + ", age=" + this.age + '}';
    }
}

定义服务接口

public interface GreetingsProvider {
    String sayHi(String name);

    String sayHello(Student student);

    Student sayHello(String name);


}

定义服务端的实现

@DubboService
public class GreetingsProviderImpl implements GreetingsProvider{
    @Override
    public String sayHi(String name) {
        return "hi, " + name;
    }

    @Override
    public String sayHello(Student student) {
        return "hello : student :" + JSON.toJSONString(student);
    }

    @Override
    public Student sayHello(String name) {
        Student student = new Student();
        student.setName(name);
        student.setAge(5);
        return student;
    }


}

服务端发布服务

  • yml配置
    dubbo:
      application:
        name: dubbo-springboot-start-provider
      protocol:
        name: dubbo
        port: -1
      registry:
        address: zookeeper://127.0.0.1:2181
      provider:
        timeout: 30000
    
  • 服务端启动类
    @SpringBootApplication
    @EnableDubbo
    public class ProviderApplication {
        public static void main(String[] args) {
            SpringApplication.run(ProviderApplication.class, args);
        }
    }
    
  • 启动ProviderApplication
    在这里插入图片描述

消费者订阅并调用

  • yml配置
    dubbo:
      application:
        name: dubbo-springboot-start-consumer
      protocol:
        name: dubbo
        port: -1
      registry:
        address: zookeeper://127.0.0.1:2181
      scan:
        basePackages: org.sjl
      consumer:
        timeout: 30000
    
  • 消费者消费任务
    @Component
    public class ConsumerTask implements CommandLineRunner {
        @DubboReference
        private GreetingsProvider greetingsProvider;
    
        @Override
        public void run(String... args) throws Exception {
            String result = greetingsProvider.sayHi("dubbo");
            System.out.println("Receive result ======> " + result);
    
        }
    }
    
  • 消费端启动类
    @SpringBootApplication
    @EnableDubbo
    public class ConsumerApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerApplication.class, args);
        }
    }
    
  • 消费端启动服务,消费成功,

二、异步调用

maven依赖和yml配置保持和集成 Springboot 调用一致

1、使用 CompletableFuture 签名的接口

定义服务接口

public interface AsyncProvider {
    CompletableFuture<String> sayHelloAsync(String name);
}

服务端的实现
通过 return CompletableFuture.supplyAsync() ,业务执行已从 Dubbo 线程切换到业务线程,避免了对 Dubbo 线程池的阻塞。
注意接口的返回类型是 CompletableFuture

@DubboService
public class AsyncProviderImpl implements AsyncProvider {
    @Override
    public CompletableFuture<String> sayHelloAsync(String name) {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println(name);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {

            }
            return "async response from provider.";
        });
    }
}

消费者消费任务
输出顺序为1、2、3

@Component
public class AsyncTask implements CommandLineRunner {
    @DubboReference
    private AsyncProvider asyncProvider;

    @Override
    public void run(String... args) throws Exception {
        // 调用直接返回CompletableFuture
        CompletableFuture<String> future = asyncProvider.sayHelloAsync("async call request");
        // 增加回调
        future.whenComplete((v, t) -> {
            if (t != null) {
            	// 异常处理
                t.printStackTrace();
            } else {
            	// 2 任务完成前置处理
                System.out.println("Response: " + v);
            }
        });
        // 1
        System.out.println("Executed before response return.");
        // 3 任务完成结果
        System.out.println("future get " + future.get());

    }
}

服务端和消费者启动类启动
消费结果如图
在这里插入图片描述

2、使用 AsyncContext

定义服务接口

public interface AsyncProvider {
    String sayHiAsync(String name);
}

服务端的实现
Dubbo 提供了一个类似 Servlet 3.0 的异步接口AsyncContext,在没有 CompletableFuture 签名接口的情况下,也可以实现 Provider 端的异步执行

@DubboService
public class AsyncProviderImpl implements AsyncProvider {
    @Override
    public String sayHiAsync(String name) {
        final AsyncContext asyncContext = RpcContext.startAsync();
        new Thread(() -> {
            // 如果要使用上下文,则必须要放在第一句执行
            asyncContext.signalContextSwitch();
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 写回响应
            asyncContext.write("Hello " + name + ", response from provider.");
        }).start();
        return null;
    }
}

消费者消费任务

@Component
public class AsyncTask2 implements CommandLineRunner {
    @DubboReference
    private AsyncProvider asyncProvider;

    @Override
    public void run(String... args) throws Exception {
        // 调用返回结果
        String asyncResult = asyncProvider.sayHiAsync("async call request");
        System.out.println("asyncResult = " + asyncResult);
    }
}

服务端和消费者启动类启动
消费结果如图
在这里插入图片描述

3、使用 RpcContext 实现消费端异步调用

定义服务接口

public interface AsyncProvider {
    String sayHello(String name);
}

服务端的实现

@DubboService
public class AsyncProviderImpl implements AsyncProvider {

    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }
        return "sync response from provider";
    }

}

消费者消费任务

@Component
public class AsyncTask3 implements CommandLineRunner {
    @DubboReference
    private AsyncProvider asyncProvider;

    @Override
    public void run(String... args) throws Exception {
    	// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
        CompletableFuture<String> future = RpcContext.getServiceContext().asyncCall(
                () -> asyncProvider.sayHello("consumer async")
        );
		// 期间可以处理其它业务
        System.out.println("consumer async:" + future.get());

    }
}

服务端和消费者启动类启动
消费结果如图
在这里插入图片描述

异步的作用能让资源更充分利用,在对接口返回速度没有要求的情况下可以尝试使用异步处理

三、泛化调用

1、基于springboot集成的泛化调用

定义服务接口

public interface GreetingsProvider {
    String sayHi(String name);

    String sayHello(Student student);

    Student sayHello(String name);

}

服务端的实现

@DubboService
public class GreetingsProviderImpl implements GreetingsProvider{
    @Override
    public String sayHi(String name) {
        return "hi, " + name;
    }

    @Override
    public String sayHello(Student student) {
        return "hello : student :" + JSON.toJSONString(student);
    }

    @Override
    public Student sayHello(String name) {
        Student student = new Student();
        student.setName(name);
        student.setAge(5);
        return student;
    }

}

消费者消费任务

  • 在设置 ReferenceConfig 时,使用 setGeneric(“true”) 来开启泛化调用
  • 配置完 ReferenceConfig 后,使用 referenceConfig.get() 获取到 GenericService 类的实例
  • 使用其 $invoke 方法获取结果
@Component
public class GeneralTask implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        // 创建注册中心配置
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("zookeeper://127.0.0.1:2181");
        // 创建服务引用配置
        ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
        // 设置接口
        referenceConfig.setInterface("org.sjl.dubbo.GreetingsProvider");
        referenceConfig.setRegistry(registryConfig);
        // 重点:设置为泛化调用
        // 注:不再推荐使用参数为布尔值的setGeneric函数
        // 应该使用referenceConfig.setGeneric("true")代替
        referenceConfig.setGeneric("true");
        // 设置超时时间
        referenceConfig.setTimeout(7000);

        // 获取服务,由于是泛化调用,所以获取的一定是GenericService类型
        GenericService genericService = referenceConfig.get();

        // 使用GenericService类对象的$invoke方法可以代替原方法使用
        // 第一个参数是需要调用的方法名
        // 第二个参数是需要调用的方法的参数类型数组,为String数组,里面存入参数的全类名。
        // 第三个参数是需要调用的方法的参数数组,为Object数组,里面存入需要的参数。
        Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"hello world"});
        // 打印结果
        Student student = JSONObject.parseObject(JSON.toJSONString(result), Student.class);
        System.err.println("invokeSayHello1(return): " + student);

        Student student1 = new Student();
        student1.setName("hello");
        student1.setAge(5);
        Object result2 = genericService.$invoke("sayHello", new String[]{"org.sjl.model.Student"}, new Object[]{student1});
        // 打印结果
        System.err.println("invokeSayHello2(return): " + result2);

        Object result3 = genericService.$invoke("sayHi", new String[]{"java.lang.String"}, new Object[]{"su"});
        // 打印结果
        System.err.println("invokeSayHello3(return): " + result3);
    }
}

服务端和消费者启动类启动
消费结果如图
在这里插入图片描述

2、基于API的泛化调用

  • 服务端不变
  • 消费者消费任务
    • 和springboot的区别是需要多注册ApplicationConfig
public class GenericConsumer {

    public static void main(String[] args){
        // 创建ApplicationConfig 比springboot需要多注册ApplicationConfig 
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("generic-call-consumer");
        // 创建注册中心配置
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("zookeeper://127.0.0.1:2181");
        applicationConfig.setRegistry(registryConfig);
        // 创建服务引用配置
        ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
        // 设置接口
        referenceConfig.setInterface("org.sjl.dubbo.GreetingsProvider");
        referenceConfig.setApplication(applicationConfig);
        // 重点:设置为泛化调用
        // 注:不再推荐使用参数为布尔值的setGeneric函数
        // 应该使用referenceConfig.setGeneric("true")代替
        referenceConfig.setGeneric("true");
        // 设置超时时间
        referenceConfig.setTimeout(7000);

        // 获取服务,由于是泛化调用,所以获取的一定是GenericService类型
        GenericService genericService = referenceConfig.get();

        // 使用GenericService类对象的$invoke方法可以代替原方法使用
        // 第一个参数是需要调用的方法名
        // 第二个参数是需要调用的方法的参数类型数组,为String数组,里面存入参数的全类名。
        // 第三个参数是需要调用的方法的参数数组,为Object数组,里面存入需要的参数。
        Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"hello world"});
        // 打印结果
        Student student = JSONObject.parseObject(JSON.toJSONString(result), Student.class);
        System.err.println("invokeSayHello1(return): " + student);

        Student student1 = new Student();
        student1.setName("hello");
        student1.setAge(5);
        Object result2 = genericService.$invoke("sayHello", new String[]{"org.sjl.model.Student"}, new Object[]{student1});
        // 打印结果
        System.err.println("invokeSayHello2(return): " + result2);

        Object result3 = genericService.$invoke("sayHi", new String[]{"java.lang.String"}, new Object[]{"su"});
        // 打印结果
        System.err.println("invokeSayHello3(return): " + result3);

    }

}

启动消费者,消费结果如图
在这里插入图片描述


总结

以上能满足日常开发的大部分场景,基础调用可以快捷进入开发、异步调用可以释放与有效利用资源、泛化调用可以不引入服务接口,同时可以进行动态注册,适合一些需要动态扩展的场景。利用好相关的应用,能够让系统更加健壮、有伸缩性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值