网关的概念

API Gateway 网关,是系统的唯一入口,处理非业务功能、统一过滤请求,提供路由、权限验证、监控、缓存、限流等功能。

统一接入

  • 路由转发 /api/v1/user,/api/v1/order,....路由到不同的服务

  • AB测试、灰度测试

  • 负载均衡 网关自带负载均衡器均衡功能,可根据负载均衡算法转发该服务的某个节点,因为要从服务中心拿节点列表,所以网关也要注册到注册中心上

  • 容灾处理 某个下级服务集群不可用时,可以直接切断对该服务的请求

  • 日志记录

流量监控

  • 限流处理

  • 服务降级

安全防护

  • 权限验证

  • 服务监控

  • 网络隔离 网关可以将内网、外网隔开,服务节点在内网中,通过内网调用服务,速度快;用户通过外网(公网ip)访问网关

网关可以调用服务,是一个特殊的消费者,消费者的负载均衡、容错保护、服务监控、限流降级它都能做到。

如果网关进行了集群,需要Nginx进行负载均衡,来确定调用哪个网关节点。

主流网关

  • zuul   和eureka、ribbon、hystrix一样,都是Netflix旗下的项目
  • kong   基于nginx的网关
  • nginx+lua    nginx是一个高性能的HTTP和反向代理服务器,lua是脚本语言,让Nginx执行Lua脚本实现网关

使用Zuul进行路由转发

(1)新建子模块api-gateway作为网关服务,创建时只需勾选Spring Cloud Discovery -> Eureka Discovery Client ,Spring Cloud Routing -> Routing[Maintenance]

也可以手动加依赖:

    <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

网关要从Eureka Server获取服务节点列表,使用Eureka Client内置的Ribbon进行负载均衡,所以要添加Eureka Client的依赖,并进行Eureka Client相关配置

Zuul的依赖中已经包含了Hystrix的依赖。

(2)引导类

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy //也可以使用@EnableZuulServer
public class ApiGatewayApplication { public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
} }

@EnableZuulProxy中已经包含了Hystrix的注解@EnableCircuitBreaker。

默认使用轮询,如果要使用其它负载均衡策略,设置方式和Eureka Client的完全相同。

(3)配置文件

server:
port: 9000 spring:
application:
#服务名称
name: api-gateway eureka:
client:
#注册中心地址
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/

127.0.0.1:9000/user-service/api/v1/user/order/1    通过网关来访问服务,后面是服务名、服务接口

需要指定路由规则:

zuul:
routes:
#指定路由规则,key是服务名,value是映射地址
user-service: /api-gateway/user-service/**
#取消原来的映射
ignored-patterns: /*-service/**

127.0.0.1:9000/api-gateway/user-service/api/v1/user/order/1     使用网关作为唯一入口,127.0.0.1:9000/api-gateway可以统一拦截处理所有的请求。

根据服务接口/api/v1/user/order/{user_id}来识别服务,所以api-gateway后面不一定要是服务名,比如可以是user。

如果不取消原来的映射方式,原来的映射方式也可以访问。

也可以使用下面的方式指定:

zuul:
prefix: /api-gateway/

这种方式只是加一个前缀,127.0.0.1:9000/api-gateway/user-service/api/v1/user/order/1 ,后面依然是服务名。


Zuul  过滤请求

新建一个包filter,包下新建一个类,继承ZuulFilter

package com.chy.mall.apigateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest;
import java.io.IOException; @Component //放到spring容器中
public class ValidateFilter extends ZuulFilter { //ZuulFilter是抽象类,需要实现里面的方法 /*
指定过滤类型(时机),"pre"是路由转发之前,"routing"是路由转发之时,"post"是返回响应给浏览器之前,"error"是发生错误时
前处理用pre,后处理用post
*/
@Override
public String filterType() {
return "pre";
} /*
要执行多个拦截器,需要指定这些拦截器调用的先后顺序,数值越小,优先级越高、越先执行
可以在FilterConstants类中查看Zuul自带的拦截器的Order,自定义拦截器的Order要参考它们进行设置,不能想设置多大就设置多大
前处理一般设置为0~9
*/
@Override
public int filterOrder() {
return 0;
} /*
是否要使用此过滤器,默认为false,需要改为true
如果需要关闭此拦截器,改为false即可
*/
@Override
public boolean shouldFilter() {
return true;
} /*
核心方法,用来过滤请求
*/
@Override
public Object run() {
RequestContext currentContext = RequestContext.getCurrentContext();
//前处理,获取request
HttpServletRequest request = currentContext.getRequest();
//获取参数
String token = request.getParameter("token");
//token要和数据库查到的进行比较,此处随便写一个代替
if (token==null || !token.equals("123456")) {
//如果token错误,直接返回响应,不转发给服务。只是不转发给服务,此方法后面部分的代码还是会执行
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(400);
//输出信息到浏览器
try {
currentContext.getResponse().getWriter().write("token is invalid");
} catch (IOException e) {
e.printStackTrace();
}
} // 有时候可能要对指定的url进行拦截处理
// ip:port后面的部分,示例 /api-gateway/user-service/api/v1/user/order/1
String requestURI = request.getRequestURI();
if (requestURI.contains("/api-gateway/user-service/......")){
//......
}
if (requestURI.equals("/api-gateway/user-service/.....")){
//......
} return null;
} }

