• 3.Dubbo各种特性使用方法


    准备

    将producer模块复制出来三份,分别修改以下内容

    • service.port

    • dubbo.protocol.port

    • HelloWordImpl.java中的输出内容

      public class HelloWordImpl implements HelloWord {
          public String helloWord(String s) {
              return "HelloWordImpl20881:"+s;
      //        return "HelloWordImpl20882:"+s;
        //      return "HelloWordImpl20883:"+s;
          }
      }
      
    • 三个生产者全部启动

    version

    消费者可通过指定版本来控制要RPC调用对应版本的生产者

    • 修改生产者实现类注解

      //指定对外暴漏的服务版本为20882
      @DubboReference(version = "20882")
      
    • 修改消费者@DubboReference

      //指定请求版本为20882的生产者
      @DubboReference(version = "20882")
      

    protocol通讯协议

    dubbo可对协议扩展,例如:Dubbo\Rest\Http\⾃定义

    配置单个协议

      protocol:
        port: 20881 #配置当前这个服务在dubbo容器中的端⼝号,每个dubbo容器内部的服务的端⼝号唯一
        name: dubbo
    

    配置多个协议

      protocols:
        protocol_1:
          id: protocol_1
          name: rest
          port: 8080
          host: 127.0.0.1
        protocol_2:
          id: protocol_2
          name: dubbo
          port: 21881
          host: 127.0.0.1
        protocol_3:
          id: protocol_3
          name: http
          port: 8181
          host: 127.0.0.1
    

    协议指定方式

    //protocol不指定,yml声明了几个协议,就会对每个协议分别创建一份对应请求协议的服务
    @DubboService(version = "20883",protocol = "配置中指定的id")
    public class HelloWordImpl implements HelloWord {
        public String helloWord(String s) {
            return "HelloWordImpl20883:"+s;
        }
    }
    

    选择Rest协议

    最新版本鼓捣了好久,最终通过翻官方代码找到的正确使用姿势

    POM

    添加GVA

            <!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-rpc-rest -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-rpc-rest</artifactId>
                <version>3.0.6</version>
            </dependency>
    

    YML

    server:
      port: 8080
    dubbo:
      application:
        name: hello-word-service #服务名称
      registry:
        address: zookeeper://192.168.0.104:2181,192.168.0.104:2182,192.168.0.104:2183,192.168.0.104:2184 #zk地址
      protocol:
        port: 6060 #rest暴漏端口
        name: rest #选择rest
    

    修改生产者实现类

    下面最终请求地址为 127.0.0.1:6066/rest/hello/helloWord?param=value

    //                               path相当于在@Path前又加了一个前缀,可省略
    @DubboService(protocol = "rest",path = "rest")
    @Path("hello") //@RequestMapping
    public class HelloWordImpl implements HelloWord {
        @GET  //发送get请求,还有put post等
        @Path("helloWord") // 方法请求路径 ,@GET和@Path = @GetMapping
        @Produces({ContentType.APPLICATION_JSON_UTF_8}) //设置ContentType,可设置多个
        public String helloWord(@QueryParam("param") String param) {
            return "HelloWordImpl20881:"+param;
        }
    }
    

    跳过注册中心,直连请求

    生产者配置

    假设生产者通过下面配置,并且实现类只标注了@DubboService(version = "v1"),未加protocol=‘’属性来指明到底用哪个通讯协议,则生产者启动会开启两个协议通讯端口

      protocols:
        protocol_20881:
          id: protocol_20881
          name: dubbo
          port: 20881
          host: 0.0.0.0
        protocol_20882:
          id: protocol_20882
          name: dubbo
          port: 20882
          host: 0.0.0.0
    

    消费者直连方式

    	//添加版本和直连的url/端口/接口全包名
    	//输入20882和20881都可调用成功,说明开启了两个通讯协议端口
    	@DubboReference(version = "v1",url ="dubbo://127.0.0.1:20882/myinterface.HelloWord")
        private HelloWord helloWord;
    
        @Test
        void contextLoads() {
            //执行代理方法
            System.out.println(helloWord.helloWord("RB"));
        }
    

    超时配置

    @DubboReference(version = "v1",timeout = 3000)
    //消费者配置超过3秒就超时报错
    @DubboService(version = "v1",timeout = 2000)
    //生产者配置超过2秒就报错
    
    • 都不配置默认超时时间1秒

    • 如果消费者或生产者只配置一方,则超时时间消费者和生产者共享配置

    • 如果消费者配置3秒过期,生产者配置2秒过期

      1. 当方法执行超过3秒后,消费者报错

      2. 当方法执行超过2秒后,生产者会打印WARN日志,但方法还会执行完,不会终止

        [04/04/22 14:47:39:086 CST] NettyServerWorker-3-1  WARN transport.AbstractServer:  [DUBBO] All clients has disconnected from /192.168.0.105:20882. You can graceful shutdown now., dubbo version: 3.0.6, current host: 192.168.0.105
        

    集群容错(高可用)机制

    dubbo为集群调⽤提供了容错⽅案:

    • failover(默认):

      当出现失败时,会进⾏重试,默认重试2次,⼀共三次调⽤。但是会出现幂等性问题。 虽然会出现幂等性问题,但是依然推荐使⽤这种容错机制,在业务层⾯解决幂等性问题:

      • ⽅案⼀:把数据的业务id作为数据库的联合主键,此时业务id不能重复。

      • ⽅案⼆(推荐):使⽤分布式锁来解决重复消费问题。

    • failfast:当出现失败时。⽴即报错,不进⾏重试。

    • failsafe:失败不报错,记⼊⽇志。

    • failback:失败就失败,开启定时任务 定时重发。

    • forking:并⾏访问多个服务器,获取某⼀个结果既视为成功。

    结论:使⽤dubbo,不推荐把重试关掉,⽽是在⾮幂等性操作的场景下,服务提供者⽅ 要做幂等性的解决⽅案(保证)。

    服务降级

    可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。

    向注册中心写入动态配置覆盖规则:

    RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
    Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
    registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
    

    其中:

    • mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
    • 还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
    • Demo
    //消费者注解添加mock
    @DubboReference(version = "v1",mock = "force:return timeout")
    

    本地存根

    类似熔断机制

    远程服务后,客户端通常只剩下接⼝,⽽实现全在服务器端,但提供⽅有些时候想在客户端 也执⾏部分逻辑,⽐如:做 ThreadLocal 缓存,提前验证参数,调⽤失败后伪造容错数据等 等,此时就需要在 API 中带上 Stub,客户端⽣成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub 1,然后把 Stub 暴露给⽤户,Stub 可以决定要不要去调 Proxy。

    消费端配置

    @DubboReference(version = "v1",stub = "com.rb.customer.HelloWord1Stub")
    private HelloWord helloWord;
    //或
    @DubboReference(version = "v1",stub = "true")  
    private HelloWord helloWord;
    

    Stub类

    public class HelloWordStub implements HelloWord {
        private final HelloWord helloWord;
        public HelloWord1Stub(HelloWord helloWord) {
            this.helloWord = helloWord;
        }
        public String helloWord(String parameter) {
            try {
                return helloWord.helloWord(parameter);
            } catch (Exception e) {
                return "Stub-Error:"+parameter;
            }
        }
    }
    

    流程

    stub配置为true

    1. dubbo会在接口HelloWord包路径下寻找HelloWordStub.java

    stub配置为自定义*Stub.java路径

    1. dubbo会在使用定义路径下的*Stub.java

      然后执行下面两步

    2. 消费端实际调用的是HelloWordStub下helloWord()方法

    3. 如果HelloWordStub类中 Dubbo提供的HelloWord类报错,则调用catch方法

    参数回调

    个人理解就是类似支付后回调的流程,用户支付后通过Dubbo调用支付宝,支付宝入账后再通过反向代理调用用户端提供的方法。

    通过参数回调从服务器端调用客户端逻辑

    参数回调方式与调用本地 callback 或 listener 相同,只需要在 Spring 的配置文件中声明哪个参数是 callback 类型即可。Dubbo 将基于长连接生成反向代理,这样就可以从服务器端调用客户端逻辑。可以参考 dubbo 项目中的示例代码

    创建生产者回调接口

    package myinterface;
    
    /**
     * 创建生产者回调接⼝
     */
    public interface CallbackListener {
        void callback(String msg);
    }
    
    

    创建哪些功能方法需要回调

    package myinterface;
    
    /**
     * 定义一个功能接口
     */
    public interface HelloWord {
        String helloWord(String parameter);
        //创建带回调的方法,key用于让dubbo知道具体回调哪个方法,callbackListener为生产者调用回调的对象
        String helloWord(String parameter,String key,CallbackListener callbackListener);
    }
    
    

    消费者端添加实现回调接口类

    package com.rb.customer;
    
    import myinterface.CallbackListener;
    import java.io.Serializable;
    
    public class CallbackServiceImpl implements CallbackListener, Serializable {
    
        @Override
        public void callback(String s) {
            //如果是支付后回调,那这就是支付成功支付宝通知用户该修改订单状态了
            System.out.println("生产者回调:"+s);
        }
    }
    

    消费者发起请求

    helloWord.helloWord("RB","V1",new CallbackServiceImpl())
    

    生产者接到请求处理

    package com.rb.producer.impl;
    
    
    import myinterface.CallbackListener;
    import myinterface.HelloWord;
    import org.apache.dubbo.common.URL;
    import org.apache.dubbo.config.annotation.Argument;
    import org.apache.dubbo.config.annotation.DubboService;
    import org.apache.dubbo.config.annotation.Method;
    import org.apache.dubbo.rpc.RpcContext;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 对接口的实现
     */
    //添加Dubbo注解
    //name:哪个方法开启回调,arguments.index:对应方法第几个参数开启回调,可配置多个
    @DubboService(version = "v1",methods = {@Method(name="helloWord",arguments = {@Argument(index = 2, callback = true)})})
    public class HelloWordImpl implements HelloWord {
        public String helloWord(String param) {
            return "HelloWordImpl20882:"+param;
        }
    
        @Override
        public String helloWord(String s, String s1, CallbackListener callbackListener) {
            callbackListener.callback("abcd");//业务处理完,要传给消费端的参数,这个也是一个代理类,调用后消费端就可收到abcd
            return "HelloWordImpl20882:"+s;
        }
    }
    
    

    执行

    当消费者发起调用后,消费者会打印CallbackServiceImpl.callback方法

    异步调用

    方法接口添加异步接口

    /**
     * 定义一个功能接口
     */
    public interface HelloWord {
        String helloWord(String parameter);
        String helloWord(String parameter,String key,CallbackListener callbackListener);
    
        //异步方法
        CompletableFuture<String> helloWordAsync(String name);
    }
    

    消费者调用

    @Test
        void contextLoads() throws IOException {
            //执行代理方法
            CompletableFuture<String> rb = helloWord.helloWordAsync("RB");
            rb.whenComplete((v, e) -> {
                if (e != null) {
                    e.printStackTrace();
                } else {
                    System.out.println("异步结果:" + v);
                }
            });
            System.out.println("当前线程调⽤结束");
            System.in.read();//阻塞,防止线程关了,回调失败
    
        }
    

    生产者实现

    package com.rb.producer.impl;
    
    
    import lombok.SneakyThrows;
    import myinterface.CallbackListener;
    import myinterface.HelloWord;
    import org.apache.dubbo.common.URL;
    import org.apache.dubbo.config.annotation.Argument;
    import org.apache.dubbo.config.annotation.DubboService;
    import org.apache.dubbo.config.annotation.Method;
    import org.apache.dubbo.rpc.RpcContext;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 对接口的实现
     */
    //添加Dubbo注解
    @DubboService(version = "v1",methods = {@Method(name="helloWord",arguments = {@Argument(index = 2, callback = true)})})
    public class HelloWordImpl implements HelloWord {
        public String helloWord(String param) {
            return "HelloWordImpl20882:"+param;
        }
    
        @Override
        public String helloWord(String s, String s1, CallbackListener callbackListener) {
            callbackListener.callback("abcd");
            return "HelloWordImpl20882:"+s;
        }
    
        @SneakyThrows
        @Override
        public CompletableFuture<String> helloWordAsync(String s) {
            System.out.println("异步调⽤:" + s);
            TimeUnit.SECONDS.sleep(2);//模拟耗时业务
            return CompletableFuture.supplyAsync(() -> {
                return "HelloWordImplAsync20882:"+s;
            });
        }
    }
    
    

    Dubbo更多高级功能

  • 相关阅读:
    Enables DNS lookups on client IP addresses 域名的分层结构
    移除 URL 中的 index.php
    Design and Architectural Goals
    The Model represents your data structures.
    If a cache file exists, it is sent directly to the browser, bypassing the normal system execution.
    UNION WHERE
    cookie less
    http 2
    UNION DISTINCT
    联合约束 CONCAT()
  • 原文地址:https://www.cnblogs.com/rb2010/p/16102954.html
Copyright © 2020-2023  润新知