赞
踩
学习一种技术最好的方式是:视屏+官方文档!!!!!
官网文档地址:https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/
注意:这个是H版的SR1对应的文档,如果想看其他版本的直接更改链接中的版本号即可,例如查看Hoxton.SR2版本,如下:
https://cloud.spring.io/spring-cloud-static/Hoxton.SR2/reference/htmlsingle/
代码地址:https://gitee.com/aismall/spring-cloud
本次笔记对应的课程为尚硅谷的Springcloud教程,课程地址:springcloud课程地址
课程包含:springcloud
+springcloud alibaba
课程分为四个等级(不一定要一次性学完,你懂得!!!):
课程大纲:
一点一点的学习下面这些组件!!!!!
由于Zuul这个网关已经被遗弃了,对比之下还是学习常用的网关技术比较好,也就是新一代的网关GateWay!!!
技术是学不完的,学习实用的即可!!!!
对Zuul网关感兴趣的可以移步至官网学习:https://github.com/Netflix/zuul/wiki
官方文档:https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-gateway
Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关;
但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关替代Zuul,那就是SpringCloud Gateway—句话:gateway是原zuul1.x版的替代
还记得下面这张图吗?
官网介绍如下:
Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5,Spring Boot 2和Project Reactor等技术。
Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如:熔断
、限流
、重试
等。
SpringCloud Gateway是Spring Cloud的一个全新项目,基于Spring 5.0+Spring Boot 2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供—种简单有效的统一的API路由管理方式。
SpringCloud Gateway作为Spring Cloud 生态系统中的网关,目标是替代Zuul
,在Spring Cloud 2.0以上版本中,没有对新版本的Zul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty
。
Spring Cloud Gateway的目标提供统一的路由方式且基于 Filter链的方式提供了网关基本的功能,例如:安全
,监控/指标
,和限流
。
作用
- 方向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
- ...
有Zuul了怎么又出来Gateway?我们为什么选择Gateway?
原因1:Netflix不太靠谱,Zuul2.0一直跳票,迟迟不发布。
原因2:Zuul1.0已经进入了维护阶段,而且Gateway是SpringCloud团队研发的,是亲儿子产品,值得信赖,而且很多功能Zuul都没有用起来,也非常的简单便捷。
原因3:Gateway是基于异步非阻塞
模型上进行开发的,性能方面不需要担心。虽然Netflix早就发布了最新的Zuul 2.x,但Spring Cloud貌似没有整合计划。而且Netflix相关组件都宣布进入维护期,不知前景如何?
多方面综合考虑Gateway是很理想的网关选择。
SpringCloud Gateway具有如下特性:
基于Spring Framework 5,Project Reactor和Spring Boot 2.0进行构建;
动态路由:能够匹配任何请求属性;
可以对路由指定Predicate (断言)
和Filter(过滤器)
;
集成Hystrix的断路器功能
(Hystrix的部分功能);
集成Spring Cloud 服务发现功能
(Eureka的功能);
易于编写的Predicate (断言)和Filter (过滤器);
请求限流功能;
支持路径重写。
SpringCloud Gateway与Zuul的区别
在SpringCloud Finchley正式版
之前,Spring Cloud推荐的网关是Netflix提供的Zuul,Zuul 1.x是一个基于阻塞I/O的API Gateway
,Zuul 1.x基于Servlet 2.5使用阻塞架构它不支持任何长连接(如WebSocket),
Zuul的设计模式和Nginx较像,每次I/О操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是Nginx用C++实现,Zuul用Java实现,而JVM本身会有第一次加载较慢的情况
,使得Zuul的性能相对较差。
Zuul 2.x理念更先进,想基于Netty非阻塞和支持长连接,但SpringCloud目前还没有整合。
Zuul 2.x的性能较Zuul 1.x有较大提升,在性能方面,根据官方提供的基准测试,Spring Cloud Gateway的RPS(每秒请求数)是Zuul的1.6倍。
Spring Cloud Gateway建立在Spring Framework 5、Project Reactor和Spring Boot2之上,使用非阻塞API
。
Spring Cloud Gateway还支持WebSocket
,并且与Spring紧密集成拥有更好的开发体验
Zuul1.x模型
Springcloud中所集成的Zuul版本,采用的是Tomcat容器,使用的是传统的Servlet IO处理模型。
Servlet的生命周期?servlet由servlet container进行生命周期管理,container启动时构造servlet对象并调用servlet init()进行初始化,container运行时接受请求,并为每个请求分配一个线程(一般从线程池中获取空闲线程)然后调用service),container关闭时调用servlet destory()销毁servlet。
上述模式的缺点:
Servlet是一个简单的网络IO模型,当请求进入Servlet container时,Servlet container就会为其绑定一个线程,在并发不高的场景
下这种模型是适用的。
但是一旦高并发(如用Jmeter压测),线程数量就会上涨,而线程资源代价是昂贵的(上线文切换,内存消耗大)严重影响请求的处理时间。
在一些简单业务场景下,不希望为每个request分配一个线程,只需要1个或几个线程就能应对极大并发的请求,这种业务场景下servlet模型没有优势。
所以Zuul 1.X是基于servlet之上的一个阻塞式处理模型,即Spring实现了处理所有request请求的一个servlet (DispatcherServlet)并由该servlet阻塞式处理处理。所以SpringCloud Zuul无法摆脱servlet模型的弊端。
Gateway模型
WebFlux是什么?官方文档
传统的Web框架,比如说: Struts2,SpringMVC等都是基于Servlet APl与Servlet容器基础之上运行的。
但是在Servlet3.1之后有了异步非阻塞
的支持,而WebFlux是一个典型非阻塞异步的框架
,它的核心是基于Reactor的相关API实现的,相对于传统的web框架来说,它可以运行在诸如Netty
,Undertow
及支持Servlet3.1的容器上。
非阻塞式+函数式编程
(Spring 5必须让你使用Java 8及其以上版本)。
Spring WebFlux
是Spring 5.0 引入的新的响应式框架,区别于Spring MVC,它不需要依赖Servlet APl,它是完全异步非阻塞的
,并且基于Reactor来实现响应式流规范。
三大核心概念
web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。
predicate就是我们的匹配条件;而fliter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了
客户端向Spring Cloud Gateway发出请求,然后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到GatewayWeb Handler。
Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(pre
)或之后(pos
t)执行业务逻辑。
Filter为pre
类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,
Filter为post
类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
核心逻辑:路由转发 + 执行过滤器链。
1、新建Module: cloud-gateway-gateway9527
。
2、POM
<dependencies> <!--gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 引入自己定义的api通用包,可以使用Payment支付Entity --> <dependency> <groupId>com.aismall</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <!--一般基础配置类--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
3、YML
server:
port: 9527
spring:
application:
name: cloud-gateway
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7003.com:7003/eureka
4、业务类:无,是不是很NICE!!!!!
5、主启动类
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527{
public static void main(String[] args) {
SpringApplication.run(GatewayMain9527.class, args);
}
}
6、9527网关如何做路由映射?
我们目前不想暴露8001端口,希望在8001外面套一层9527
我们需要配置路由转发,这个要结合cloud-provider-payment8001
中具体的访问地址来做,看看controller的访问地址,下面给出cloud-provider-payment8001
服务中的controller
的代码:
@RestController @Slf4j public class PaymentController{ @Resource private PaymentService paymentService; @Resource private DiscoveryClient discoveryClient; @Value("${server.port}") private String serverPort;//添加serverPort @PostMapping(value = "/payment/create") public CommonResult create(@RequestBody Payment payment) { int result = paymentService.create(payment); log.info("*****插入结果:"+result); if(result > 0) { return new CommonResult(200,"插入数据库成功,serverPort: "+serverPort, result); }else{ return new CommonResult(444,"插入数据库失败,serverPort: "+serverPort,null); } } @GetMapping(value = "/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) { Payment payment = paymentService.getPaymentById(id); if(payment != null) { return new CommonResult(200,"查询成功,serverPort: "+serverPort,payment); }else{ return new CommonResult(444,"没有对应记录,查询ID: "+id,null); } } @GetMapping("/payment/lb") public String getPaymentLB(){ return serverPort; } @GetMapping("/payment/discovery") public DiscoveryClient discovery(){ // 得到所有服务名 List<String> services = discoveryClient.getServices(); services.forEach(ele->{ log.info("***service***"+ele); }); // 得到服务名对应的信息 List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE"); instances.forEach(ele->{ log.info(ele.getServiceId()+"\t"+ele.getHost()+"\t"+ele.getPort()+"\t"+ele.getUri()); }); return discoveryClient; } // 新增一个方法 @GetMapping(value = "/payment/feign/timeout") public String paymentFeignTimeout(){ // 业务逻辑处理正确,但是需要耗费3秒钟 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } return serverPort; } }
7、修改9527的YML:新增网关配置
配置两个路由转发:
- @GetMapping(value = "/payment/get/{id}")
- http://localhost:8001/payment/get/{id}
- @GetMapping("/payment/lb")
- http://localhost:8001/payment/lb
server: port: 9527 spring: application: name: cloud-gateway #############################新增网关配置########################### cloud: gateway: routes: - id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/get/** # 断言,路径相匹配的进行路由 - id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/lb/** # 断言,路径相匹配的进行路由 #################################################################### eureka: instance: hostname: cloud-gateway-service client: #服务提供者provider注册进eureka服务列表内 service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7003.com:7003/eureka
8、测试
- 启动7003
- 启动8001:cloud-provider-payment8001
- 启动9527网关
- 添加网关前:http://localhost:8001/payment/get/1
- 添加网关后:http://localhost:9527/payment/get/1
- 两者访问成功,返回相同结果
方式一:在网关服务的配置文件yml中配置,见上一章节
方式二:代码中注入RouteLocator的Bean
如何实现那?
业务需求:通过9527网关访问到国内的百度新闻网址
百度国内新闻网址:http://news.baidu.com/guonei
修改cloud-gateway-gateway9527
添加一个配置类如下:
@Configuration
public class GateWayConfig{
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_aismall",
r -> r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
测试:启动cloud-gateway-gateway9527微服务
http://localhost:9527/guonei
,返回http://news.baidu.com/guonei
相同的页面。默认情况下Gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能(不写死一个地址)。
启动
- eureka7003
- payment8001/8002
修改9527
的POM文件:添加过就不用再加了
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
修改9527的YML文件:
需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。
lb://serviceName是spring cloud gateway在微服务中自动为我们创建的负载均衡uri。
server: port: 9527 spring: application: name: cloud-gateway #############################新增网关配置########################### cloud: gateway: discovery: locator: enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由 routes: - id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 #uri: http://localhost:8001 #匹配后提供服务的路由地址 uri: lb://cloud-payment-service #匹配后提供服务的路由地址(不能写死,写注册的服务名) predicates: - Path=/payment/get/** # 断言,路径相匹配的进行路由 - id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 #uri: http://localhost:8001 #匹配后提供服务的路由地址 uri: lb://cloud-payment-service #匹配后提供服务的路由地址(不能写死,写注册的服务名) predicates: - Path=/payment/lb/** # 断言,路径相匹配的进行路由 #################################################################### eureka: instance: hostname: cloud-gateway-service client: #服务提供者provider注册进eureka服务列表内 service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka
测试
浏览器输入:http://localhost:9527/payment/lb
结果:不停刷新页面,8001/8002两个端口切换。
Route Predicate Factories这个是什么?
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。
Spring Cloud Gateway包括许多内置的Route Predicate工厂
,所有这些Predicate都与HTTP请求的不同属性匹配,多个RoutePredicate工厂可以进行组合。
Spring Cloud Gateway创建Route 对象时,使用RoutePredicateFactory 创建 Predicate对象,Predicate 对象可以赋值给Route。Spring Cloud Gateway包含许多内置的Route Predicate Factories,所有这些谓词都匹配HTTP请求的不同属性,多种谓词工厂可以组合,并通过逻辑and。、
常用的Route Predicate Factory
- The After Route Predicate Factory
- The Before Route Predicate Factory
- The Between Route Predicate Factory
- The Cookie Route Predicate Factory
- The Header Route Predicate Factory
- The Host Route Predicate Factory
- The Method Route Predicate Factory
- The Path Route Predicate Factory
- The Query Route Predicate Factory
- The RemoteAddr Route Predicate Factory
- The weight Route Predicate Factory
讨论几个Route Predicate Factory的使用:在9527中配置
The After Route Predicate Factory
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
# 这个时间后才能起效
- After= 2021-08-21T16:19:55.980+08:00[Asia/Shanghai]
访问链接:http://localhost:9527/
时间戳字符串
:public class TimeStamp{
public static void main(String[] args){
ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
System.out.println(zbj);
//2021-02-22T15:51:37.485+08:00[Asia/Shanghai]
}
}
The Between Route Predicate Factory
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://example.org
# 两个时间点之间
predicates:
- Between=2021-08-21T16:19:55.980+08:00[Asia/Shanghai], 2022-08-21T16:19:55.980+08:00[Asia/Shanghai]
访问链接:http://localhost:9527/
The Cookie Route Predicate Factory
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
The cookie route predicate factory takes two parameters, the cookie name and a regular expression.
This predicate matches cookies that have the given name and whose values match the regular expression.
测试:
该命令相当于发get请求,且没带cookie
- curl http://localhost:9527/
带cookie的
- curl http://localhost:9527/ --cookie "chocolate=chip"
The Header Route Predicate Factory
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
The header route predicate factory takes two parameters, the header name and a regular expression.
This predicate matches with a header that has the given name whose value matches the regular expression.
测试
带指定请求头的参数的CURL命令
- curl http://localhost:9527/ -H "X-Request-Id:123"
小结
Spring Cloud Gateway的Filter:
pre
post
- GatewayFilter - 有31种
- GlobalFilter - 有10种
- 上面的过滤器可以参考官方文档自己配一配,感受一下!!!!
- GlobalFilter
- Ordered
- 全局日志记录
- 统一网关鉴权
- ...
代码案例:GateWay9527项目添加MyLogGateWayFilter
类:
@Component @Slf4j public class MyLogGateWayFilter implements GlobalFilter,Ordered{ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){ log.info("***********come in MyLogGateWayFilter: "+new Date()); String uname = exchange.getRequest().getQueryParams().getFirst("uname"); if(uname == null) { log.info("*******用户名为null,非法用户,o(╥﹏╥)o"); exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder(){ return 0; } }
测试:
启动
- EurekaMain7003
- PaymentMain8001
- GateWayMain9527
- PaymentMain8002
浏览器输入:
- http://localhost:9527/payment/lb - 反回异常
- http://localhost:9527/payment/lb?uname=abc - 正常访问
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。