第六节,采用网关过滤对系统异常统一处理
(1)在zuul模块下新建maven项目,命名为zuul-gateway-filter-exception。
(2)pom文件添加依赖。
(3)配置文件添加配置。
(4)添加一个启动类。
然后是各种过滤器的实践:
(5)先新建一个权限校验的过滤器,如下:
package com.twf.zuul.gateway.filter.exception.filter;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
@Component
public class AccessFilter extends ZuulFilter{
private static final Logger logger = LoggerFactory.getLogger(AccessFilter.class);
/**
* 是否开启过滤器,默认为false,不开启。我们现在开启它,就设为true。
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的作用,权限校验
*/
@Override
public Object run() {
RequestContext rc=RequestContext.getCurrentContext();
HttpServletRequest request=rc.getRequest();
logger.info("---------------------------pre1-------------------------------");
String token = request.getParameter("token");
if (token == null) {
logger.warn("token is null...............");
rc.setSendZuulResponse(false); // false代表结束请求,不再继续下级传递
rc.setResponseStatusCode(401);
rc.setResponseBody("{\"result\":\"token is null\"}");
rc.getResponse().setContentType("text/html;charset=utf-8");
} else {
logger.info("token is ok");
}
return null;
}
@Override
public String filterType() {
return "pre";
}
/**
* 控制执行顺序
*/
@Override
public int filterOrder() {
return 0;
}
}
启动该项目和ProductCoreApplication,浏览器访问http://localhost:8118/e-book-product/product/findAllProduct,界面如下:
然后,我们再访问http://localhost:8118/e-book-product/product/findAllProduct?token=aaa,数据就能正常出来了。
(6)下面测试一下过滤器的执行顺序,复制AccessFilter.java,重命名为TestPreFilter,修改代码如下:
package com.twf.zuul.gateway.filter.exception.filter;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
/**
* 这个类的目的就是为了验证filterOrder的顺序
* @author twf
*
*/
@Component
public class TestPreFilter extends ZuulFilter{
private static final Logger logger = LoggerFactory.getLogger(TestPreFilter.class);
/**
* 是否开启过滤器,默认为false,不开启。我们现在开启它,就设为true。
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的作用,权限校验
*/
@Override
public Object run() {
RequestContext rc=RequestContext.getCurrentContext();
HttpServletRequest request=rc.getRequest();
logger.info("---------------------------pre2-------------------------------");
return null;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
}
然后浏览器访问http://localhost:8118/e-book-product/product/findAllProduct?token=aaa,后台打印如下:
出现这个顺序,说明,filterOrder()方法返回的值越小,越先被执行。
(7)测试过滤器的post类型,复制AccessFilter.java,重命名为PostFilter.java,修改代码如下:
package com.twf.zuul.gateway.filter.exception.filter;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
@Component
public class PostFilter extends ZuulFilter{
private static final Logger logger = LoggerFactory.getLogger(PostFilter.class);
/**
* 是否开启过滤器,默认为false,不开启。我们现在开启它,就设为true。
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的作用
*/
@Override
public Object run() {
RequestContext rc=RequestContext.getCurrentContext();
HttpServletRequest request=rc.getRequest();
logger.info("---------------------------post-------------------------------");
return null;
}
@Override
public String filterType() {
return "post"; // 这里一定要改为post
}
/**
* 控制执行顺序
*/
@Override
public int filterOrder() {
return 0;
}
}
浏览器访问http://localhost:8118/e-book-product/product/findAllProduct?token=aaa,后台打印如下:
从上图可以看出,post类型的过滤器在pre类型过滤器的后面执行。
(8)测试过滤器的error类型,复制PostFilter,重命名为ErrorFilter,修改代码如下:
package com.twf.zuul.gateway.filter.exception.filter;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
@Component
public class ErrorFilter extends ZuulFilter{
private static final Logger logger = LoggerFactory.getLogger(ErrorFilter.class);
/**
* 是否开启过滤器,默认为false,不开启。我们现在开启它,就设为true。
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的作用,权限校验
*/
@Override
public Object run() {
RequestContext rc=RequestContext.getCurrentContext();
HttpServletRequest request=rc.getRequest();
logger.info("---------------------------error-------------------------------");
return null;
}
@Override
public String filterType() {
return "error";
}
/**
* 控制执行顺序
*/
@Override
public int filterOrder() {
return 0;
}
}
启动,浏览器访问http://localhost:8118/e-book-product/product/findAllProduct?token=aaa,后台打印如下:
(9)加一个类,铺获异常,代码如下。
package com.twf.zuul.gateway.filter.exception.filter;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 异常的统一捕获类
* @author twf
*
*/
@RestController
public class ErrorGatewayController implements ErrorController{
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping("/error")
public String error() {
return "{\"result\":\"500 error!!!\"}";
}
}
重启服务,访问http://localhost:8118/e-book-product/product/findAllProduct?token=aaa,界面如下:
第七节,网关容错:zuul和hystrix无缝结合
zuul天生就集成了hystrix,我们不需要做额外的操作去结合它们。
(1)我们启动ZuulFilterException,ProductCoreApplication,e-book-consumer-hystrix-dashboard这三个服务。
(2)浏览器访问http://localhost:8118/e-book-product/product/findAllProduct?token=aaa,界面如下:
(3)浏览器访问http://localhost:8118/hystrix.stream,界面如下:
(4)浏览器访问http://localhost:8107/hystrix,界面如下:
输入http://localhost:8118/hystrix.stream,点“Monitor Stream”,界面如下:
第八节,网关容错:网关如何实现服务降级
我们已经学过ribbon的服务降级,feign的服务降级,现在来看看网关的服务降级。
(1)在zuul模块下新建maven项目,命名为zuul-gateway-fallback,
(2)修改配置文件,加入相应依赖;
(3)添加一个配置文件,加入相应的配置;
(4)添加一个启动类;
(5)添加一个provider,代码如下:
package com.twf.zuul.gateway.fallback.fallback;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
@Component
public class ProductFallbackProvider implements ZuulFallbackProvider {
/**
* 代表为哪个服务提供fallback,这里我们为e-book-product这个服务提供fallback
*/
@Override
public String getRoute() {
return "e-book-product";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public InputStream getBody() throws IOException {
String input = "商品服务不可用,请联系管理员!";
return new ByteArrayInputStream(input.getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders header = new HttpHeaders();
MediaType mt = new MediaType("application","json",Charset.forName("UTF-8"));
header.setContentType(mt);
return header;
}
@Override
public HttpStatus getStatusCode() throws IOException {
// httpresponse的fallback的状态码,HttpStatus值
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
// httpresponse的fallback的状态码,int值
return this.getStatusCode().value();
}
@Override
public String getStatusText() throws IOException {
// httpresponse的fallback的状态码,String值
return this.getStatusCode().getReasonPhrase();
}
@Override
public void close() {
}};
}
}
(6)启动该项目及ProductCoreApplication,浏览器访问http://localhost:8119/e-book-product/product/findAllProduct,界面如下:
(7)停掉ProductCoreApplication,再访问,出现如下结果:
说明降级操作成功。
第九节,在高并发的情况下,网关如何实现限流达到自我保护
(1)在zuul模块下新建maven项目,命名为zuul-gateway-ratelimit。
(2)修改pom文件,加入如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>1.3.4.RELEASE</version>
</dependency>
(3)修改配置文件,配置如下:
spring.application.name=zuul-gateway
server.port=8120
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8762/eureka/
zuul.routes.book-product.path=/book-product/**
zuul.routes.book-product.serviceId=e-book-product
#开启限流
zuul.ratelimit.enabled=true
#60s内请求超时3次,服务端就抛出异常,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
(4)添加一个启动类。
(5)启动该项目,和ProductCoreApplication。
(6)浏览器访问http://localhost:8120/book-product/product/findAllProduct,界面如下:
然后在刷新几次,就变成下面这样了:
后台也报如下异常:
(7)网关限流参数说明。
(8)修改配置文件,注释掉某一个服务的局部配置,添加限流的全局配置。
spring.application.name=zuul-gateway
server.port=8120
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8762/eureka/
#----------------------------针对某个服务进行限流--------------------------
#zuul.routes.book-product.path=/book-product/**
#zuul.routes.book-product.serviceId=e-book-product
#开启限流
#zuul.ratelimit.enabled=true
##60s内请求超时3次,服务端就抛出异常,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
#---------------------------------针对所有服务进行限流------------------------
#加全局配置
zuul.ratelimit.enabled=true
zuul.ratelimit.default-policy.limit=3
zuul.ratelimit.default-policy.refresh-interval=60
zuul.ratelimit.default-policy.type=origin
(9)浏览器访问http://localhost:8120/e-book-product/product/findAllProduct,结果也是前3次访问正常,之后就不能访问了,错误同上。
第十节,zuul性能调优
(1)在zuul模块下新建maven项目,命名为zuul-gateway-timeout。
(2)修改pom文件,加入相应依赖。
(3)添加一个配置文件,配置如下:
spring.application.name=zuul-gateway
server.port=8121
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8762/eureka/
(4)添加一个启动类。
(5)修改e-book-product-core:
(6)浏览器访问http://localhost:8121/e-book-product/product/findAllProduct,界面如下:
无论刷新多少次,都不能访问成功。这是因为,zuul底层采用了ribbon和hystrix来通信,hystrix的超时时间为1s,而我们接口休眠了2s,这样网关永远都不能访问这个接口了,原理图如下:
(7)改造配置文件,加入如下配置,对zuul进行性能调优。
#第一层hystrix超时时间设置
#默认情况下是线程池隔离,超时时间为1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=8000
#第二层ribbon超时时间设置,设置比第一层小
# 请求连接的超时时间:默认5s
ribbon.ConnectTimeout=5000
#请求处理的超时时间:默认5s
ribbon.ReadTimeout=5000
(8)重启该服务,重新访问,数据正常了。
源码点这里。