本文章将成系列介绍:包含但不限于高性能微服务API网关Soul的环境搭建、源码设计
本章节介绍使用soul进行http请求的代理
概述
配置http请求的代理将使用到:
-
divide插件
-
soul-spring-boot-starter-plugin-divide、soul-spring-boot-starter-plugin-httpclient依赖(网关端)
-
soul-spring-boot-starter-client-springmvc依赖(业务系统端)
-
yml文件的配置
soul: http: adminUrl: http://localhost:9095 #为你启动的soul-admin 项目的ip + 端口,注意要加http:// port: 8189 #你本项目的启动端口 contextPath: /http #为你的这个mvc项目在soul网关的路由前缀。 比如/order,/product等等,网关会根据你的这个前缀来进行路由. appName: http #你的应用名称,不配置的话,会默认取`spring.application.name`的值 full: false #设置true 代表代理你的整个服务,false表示代理你其中某几个controller
-
@SoulSpringMvcClient 注解(加在controller的接口上)
-
举例
你可以把注解加到 Controller 类上面, 里面的path属性则为前缀,如果含有 /** 代表你的整个接口需要被网关代理。
举例子 (1): 代表 /test/payment, /test/findByUserId 都会被网关代理。
@RestController @RequestMapping("/test") @SoulSpringMvcClient(path = "/test/**") public class HttpTestController { /** * Post user dto. * * @param userDTO the user dto * @return the user dto */ @PostMapping("/payment") public UserDTO post(@RequestBody final UserDTO userDTO) { return userDTO; } /** * Find by user id string. * * @param userId the user id * @return the string */ @GetMapping("/findByUserId") public UserDTO findByUserId(@RequestParam("userId") final String userId) { UserDTO userDTO = new UserDTO(); userDTO.setUserId(userId); userDTO.setUserName("hello world"); return userDTO; } }
举例子 (2): 代表 /order/save,会被网关代理,而/order/findById 则不会。
@RestController @RequestMapping("/order") @SoulSpringMvcClient(path = "/order") public class OrderController { /** * Save order dto. * * @param orderDTO the order dto * @return the order dto */ @PostMapping("/save") @SoulSpringMvcClient(path = "/save" , desc = "Save order") public OrderDTO save(@RequestBody final OrderDTO orderDTO) { orderDTO.setName("hello world save order"); return orderDTO; } /** * Find by id order dto. * * @param id the id * @return the order dto */ @GetMapping("/findById") public OrderDTO findById(@RequestParam("id") final String id) { OrderDTO orderDTO = new OrderDTO(); orderDTO.setId(id); orderDTO.setName("hello world findById"); return orderDTO; } }
-
示例步骤
-
启动soul-admin(插件和其他信息配置的管理后台)、soul-bootstrap(网关项目);
-
配置业务工程(以soul-examples项下soul-examples-http为例)中的controller、application.yml,在application.yml中添加:
soul: http: adminUrl: http://localhost:9095 #为你启动的soul-admin 项目的ip + 端口,注意要加http:// port: 8189 #你本项目的启动端口 contextPath: /http #为你的这个mvc项目在soul网关的路由前缀。 比如/order,/product等等,网关会根据你的这个前缀来进行路由. appName: http #你的应用名称,不配置的话,会默认取`spring.application.name`的值 full: false #设置true 代表代理你的整个服务,false表示代理你其中某几个controller
为controller中你所需的程序加上对应注解;
-
启动业务工程,会打印出对应的URI映射信息,如:
-
在soul-admin页面中可进行divide插件配置
divide插件介绍
divide插件是网关处理
http协议
请求的核心处理插件。插件设置
-
开启插件,
soul-admin
--> 插件管理–>divide
设置为启用; -
divide插件,配合如下 starter一起才能生效(网关项目中依赖即可)
<!--if you use http proxy start this--> <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-plugin-divide</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-plugin-httpclient</artifactId> <version>${project.version}</version> </dependency> <!--if you use http proxy end this-->
插件讲解
-
divide插件是进行http正向代理的插件,所有http类型的请求,都是由该插件进行负载均衡的调用。
-
选择器和规则,请详细看 : 选择器与规则介绍
-
http配置,是网关匹配到流量以后,真实调用的http配置,可以配置多个,设置负载均衡权重,具体的负载均衡策略,在规则中指定。
-
配置详解 :
- 第一个框:hostName,一般填写
localhost
,该字段暂时没使用。 - 第二个框:http协议,一般填写
http://
或者https://
,不填写默认为:http://
- 第三个框:ip与端口,这里填写你真实服务的 ip + 端口。
- 第四个框:负载均衡权重。
- 第一个框:hostName,一般填写
-
ip + port 检测
- 在soul-admin 会有一个定时任务来扫描 配置的ip端口,如果发现下线,则会除该 ip + port
- 可以进行如下配置 :
-
soul.upstream.check:true 默认为 ture,设置为false,不检测 soul.upstream.scheduledTime:10 定时检测时间间隔,默认10秒
选择器与规则介绍
- 一个插件有多个选择器,一个选择器对应多种规则。选择器相当于是对流量的第一次筛选,规则就是最终的筛选。
- 我们想象一下,在一个插件里面,我们是不是希望根据我们的配置,达到满足条件的流量,我们插件才去执行它?
- 选择器和规则就是为了让流量在满足特定的条件下,才去执行我们想要的,这个你首先头脑要点数。
选择器详解
- 名称:为你的选择器起一个容易分辨的名字
- 类型:custom flow 是自定义流量。full flow 是全流量。自定义流量就是请求会走你下面的匹配方式与条件。全流量则不走。
- 匹配方式:and 或者or 是指下面多个条件是按照and 还是or的方式来组合。
- 条件:
- uri:是指你根据uri的方式来筛选流量,match的方式支持模糊匹配(/**)
- header:是指根据请求头里面的字段来筛选流量。
- query: 是指根据uri的查询条件来进行筛选流量。
- ip:是指根据你请求的真实ip,来筛选流量。
- host:是指根据你请求的真实host,来筛选流量。
- post:建议不要使用。
- 条件匹配:
- match : 模糊匹配,建议和uri条件搭配,支持 restful风格的匹配。(/test/**)
- = : 前后值相等,才能匹配。
- regEx : 正则匹配,表示前面一个值去匹配后面的正则表达式。
- like :字符串模糊匹配。
- 是否开启:打开才会生效
- 打印日志:打开的时候,当匹配上的时候,会打印匹配日志。
- 执行顺序:当多个选择器的时候,执行顺序小的优先执行。
上述图片中表示:当请求的
uri
前缀是/http
,会转发到192.168.137.1:8189
这个服务。选择器建议 : 可以
uri
条件,match
前缀 (/contextPath),进行第一道流量筛选。规则详解
当流量经过选择器匹配成功之后,会进入规则来进行最终的流量匹配。
规则是对流量最终执行逻辑的确认。
-
- 名称:为你的规则起一个容易分辨的名字
- 匹配方式:and 或者or 是指下面多个条件是按照and 还是or。
- 条件:
- uri:是指你根据uri的方式来筛选流量,match的方式支持模糊匹配(/**)
- header:是指根据请求头里面的字段来筛选流量。
- query: 是指根据uri的查询条件来进行筛选流量。
- ip:是指根据你请求的真实ip,来筛选流量。
- host:是指根据你请求的真实host,来筛选流量。
- post:建议不要使用。
- 条件匹配:
- match : 模糊匹配,建议和uri条件搭配,支持 restful风格的匹配。(/test/**)
- = : 前后值相等,才能匹配。
- regEx : 正则匹配,表示前面一个值去匹配后面的正则表达式。
- like :字符串模糊匹配。
- 是否开启:打开才会生效。
- 打印日志:打开的时候,当匹配上的时候,会打印匹配日志。
- 执行顺序:当多个选择器的时候,执行顺序小的优先执行。
- 处理:每个插件的规则处理不一样,具体的差有具体的处理,具体请查看每个对应插件的处理。
- 上图表示:当
uri
等于/http/order/save
的时候该规则被匹配,就会执行该规则中,负载策略是random
联合选择器,我们来表述一下 :当一个 请求的
uri
为/http/order/save
, 会通过random
的方式,转发到192.168.137.1:8189
。规则建议: 可以
uri
条件,match
最真实的uri路径
,进行流量的最终筛选 。
- 配置完成后进行http请求映射,访问网关divide选择器对应的规则列表URI,如:
http://localhost:9195/http/test/findByUserId?userId=3
,则soul会自动代理到http://localhost:8189/test/findByUserId?userId=3
源码跟踪
根据代理时日志打印:SoulBootstrapApplication打印出的线程类跟踪AbstractSoulPlugin,WebClientPlugin两个类:
2021-01-16 03:51:20.890 INFO 19912 — [-work-threads-3] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http
2021-01-16 03:51:20.893 INFO 19912 — [-work-threads-3] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/**
2021-01-16 03:51:20.895 INFO 19912 — [-work-threads-3] o.d.s.plugin.httpclient.WebClientPlugin : you request,The resulting urlPath is :http://192.168.137.1:8189/test/findByUserId?userId=3, retryTimes: 0
AbstractSoulPlugin:
在execute方法中进行每个插件的链式执行,分别匹配判断其选择器及规则匹配。
AbstractSoulPlugin.execute执行divid插件doExecute方法,返回chain.execute(exchange);之后进行响应式编程的plugin.execut,执行WebClientPlugin的execute方法进行转发实时URI。
读源码感觉只懂了不到10%,白天再参考下同学大牛的总结,今天太晚了就到这里。
另外,选择器规则注册后为持久化数据,若需取消则需要手动配置取消规则。