在集中式环境中服务的机器台只有一台,这样对于服务不仅存在服务单点故障问题而且还存在流量问题。为了解决这个问题,就引入的分布式与集群概念。
分布式:一个业务分拆多个子业务,部署在不同的服务器上
集群:同一个业务,部署在多个服务器上
1、 dubbo 服务治理
当请求来临时,如何从多个服务器中,选择一个有效、合适的服务器,这个集群所需要面对一问题。所以在集群里面就引申出负载均衡(LoadBalance),高可用(HA),路由(Route)等概念。我们来看一下 dubbo 在进行服务调用的时候是如何处理的。
这张集群容错包含以下几个角色:
Invoker
:对Provider
(服务提供者) 的一个可调用 Service 接口的抽象,Invoker
封装了Provider
地址及Service
接口信息。Cluster
:Directory
中的多个Invoker
伪装成一个Invoker
,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个Directory
:代表多个Invoker
,可以把它看成List<Invoker>
,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更Router
: 负责从多个Invoker
中按路由规则选出子集,比如读写分离,应用隔离等LoadBalance
:LoadBalance
负责从多个Invoker
中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选.
2、 路由服务
下面我们来分析一下 Route, 也就是路由服务。我们可以来看一下 维基百科, 对于路由服务的描述。
路由是在一个网络中,或在多个网络之间或跨多个网络中选择一条路径的过程。路由是为许多类型的网络执行的,包括电路交换网络,如公共交换电话网络(PSTN)和计算机网络,如因特网。
在分组交换网络中,路由是通过特定的包转发机制将网络数据包从其源引导到目的地的高级决策。包转发是从一个网络接口到另一个网络接口的逻辑处理网络数据包的传输。中间节点通常是网络硬件设备,如路由器、桥、网关、防火墙或交换机。通用计算机也转发数据包并执行路由,尽管它们没有专门针对任务的优化硬件。路由过程通常在路由表的基础上进行转发,它维护了到各种网络目的地的路由记录。因此,在路由器的内存中构建路由表对于有效的路由是非常重要的。大多数路由算法一次只使用一个网络路径。多路径路由技术支持使用多种可选路径。
在狭义的术语中,路由通常与桥接的关系形成对比,即网络地址是结构化的,并且类似的地址意味着在网络中接近。结构化的地址允许单个路由表条目表示到一组设备的路由。在大型网络中,结构化寻址(狭义的路由)优于非结构化寻址(桥接)。路由已经成为在互联网上寻址的主要形式。
3、Route
在 dubbo 中路由规则决定一次服务调用的目标服务器,分为条件路由规则和脚本路由规则,并且支持可扩展(SPI)。
下面就是 dubbo 里面 Route 路由接口的定义:
public interface Router extends Comparable<Router> {
URL getUrl();
<T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}
调用 route
方法,传入从目录服务获取到的 Invoke 列表,通过 URL 或者 Invocation 里面配置的条件筛选出满足条件的 Invoke 列表。
下面是 dubbo 路由服务的类图:
dubbo 默认会在 AbstractDirectory#setRouters
自动添加 MockInvokersSelector
路由规则。
3.1 MockInvokersSelector
MockInvokersSelector:其实就是用于路由 Mock 服务与非 Mock 服务。
public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,
URL url, final Invocation invocation) throws RpcException {
if (invocation.getAttachments() == null) {
return getNormalInvokers(invokers);
} else {
String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK);
if (value == null)
return getNormalInvokers(invokers);
else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
return getMockedInvokers(invokers);
}
}
return invokers;
}
上面的代码逻辑其实就是:
- 如果 Invocation 的扩展参数不为空 并且 Invocation 的扩展参数里面包含
invocation.need.mock
参数并且值为true
就获取 Invoke 列表里面protocol
为mock
的 Invoke 列表。 - 否则获取Invoke 列表里面
protocol
为非mock
的 Invoke 列表。
3.2 ConditionRouter
ConditionRouter:基于条件表达式的路由规则,它的条件规则如下:
=>
之前的为消费者匹配条件,所有参数和消费者的 URL 进行对比,当消费者满足匹配条件时,对该消费者执行后面的过滤规则。=>
之后为提供者地址列表的过滤条件,所有参数和提供者的 URL 进行对比,消费者最终只拿到过滤后的地址列表。- 如果匹配条件为空,表示对所有消费方应用,如:
=> host != 10.20.153.11
- 如果过滤条件为空,表示禁止访问,如:
host = 10.20.153.10 =>
参数支持:
- 服务调用信息,如:method, argument 等,暂不支持参数路由
- URL 本身的字段,如:protocol, host, port 等
- 以及 URL 上的所有参数,如:application, organization 等
条件支持:
- 等号
=
表示"匹配",如:host = 10.20.153.10
- 不等号
!=
表示"不匹配",如:host != 10.20.153.10
值支持:
- 以逗号
,
分隔多个值,如:host != 10.20.153.10,10.20.153.11
- 以星号
*
结尾,表示通配,如:host != 10.20.*
- 以美元符
$
开头,表示引用消费者参数,如:host = $host
3.3 ScriptRouter
ScriptRouter:脚本路由规则,脚本路由规则支持 JDK 脚本引擎的所有脚本,比如:javascript, jruby, groovy 等,通过 type=javascript
参数设置脚本类型,缺省为 javascript。
基于脚本引擎的路由规则,如:
(function route(invokers) {
var result = new java.util.ArrayList(invokers.size());
for (i = 0; i < invokers.size(); i ++) {
if ("10.20.153.10".equals(invokers.get(i).getUrl().getHost())) {
result.add(invokers.get(i));
}
}
return result;
} (invokers)); // 表示立即执行方法
4、Route 功能
通过配置不同的 Route 规则,我们可以实现以下功能。
- 排除预发布机:
=> host != 172.22.3.91
- 白名单:
host != 10.20.153.10,10.20.153.11 =>
- 黑名单:
host = 10.20.153.10,10.20.153.11 =>
- 服务寄宿在应用上,只暴露一部分的机器,防止整个集群挂掉:
=> host = 172.22.3.1*,172.22.3.2*
- 为重要应用提供额外的机器:
application != kylin => host != 172.22.3.95,172.22.3.96
- 读写分离:
method = find*,list*,get*,is* => host = 172.22.3.94,172.22.3.95,172.22.3.96
method != find*,list*,get*,is* => host = 172.22.3.97,172.22.3.98
- 前后台分离:
application = bops => host = 172.22.3.91,172.22.3.92,172.22.3.93
application != bops => host = 172.22.3.94,172.22.3.95,172.22.3.96
- 隔离不同机房网段:
host != 172.22.3.* => host != 172.22.3.*
- 提供者与消费者部署在同集群内,本机只访问本机的服务:
=> host = $host
参考文章:
1.http://en.wikipedia.org/wiki/Routing
2.http://dubbo.apache.org/books/dubbo-user-book/demos/routing-rule.html
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。