dubbo应用

175 阅读3分钟

这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

SPI

流程:

  • 引入pom
  • 实现接口,接口上要有@SPI,并在resource/META-INF/dubbo下建立包名.接口名的文件,并在里面按照key=value的格式维护实现类
  • 加载时用ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(.class)获取扩展加载类,extensionLoader.getSupportedExtensions()就可以获取所有扩展点名称, 通过extensionLoader.getExtension(name)就可以获取所有扩展点

目的

  • JDK会一次性实例化所有扩展点
  • 如果有扩展点失败,所有的都无法使用
  • 提供了对扩展点包装功能, 还可以通过set方式进行注入

Adaptive:

动态的选择具体的扩展点, 通过url的方式选择

过滤器

Filter机制在执行目标程序前后执行指定的代码

  • 实现 org.apache.dubbo.rpc.Filter
  • 添加@Activate,并通过group指定是生产端还是消费端
  • 在META-INF.dubbo下建立org.apache.dubbo.rpc.Filter文件, 内容是key=包名.类名

负载均衡

dubbo提供了多种负载均衡, 包含随机,轮询, 最少调用数,一致性hash,缺省为随机。

@Reference(loadbalance="random")
@Service(loadbalance="random")

自定义:

  • 实现org.apache.dubbo.rpc.cluster.loadBalance
  • 在META-INF.dubbo下建立org.apache.dubbo.rpc.cluster.loadBalance文件, 内容是key=包名.类名

异步调用

消费端利用Future异步等待和获取结果, 通过XML方式导入

<dubbo:reference id="" interface="">
    <dubbo:method name="" async="true"/>
</dubbo:reference>

在调用完方法后,立刻用

Future<Object> future = RpcContext.getContext().getFuture();

线程池

dubbo已有

  • fix创建固定大小线程池, 默认200,没有等待队列,一般会采用
  • cache: 创建非固定大小的线程池

自定义

由于使用fix线程池可能导致具体某些业务因为线程数量不足发生错误,但是业务是无感知的,只有发生错误才能发现。需要通过某些手段进行监控。

public class WatchThreadPool extends FixedThreadPool implements Runable{
    private static final double ALARM_PERCENT =0.9;
    private final Map<URL, ThreadPoolExecutor> THREADPOOLS = new ConcurrentHashMap<>();
    public void run (){
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(this, 1, 3, TimeUnit.SECOND);
    }
    
    public Executor getExecutor(URL url){
        final Executor executor = supper.getExecutor(url);
        if(executor instanceof ThreadPoolExecutor){
            THREADPOOLS.put(url, (ThreadPoolExecutor)executor);
        }
        return executor;
    }
    
    public void run(){
        for(Map.Entry<URL, ThreadPoolExecutor> entry: THREADPOOLS){
            final URL url = entry.getKey();
            final ThreadPoolExecutor executor = entry.getValue();
            final int activeCount = executor.getActiveCount();
            final int poolSize = executor.getCorePoolSize();
            double usedPercent = activeCount/ poolSize;
            if(usedPercent > ALARM_PERCENT){
                //TODO 日志等... 
            }
        }
    }
}

在META-INF.dubbo下建立org.apache.dubbo.common.threadpool.ThreadPool,内容是key=包名.类名。 在服务提供方引入依赖后,设置dubbo.provider.threadpool=watching

路由

决定请求的目标机器。 本质上通过在zk上保存一个节点数据,消费者通过监听服务路径,感知路由规则匹配

  • route:// 表示路由规则, 支持条件路由和脚本路由, 必填
  • ip: 对ip地址生效, 如果对所有ip生效,0.0.0.0, 必填
  • com....: 指定服务 必填
  • group=foo: 对指定服务的指定group生效
  • version: 对指定服务的指定版本生效
  • category=routes: 动态配置类型 必填
  • dynamic=flase: 数据为持久数据 必填
  • enabled=true 覆盖规则生效, 缺省生效
  • force =false: 路由结果为空,是否强制执行,缺省为false
  • runtime=false: 是否在每次调用执行路由规则, 如果用了参数路由必须为true, 缺省为false
  • priority=1 优先级 用于排序,优先级越大越靠前执行,缺省0
  • rule=URL.encoding("host=10.20.10.10=> host=10.20.10.11"), 路由规则内容,必填

服务降级

dubbo管理控制台配置

  • mock=force:return null
  • mock=fail:return null

返回简单值或null

@Reference(mock="force: return null")

@Reference(mock="return null")

Java代码

在registry.register的时候添加mock=foce:return+null