您的位置:首页 > 编程语言 > Java开发

API网关服务:spring cloud zuul

2018-05-12 15:30 681 查看

路由

首先由两个服务:  A , B

在网关yml中配置规则:

zuul.routes.A.path=/A/**

zuul.routes.A.serviceId=A

zuul.routes.B.path=/B/**

zuul.routes.B.serviceId=B

请求:http://localhost:5555/A/hello会被转发到A服务上

http://localhost:5555/B/hello会被转发到B服务上

请求过滤

自定义过滤器,需要继承ZuulFilter抽象类,并且实现它的四个抽象方法就行。

@Component
public class MurphyTokenFilter extends ZuulFilter {

@Autowired
private IEsiAccountLoginService accountLoginService;
@Value("${spring.redis.host}")
private String redisHost;

/***
* 过滤器类型,过滤器在哪个声明周期执行
* pre:请求被路由之前
* routing:在路由请求时调用
* post:在routing和error过滤器之后被调用
* error:处理请求发生错误时调用
* @return
*/
@Override
public String filterType() {
return "pre";
}

/***
* 当有多个过滤器的时候,这个代表执行顺序,数值越小优先级越高
* @return
*/
@Override
public int filterOrder() {
return 10;
}

/***
* 判断过滤器是否被执行
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}

/***
* 业务代码
* @return
*/
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.setSendZuulResponse(false);//表示过滤掉该请求,不进行路由转发
ctx.setResponseStatusCode(Response.SC_UNAUTHORIZED);//返回错误码
ctx.setResponseBody(JSON.toJSONString("请求验证不通过,不进行转发"));
ctx.getResponse().setContentType(CONTENT_TYPE_JSON);
return false;
}

}

服务路由的默认规则

zuul会为每个服务默认维护一个路由规则:


但我们不希望所有服务都设置这种默认规则,所以可以做例外设置:

zuul.ignored-services=*

这样zuul对所有服务都不自动创建默认路由规则,这时就要在配置文件逐个为每个服务添加路由规则

路径匹配

路由匹配路径采用了ant的风格,有三种通配符


这时可能会遇到一种情况,一个url路径可能被多个不同表达式匹配上,比如有两个规则:

zuul.routes.user-service.path=/user-service/**

zool.routes.user-service.serviceId=user-service


zuul.routes.user-service-ext.path=/user-service/ext/**

zool.routes.user-service-ext.serviceId=user-service-ext

此时当调用/user-service/ext/**的时候两个路由规则都能匹配上。

做路由匹配的时候采用的线性遍历的方式,当获取到第一个匹配规则时就结束匹配,所以当有多个匹配规则匹配上时,根据规则的保存顺序只取第一个。

由于properties的配置内容无法保证有序,所以出现这种情况为了保证优先顺序,需要使用yml文件来配置,以实现有序的路由规则。

忽略表达式

zuul.ignored-patterns=/**/hello/**

zuul.routes.api-a.path=/api-a/**

zuul.routes.api-a.serviceId=hello-service

此时访问hello-service的hello接口时不通

Hystrix和ribbon的支持



过滤器详解

四种类型过滤器生命周期如图:



核心过滤器

在zuul启动的时候会默认开启一些核心过滤器


pre过滤器
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.netflix.zuul.filters.pre;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.servlet.DispatcherServlet;

public class ServletDetectionFilter extends ZuulFilter {
public ServletDetectionFilter() {
}

public String filterType() {
return "pre";
}

/***
* 第一个被执行
* @return
*/
public int filterOrder() {
return -3;
}

public boolean shouldFilter() {
return true;
}

/***
*  检查请求是通过spring的dispatcherServlet转发还是zuulServlet
* @return
*/
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
if(!(request instanceof HttpServletRequestWrapper) && this.isDispatcherServletRequest(request)) {
ctx.set("isDispatcherServletRequest", Boolean.valueOf(true));//dispatcherServlet
} else {
ctx.set("isDispatcherServletRequest", Boolean.valueOf(false));//zuulServlet
}

return null;
}

private boolean isDispatcherServletRequest(HttpServletRequest request) {
return request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null;
}
}

Servlet30WrapperFilter:主要是将httpServletRequest对象转换成Servlet30RequestWrapper对象

FromBodyWrapperFilter:该过滤器只处理contentType为application/x-www-form-urlencoded和multipart/form-data的请求,并且必须是spirng   servletDispathcer处理的请求。作用是将符合条件的请求体包装成FormBodyRequestWrapper对象。

DebugFilter:将debugRouting和debugRequest值设置为true

PreDecorationFilter:没看懂干嘛的 

Routing过滤器

RibbonRoutingFilter:通过ribbon和hystrix向服务实例发起请求,并且返回结果

SimpleHostRoutingFilter:只对通过url配置路由规则的请求处理,直接向url的物理地址发起请求,通过httpclient没有使用hystrix,所以这类请求并没有线程隔离和断路器保护

Send'ForwardFilter:没看懂

Post过滤器






异常处理

保证所有代码都在try catch代码块内,当发生异常的时候:

ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(Response.SC_UNAUTHORIZED);
ctx.setResponseBody(JSON.toJSONString(CommonUtils.getFailResultBean(localMsg)));
ctx.getResponse().setContentType(CONTENT_TYPE_JSON);
return false;
这样设置就能保证正常流转到SendErrorFilter处理异常信息返回。

但不能保证所有代码都在try catch中,所以当有异常抛出怎么办?定义error类型filter统一接受,然后再到SendErrorFilter

不足与优化

当在pre、routing中抛出异常的时候,会跳到error过滤器,error处理完会流转到post处理器做输出。但是当在post过滤器中抛出异常的时候,会跳到error处理器,error处理完后不会再跳到post过滤器了,没有post的处理输出,前端就看不到异常信息。这个问题该如何处理?

可以在errorFilter A之后再定义一个errorFilter B,B要继承SendErrorFilter,复用他的run方法,重写类型、执行顺序、shouldFilter,这样当postFilter抛出异常的时候到A,A处理完到B,B的作用和SendErrorFilter一样。

但又有个问题,上边的B应该是只处理PostFilter抛出的异常,如何在B中判断是PostFilter抛出的异常呢?也就是shouldFIlter应该怎么返回呢?

扩展过滤器,增加上下文属性。

zuul的核心处理器为FilterProcessor



最后在B的shouldFilter方法中可以拿到抛出异常的过滤器类型。








阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: