dubbo的基本应用与高级应用

1负载均衡


    提供了多种负载均衡策略,缺省为random随机调用
    负载均衡策略:
        1)Random LoadBalance(随机)
        2)RoundRobin (轮询)
        3)LeastActive(最少活跃调用数)
        4)ConsistentHash(一致性) 参数hash值一样,调用的是同一台机器


2集群容错


    重试是在消费端发起的
    超时报错了,服务端时间是3s,消费端配置的是1s超时,重试设置的是2次。服务端会执行3次,每次都会返回,但是消费端已经超时,连接已经没有了所以返回了也没用
    提供了多种集群容错策略
    集群容错策略:
    1)Failover Cluster(失败自动切换)
        失败自动切换,当出现失败,重试其他服务器。重试会带来更长延迟,可通过retries="2"来设置重试次数(不含第一次)
    2)Failfast Cluster(快速失败)
    3)Failsafe Cluster(失败安全)
        出现异常时,直接忽略。通常用于写入审计日志等操作。
    4)Failback Cluster(失败自动恢复)
        后台记录失败请求,定时重发。通常用于消息通知操作。
    5)Forking Cluster(并行调用多个服务,只要一个成功即返回)
        通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过forks="2"来设置最大并行数
    6)Broadcast Cluster(广播调用所有提供者)
        逐个调用,任意一台报错即报错。通常用于通知所有提供者更新缓存或日志等本地资源信息
    


3服务降级


    mock是stub的一个子集
    在服务消费者注册的地方配置mock参数,mock=true走默认的规则(HelloWorldService对应HelloWorldServiceMock),也可以用mock=类的全路径制定实现类来处理。
    可以通过服务降级功能 [1] 临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。
    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"));
        1)mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
        2)还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。


4本地存根


    本地存根是在消费端执行的
    远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub [1],然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。

                                /user-guide/images/stub.jpg
    在服务消费者注册的地方配置stub参数,stub=true走默认的规则(HelloWorldService对应HelloWorldServiceStub),也可以用stub=类的全路径制定实现类来提供 Stub 的实现:
    
    package com.foo;
    public class BarServiceStub implements BarService {
        private final BarService barService;
        
        // 构造函数传入真正的远程代理对象
        public BarServiceStub(BarService barService){
            this.barService = barService;
        }
     
        public String sayHello(String name) {
            // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
            try {
                return barService.sayHello(name);
            } catch (Exception e) {
                // 你可以容错,可以做任何AOP拦截事项
                return "容错数据";
            }
        }
    }
        1)Stub 必须有可传入 Proxy 的构造函数

        2)在 interface 旁边放一个 Stub 实现,它实现 BarService 接口,并有一个传入远程 BarService 实例的构造函数


5本地伪装

 

本地伪装 [1] 通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。

在 spring 配置文件中按以下方式配置:

<dubbo:reference interface="com.foo.BarService" mock="true" />

<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" />

在工程中提供 Mock 实现 [2]

package com.foo;
public class BarServiceMock implements BarService {
    public String sayHello(String name) {
        // 你可以伪造容错数据,此方法只在出现RpcException时被执行
        return "容错数据";
    }
}

如果服务的消费方经常需要 try-catch 捕获异常,如:

Offer offer = null;
try {
    offer = offerService.findOffer(offerId);
} catch (RpcException e) {
   logger.error(e);
}

请考虑改为 Mock 实现,并在 Mock 实现中 return null。如果只是想简单的忽略异常,在 2.0.11 以上版本可用:

<dubbo:reference interface="com.foo.BarService" mock="return null" />

进阶用法

return

使用 return 来返回一个字符串表示的对象,作为 Mock 的返回值。合法的字符串可以是:

  • empty: 代表空,基本类型的默认值,或者集合类的空值
  • nullnull
  • truetrue
  • falsefalse
  • JSON 格式: 反序列化 JSON 所得到的对象

throw

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

当调用出错时,抛出一个默认的 RPCException:

