目录
前言
通过前面文章的讲解,我们已经知道微服务下,给同一服务部署多个节点是基本架构。服务部署之后,客户端调用需要进行寻址,找寻调用服务的每一个部署节点和信息。前端调用请求会通过一定的路由规则,最终请求落到一个节点中。当然,通用性的路由方式,比如请求随机的路由到已部署的节点上,这样就达成了负载均衡的效果。如果我们需要有选择性的进行节点选择和控制呢?特定的请求指向特定的节点。这里就用到了dapeng
的特性之一,服务路由来进行控制了。
快速入门
下面举一个简单例子,来使用dapeng服务路由模块
需求:
现在有一个微服务helloService
,最近新增了一个服务接口方法invite
,该方法可以邀请客人到自家做客。由于此服务之前的版本已上线,我们需要新增一个节点部署这个服务的新版本,然后通过路由的方式让所有调用invite
方法的请求过滤到部署新版本服务的节点上来。
路由规则表达式
此需求是典型的通过方法进行路由的方式,我们只要确定调用的是invite
这个方法,就路由他到新版本节点上去。于是配置表达式可以如下:
//新版本节点部署在 192.168.10.2
method match 'helloService' => ip"192.168.10.2"
配置中心配置
配置中心是dapeng开源之一,如果不使用配置中心也可以使用dapeng命令行工具
dapeng-cli
或者直接使用原生的zk操作,将表达式写入当前服务配置信息所在的zk节点上。
配置完成后,点击保存修改按钮,然后配置中心会自动对表达式进行校验。如果表达式写法不正确,会提示修改失败,这时需要重新配置正确。
启动灰度服务(新服务)节点
在发布路由之前需要先启动灰度服务节点。这样做的原因是,避免发布路由规则后,根据规则请求的到灰度节点后,此时节点并没启动,会出现无可用服务实例的错。
发布路由
当我们配置好路由规则以后,再对服务进行发布。这样做的好处是,提前配好路由规则以后,当新版本服务节点启动之后,我们直接发布路由规则即可。
灰度验证
启动节点、成功发布路由后,变可进行灰度验证了。我们可以观察日志,不符合路由的请求都不会被路由到灰度服务上来。而通过了指定规则的请求都来到了灰度服务节点。此次灰度发布成功。
dapeng服务路由功能
按实现功能划分
黑名单
可以将某个服务实例 ip 加入黑名单,屏蔽对其访问。 otherwise => ~ip”192.168.1.1” 此功能常用在临时屏蔽对某个服务节点的请求。
读写分离
method match r"get." , r"list.*" => ip"192.168.1.1"
otherwise => ip"192.168.1.2"
以get
或者list
开头的方法请求都指向1节点,其他的请求都会路由到2节点。达到请求读写分离的作用。
注意: 此功能需要业务代码的方法遵循一定的命名规则,读的方法都以get list等进行命名。
轻重服务
根据服务类名,将不同的服务路由到不同的服务器上。
服务降级
将指向某个服务的请求,直接路由到不可用ip,导致该服务无法使用,做到降级
按路由策略划分
- 1.支持根据 服务名(serviceName),方法名(methodName),版本号(versionName) 进行 路由。
- 2.支持上述服务名,方法名 模糊匹配(正则) 进行路由
- 3.根据 callIp 匹配指定 ip 规则 进行路由
- 4.根据callId。整数范围 ,整数取模 进行路由
- 5.可以路由不匹配模式 ~
- 6.可以左边使用otherwise 全部匹配 并 路由到指定 ip
- 7.ip匹配支持掩码规则和正常精确匹配(不带掩码)
- 8.附加匹配条件,可以以cookie的形式附加匹配规则
例如我们希望根据请求字段里的门店id(store_id
)进行路由,可以以如下的形式传递参数
http://127.0.0.1/createOrder?cookie_storeId = 12345678
dapeng网关会自动的将所携带的参数设置到dapeng
请求上下文InvocationContext
中去,在到服务路由这一步时,即可拿出设置的值,并进行路由。
然后路由表达式可以这样配置:
cookie_storeId match "12345678" => ip"192.168.10.1"
otherwise => ip"192.168.10.2"
当门店id为12345678
的发出的请求都会路由到1节点上面去。这样可以大大方便我们的生产灰度和部署。
路由表达式介绍
配置路由表达式是服务路由的第一步。相信学过编译原理的朋友们会比较熟悉。我们要做的就是将一条表达式翻译成程序要执行的逻辑。根据不同的需求去写一条对应的表达式,再通过自上而下的解析方式对表达式进行完整的解析,然后通过dapeng的服务语法解析器对每一次请求进行路由解析处理,判断是否路由成功
使用规则
路由表达式整体规则如下
express1 ; express2 => ip-express
- 左边为匹配规则,以分号区分多个匹配规则,需要全部满足,才能匹配成功
express
method match "getFoo" ,"setFoo"
每一个子表达式形式如上,可以通过 逗号(,)匹配多个条件,这里条件只要满足其一即可
=>
=>
是 Then
表达式,划分左边和右边,如果左边匹配成功,将指向右边的 ip 表达式
right ip表达式
ip"192.168.1.12"
表示如果匹配成功,请求将路由到192.168.1.12的ip的服务节点上 ip支持掩码的匹配形式。 如 ip”192.168.1.0/24” 可以路由到 “192.168.1.0”的一个范围。
具体例子和功能
1.匹配字符串模式的变量:
- method match "getSkuById" => ip"192.168.12.12"
作用:将方法为getSkuById的请求路由到
2.正则表达形式:
可以通过正则的形式进行匹配,如下,可以将以get开头的请求路由到12的机器上,将set开头的请求路由到13的机器上。
method match r"get.*" => ip"192.168.12.12"
method match r"set.*" => ip"192.168.12.13"
3.匹配请求ip地址,可以应用到黑名单
calleeIp match ip'192.168.1.101' => ip"192.168.2.105/30"
表示,请求ip为’192.168.1.101’的请求 将会 路由到 192.168.2.105/30及其掩码的ip的服务实例中
calleeIp match ip'192.168.1.101' => ip"0.0.0.0"
表示将请求为101的ip路由到无效的ip上,实现黑名单的功能
4.可以根据请求用户的id进行路由。
整数范围路由
userId match 10..1000 => ip"192.168.12.1"
表示将请求用户id为10 到 1000 的用户 路由到 ip为192.168.12.1的服务实例
取模路由
userId match %“1024n+6” => ip"192.168.12.1"
表示将请求用户id与1024取模结果为6时,路由到 ip为192.168.12.1的服务实例
userId match %“1024n+3..5” => ip"192.168.12.1"
表示将请求用户id与1024取模结果为3到5之间时,路由到 ip为192.168.12.1的服务实例
5.不匹配模式:
method match r"set.*" => ~ip"192.168.12.14"
表示以set开头的方法将不会路由到 ip 为 192.168.12.14 的 服务实例
6.otherwise 模式
otherwise => ip"192.168.12.12"
表示左侧所有都匹配,一般作为路由规则的最后一条执行,表示前面所有路由规则都不满足时,最后执行的路由规则
7.多条件模式
method match r"set.*" ; version match "1.0.0" => ip'192.168.1.103'
同时满足上述两个条件的请求,才会路由到右侧Ip的实例上
8.多条件模式(情形二)
method match r"set.*",r"insert.*" => ip"192.123.12.11"
这种情形是,当请求的方法名为 set开头 或者 insert开头时都可以匹配成功,路由到右侧Ip
9.路由多个Ip模式
serviceName match "com.today.service.MemberService" => ip"192.168.12.1",ip"192.168.12.2"
上述情形表示符合左边的条件,可以路由到上述右侧两个ip上
10.多路由表达式
method match "setFoo" => ip"192.168.10.12/24"
method match "getFoo" => ip"192.168.12.14"
otherwise => ip"192.168.12.18"
上述情形为多个路由表达式写法,每个路由表达式 换行分隔
我们会从最上面一条路由表达式开始进行匹配,当匹配到时即停止,不在继续向下匹配。 如果没有匹配到,将继续向下进行解析。 如上,当前两条都不符合时,即可路由到第三条,otherwise表示所有都符合的规则,这样最终将会路由到”192.168.12.18”的ip上
应用场景
灰度发布
快速入门的例子就是一个典型的灰度发布案例。这里我们来细说一下灰度发布需要注意的一些事项。
1.搞清业务方的需求。
- 灰度哪些服务,可能有多个,比如
helloService
,helloAdminService
- 灰度的服务
api
接口有没变更,有没有新增接口(这直接关系到灰度的服务元数据是否与老版本一致) - 确认灰度的服务需要兼容老版本服务(如果不兼容,需要非常注意。)
2.灰度之前过程
假设我们的灰度节点都部署在ip为192.168.10.1的服务器上
- 在启动灰度机(
192.168.10.1
)之前。配置好路由规则,屏蔽灰度机。otherwise => ~ip”192.168.10.1” - 这时候可以启动灰度机,当灰度机启动完成后,配置需要的路由规则。并发布到zk上,此时灰度路由生效。
- 但是,需要注意的是,如果有api和元数据变更的情况,这时候需要重新获取元数据信息。因为灰度服务启动过程中,会触发元数据变更。 这时候client会去获取元数据信息,但此时,灰度的路由规则还没有配置上。 所以这时候需要重启一个正常节点,让zk节点重新变更。client端会重新去获取元数据信息,这时候,路由配置的getServiceMeataData生效。 所有的元数据信息会从灰度服务中去拿。这时候灰度前置完成。
- 后期提供 reloadMeta接口,手动调用重新reload元数据信息,就不需要重启服务来触发元数据变更了
灰度过程
- 针对有多个服务的灰度需求,需要在对应的服务节点上配置路由规则
- 如果元数据信息没有变更,不用灰度getMetadataService接口,否则,必须要灰度这个方法,让所以client端拿到的元数据信息,是从灰度服务去拿的。 这样才能获取全面的api信息。
- 确认灰度过程中,查看相关灰度服务日志,没有报错和异常,则证明灰度成功
灰度结束
- 如果灰度验证成功,准备将老版本更新,正式上生产。需要注意
- 取消路由规则,是完全取消,不用屏蔽灰度服务节点。因为灰度服务和正式生产版本已经一致,不会有影响。
- 如果生产上线成功,并验证一段时间没有问题,才可以关掉灰度机(因为在这段时间内,可能正式生产版本出现不稳定宕机时,灰度机还能作为临时保障节点进行请求。)
读写分离
现在将helloService所有的get和set方法区分开来进行路由,使用路由规则的正则匹配模式进行匹配,达到读写分离的作用。
黑名单
屏蔽某个ip的请求,可以直接根据某个请求ip进行屏蔽,路由规则也比较简单
callerIp match "112.103.1.1" => ip"192.168.1.1"
我们可以为这个请求路由一个不存在的服务节点,那么这个ip请求的服务时都会报无可用服务实例。
设计以及实现
总结
dapeng服务路由时一款非常强大的路由工具,可以支持很多功能,很多方式和策略进行路由,对我们平时开发和部署,灰度发布等都有十分重要的作用。更多功能,详见dapeng开源 dapeng-soa