前言
一、Netflix Zuul
Zuul1.0的通信模型
Zuul2.0的通信模型
Zuul是通过Servlet来实现的(Servlet 会为每个请求绑创建一个线程,而线程上线文切换,内存消耗大),Zuul通过自定义的ZuulServlet(类似于Spring MVC的DispatcherServlet)来对请求进行控制(一系列过滤器处理Http请求)。
所有的Request都要经过ZuulServlet的处理,三个核心的方法preRoute(),route(), postRoute(),zuul对request处理逻辑都在这三个方法里,ZuulServlet交给ZuulRunner去执行。
ZuulRunner直接将执行逻辑交由FilterProcessor处理,ZuulServlet、ZuulRunner、FilterProcessor都是单例。
FilterProcessor对filter的处理逻辑。
1.根据Type获取所有输入该Type的filter,List<ZuulFilter> list。
2.遍历该list,执行每个filter的处理逻辑,processZuulFilter(ZuulFilter filter)。
3.RequestContext对每个filter的执行状况进行记录,此处的执行状态主要包括其执行时间、以及执行成功或者失败,若失败则对异常封装后抛出。
4.zuul框架对每个filter的执行结果都没有太多的处理,没把上一filter的执行结果交由下一个将要执行的filter,仅记录执行状态,如果执行失败抛出异常并终止执行。
1、Zuul过滤器的功能
1.身份验证和安全性 - 确定每个资源的身份验证要求并拒绝不满足这些要求的请求。
2.洞察和监控 - 在边缘跟踪有意义的数据和统计数据,以便为我们提供准确的生产视图。
3.动态路由 - 根据需要动态地将请求路由到不同的后端群集。
4.压力测试 - 逐渐增加群集的流量以衡量性能。
5.负载分配 - 为每种类型的请求分配容量并删除超过限制的请求。
6.静态响应处理 - 直接在边缘构建一些响应,而不是将它们转发到内部集群。
2、Zuul 生命周期(四类过滤)
1.PRE: 在请求被路由之前调用。可在集群中选择请求的微服务、认证鉴权,限流等。
2.ROUTING: 将请求路由到微服务。可构建发送给微服务的请求,并使用Apache HttpClient或 Ribbon请求微服务,现在也支持OKHTTP。
3.POST: 在路由到微服务以后执行。可在这种过滤中处理逻辑,如收集统计信息和指标、将响应从微服务发送给客户端等。
4.ERROR: 在其他阶段发生错误时执行该过滤器。可做全局异常处理。
3、Zuul的使用
1.配置
server:
port: 80
spring:
application:
name: demo-zuul
eureka:
client:
enabled: true #该客户端是否可用
service-url:
defaultZone: http://localhost:8761/eureka #注册中心地址
register-with-eureka: true #注册该服务,默认为true
fetch-registry: true #获取服务列表,默认为true
2.启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapper;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableZuulProxy//开启网关功能
public class DemoZuulApplication {
public static void main(String[] args) {
SpringApplication.run(DemoZuulApplication.class, args);
}
//配置动态路由规则
@Bean
public PatternServiceRouteMapper getPatternServiceRouteMapper() {
return new PatternServiceRouteMapper("(?<name>^.+)", "${name}");
}
}
3.写自己的过滤器
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
@Component
public class TokenFilter extends ZuulFilter {
/**
* 拦截类型,4种类型 pre route error post
*/
@Override
public String filterType() {
// FilterConstants.PRE_TYPE;
// FilterConstants.ROUTE_TYPE;
// FilterConstants.ERROR_TYPE;
// FilterConstants.POST_TYPE;
return FilterConstants.PRE_TYPE;
}
/**
* 该过滤器在所有过滤器的执行顺序值,值越小,越前面执行
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 是否拦截
*/
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
// RequestContext ctx = RequestContext.getCurrentContext();
// ctx.getBoolean("isOk");
HttpServletRequest request = ctx.getRequest();
String requestURI = request.getRequestURI();
//排除拦截的url
if (requestURI.equals("/demo-member/user/loadBalance")) {
return false;
}
return true;
}
/**
* 过滤器具体的业务逻辑
*/
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String token = request.getParameter("token");
// ctx.set("isOk",true); // 可以在上下文里面设置一个key,在下一次拦截时,就可以获取到
if (null == token) {
ctx.setResponseBody("token is null");
ctx.setResponseStatusCode(400);
ctx.setSendZuulResponse(false);
return null;
}
if (!"123456".equals(token)) {
ctx.setResponseBody("token is error");
ctx.setResponseStatusCode(400);
ctx.setSendZuulResponse(false);
return null;
}
ctx.setSendZuulResponse(true);
return null;
}
}
二、Spring Cloud Gateway
spring cloud gateway 的核心是一系列过滤器,可将客户端的请求转到不同服务器,可简称为过滤和路由。与Zuul的主要的区别在底层的通信框架上,Gateway 底层使用通信框架Netty提供非阻塞异步请求处理,内嵌 Hystrix 断路器。
(1)基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0。
(2)集成 Hystrix 断路器。
(3)集成 Spring Cloud DiscoveryClient。
(4)Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters。
(5)具备一些网关的高级功能:动态路由、限流、路径重写。
GateWay的通信模型
GateWay的路由流程
GateWay内部工作流程
1、GateWay三大组件
1.路由 Route
id:路由标识,要求唯一,名称任意(默认uuid)。
uri:请求最终被转发到的目标地址。
order: 路由优先级,数字越小,优先级越高。
predicates:断言数组,即判断条件,如果返回值是boolean,则转发请求到 uri 属性指定的服务中。
filters:过滤器,在请求传递过程中,可做一些逻辑处理。
2.断言 Predicate
接受一个输入参数,返回一个布尔值结果。用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。
过滤器 filter:
生命周期:
PRE:在路由之前调用。可实现身份验证、在集群中选择请求的微服务、记录调试信息等。
POST:在路由到微服务后调用。可用来为响应添加标准的 HTTP Header、收集统计信息和指标等。
作用范围
GatewayFilter:单个路由或一个组的路由上(要在配置文件中配置)。
GlobalFilter:所有的路由上(无需配置,全局生效)。
2、GateWay的配置及使用
1.配置
spring:
cloud:
gateway:
routes: # 网关路由配置
- id: user-service # 路由id,自定义,只要唯一即可
# uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
- Method=GET,POST
- Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
- After=2022-05-05T02:00:00.000+08:00[Asia/Shanghai] # 路由指定时间后生效、还有其他的参数(Before、Between)
filters: #过滤器
- AddResponseHeader=X-Response-test1, test1
# - RewritePath=/service1/(?<segment>.*), /$\{segment} # 重写路由地址,http://127.0.0.1:21000/service1/hystrix/calculate 会转发到 http://127.0.0.1:20004/hystrix/calculate,由于 YAML 规范,$ 被 $\ 取代。
- RewritePath=/service1/(?<segment>.*), /service1/$\{segment}
# - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
- RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=123456 # 重写 response 请求头
- StripPrefix=0 # 过滤器StripPrefix,作用是去掉请求路径的最前面n个部分截取掉。StripPrefix=1就代表截取路径的个数为1,比如前端过来请求/test/good/1/view,匹配成功后,路由到后端的请求路径就会变成http://localhost:8888/good/1/view
- AddRequestHeader=Accept-Language, zh,zh-CN;q=0.9 # 将 Accept-Language=zh,zh-CN;q=0.9 添加到所有匹配请求的 header中
- AddRequestParameter=host, 127.0.0.1 # 将 host=127.0.0.1 添加到所有匹配请求的 Param 参数中
- AddResponseHeader=X-Response-Foo, Bar # 将X-Response-Foo:BarHeaders 添加到所有匹配请求的下游响应的 Headers 中。
- PrefixPath=/mypath # 将/mypath作为所有匹配请求的路径的前缀。因此,对 /service1 的请求将发送到 /mypath/service1。
- RedirectTo=302, https://blog.csdn.net/qq_41538097/article/details/124626658 # 所有匹配到的请求都重定向到该地址
- RemoveRequestHeader=X-Request-Foo # 这将删除 Request Headers 中的 X-Request-Foo,然后将其发送到下游
- RemoveResponseHeader=X-Response-Foo # 这将从响应中删除 Response Headers 中的 X-Response-Foo,然后将其返回给网关 Client 端。
- RemoveRequestParameter=red # 将 red 参数发送到下游之前将其删除
- SetPath=/{segment} # 请求路径/red/blue 设置为 /blue 发送到下游请求
- SetRequestHeader=X-Request-Red, Blue # 注意:替换(而不是添加),替换 Request Header 的 X-Request-Red=Blue
- SetResponseHeader=X-Response-Red, Blue # 注意:替换(而不是添加),替换 Response Header 的 X-Response-Red=Blue
- SetStatus=401 # 无论哪种情况,响应的 HTTP 状态都设置为 401。也可以是枚举的字符串:NOT_FOUND
- name: RequestRateLimiter # 令牌桶算法,IP 限流
args:
redis-rate-limiter.replenishRate: 20 # 每秒允许多少个请求
redis-rate-limiter.burstCapacity: 10 # 允许用户在一秒钟内执行的最大请求数,将此值设置为零将阻止所有请求。
- name: Retry
args:
retries: 3 # 请求失败重试 3 次
statuses: BAD_GATEWAY
methods: GET,POST
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
# 降级配置
- name: Hystrix
args:
name: testOne
# 降级接口的地址
fallbackUri: forward:/junFallback
- name: RequestSize # 限制请求大小,超过则拒绝,如果未配置,则默认请求大小设置为 5 MB。
args:
maxSize: 500MB # 单位默认B,支持 B、KB、MB、GB、TB
- name: SetRequestHostHeader # 覆盖主机标头(HTTP/1.1 请求头 Headers 默认包含 Host 头,可以是 IP,域名)
args:
host: 127.0.0.1
- id: order-service # 路由id,自定义,只要唯一即可
uri: lb://order-service
order: 8003
predicates:
- Path=/order-service/oauth/token
filters:
- AddResponseHeader=X-Response-test2, test2
# hystrix 信号量隔离,2秒后自动超时
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 2000
shareSecurityContext: true
2.全局过滤器
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 自定义过滤器
*/
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取请求参数
MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
// 2.获取authorization参数
String auth = params.getFirst("authorization");
// 3.校验
if ("admin".equals(auth)) {
// 放行
return chain.filter(exchange);
}
// 4.拦截
// 4.1.禁止访问,设置状态码
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
// 4.2.结束处理
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -1;
}
}
/**
* 降级配置
*/
@RestController
public class JunHystrixController {
@RequestMapping("/junFallback")
public Map<String,String> junFallback(){
System.err.println("服务降级中");
Map<String,String> map = new HashMap<>();
map.put("resultCode","fail");
map.put("resultMessage","服务异常");
map.put("resultObj","null");
return map;
}
}
3、gateway的限流
1、计数器算法:以QPS为100举例,如果1秒钟内钱200ms请求数量到达了100,后面800ms中的请求都会被拒绝,这种情况称为”突刺现象“
2、漏桶算法:可以解决突刺现象。比如创建一个很大的队列来接收请求,一个较小的线程池来处理请求。但是也有极限情况,当队列满了时, 请求也会拒绝掉。
3、令牌桶算法:可以说是漏桶算法的改进。在桶中放令牌,请求获取令牌后才能继续执行。如果桶中无令牌,请求可以选择进行等待或直接拒绝。
4、gateway网关负载均衡策略
(1)轮询策略
将请求均匀地分配到每个服务器上。请求量增加时,会导致某些服务器的负载过高。
(2)加权轮询策略
在轮询策略的基础上加了权重的概念,每个服务器都有一个权重值,权重值越高的服务器会被分配更多的请求,动态地调整权重值。
(3)IP哈希策略
通过哈希算法将客户端IP地址转换为一个数字,根据该数字来选择服务器。