<dubbo:reference interface="com.foo.BarService" mock="throw" />

当调用出错时,抛出指定的 Exception:

<dubbo:reference interface="com.foo.BarService" mock="throw com.foo.MockException" />

force 和 fail

在 2.6.6 以上的版本,可以开始在 Spring XML 配置文件中使用 fail: 和 force:force: 代表强制使用 Mock 行为,在这种情况下不会走远程调用。fail: 与默认行为一致,只有当远程调用发生错误时才使用 Mock 行为。force: 和 fail: 都支持与 throw 或者 return 组合使用。

强制返回指定值:

<dubbo:reference interface="com.foo.BarService" mock="force:return fake" />

强制抛出指定异常:

<dubbo:reference interface="com.foo.BarService" mock="force:throw com.foo.MockException" />

在方法级别配置 Mock

Mock 可以在方法级别上指定,假定 com.foo.BarService 上有好几个方法,我们可以单独为 sayHello() 方法指定 Mock 行为。具体配置如下所示,在本例中,只要 sayHello() 被调用到时,强制返回 "fake":

<dubbo:reference id="demoService" check="false" interface="com.foo.BarService">
    <dubbo:parameter key="sayHello.mock" value="force:return fake"/>
</dubbo:reference>

  1. Mock 是 Stub 的一个子集,便于服务提供方在客户端执行容错逻辑,因经常需要在出现 RpcException (比如网络失败,超时等)时进行容错,而在出现业务异常(比如登录用户名密码错误)时不需要容错,如果用 Stub,可能就需要捕获并依赖 RpcException 类,而用 Mock 就可以不依赖 RpcException,因为它的约定就是只有出现 RpcException 时才执行。 ↩︎

  2. 在 interface 旁放一个 Mock 实现,它实现 BarService 接口,并有一个无参构造函数

     
6CallBack

 

服务回调方法是消费者实现的

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

服务接口示例

CallbackService.java

package com.callback;
 
public interface CallbackService {
    void addListener(String key, CallbackListener listener);
}

CallbackListener.java

package com.callback;
 
public interface CallbackListener {
    void changed(String msg);
}

服务提供者接口实现示例

package com.callback.impl;
 
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
import com.callback.CallbackListener;
import com.callback.CallbackService;
 
public class CallbackServiceImpl implements CallbackService {
     
    private final Map<String, CallbackListener> listeners = new ConcurrentHashMap<String, CallbackListener>();
  
    public CallbackServiceImpl() {
        Thread t = new Thread(new Runnable() {
            public void run() {
                while(true) {
                    try {
                        for(Map.Entry<String, CallbackListener> entry : listeners.entrySet()){
                           try {
                               entry.getValue().changed(getChanged(entry.getKey()));
                           } catch (Throwable t) {
                               listeners.remove(entry.getKey());
                           }
                        }
                        Thread.sleep(5000); // 定时触发变更通知
                    } catch (Throwable t) { // 防御容错
                        t.printStackTrace();
                    }
                }
            }
        });
        t.setDaemon(true);
        t.start();
    }
  
    public void addListener(String key, CallbackListener listener) {
        listeners.put(key, listener);
        listener.changed(getChanged(key)); // 发送变更通知
    }
     
    private String getChanged(String key) {
        return "Changed: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    }
}

服务提供者配置示例

<bean id="callbackService" class="com.callback.impl.CallbackServiceImpl" />
<dubbo:service interface="com.callback.CallbackService" ref="callbackService" connections="1" callbacks="1000">
    <dubbo:method name="addListener">
        <dubbo:argument index="1" callback="true" />
        <!--也可以通过指定类型的方式-->
        <!--<dubbo:argument type="com.demo.CallbackListener" callback="true" />-->
    </dubbo:method>
</dubbo:service>

服务消费者配置示例

<dubbo:reference id="callbackService" interface="com.callback.CallbackService" />

服务消费者调用示例

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml");
context.start();
 
CallbackService callbackService = (CallbackService) context.getBean("callbackService");
 
callbackService.addListener("foo.bar", new CallbackListener(){
    public void changed(String msg) {
        System.out.println("callback1:" + msg);
    }
});

