API网关
API 网关(API Gateway)主要负责服务请求路由、组合及协议转换。
API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。
API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。
Netflix zuul的特点及功能
Zuul是从设备和web站点来的所有访问Netflix 流程序的请求的前门。作为一个边缘服务程序,Zuul带来动态路由、监视、弹性和安全能力。它也有能力把请求路由到多个适当的 Amazon Auto Scaling Groups。
Zuul使用很多不同类型的过滤器,可以快速灵活地把功能应用到我们的边缘服务。这些过滤器帮助我们执行下列功能:
- 认证和安全-为每个需要的资源做识别认证,拒绝不安全的请求
- 洞察和监控-跟踪有意义的数据,在边缘做统计,以便准确了解产品的运行情况
- 动态路由-把请求动态路由到不同的后端集群
- 压力测试-逐渐增加集群的流量,来评估性能
- 甩负荷-为每种请求分配容量,拒绝超限的请求
- 静态响应处理-在边缘直接生成一些响应而不访问内部集群
- 多区域弹性-跟 AWS区域做路由,让ELB的使用更多样化,让边缘接近成员
Spring Cloud Zuul
在微服务框架中,API网关对于微服务至关重要,所以在Spring Cloud 中提供了基于Netflix Zuul实现的API网关组件——Spring Cloud Zuul,兼容Spring Cloud体系。
官方文档描述如下:
- front door. API Gateway.Zuul is a JVM based router and server side load balancer by Netflix.(所有请求的入口)
- As an edge service application, Zuul is built to enable dynamic routing, monitoring, resiliency and security. (作为边界应用服务,zuul能实现动态路由、监控、弹性与安全性。)
Spring Cloud Zuul功能实现
Spring Cloud Zuul有如下的主要功能:
Authentication 认证,Insights 洞察力,Stress Testing 压力测试,Canary Testing 金丝雀测试,Dynamic Routing 动态路由,Service Migration 服务迁移,Load Shedding 减载,Security 安全校验,Static Response handling 静态响应处理,Active/Active traffic management 流量控制。
在微服务中,对于路由规则和服务实例的维护问题,可以通过与Spring Cloud Eureka进行整合,从Euerka中获取其它服务的实例信息,不需要人工干预;路由规则默认通过以服务名作为ContextPath的方式创建路由映射,可以轻松实现大部分路由需求(也可以手工配置老的url);对于类似的签名校验和登陆校验,通过提供一套过滤机制,可以很号的支持各种非业务性质的处理
Spring Cloud Zuul请求响应过程
Spring Cloud Zuul微服务集成
1.引入依赖,引入最新版本1.4.7.RELEASE
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zuul -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zuul</artifactId><version>1.4.7.RELEASE</version>
</dependency>
spring-cloud-starter-zuul依赖中,不仅包含了Netflix Zuul的核心依赖zuul-core,还包含spring-cloud-starter-hystrix依赖,在网关的转发服务中实现对微服务的转发时候的保护机制;
spring-cloud-starter-ribbon依赖,在网关服务进行路由转发的时候客户端的负载均衡以及路由重试;spring-cloud-starter-actuator依赖,提供常规的微服务管理度端点。
2.配置文件:
- 静态路由:
zuul:routes:myroute1:path: /mypath/**url: http://localhost:8080 (注意这里url要http://开头)
Path和Url要一对一进行手工配置,这种要手工维护Url转发路径,维护具体的微服务实例增加了工作量
- 动态路由:
zuul:routes:myroutes1: #路由ID唯一path: /mypath/**serviceId: myserverId
根据serviceId进行负载均衡和动态的路由转发,这种面向服务的转发规则方式,不需要维护具体的微服务位置。
- 转发规则
(1)忽略特定微服务转发
zuul:
ignored-services: '*' #对微服务不进行默认路由转发,几个微服务可以用“,”隔开
(2)转发过滤前缀
zuul:
stripPrefix=true // 转发会过滤掉前缀prefix: /myapi //增加前缀routes:local:path: /myapi/** //默认时转发到服务的请求是 /**url: forward:/local
请求路径http://localhost:8888/ myapi /local/abc http://localhost:8888前端服务器
转发路径:http://localhost:9006/local/abc
(3)转发添加前缀
如果stripPrefix=false,转发的请求是
/myusers/**
zuul.prefix=/api
会对所有的path增加一个/api前缀
zuul:prefix: /myapi #增加前缀ignored-patterns: /**/hello/** #忽略该路径不进行转发routes:local:path: /local/**url: forward:/local
请求路径http://localhost:8888 /local/abc http://localhost:8888前端服务器
转发路径:http://localhost:9006/myapi/local/abc
- 超时重试配置
(1) 重试机制配置
#关闭全局重试机制
zuul:retryable: false
#关闭个别服务的重试机制
zuul:routes:feign-consumer:retryable: false
(2)超时配置
hystrix:command: default: execution: isolation: thread.timeoutInMilliseconds: 10000 #熔断设定
ribbon: ConnectTimeout: 3000 #表示路由转发请求的时候,创建请求连接的超时时间ReadTimeout: 5000 #用来设置路由转发请求的超时时间,它的超时是对请求建立连接之后的处理时间
- 两种路由配置对应不一样的超时配置
1.ribbon.ReadTimeout,ribbon.SocketTimeout这两个就是ribbon超时时间设置。
2.zuul.host.connect-timeout-millis, zuul.host.socket-timeout-millis这两个配置,这两个和上面的ribbon都是配超时的。
区别在于,如果路由方式是serviceId的方式,那么ribbon的生效,如果是url的方式,则zuul.host开头的生效
使用url的方式将没有线程隔离和断路器的保护,并且也不会具备负载均衡的能力。所以不建议使用url的方式
- zuul中几种超时情况
1.hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds:该参数用来设置API网关中路由转发请求的执行时间,执行超过配置值之后Hystrix会将执行命令标记为TIMEOUT并抛出异常。
2.ribbon.ReadTimeout:配置值小于hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds配置值时,路由请求出现超时,会自动进行路由重试,如果依旧失败出现重试失败;
ribbon.ReadTimeout:配置值大于hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds配置值时,路由请求出现超时,返回TIMEOUT的错误信息。
3.ConnectTimeout和ribbon.ReadTimeout相似,唯一的区别就在于它的超时是对请求建立连接之后的处理时间,具有重试机制。
zuul.retryable=false和zuul.routes.< route>.retryable=false可以关闭重试机制
- 其他配置
(1)设置Host头信息,传递敏感头部信息
zuul:add-host-header: true //表示API网关在进行请求路由转发前为请求设置Host头信息routes:feign-consumer:sensitiveHeaders: Cookie,Set-Cookie,Authorization
默认情况下,敏感的头信息无法经过API网关进行传递,我们可以通过上面配置使之可以传递
3.启动类
新建一个springboot项目,添加注解
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaServer
public class GatewayServiceZuulApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServiceZuulApplication.class, args);}
}
@EnableZuulProxy简单理解为@EnableZuulServer的增强版,当Zuul与Eureka、Ribbon等组件配合使用时,我们使用@EnableZuulProxy。
@EnableEurekaServer Eureka作为注册中心时,添加该注解。
spring Cloud Zuul核心实现
1、zuul核心过滤器
1.1.四种过滤器类型,Zuul中默认定义了四种不同生命周期的过滤器类型
- pre 可以在请求被路由之前调用
- routing 在路由请求时被调用,route型过滤器在pre过滤器后面运行,是用来发送请求给其他的服务用的,在这里它的大部分工作是转换请求和响应数据给前台客户端。
- post 在routing和error过滤器之后被调用
- error 处理请求时发生错误时被调用
1.2. 自定义zuul的过滤器需要继承ZuulFilter接口,实现com.netflix.zuul.ZuulFilter接口中定义的4个抽象方法:
(1)String filterType()方法
filterType:该函数需要返回一个字符串代表过滤器的类型,而这个类型就是在http请求过程中定义的各个阶段。
(2)int filterOrder()方法
通过int值来定义过滤器的执行顺序,数值越小优先级越高。
(3)boolean shouldFilter()方法
返回一个boolean值来判断该过滤器是否要执行。我们可以通过此方法来指定过滤器的有效范围。
(4)Object run()方法
过滤器的具体逻辑。在该函数中,我们可以实现自定义的过滤逻辑,来确定是否要拦截当前的请求,不对其进行后续的路由,或是在请求路由返回结果之后,对处理结果做一些加工等。
示例代码:
public class MyFilter extends ZuulFilter {
public boolean shouldFilter() {
return true;}public Object run() {
System.out.println("执行 MyFilter 过滤器");return null;}@Overridepublic String filterType() {
return FilterConstants.ROUTE_TYPE;}@Overridepublic int filterOrder() {
return 1;}}
配置类:
@Configurationpublic class FilterConfig {
@Beanpublic MyFilter myFilter() {
return new MyFilter();}}
2、zuul限流实现
2.1.zuul的限流介绍
Zuul的限流实现是基于redis实现限流策略,主要的常见的限流算法有:令牌桶、漏桶。 计数器也可以进行粗暴限流实现。服务限流主要是保护服务节点或者数据节点,防止瞬时流量过大造成服务和数据崩溃,导致服务不可用。
2.2.限流策略
(1)令牌桶策略
大小固定的令牌桶可自行以恒定的速率源源不断地产生令牌。如果令牌不被消耗,或者被消耗的速度小于产生的速度,令牌就会不断地增多,直到把桶填满。后面再产生的令牌就会从桶中溢出。最后桶中可以保存的最大令牌数永远不会超过桶的大小。
- 所有的流量在放行之前需要获取一定量的 token;
- 所有的 token 存放在一个 bucket(桶)当中,每 1/r 秒,都会往这个 bucket 当中加入一个 token;
- bucket 有最大容量(capacity or limit),在 bucket 中的 token 数量等于最大容量,而且没有 token 消耗时,新的额外的 token 会被抛弃。
这种实现方法有几个优势: - 避免了给每一个 Bucket 设置一个定时器这种笨办法,
- 数据结构需要的内存量很小,只需要储存 Bucket 中剩余的 Token 量以及上次补充 Token 的时间戳就可以了;
- 只有在用户访问的时候,才会计算 Token 补充量,对于系统的计算资源占用量也较小。
(2)漏桶策略
漏桶算法强制一个常量的输出速率而不管输入数据流的突发性:
流程:
到达的数据包(网络层的PDU)被放置在底部具有漏孔的桶中(数据包缓存);
漏桶最多可以排队b个字节,漏桶的这个尺寸受限于有效的系统内存。如果数据包到达的时候漏桶已经满了,那么数据包应被丢弃;
数据包从漏桶中漏出,以常量速率(r字节/秒)注入网络,因此平滑了突发流量。