在我的上一篇博客中讲解了什么是网关,网关可以做什么,以及路由和过滤器的类型。如果大家感兴趣的话可以看下springcloud服务网关详细讲解
然后这篇文章主要讲解网关过滤器对系统异常统一处理,zuul和hystrix结合,网关实现服务降级,实现限流等内容。
网关过滤器对系统异常统一处理
目录结构:
在这个项目中我们不仅做了异常处理还做了权限验证和post验证。
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>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
配置文件中的代码:
spring.application.name=zull-gateway
server.port=9010
eureka.client.serviceUrl.defaultZone=http://user:test@eureka2:8762/eureka/,http://user:test@eureka1:8761/eureka/
eureka.instance.perferIpAddress=true
ribbon.ReadTimeout=60000
ribbon.ConnectTimeout=60000
ZuulApplication代码:
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
AccessFilter进行权限验证,代码
@Component
public class AccessFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(AccessFilter.class);
@Override
public Object run() throws ZuulException {
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); //代表结束请求,不再继续下级传递
rc.setResponseStatusCode(401);
rc.setResponseBody("{\"result\":\"token is null\"}");
rc.getResponse().setContentType("text/html;charset=utf-8");
}else {
// TODO redis 验证
logger.info("token is OK");
}
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
// TODO Auto-generated method stub
return 0;
}
@Override
public String filterType() {
return "pre";
}
}
启动product和项目,测试结果:
携带token的就可以访问。
验证filterOrder的顺序:
filterOrder的数字越小级别就越高。TestPreFilter代码:
@Component
public class TestPreFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(TestPreFilter.class);
@Override
public Object run() throws ZuulException {
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("------------------pre2--------------------");
// throw new RuntimeException("error");
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
// TODO Auto-generated method stub
return 1;
}
@Override
public String filterType() {
return "pre";
}
}
测试结果:
TestPreFilter中为1,AccessFilter 为0,说明验证成功。
post验证
PostFilter代码:
@Component
public class PostFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(PostFilter.class);
@Override
public Object run() throws ZuulException {
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("------------------post--------------------");
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
// TODO Auto-generated method stub
return 0;
}
@Override
public String filterType() {
return "post";
}
}
启动项目测试结果:
验证成功。
异常处理
ErrorFilter代码:
@Component
public class ErrorFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(ErrorFilter.class);
@Override
public Object run() throws ZuulException {
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("------------------error--------------------");
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public String filterType() {
return "error";
}
}
在TestPreFilter中抛出一个异常throw new RuntimeException(“error”);测试:
测试结果:
先是error再是post。因为出现异常过后会被error接收,然后还会返回给客户端,所以再是post然后再返回给客户。
出现这种异常我们要加入一个异常处理类ErrorGatewayController。
ErrorGatewayController代码:
@RestController
public class ErrorGatewayController implements ErrorController{
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping("/error")
public String error() {
return "{\"result\":\"500 error !!!\"}";
}
}
测试结果:
因为异常已经被我们拦截,所以出现500错误。
网关实现服务降级
zuul和hystrix可以无缝结合,启动一个zuul和product和dashboard。就可以参照springcloud数据监控进行结合。
网关服务降级的目录结构:
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>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
配置文件代码:
spring.application.name=zull-gateway
server.port=9010
eureka.client.serviceUrl.defaultZone=http://user:test@eureka2:8762/eureka/,http://user:test@eureka1:8761/eureka/
eureka.instance.perferIpAddress=true
ZuulApplication代码:
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
ProductFallbackProvider代码:
@Component
public class ProductFallbackProvider implements ZuulFallbackProvider{
@Override
public String getRoute() {
// 代表为哪个服务提供fallback
return "e-book-product";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@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 InputStream getBody() throws IOException {
String input = "商品服务不可以用,请联系管理员!";
return new ByteArrayInputStream(input.getBytes());
}
@Override
public String getStatusText() throws IOException {
// httpresponse的fallback的状态码,string
return this.getStatusCode().getReasonPhrase();
}
@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 void close() {
}
};
}
}
代码中都有方法如何运用的详细注释,启动product和zuul项目进行测试:
关闭product服务测试:
这就是网关的降级服务。
网关限流
在高并发下,如果一个IP请求多次就可以对它进行限流,在某个时间内不能访问,然后超过某个时间又可以访问。
项目的目录结构:
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>
配置 文件中的代码:
spring.application.name=zull-gateway
#配置自定义端口
server.port=9010
#指定一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:test@eureka2:8762/eureka/,http://user:test@eureka1:8761/eureka/
#默认是hostname 注册,改成IP 注册
eureka.instance.perferIpAddress=true
# 针对某个服务进行限流
#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
ZuulApplication代码:
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
启动项目测试,访问http://localhost:9010/e-book-product/product/list三次过后就会出现这个:
提示请求访问次数过多。说明限流成功。
网关的2层超时调优
代码和限流一样,只需要修改配置文件。
先做一个测试,在服务端product中的方法中加入睡眠2秒。
然后启动zuul和product进行测试:
就会访问超时。
通过这个图我们就可以明白,先是访问的hystrix,我们设置睡眠的时间为2秒,所以就会死掉。
在配置文件中重新设置超时时间:
#第一层hystrix超时时间设置
#默认情况下是线程池隔离,超时时间1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=8000
#第二层ribbon超时时间设置:设置比第一层小
# 请求连接的超时时间: 默认5s
ribbon.ConnectTimeout=5000
# 请求处理的超时时间: 默认5s
ribbon.ReadTimeout=5000
需要注意的是ribbon的时间不能超过hystrix。
重新启动测试:
访问成功。
好了,服务网关的讲解就结束了,如果想要demo的资料可以叫我QQ997355706