  1. 2.0.6 及其以上版本支持


7异步调用

 

从v2.7.0开始,Dubbo的所有异步编程接口开始以CompletableFuture为基础

基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小。

                            /user-guide/images/future.jpg

使用CompletableFuture签名的接口

需要服务提供者事先定义CompletableFuture签名的服务,具体参见服务端异步执行接口定义:

public interface AsyncService {
    CompletableFuture<String> sayHello(String name);
}

注意接口的返回类型是CompletableFuture<String>

XML引用服务:

<dubbo:reference id="asyncService" timeout="10000" interface="com.alibaba.dubbo.samples.async.api.AsyncService"/>

调用远程服务:

// 调用直接返回CompletableFuture
CompletableFuture<String> future = asyncService.sayHello("async call request");
// 增加回调
future.whenComplete((v, t) -> {
    if (t != null) {
        t.printStackTrace();
    } else {
        System.out.println("Response: " + v);
    }
});
// 早于结果输出
System.out.println("Executed before response return.");

使用RpcContext

在 consumer.xml 中配置:

<dubbo:reference id="asyncService" interface="org.apache.dubbo.samples.governance.api.AsyncService">
      <dubbo:method name="sayHello" async="true" />
</dubbo:reference>

调用代码:

// 此调用会立即返回null
asyncService.sayHello("world");
// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
CompletableFuture<String> helloFuture = RpcContext.getContext().getCompletableFuture();
// 为Future添加回调
helloFuture.whenComplete((retValue, exception) -> {
    if (exception == null) {
        System.out.println(retValue);
    } else {
        exception.printStackTrace();
    }
});

或者,你也可以这样做异步调用:

CompletableFuture<String> future = RpcContext.getContext().asyncCall(
    () -> {
        asyncService.sayHello("oneway call request1");
    }
);

future.get();

重载服务接口

如果你只有这样的同步服务定义,而又不喜欢RpcContext的异步使用方式。

public interface GreetingsService {
    String sayHi(String name);
}

那还有一种方式,就是利用Java 8提供的default接口实现,重载一个带有带有CompletableFuture签名的方法。

有两种方式来实现:

  1. 提供方或消费方自己修改接口签名
public interface GreetingsService {
    String sayHi(String name);
    
    // AsyncSignal is totally optional, you can use any parameter type as long as java allows your to do that.
    default CompletableFuture<String> sayHi(String name, AsyncSignal signal) {
        return CompletableFuture.completedFuture(sayHi(name));
    }
}
  1. Dubbo官方提供compiler hacker,编译期自动重写同步方法,请在此讨论和跟进具体进展。

你也可以设置是否等待消息发出: [1]

  • sent="true" 等待消息发出,消息发送失败将抛出异常。
  • sent="false" 不等待消息发出,将消息放入 IO 队列,即刻返回。
<dubbo:method name="findFoo" async="true" sent="true" />

如果你只是想异步,完全忽略返回值,可以配置 return="false",以减少 Future 对象的创建和管理成本:

<dubbo:method name="findFoo" async="true" return="false" />

  1. 异步总是不等待返回


8泛化调用

泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。

通过 Spring 使用泛化调用

在 Spring 配置申明 generic="true"

<dubbo:reference id="barService" interface="com.foo.BarService" generic="true" />

在 Java 代码获取 barService 并开始泛化调用:

GenericService barService = (GenericService) applicationContext.getBean("barService");
Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" });

通过 API 方式使用泛化调用

import org.apache.dubbo.rpc.service.GenericService; 
... 
 
// 引用远程服务 
// 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>(); 
// 弱类型接口名
reference.setInterface("com.xxx.XxxService");  
reference.setVersion("1.0.0");
// 声明为泛化接口 
reference.setGeneric(true);  

// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用  
GenericService genericService = reference.get(); 
 
// 基本类型以及Date,List,Map等不需要转换,直接调用 
Object result = genericService.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"world"}); 
 
