SOFA 源码分析— 自定义路由寻址

Wesley13
• 阅读 670

SOFA 源码分析— 自定义路由寻址

前言

SOFA-RPC 中对服务地址的选择也抽象为了一条处理链,由每一个 Router 进行处理。同 Filter 一样, SOFA-RPC 对 Router 提供了同样的扩展能力。

那么就看看 SOFA 是如何处理的。

如何使用

官方教程如下:

@Extension(value = "customerRouter")
@AutoActive(consumerSide = true)
public class CustomerRouter extends Router {
   
    @Override
    public void init(ConsumerBootstrap consumerBootstrap) {
        
    }
    @Override
    public boolean needToLoad(ConsumerBootstrap consumerBootstrap) {
       return ture;
    }
    @Override
    public List<ProviderInfo> route(SofaRequest request, List<ProviderInfo> providerInfos) {
        return providerInfos;
    }

新建扩展文件 META-INF/services/sofa-rpc/com.alipay.sofa.rpc.client.Router 。内容如下:

customerRouter=com.alipay.sofa.rpc.custom.CustomRouter

如上自定义了一个 CustomerRouter ,生效于所有消费者。其中 init 参数 ConsumerBootstrap 是引用服务的包装类,能够拿到 ConsumerConfig ,代理类,服务地址池等对象。 needToLoad 表示是否生效该 Router , route 方法即筛选地址的方法。

可以看到,Router 也是通过 SOFA 的扩展机制实现的,通过定义一个 SPI 文件,能够有效的解耦。

然后我们再来看看他的原理。

源码解析

在 SOFA 中, Router 是个抽象类,内部定义了 4 个方法:

//初始化
public void init(ConsumerBootstrap consumerBootstrap) {
}

//是否自动加载
public boolean needToLoad(ConsumerBootstrap consumerBootstrap) {
    return true;
}
// 筛选Provider
public abstract List<ProviderInfo> route(SofaRequest request, List<ProviderInfo> providerInfos);

//记录路由路径记录
protected void recordRouterWay(String routerName) {
    if (RpcInternalContext.isAttachmentEnable()) {
        RpcInternalContext context = RpcInternalContext.getContext();
        String record = (String) context.getAttachment(RpcConstants.INTERNAL_KEY_ROUTER_RECORD);
        record = record == null ? routerName : record + ">" + routerName;
        context.setAttachment(RpcConstants.INTERNAL_KEY_ROUTER_RECORD, record);
    }
}

子类必须实现 route 方法,该方法的参数是 SofaRequest 和一个 ProviderInfo List。然后,返回值是筛选过的 ProviderInfo List。即用户可以在自定义的 Router 中筛选 Router 使用。负载均衡会从用户的 ProviderInfo List 中选择一个 ProviderInfo 进行调用。

路由顺序按照 Extension 注解的 order 进行从小到大排序。

同时, SOFA 上下文 RpcInternalContext 会记录此次调用的路径,也就是路由名字。

而目前框架中有 3 个实现类:

  1. DirectUrlRouter 直连路由,needToLoad 判断条件是客户端是否设置了只来路由。路由规则是:从地址保持器中选取直连地址,然后添加到 List 中。
  2. ExcludeRouter 要排除的过滤器,目前暂没有定义。用户可自己扩展。
  3. RegistryRouter 从注册中心获取地址进行路由。needToLoad 条件是:如果没有设置直连地址且从注册中心订阅服务。路由规则:从地址保持器中获取默认的(注册中心)服务列表,并添加进 List 返回。

其中,这个地址保持/管理器是什么鬼呢?

每个客户端都有一个地址管理器 —— AddressHolder。管理着服务的分组。SingleGroupAddressHolder 是 AddressHolder 的具体实现类,也是通过扩展机制实现的。他是一个只支持单个分组的地址选择器(额外存一个直连分组)。

他内部有 2 个 List, 一个是直连地址列表,另一个是注册中心的地址列表。

在 Cluster 初始化的时候,会先初始化 routerChain Router 链,该实例中包含了一个 Router 数组,用于保存路由实例。

同时还会初始化服务端列表,即调用 consumerBootstrap.subscribe() 方法,该方法在 DefaultConsumerBootstrap 中实现如下:

  1. 如果直连地址(逗号或者分号分割)不是空,则返回一个包装了直连地址的 List
  2. 如果直连地址是空的,则从注册中心获取服务列表。

得到服务列表后,则添加到地址管理器中。同时向事件总线丢一个 ProviderInfoUpdateAllEvent 事件。包括建立长连接——也就是初始化 RpcClient,在调用 updateAllProviders 方法后,会异步的在一个 initPool 线程池中启动多个线程初始化长连接。

好了,现在地址管理器里面已经有连接了。

当客户端调用的时候,也就是 doInvoke 方法,会先从从 routerChain 中获取过滤后的 List ,然后,调用负载均衡的 select 方法,从这些可选的路由中选取一个 ProviderInfo 进行调用。

以上,就是自定义路由寻址,地址管理器的实现原理。

总结

SOFA 为框架用户定义了 Router ,用户可以实现 route 方法,并在该方法中实现过滤策略,从而返回一个用户设置的服务列表,值得注意的是,用户需要定义 order 属性,因为 Router 是从小到大排序的,顺序对于整体逻辑来说非常重要。

和 Router 息息相关的还有地址管理器 —— AddressHolder,该类会管理服务列表,客户端在初始化的时候,会将地址都保存到 AddressHolder 中,在之后的负载均衡选择服务的时候,会从地址管理器中获取服务列表(已经路由过滤的)进行选择。

还有一点需要注意:地址管理器每次 update 的时候,会全量更新连接管理器。如果有新增的服务,就会建立长连接。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这