服务路由

目录

前言

通过前面文章的讲解,我们已经知道微服务下,给同一服务部署多个节点是基本架构。服务部署之后,客户端调用需要进行寻址,找寻调用服务的每一个部署节点和信息。前端调用请求会通过一定的路由规则,最终请求落到一个节点中。当然,通用性的路由方式,比如请求随机的路由到已部署的节点上,这样就达成了负载均衡的效果。如果我们需要有选择性的进行节点选择和控制呢?特定的请求指向特定的节点。这里就用到了dapeng的特性之一,服务路由来进行控制了。

快速入门

下面举一个简单例子,来使用dapeng服务路由模块

需求:

现在有一个微服务helloService,最近新增了一个服务接口方法invite,该方法可以邀请客人到自家做客。由于此服务之前的版本已上线,我们需要新增一个节点部署这个服务的新版本,然后通过路由的方式让所有调用invite方法的请求过滤到部署新版本服务的节点上来。

路由规则表达式

此需求是典型的通过方法进行路由的方式,我们只要确定调用的是invite这个方法,就路由他到新版本节点上去。于是配置表达式可以如下:

//新版本节点部署在 192.168.10.2
method match 'helloService' => ip"192.168.10.2"

配置中心配置

配置中心是dapeng开源之一,如果不使用配置中心也可以使用dapeng命令行工具dapeng-cli或者直接使用原生的zk操作,将表达式写入当前服务配置信息所在的zk节点上。

路由.png

配置完成后,点击保存修改按钮,然后配置中心会自动对表达式进行校验。如果表达式写法不正确,会提示修改失败,这时需要重新配置正确。

启动灰度服务(新服务)节点

在发布路由之前需要先启动灰度服务节点。这样做的原因是,避免发布路由规则后,根据规则请求的到灰度节点后,此时节点并没启动,会出现无可用服务实例的错。

发布路由

当我们配置好路由规则以后,再对服务进行发布。这样做的好处是,提前配好路由规则以后,当新版本服务节点启动之后,我们直接发布路由规则即可。

灰度验证

启动节点、成功发布路由后,变可进行灰度验证了。我们可以观察日志,不符合路由的请求都不会被路由到灰度服务上来。而通过了指定规则的请求都来到了灰度服务节点。此次灰度发布成功。

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请求的服务时都会报无可用服务实例。

设计以及实现

详见 Service Router

总结

dapeng服务路由时一款非常强大的路由工具,可以支持很多功能,很多方式和策略进行路由,对我们平时开发和部署,灰度发布等都有十分重要的作用。更多功能,详见dapeng开源 dapeng-soa