// 用Map表示POJO参数,如果返回值为POJO也将自动转成Map 
Map<String, Object> person = new HashMap<String, Object>(); 
person.put("name", "xxx"); 
person.put("password", "yyy"); 
// 如果返回POJO将自动转成Map 
Object result = genericService.$invoke("findPerson", new String[]
{"com.xxx.Person"}, new Object[]{person}); 
 
...

有关泛化类型的进一步解释

假设存在 POJO 如:

package com.xxx;

public class PersonImpl implements Person {
    private String name;
    private String password;

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

则 POJO 数据:

Person person = new PersonImpl(); 
person.setName("xxx"); 
person.setPassword("yyy");

可用下面 Map 表示:

Map<String, Object> map = new HashMap<String, Object>(); 
// 注意:如果参数类型是接口,或者List等丢失泛型,可通过class属性指定类型。
map.put("class", "com.xxx.PersonImpl"); 
map.put("name", "xxx"); 
map.put("password", "yyy");

泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。

在 Java 代码中实现 GenericService 接口:

package com.foo;
public class MyGenericService implements GenericService {
 
    public Object $invoke(String methodName, String[] parameterTypes, Object[] args) throws GenericException {
        if ("sayHello".equals(methodName)) {
            return "Welcome " + args[0];
        }
    }
}

通过 Spring 暴露泛化实现

在 Spring 配置申明服务的实现:

<bean id="genericService" class="com.foo.MyGenericService" />
<dubbo:service interface="com.foo.BarService" ref="genericService" />

通过 API 方式暴露泛化实现

... 
// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口实现 
GenericService xxxService = new XxxGenericService(); 

// 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存 
ServiceConfig<GenericService> service = new ServiceConfig<GenericService>();
// 弱类型接口名 
service.setInterface("com.xxx.XxxService");  
service.setVersion("1.0.0"); 
// 指向一个通用服务实现 
service.setRef(xxxService); 
 
// 暴露及注册服务 
service.export();


9管理台

 

目前的管理控制台已经发布0.1版本,结构上采取了前后端分离的方式,前端使用Vue和Vuetify分别作为Javascript框架和UI框架,后端采用Spring Boot框架。既可以按照标准的Maven方式进行打包,部署,也可以采用前后端分离的部署方式,方便开发,功能上,目前具备了服务查询,服务治理(包括Dubbo2.7中新增的治理规则)以及服务测试三部分内容。

Maven方式部署

  • 安装
git clone https://github.com/apache/dubbo-admin.git
cd dubbo-admin
mvn clean package
cd dubbo-admin-distribution/target
java -jar dubbo-admin-0.1.jar

前后端分离部署

  • 前端
cd dubbo-admin-ui 
npm install 
npm run dev 
  • 后端
cd dubbo-admin-server
mvn clean package 
cd target
java -jar dubbo-admin-server-0.1.jar

配置: [1]

配置文件为:

dubbo-admin-server/src/main/resources/application.properties

主要的配置有:

admin.config-center=zookeeper://127.0.0.1:2181
admin.registry.address=zookeeper://127.0.0.1:2181
admin.metadata-report.address=zookeeper://127.0.0.1:2181

三个配置项分别指定了配置中心,注册中心和元数据中心的地址,关于这三个中心的详细说明,可以参考这里。 也可以和Dubbo2.7一样,在配置中心指定元数据和注册中心的地址,以zookeeper为例,配置的路径和内容如下:

# /dubbo/config/dubbo/dubbo.properties
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.metadata-report.address=zookeeper://127.0.0.1:2181

配置中心里的地址会覆盖掉本地application.properties的配置

其他配置请访问github中的文档:

https://github.com/apache/dubbo-admin

  1. 当前版本中未实现登录功能,会在后续版本加上

   

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值