目录
如下图所示,订单服务 调用 商品服务 的 流程的变化从1-4。

存在问题:此时,如果有新的客户端分别调用 订单服务 和 商品服务,那么这个客户端需要维护新的ip和port。而新客户端调用这些微服务时候,微服务又需要解决进行认证鉴权、安全校验、跨域问题。如果微服务 订单服务 和 商品服务 可能有成百上千个。此时存在的问题是:
解决方案:使用服务网关。而服务网关也是一个微服务,也可从注册中心获取ip。因此,所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。添加上API网关之后,系统的架构图变成了如下所示:

| 名称 | 作用 | 问题 |
| Ngnix+lua | 使用nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用。lua是一种脚本语言,可以来编写一些简单的逻辑,nginx支持lua脚本。 | 无 |
| Kong | 基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。 | 1、只支持Http协议 2、二次开发,自由扩展困难 3、提供管理API,缺乏更易用的管控、配置方式。 |
| zuul | Netflix开源的网关,功能丰富,使用JANA开发,易于二次开发 | zuul1.0 缺乏管控,无法动态配置;依颡组件较多;处理Http请求依赖的是Web容器,性能不如Nginx |
| Spring cloud Gateway | Spring cloud Gateway Spring公司为了替换Zuul而开发的网关服务 | 无 |
| zuul | Spring cloud Gateway |
| Zuul 1.x,是基于阻塞V/O的API网关 | Spring Cloud Gateway基于异步非阻塞模型,建立在Spring Framework5、Project Reactor和Spring Boot 2之上 |
| Zuul 1.x,是基于Servlet 2.5,使用阻塞架构和传统的IO处理模型(高并发线程数量上涨,严重影响请求处理时间),它不支持任何长连接(如WebSocket) | Spring Cloud Gateway基于webplus(webplus基于Servlet 3.x异步非阻塞的模型,核心是Reactor响应式编程),支持WebSocket |
| Zuul 1.x,设计模式和Nginx较像,每次V/О操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成。 但是差别是Nginx用C++实现,Zuul用Java实现,故而JVM本身会有第—次加载较慢的情况,使得Zuul的性能相对较差。 | 根据官方提供的基准测试,Spring Cloud Gateway的RPS(每秒请求数)是Zuul的1.6倍。 |
| Zuul 2.x 理念更先进,想基于Netty非阻塞和支持长连接,性能较Zuul 1.x有较大提升,但SpringCloud目前还没有整合。 | 与Spring紧密集成拥有更好的开发体验 |

Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。它的目标是替代 Netilx Zuul,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标和限流。