不建议使用大量的过滤器,因为会加大时间开销,拉低性能。

网关中一般写通用的过滤,比如校验用户是否登录;如果是对部分服务进行过滤,最好在shouldFilter()中就判断请求地址。

如果只是个别服务的过滤,写在服务自身中。


zuul  限流

系统性能都是有限的,过多的请求|访问量会冲垮应用,所以一般都要做限流。

hystrix是服务层的限流,zuul可以使用谷歌的guava框架对请求进行限流,zuul的限流是网关层的限流,也可以在nginx上进行限流。

示例:zuul -> 服务A -> 服务B ,zuul使用guava对服务A进行限流,服务A使用hystrix限流A本身对服务B的调用请求

zuul本身已经集成了guava,直接用即可

package com.chy.mall.apigateway.filter;

import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest; @Component
public class UserServiceLimitFilter extends ZuulFilter {
//RateLimiter是com.google.common.util.concurrent.RateLimiter,不要导错了
//指定最大访问量,数值一般是压测得到的qps上限,此处设置为1000
private static final RateLimiter RATE_LIMITER = RateLimiter.create(1000); @Override
public String filterType() {
return "pre";
} @Override
public int filterOrder() {
//限流的过滤器放在自定义前处理的最前面
return -4;
} @Override
public boolean shouldFilter() {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String requestURI = request.getRequestURI();
//设置要要限流的服务,此处限流的服务是user-service,通过zuul请求user-service的qps不能超过1000
     //如果多个服务的qps上限都一样,可以一起设置
if (requestURI.contains("/api-gateway/user-service/")){
return true;
}
return false;
} @Override
public Object run() {
RequestContext currentContext = RequestContext.getCurrentContext();
//如果超过设置的上限,直接返回响应,不转发给服务
if (!RATE_LIMITER.tryAcquire()){
//直接返回响应,不转发给服务
currentContext.setSendZuulResponse(false);
//返回给浏览器的状态码是429 过多请求,可以使用常量,也可以直接写int型的状态码
currentContext.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
} return null;
} }

zuul  请求头过滤

Zuul默认会对请求头进行过滤处理:

    private Set<String> sensitiveHeaders = new LinkedHashSet(Arrays.asList("Cookie", "Set-Cookie", "Authorization"));

默认会将请求头中的这3部分剔除,再将请求转发给服务,所有在服务中是取不到cookie的。

需要设置一下:

zuul:
routes:
#指定路由规则,前面是服务名,后面是映射地址
user-service: /api-gateway/user/**
#取消原来的映射
ignored-patterns: /*-service/**
#取消对请求头的默认处理
sensitive-headers:

配置文件中设置了此字段,会调用setter方法设置此字段。

不设置值,即为null,用null覆盖默认值。


zuul  上传文件限制

zuul默认对请求大小、上传文件的大小有限制,需要设置一下

spring:
application:
#服务名称
name: api-gateway
servlet:
multipart:
#设置请求的最大尺寸,单位M,默认10
max-request-size: 1024
#上传文件允许的单个文件的最大尺寸,单位M,默认1
max-file-size: 1024

zuul  集群

使用网关之后,ajax的url、<a>链接的href、<form>的action、服务调用地址,都要写成网关地址,通过网关统一访问,不再直接访问服务。

zuul集群之后,使用nginx做负载均衡,由nginx确定将请求转发到哪个网关节点,所有的请求都向nginx发起,上面的那些地址也都要写成nginx的地址。

如果只有一台nginx服务器,这台nginx挂了之后系统就不可用了,所以往往要用 nginx+lvs+keepalive  实现nginx的高可用。

最新文章

  1. 嵌入式:J-link刷固件(坑)
  2. px与rem关系及转换
  3. ALT+TAB切换时小图标的添加 界面透明 屏幕大小 竖行字体 进程信息
  4. [问题2014S10] 解答
  5. sencha combobox下拉框不用jsonstore,直接使用字符串数组做数据源
  6. 【消息队列MQ】各类MQ比较
  7. IOS证书的申请和使用
  8. Access 2003版数据库在Win7 64位系统下的不适应
  9. 使用struts2标签&lt;s:action无法显示引用页面问题
  10. JS中Date.parse方法返回NaN解决方案
  11. Java设计模式——Observer(观察者)模式
  12. javascript 冒泡和事件源 形成的事件委托
  13. 浅谈Struts2(二)
  14. mousewheel 与 DOMMouseScroll
  15. Chrome 无法自动填充密码
  16. dotnet不是内部或外部的命令,也不是可运行的程序或批处理文件
  17. Confluence 6 修改日志文件的大小数量和级别
  18. K-means聚类算法及python代码实现
  19. winform跨线程访问控件
  20. python3基础:字符串、文本文件

热门文章

  1. JS中变量、作用域的本质,定义及使用方法
  2. linux下安装lxml包
  3. 如何在Mac和Windows PC之间无线共享文件
  4. 证明task线程是来源于线程池的,线程重用
  5. 快速解决docker生成镜像时提示Could not resolve ‘archive.ubuntu.com’问题
  6. 处理异常 ‘try’——‘except’ 方法
  7. Chocolaty
  8. 【35】单层卷积网络(simple convolution)
  9. BugkuCTF flag.php(反序列化)
  10. P1339 热浪【最短路】