拓展应用篇
所谓拓展都是利用Dubbo自身的SPI机制来实现的
自定义事件机制
这个不是SPI机制,但我觉得也算是一种拓展,就放过来了,虽然完全可以被下面的过滤器取代
在调用之前、调用之后、出现异常时,会触发 oninvoke
、onreturn
、onthrow
三个事件,可以配置当事件发生时,通知哪个类的哪个方法
自定义拓展类
在consumer中新建一个文件夹event,新建一个接口 TestEvent
public interface TestEvent {
//必须具有与真实的被调用方法相同的入参列表
void oninvoke(String test);
//2.1 至少要有一个入参且第一个入参必须与被调用方法的返回类型相同,接收返回结果
//2.2 可以有多个参数,多个参数的情况下,第一个为返回结果,后面都是入参
String onreturn(String result,String test);
//3.1 至少要有一个入参且第一个入参类型为Throwable或其子类
//3.2 可以有多个参数,多个参数的情况下,第一个为Throwable,后面都是入参
void onthrow(Throwable throwable,String test);
}
新建一个实现类,并注入到spring容器 **TestEventImpl **
@Slf4j
@Component("testEvent")
public class TestEventImpl implements TestEvent{
@Override
public void oninvoke(String test) {
log.info("test方法调用前");
}
@Override
public String onreturn(String result, String test) {
log.info("test方法调用后");
return null;
}
@Override
public void onthrow(Throwable throwable, String test) {
log.info("test方法异常时",throwable);
}
}
生效方法
我们在调用者注解上配置即可:
// 指定需要监听的方法
// testEvent:是那个实现类注入到spring容器中的bean名称
@DubboReference(check = false,methods = {@Method(name = "test",oninvoke = "testEvent.oninvoke", onreturn = "testEvent.onreturn", onthrow = "testEvent.onthrow")})
效果:
自定义过滤器
面向切面编程都知道了,这同样是如此
自定义拓展类
在consumer中新建一个文件夹filter,专门来放拓展类
新增自定义过滤器类 MyDubboFilter实现Filter 接口
public class MyDubboFilter implements Filter {
private final static Logger log= LoggerFactory.getLogger(MyDubboFilter.class);
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
log.info("dubbo 方法执行前.....");
Result result = invoker.invoke(invocation);
log.info("dubbo 方法执行后.....");
return result;
}
}
在资源文件夹resources中新建META-INF文件夹,然后在其中新建dubbo文件夹
新建一个名为:org.apache.dubbo.rpc.Filter 的文件,文件内容如下:
格式为: 自定义过滤器名称=自定义过滤器类的全路径
myFilter=cn.colins.consumer.filter.MyDubboFilter
现在整体结构如下:
生效方式
此时我们的过滤器已经定义完了,现在需要他生效,通常有两种方式:
服务级别:针对暴露的服务在注解上加上filter参数,如:
// 调用方注解 配置
@DubboReference(check = false,filter = "myFilter")
// 提供方注解 配置
@DubboService(filter = "myFilter")
// filter值 就是自定义过滤器的名称
// 可以配置多个用,隔开 如:"xxx,yyy"
// 先后顺序就是加载的执行顺序 也可以让某个过滤器不执行 如:"xxx,yyy,-ccc" ccc过滤器不生效
以上配置只能针对注解所代表的某个服务接口
全局级别:在实现类上打上激活注解,如:
@Activate(group = {CommonConstants.CONSUMER,CommonConstants.PROVIDER})
@Activate()怎么用的,先别管,先这么加着,后面再说
@Activate(group = {CommonConstants.CONSUMER,CommonConstants.PROVIDER})
public class MyDubboFilter implements Filter {
private final static Logger log= LoggerFactory.getLogger(MyDubboFilter.class);
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
log.info("dubbo 方法执行前.....");
Result result = invoker.invoke(invocation);
log.info("dubbo 方法执行后.....");
return result;
}
}
效果:
自定义负载均衡
虽然已经内置了好几种策略了,但是万一出现特殊场景呢是吧
自定义拓展类
同理新建一个类MyLoadBalance 实现LoadBalance 接口
public class MyLoadBalance implements LoadBalance {
private final static Logger log= LoggerFactory.getLogger(MyLoadBalance.class);
@Override
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
log.info("自定义负载均衡");
// 为了测试无脑取第一个
return invokers.get(0);
}
}
在META-INF/dubbo下新建一个文本文件 org.apache.dubbo.rpc.cluster.LoadBalance 内容如下:
myLoadBalance=myFilter=cn.colins.consumer.filter.MyLoadBalance
生效方式
全局的方式和上述一样加入激活注解@Activate,这里就不再说明了,服务级别的则是修改一下参数:
// 调用方注解 配置
@DubboReference(check = false,loadbalance= "myLoadBalance")
// 提供方注解 配置
@DubboService(loadbalance= "myLoadBalance")
效果: 注意需要启动多个提供者,只有一个的话不需要负载所以没效果
自定义服务引用监听
当有服务引用时或者服务引用被销毁时,触发该事件。用在调用方
自定义拓展类
在调用者工程下
新建一个MyListener类,实现InvokerListener接口
public class MyListener implements InvokerListener {
private final static Logger log= LoggerFactory.getLogger(MyListener.class);
@Override
public void referred(Invoker<?> invoker) throws RpcException {
log.info("当有服务引用时被调用: {}",invoker.getUrl());
}
@Override
public void destroyed(Invoker<?> invoker) {
log.info("当服务引用被销毁时被调用: {}",invoker.getUrl());
}
}
在META-INF/dubbo下新建一个文本文件org.apache.dubbo.rpc.InvokerListener内容如下:
myListener=cn.colins.consumer.filter.MyListener
生效方式
全局的方式和上述一样加入激活注解@Activate,这里就不再说明了,服务级别的则是修改一下参数:
// 调用方注解 添加listener参数
@DubboReference(check = false,protocol = "dubbo",listener = "myListener")
效果: 重启提供方查看效果
自定义服务暴露监听
这个和上面那个相辅相成,上面那个针对调用方,这个针对提供方,有服务暴露的时候或者有服务销毁的时候触发调用
自定义拓展类
注意:以上都是在调用者工程下新建的拓展类,这里需要在提供者工程下新建
同样是新建一个filter文件夹,然后新建一个MyExporterListener实现ExporterListener接口
@Activate(group = CommonConstants.PROVIDER)
public class MyExporterListener implements ExporterListener {
private final static Logger log= LoggerFactory.getLogger(MyExporterListener.class);
@Override
public void exported(Exporter<?> exporter) throws RpcException {
log.info("有服务暴露了:{} ",exporter.getInvoker().getUrl());
}
@Override
public void unexported(Exporter<?> exporter) {
log.info("有服务取消暴露了:{} ",exporter.getInvoker().getUrl());
}
}
老规矩,在META-INF/dubbo下新建一个文本文件org.apache.dubbo.rpc.ExporterListener内容如下:
myExListener=cn.colins.producer.filter.MyExporterListener
生效方式
一样是有全局加载和服务加载两种,上述已经加上了@Activate注解是全局的,服务加载方式为:
// 提供方注解 添加listener参数
@DubboService(listener = "myExListener")
效果:
自定义路由
为什么有了负载均衡还需要自定义路由?因为默认情况下负载均衡是对所有提供方的均衡调用,假设我现在有个场景需要根据条件调用里面某一部分的提供方,这就不是负载均衡能控制的了
自定义拓展类
在调用方工程实现,都是在filter文件夹下
我们先新建一个类MyRouter继承AbstractRouter
public class MyRouter extends AbstractRouter {
public MyRouter(URL url){
setUrl(url);
}
private final static Logger log= LoggerFactory.getLogger(MyRouter.class);
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
log.info("url:{}",url);
// 根据已有条件过滤invokers 返回即可
// 返回的invokers 就会走负载策略
invokers.stream().filter(invoker->{
log.info("Provider id: {}",invoker.getUrl().getHost());
log.info("Provider port: {}",invoker.getUrl().getPort());
log.info("Provider protocol: {}",invoker.getUrl().getProtocol());
log.info("Provider methods : {}",invoker.getUrl().getParameter("methods", Collections.emptyList()));
log.info("Provider Parameter: {}",invoker.getUrl().getParameters());
return true;
}).collect(Collectors.toList());
return invokers;
}
}
然后新建一个MyRouterFactory实现RouterFactory
public class MyRouterFactory implements RouterFactory {
@Override
public Router getRouter(URL url) {
return new MyRouter(url);
}
}
主要SPI机制加载的是这个工程
老规矩,在META-INF/dubbo下新建一个文本文件org.apache.dubbo.rpc.cluster.RouterFactory内容如下:
myRouter=cn.colins.consumer.filter.MyRouterFactory
生效方式
这个生效和之前的不太一样,需要在配置文件中加:
consumer:
router: myRouter
整体如下:
效果: 说明走了我们的路由
自定义集群策略
自带的集群策略我们之前说了,有以下几种:
- failover:失败自动切换,默认值;当调用失败时,自动切换到另一个可用的服务提供者进行重试,重试次数由 retries参数指定
- failfast:快速失败;当调用失败时,立即报错,不进行任何重试操作;
- failsafe:失败安全;当调用失败时,直接忽略错误,不进行任何处理;
- failback:失败自动恢复。当调用失败时,记录失败的请求并异步重试,重试次数由 retries参数指定。
- forking:并行调用多个服务器,只要有一个成功即返回,
- broadcast:广播调用所有提供者,任意一个报错则报错
要是没满意的咋整,自定义呗
自定义拓展类
还是在 调用方 工程下测试
在filter文件夹下新增MyCluster实现Cluster接口
public class MyCluster implements Cluster {
private final static Logger log= LoggerFactory.getLogger(MyCluster.class);
@Override
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
return new AbstractClusterInvoker<T>(directory) {
// Invocation 调用方法相关信息
// invokers 服务提供者集合
// loadbalance 负载均衡策略
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
log.info("这是我自定义的集群策略");
// 只是测试无脑选第一个
return invokers.get(0).invoke(invocation);
}
};
}
}
老规矩,在META-INF/dubbo下新建一个文本文件org.apache.dubbo.rpc.cluster.Cluster内容如下:
myCluster=cn.colins.consumer.filter.MyCluster
生效方式
全局的还是和之前一样加激活注解,服务级别的如下:
@DubboReference(check = false,cluster = "myCluster")
效果: 使用需要注意上下文参数的传递(这里没管就丢失了)