Gateway核心是:route、Predicate、Filter。
流程:通过网关服务,对访问路径进行请求,如果请求符合断言,则调用订单服务模块。
完整pom如下:
"1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloudbaseartifactId> <groupId>com.hwadee.springcloud2022groupId> <version>0.0.1-SNAPSHOTversion> parent> <modelVersion>4.0.0modelVersion> <groupId>com.hwadee.springcloudgroupId> <artifactId>routeServer7000artifactId> <version>0.0.1-SNAPSHOTversion> <dependencies> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-gatewayartifactId> dependency> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId> dependency> dependencies> project>
注意:如果GetAway网关服务模块中引入GetAway依赖,则不能引入 starter-web, 如果引入 starter-web 则会引入tomcat,与 GetAway 底层的webFlux冲突,如下图:
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
-
- @SpringBootApplication
- @EnableEurekaClient// 启动 eureka 客户端
- public class RouteServerApplication {
- public static void main(String[] args) {
- SpringApplication.run(RouteServerApplication.class, args);
- }
- }
添加配置文件,配置文件中配置GetAway网关服务。
- server:
- port: 7000
- spring:
- application:
- name: route-service # 为当前商品服务命名
- cloud:
- gateway:
- routes: #路由数组[路由就是指定当请求满足什么条件的时候转到哪个微服务〕
- - id: order_route #当前路由的标识,要求唯一,默认uuid。自定义命名:一般是 要转发的服务名_route
- uri: http://localhost:9000 #请求最终要被转发到的 订单服务 的地址
- order: 1 #路由的优先级,数字越小级别越高
- predicates: #断言(条件判断返回值类型是boolean,就是路由转发要满足的条件)
- - Path=/order-serv/** #当请求路径满足Path指定的规则时,才进行路由转发,如/order-serv/order/select/22
- filters: #过滤器,请求在传递过程中可以通过过滤器对请求进行一定的修改
- - StripPrefix=1 #使用 StripPrefix= x 是指在转发前去掉几层路径。见下面属性说明案例。
-
-
- - id: order_route #当前路由的标识,要求唯一
- uri: http://localhost:9001 #请求最终要被转发到的 产品服务 的地址
- order: 1 #路由的优先级,数字越小级别越高
- predicates: #断言(条件判断返回值类型是boolean,就是路由转发要满足的条件)
- - Path=/product-serv/** #当请求路径满足Path指定的规则时,才进行路由转发
- filters: #过滤器,请求在传递过程中可以通过过滤器对请求进行一定的修改
- - StripPrefix=1 #使用 StripPrefix= 1 在转发前去掉1层路径。
-
-
- eureka:
- client:
- service-url: # 配置服务注册地址,与 eureka-server 中暴露地址保持一致
- defaultZone: http://localhost:8000/eureka
- instance:
- prefer-ip-address: true # 是否使用 IP 地址注册,默认 false
- # instance-id: product-service # 实例 id,服务的唯一标识
- instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置
- lease-renewal-interval-in-seconds: 5 # 发送心跳的间隔,单位秒,默认 30
- lease-expiration-duration-in-seconds: 10 # 续约到期时间,单位秒,默认90
属性介绍说明:
| routes | #路由数组[路由就是指定当请求满足什么条件的时候转到哪个微服务] |
| id | id: order_route # 表示当前路由的标识,要求唯一,默认是uuid。 一般是转发到哪个服务就在哪个服务名后加route |
| uri | #请求最终要被转发到的哪个服务的地址,如:http://localhost:8081 |
| order | order:1 #路由的优先级,数字越小级别越高 |
| predicates | #断言(条件判断返回值类型是boolean,就是路由转发要满足的条件) |
| Path | # - Path=/product-serv/**,当请求路径满足Path指定的规则时,才进行路由转发,如:/product-serv/order/buy/22 满足条件,会进行路由转发。 |
| filters | #过滤器,请求在传递过程中可以通过过滤器对请求进行一定的修改 |
| StripPrefix | # -StripPrefix = 1 是指转发之前去掉1层路径,例如: 我们的需求是 当浏览器访问路径是http://localhost:8081/order-serv/order/buy/22时候, |
创建 订单服务模块 及创建 商品服务模块 见第十章
删除 商品服务 模块中的 controller 内 selectHystrixBreaker方法里的超时代码,修改如下:
- import com.hwadee.springcloud.entity.Product;
- import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
- import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
- import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
- import java.math.BigDecimal;
-
- @RestController
- @RequestMapping("/product")
- public class ProductController {
- //方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip
- @Value("${server.port}")
- private String port;
- @Value("${spring.cloud.client.ip-address}")
- private String ip;
-
- @RequestMapping(value = "/select/{id}")
- @HystrixCommand(fallbackMethod = "selectHystrixBreakerFallback", commandProperties = {
- @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
- @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),//请求次数
- @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期
- @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),//失败率达到多少后跳闸
- })
- public Product selectHystrixBreaker(@PathVariable Long id) {
- // 程序异常 测试异常熔断
- if (id<=0) {
- throw new RuntimeException("id无效");
- }
-
- // 程序正常执行
- Product product = new Product();
- product.setId(id);
- // 后面需要测试负载均衡,所以返回 ip 地址及端口号
- product.setName("当前访问服务地址:" + ip + ":" + port + " " + "从购物车删除订单,订单号:" + id);
- product.setPrice(new BigDecimal(10000.0));
- System.out.println(product);
-
- return product;
- }
-
- public Product selectHystrixBreakerFallback(Long id) {
- Product product = new Product();
- product.setId(id);
- product.setName("当前访问服务地址:" + ip + ":" + port + " " + "查询订单异常,通过注解 @HystrixCommand()指定的备选方案进行服务熔断");
- product.setPrice(new BigDecimal(10000.0));
- return product;
- }
- }
分别启动 注册中心、订单服务、商品服务、和网关服务
分别访问以下地址查看使用网关之前和使用网关之后的效果。
访问商品服务:http://localhost:9001/product/select/2
访问订单服务:http://localhost:9000/order/select/22
访问商品服务:http://localhost:7000/product-serv/product/select/22
访问订单服务:http://localhost:7000/order-serv/order/select/22




第十二章:Spring Cloud Config 统一配置中心详解