微服务是无状态的,是不存在session的,session需要存放在网关中。
网关的职责:
1.统一入口:为全部微服务提供唯一的入口点,网关起到外部和内部隔离,保障了后台服务的安全性。
2.鉴权校验:识别每个请求的权限,拒绝不符合要求的请求。
3.动态路由:动态地将请求路由到不同的后端集群中。
3.减少客户端与服务的耦合,服务可以独立发展,通过网管层来做映射。
集成网关的步骤:
1.加入eureka依赖,网关依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency>
2.在网关项目的启动类上添加@EnableZuulProxy
通过网关访问其他微服务的方法:网关服务ip/网关服务端口/要调用的微服务名/微服务路径
=============网关的路由器==============
zuul可以有四种路由规则:
1.采用url指定路由方法
2.采用服务指定路由方法
3.路由的排除方法
4.路由的添加前缀方法
四种方法均无须代码实现,在配置文件中配置即可
1)url配置,匹配关键字,如果包含关键字就跳转到指定的url中
zuul.routes.book-product.path=/book-product/**
zuul.routes.book-product.url=http://127.0.0.1:8083/
通过以上配置,例如访问网关ip/网关端口/book-product/product/list就会跳转到127.0.0.1:8083/product/list
2)路由服务指定
#将路径/book/product/引到eureka的e-book-product服务上
#规则:zuul.routes.路径名.path=[自定义路径]
zuul.routes.路径名.serviceId=[eureka上注册的服务名]
zuul.routes.book-product.path=/book-product/**
zuul.routes.book-product.serviceId=e-book-product
例如访问:localhost:9010/book-product/product/list => localhost:9010/e-book-prouct/product/list
服务指定更为简单的方式:
zuul.routes.[eureka上注册的服务名].path=[路径规则] => zuul.routes.e-book-product.path=/book-product/**
3.路由排除:排除某几个服务
zuul.ignored-services=e-book-product
#排除e-book-product服务之后,http://127.0.0.1/e-book-product/product/list这个地址将为空,需要排除多个服务时,多个服务之间用逗号隔开。
路由排除:首先排除所有服务,随后针对需要添加服务
#先排除所有服务,后根据需要添加服务
zuul.ignored-services=*
zuul.routes.e-book-consumer-hystrix.path=/book-consumer/**
路由排除:排除指定关键字的路径
#排除所有包括/list/list的路径
zuul.ignored-patterns=/**/list/**
4.路由的添加前缀
zuul.prefix=/api
zuul.routes.e-book-product.path=/book-product/**
此种情况下请求地址:
#http://127.0.0.1:9010/book-product/list => http://127.0.0.1:9010/api/book-product/list
========================过滤器==================
搭建过滤器的步骤:
1.创建过滤器类并继承抽象类ZuulFilter,并实现其中的方法。
2.在实现的run方法中定义过滤器要做的事情。
网关的过滤器有四种类型:pre,routing,post,error
pre:在请求被路由之前被调用,一般用于身份权限认证,记录调用日志等等。
routing:在路由执行之后被调用。
post:在routing和error过滤器之后被调用,可用于信息搜集,统计信息例如性能指标,对response的结构做特殊处理。
error:处理请求发生错误时被调用,用于异常处理封装。
===============采用网关过滤器对系统异常统一处理=================
权限认证:一般选用"pre"模式,即在请求被路由之前执行过滤器,从请求中获取token(登录验证功能)
1.在过滤器的run方法中,首先获取请求上下文对象RequestContext,再从请求上下文对象中获取到HttpServletRequest对象,在HttpServletRequest对象中包含了请求的token。
public Object run() {
logger.info("----------------------AccessFilter------------------------");
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
String token = request.getParameter("token");
if (token == null){
logger.info("token is null");
rc.setSendZuulResponse(false);//表示结束请求,不再往下层传递
rc.setResponseStatusCode(401);
rc.setResponseBody("{\"result\":\"token is null\"}");
rc.getResponse().setContentType("text/html;charset=utf-8");
}else{
// TODO: 2020/3/6 到redis中验证token
logger.info("token is ok");
}
return null;
}
2.在网关过滤器中对异常做统一处理,添加一个类实现ErrorController
@RestController
public class ErrorGatewayController implements ErrorController{
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping("/error")
public String error(){
return "{\"result\":\"500 error!!!\"}";
}
}
使用网关的过滤器做异常的统一处理,需要定义一个/error页面来返回错误信息。
如果把某个过滤器定义为"post"类型,它将在最后被调用。
================网关容错=================
zuul包本身就以来了hystrix的包:网关IP:网关端口/hystrix.stream
网关如何实现服务降级
1.创建类实现ZuulFallbackProvider接口
@Component
public class ProductFallbackProvider implements ZuulFallbackProvider{
@Override
public String getRoute() {
//需要为哪一个服务提供降级
return "e-book-product";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
//httpresponse的fallback的状态码,HttpStatus的值
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
//httpresponse的fallback的状态码,int值
@Override
public int getRawStatusCode() throws IOException {
return this.getStatusCode().value();
}
//httpresponse的fallback状态码,string类型
@Override
public String getStatusText() throws IOException {
return this.getStatusCode().getReasonPhrase();
}
@Override
public void close() {
}
//告知触发fallback的原因
@Override
public InputStream getBody() throws IOException {
String input = "服务不可用,请联系管理员";
return new ByteArrayInputStream(input.getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
MediaType mt = new MediaType("application","json", Charset.forName("UTF-8"));
headers.setContentType(mt);
return headers;
}
};
}
}
----------------------------------------------------------------------------
Zuul请求的生命周期
一个Http请求首先经过pre类型的过滤器(即在路由转发之前),在路由执行之后routing类型的过滤器被调用,接着进入post类型的过滤器,这中间一旦发生异常,将会进入error类型的过滤器,post类型总是在最后的(即便是有error)。
在高并发的情况下,网关如何实现限流达到自我保护
1.限制某个ip在某个时间段的请求(ratelimit)
加入zuul的限流jar包
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-zuul-ratelimit</artifactId> </dependency>
2.在配置文件中加入限流配置
#开启限流
zuul.ratelimit.enable=true
#60s内请求超过三次,服务端就抛出异常,60s后可恢复正常请求
zuul.ratelimit.policies.book-product.limit=3
zuul.ratelimit.policies.book-product.refresh-interval=60
#针对某个IP进行限流,不影响其他IP
zuul.ratelimit.policies.book-product.type=origin
以上是针对book-product服务的限流,以下是全局配置限流:
zuul.ratelimit.enabled=true
zuul.ratelimit.default-policy.limit=3
zuul.ratelimit.default-policy.refresh-interval=60
zuul.ratelimit.default-policy.type=origin
=======zuul性能调优:网关的2层超时调优========
zuul的底层采用了ribbon和hystrix进行通信
hystrix默认情况下采用线程池隔离,超时时间是1000ms
ribbon的默认超时时间是5s,ribbon超时会进行集群重试
如果把服务端的接口中睡眠2s,hystrix就会超时,以至于请求到达不了ribbon
所以在这时,ribbon的超时时间必须小于hystrix的超时时间。请求时先经过hystrix,再经过ribbon,如果hystrix的超时时间大于ribbon,请求有可能在通过hystrix的时候,ribbon已经超时了。
在配置文件中配置超时时间。
#第一层hystrix超时时间设置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=8000
#ribbon请求连接的超时时间,默认5秒
ribbon.connectTimeout=5000
#ribbon请求处理的超时时间,默认5秒
ribbon.ReadTimeout=5000