拓展应用篇

所谓拓展都是利用Dubbo自身的SPI机制来实现的

自定义事件机制

这个不是SPI机制,但我觉得也算是一种拓展,就放过来了,虽然完全可以被下面的过滤器取代

在调用之前、调用之后、出现异常时,会触发 oninvokeonreturnonthrow 三个事件,可以配置当事件发生时,通知哪个类的哪个方法

自定义拓展类

在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")

效果: 使用需要注意上下文参数的传递(这里没管就丢失了)

Last Updated: