大家好,又见面了,我是你们的朋友全栈君。
为啥要有网关
大型系统在设计之初就会拆分为多个微服务,客户不可能都按每个服务的服务器地址进行访问,因为每个服务对应一个指定的Url,人咋记那么多的地址,这样我们是不是需要一个统一的入口公开给客户,去解决这种调用问题,同时,AJAX虽说可以进行异步请求实现局部刷新,但是不能解决跨域对吧,之前我们怎么进行跨域处理的,用的是在controller层添加@CrossOrign注解,解决跨域问题。单体项目还好说,那么在微服务项目中可能又成千上百的服务,那我都要一个个加吗?而且有的服务还可能存在着没有controller层的问题,我在过滤器、拦截器层面进行业务设计,那不G了?能不能在一个统一的地方进行解决?为了在项目简化前端调用的逻辑,同时优化内部服务的相互调用,也能更好的保护内部服务,网关应运而生。
概述
其实说到底,网关就是给一个指定的URL,让内外部的业务调用使用这指定的URL从而简化调用,还可以进行权限验证与限流的操作
Spring Cloud Gateway网关(后面简称SCG)
基于Spring 5.0 以及 Spring boot 2.0和一堆技术进行开发的一个网关组件,跟概述一样,作用提供一个指定的API入口,负责服务请求路由、结合、协议转换,并且基于过滤链可以提供权限认证与监控限流等功能
优缺点
优:性能厉害(是zuul的1.6倍zuul,zuul可处理百万并发)、
功能强大(内置了很多使用功能,例如限流与转发、监控),
好拓展
缺:依赖于Netty(网络通讯框架比Tomcat更牛可处理大量并发)与WebFlux(基于的是大量的异步通讯机制),跟之前的serverlet编程模型不同
需要Springboot 2.0以上才能用
webFlux的优点
就是tomcat接到一个请求之后,会从线程池中拿一个线程进行,服务于这个请求,Tomcat的线程池中的线程数是有限的,但是现在的这个线程接完请求之后还有对其进行处理,处理完请求后,才将线程放回池中,循环执行。但当Tomcat线程都在处理业务的时候,就会出现线程数不够的情况就不会再接请求了。而这个WebFlux就可实现Tomcat的线程只接受请求,处理业务的时候尽量交给后面的线程进行操作,解决这个问题。
使用gateway
1.创建一个网关服务模块 例sca-gateway
2.添加依赖:(注意添加了gateway依赖以后,不能添加spring web会冲突)
代码语言:javascript复制 <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
3.编写bootstrap.yml
首先配置路由元素,写好要被网关管理的服务的地址,当你的请求发过来的时候,首先要经过断言predicates,判断端口号与端口号后面的东西,如果和写的请求规则相同,然后进行过滤,去掉指定的路径,这里用到的过滤器是局部过滤器
代码语言:javascript复制spring:
application:
name: sca-gateway
cloud:
gateway:
routes: #路由元素,此元素下可以有多个路由
- id: route01
uri: http://localhost:8081/ # 网关帮我们转发的的url URL是URI的一个子集
predicates: ### 断言(谓词)匹配请求规则 定义请求转发逻辑的,满足下面的条件才会被转发
- Path=/nacos/provider/echo/** #请求路径定义,此路径对应uri中的资源
filters: #过滤器特殊的拦截器,写到这个位置的是局部过滤器
- StripPrefix=1 # 过滤掉path中的第一层路径,例如nacos
server:
port: 9000
知识点:URL是URI的一个子集,uri统一资源标识 url统一资源定位
知识点:NoSuchBeanDefintionException 找不到bean的异常
知识点:- Path=/nacos/provider/echo/** 两个星号代表着可以/a/v/b/c/… 单个标识/a
Gateway的负载均衡设计
负载均衡?
因为网关是一个请求的统一入口,要处理超高并发量的请求,所有的服务都会在网关层面进行底层的一个映射,所以在访问服务的时要基于服务serviceId 服务名去查找对应的服务,让请求从网关层实现负载均衡转发,以平衡服务实例的处理能力
Netty为什么性能这么好为什么不都用Netty,因为Netty是一个网络编程框架因此就需要二次开发!web依赖简单好用
Gateway中负载均衡实现
第一步:项目中添加服务发现依赖,为的是去发现网关可以访问的服务具体地址
lb是一个网关层面的协议名,底层也是基于ribbon实现
代码语言:javascript复制server:
port: 9000
spring:
application:
name: sca-gateway
cloud:
gateway:
routes: #配置网关路由规则
- id: route01 #路由id,自己指定一个唯一值即可
uri: lb://sca-provider #lb为服务前缀(负载均衡单词的缩写),不能随意写
predicates: ###断言(谓词):匹配请求规则
- Path=/nacos/provider/echo/** #请求路径定义,此路径对应uri中的资源
filters: ##网关过滤器,用于对谓词中的内容进行判断分析以及处理
- StripPrefix=1 #转发之前去掉path中第一层路径,例如nacos
discovery:
locator:
enabled: true #开启通过服务注册中心的serviceId创建路由
nacos:
discovery:
server-addr: localhost:8848
其次记得在配置文件把gateway日志打开
logging: level: com.jt: debug
第二步:启动多个provider进行测试
Gateway的执行流程
首先Gateway Client会发起请求,会有一个RoutePredicateHandlerMapping,根据你的url去获取对应的handler(处理器),但在去找handler之前要对其url进行校验,校验的就是我们在yml里设置的断言,首先回去断言工程创建GatewayPredicate对象,然后执行该对象中的test方法,返回值为boolean,当返回true的时候,会去寻找其handler,这里调用的是FilterWebHandler,这个处理器会去调用GatewayFilter也就是一个自定义的过滤链chain(责任链模式),也就是局部过滤器,之后再走全局过滤器,在其中实现负载均衡(ribbon),之后再进入对应的服务
断言增强分析
predicate断言又称作为谓词,只有当断言结果都为真的时候,才会执行真正的路由,也就是判断是否能进行路由转发的规则,Gateway所有的谓词都时间接或者直接的实现了RoutePredicateFactory接口,这些工厂负责创建谓词对象,或者通过谓词对象来判断请求合法性。
如何创建全局过滤器(针对所有的请求)
举个栗子:
创建一个类命名为AuthGlobalFilter,要去实现一个全局过滤器的标准(GlobalFilter 接口)
重写其中的filter(exchange,chain【过滤链】)(返回值是一个Mono)方法,进行自定义过滤器。
- 获取请求对象/响应对象:
- 获取请求使用的是filter方法中的exchange参数点出来的getRequest(),默认获取所有请求参数、还可以获取第一个、与指定的某个 get的是获取指定的,getFirst获取第一个
- exchange.getResponse();可以获取一个响应对象,然后可以设置其响应码等数据
- 获取请求中数据/设置响应中的数据
- 对请求数据进行分析处理
- 认证成功与失败
- 成功 返回 chain.filter(交互对象exchange);继续向后执行
具体实现一个小业务设置网关的黑名单不通过sentinel: 1.首先写yml文件(指定黑名单 -就是一个分隔符,会自动存到一个list集合中)
2.写具体的全局过滤器
两种可以获取对应yml文件中的值
- @Value(“web.request.blackUrls”)
- 在这个接口上添加@ConfigurationProperties(“web.request”)
- 当系统底层读取@ConfigurationProperties(prefix = “web.request”),注解中描述的内容时,会自动基于名字调用set方法 @param blackPaths
- 要记得去写set方法
package com.jt.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
@ConfigurationProperties("web.request")
@Component
public class urlGlobalFilter implements GlobalFilter {
List<String> list = new ArrayList<>();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String uri = request.getURI().toString();
System.out.println(list);
for (String l1:list){
if (uri.contains(l1)){
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
}
return chain.filter(exchange);
}
/*
* 当系统底层读取@ConfigurationProperties(prefix = "web.request")
* 注解中描述的内容时,会自动基于名字调用set方法
* @param blackPaths
* */
public void setList(List<String> blackPaths){
this.list = blackPaths;
}
}
Gateway的网关限流设计及其实现(细水长流)
为啥要设置网关,因为网关是访问服务的唯一入口,所以要处理高并发的访问
Burst size :0 请求瞬时并发是否允许额外的请求通过网关
实现步骤
【1】导入依赖,与其他正常服务的sentinel依赖有所不同
代码语言:javascript复制 <dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
【2】在启动参数上添加
【3】sentinel的降级设定与正常服务添加一样
【4】设置降级可以分为api组限流与routeid限流
routeId限流就是根据你在yml的route一样
api分组,是可以把一些请求链路放在一起,进行批量设置
【5】api分组设置步骤
【6】设置流控规则
【7】根据指定的参数进行限流(举个栗子,用请求头里面的参数进行匹配,匹配到,就会执行对应的限流规则)
【8】当你想响应的值不为默认的,就要写这么一个配置类,来指定限流异常处理器
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/183079.html原文链接:https://javaforall.cn