【Spring cloud Zuul】服务网关Zuul入门


声明:本文转载自https://my.oschina.net/u/998475/blog/1615836,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

API网关服务对微服务框架的重要性:

1. 作为系统的统一入口,屏蔽系统的内部各个微服务的细节。

2. 与微服务治理框架结合,实现自动化的服务实例维护以及负载均衡的路由转发。

3. 可以实现接口权限校验与微服务业务逻辑解耦。

4. 通过服务网关中的过滤器,在各生命周期中去校验请求的内容,将原本在对外服务层的校验前移,保证了微服务的无状态性,同时降低了微服务的测试难度,让服务本身更集中关注业务逻辑的处理。

接下来实现一个会打印服务请求路径的网关:ApiGateway

要实现一个服务网关,使用Spring boot + Spring cloud Zuul来实现是一个非常方便的事情。只需要在普通的 Springboot 的启动类上增加 @enableZuulProxy ,然后在配置文件配上相关的参数即可。

下面是 ApiGateWay 项目的详细信息:

1. 项目目录结构:

├── build.gradle ├── gradle │   └── wrapper │       ├── gradle-wrapper.jar │       └── gradle-wrapper.properties ├── settings.gradle └── src     ├── main     │   ├── java     │   │   └── com     │   │       └── simonton     │   │           └── api     │   │               ├── ApiGateway.java     │   │               └── log     │   │                   └── LogPrintFilter.java     │   └── resources     │       └── application.yml     └── test         └── java 

 

2. build.gradle内容:

buildscript { 	repositories { 		jcenter() 	} 	 	dependencies { 		classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.5.6.RELEASE' 	} }  apply plugin: 'java' apply plugin: 'org.springframework.boot'  repositories {     jcenter() }  dependencies { 	compile 'org.springframework.boot:spring-boot-starter-web' 	compile 'org.springframework.cloud:spring-cloud-starter-zuul:1.4.2.RELEASE' }  

3. 代码核心 ApiGateway 内容:

package com.simonton.api;  import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; import org.springframework.context.annotation.Bean;  import com.simonton.api.log.LogPrintFilter;  @EnableZuulProxy @SpringBootApplication public class ApiGateway { 	public static void main(String[] args) { 		SpringApplication.run(ApiGateway.class, args); 	} 	 	@Bean 	public LogPrintFilter logPrintFilter() { 		return new LogPrintFilter(); 	} } 

4. 日志打印过滤器 内容:

/**  *   */ package com.simonton.api.log;  import javax.servlet.http.HttpServletRequest;  import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;  import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext;  /**  * 打印请求的服务  *   * @author simonton  *  */ public class LogPrintFilter extends ZuulFilter {  	@Override 	public boolean shouldFilter() { 		return true; 	}  	@Override 	public Object run() { 		RequestContext contenxt = RequestContext.getCurrentContext(); 		HttpServletRequest request = contenxt.getRequest(); 		String url = request.getRequestURI(); 		System.out.println("@request url: " + url); 		return null; 	}  	@Override 	public String filterType() { 		return FilterConstants.PRE_TYPE; 	}  	@Override 	public int filterOrder() { 		return 0; 	}  } 

4. 配置文件 application.yml 内容:

server:   port: 7070  zuul:   routes:     baidu:        path: /demo/**       url: http://localhost:9090/demo   add-host-header: true

ps: 当做服务路由分发时,host 信息设置不正确时,容易出现服务请求302跳转, add-host-header=true可以解决这个问题。

接下来我们来分析下,为什么只要在 springboot 启动类上加上 @enableZuulProxy就能实现服务的分发及过滤?

我们先来看@enableZuulProxy这个实现:

@EnableCircuitBreaker @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(ZuulProxyMarkerConfiguration.class) public @interface EnableZuulProxy { }

从源码可以看出,@enableZuulProxy只是导入了配置类 ZuulProxyMarkerConfiguration。

那ZuulProxyMarkerConfiguration又做了什么呢?

@Configuration public class ZuulProxyMarkerConfiguration { 	@Bean 	public Marker zuulProxyMarkerBean() { 		return new Marker(); 	}  	class Marker { 	} }

ZuulProxyMarkerConfiguration它更简单,只是 new 了一个 内部类 Marker对象。事实上本人看到这里非常郁闷,这个Marker类是个什么鬼?后来一搜源码才发现,是@ConditionalOnBean 在搞鬼。

在 ZuulServerAutoConfiguration 配置类中,它的实例化依赖羽刚才ZuulProxyMarkerConfiguration的 Marker 类。下面是ZuulServerAutoConfiguration部分源码:

@Configuration @EnableConfigurationProperties({ ZuulProperties.class }) @ConditionalOnClass(ZuulServlet.class) @ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class) // Make sure to get the ServerProperties from the same place as a normal web app would @Import(ServerPropertiesAutoConfiguration.class) public class ZuulServerAutoConfiguration {

到此为止,我才看到了关键的类ZuulServerAutoConfiguration出场 ,该类中会把 ZuulController 注册到 spring mvc的处理逻辑中。

	@Bean 	public ZuulController zuulController() { 		return new ZuulController(); 	}  	@Bean 	public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) { 		ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController()); 		mapping.setErrorController(this.errorController); 		return mapping; 	}

ZuulHandlerMapping 继承自 AbstractUrlHandlerMapping,它将 Spring cloud zuul 的ZuulController关联进 Spring MVC, 可以处理 request 请求。

那 ZuulController 又做了什么呢? 它将收到的请求统一交给 ZuulServlet 的 service方法 处理。

ZuulServlet 核心代码:

    @Override     public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {         try {             init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);              // Marks this request as having passed through the "Zuul engine", as opposed to servlets             // explicitly bound in web.xml, for which requests will not have the same data attached             RequestContext context = RequestContext.getCurrentContext();             context.setZuulEngineRan();              try {                 preRoute();             } catch (ZuulException e) {                 error(e);                 postRoute();                 return;             }             try {                 route();             } catch (ZuulException e) {                 error(e);                 postRoute();                 return;             }             try {                 postRoute();             } catch (ZuulException e) {                 error(e);                 return;             }          } catch (Throwable e) {             error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));         } finally {             RequestContext.getCurrentContext().unset();         }     }

从源码可知,对于 Zuul 定义的不同类型的过滤器: pre, post, route, err 就是在这里被相应的处理。在ZuulServlet类中又引入了 ZuulRunner, ZuulRunner 中又使用 FilterProcessor 类专门执行对应的过滤器逻辑:

    public Object runFilters(String sType) throws Throwable {         if (RequestContext.getCurrentContext().debugRouting()) {             Debug.addRoutingDebug("Invoking {" + sType + "} type filters");         }         boolean bResult = false;         List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);         if (list != null) {             for (int i = 0; i < list.size(); i++) {                 ZuulFilter zuulFilter = list.get(i);                 Object result = processZuulFilter(zuulFilter);                 if (result != null && result instanceof Boolean) {                     bResult |= ((Boolean) result);                 }             }         }         return bResult;     }

从源码可知,FilterProcessor 通过 FilterLoader 把同一类型的filter 捞出来,按顺序执行。这里的这个顺序可是有规则的,每个 ZuulFilter 可以通过设置 filterOrder 来改变执行顺序的,原因是 ZuulFilter 实现了 Comparable 接口:

public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {      public int compareTo(ZuulFilter filter) {         return Integer.compare(this.filterOrder(), filter.filterOrder());     }      public ZuulFilterResult runFilter() {         ZuulFilterResult zr = new ZuulFilterResult();         if (!isFilterDisabled()) {             if (shouldFilter()) {                 Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());                 try {                     Object res = run();                     zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);                 } catch (Throwable e) {                     t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");                     zr = new ZuulFilterResult(ExecutionStatus.FAILED);                     zr.setException(e);                 } finally {                     t.stopAndLog();                 }             } else {                 zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);             }         }         return zr;     }

好了,到目前为止,我们已经看到了Spring cloud Zuul 是如何开始处理 request 请求,及怎么处理过滤请求的。

 

本文发表于2018年01月31日 14:32
(c)注:本文转载自https://my.oschina.net/u/998475/blog/1615836,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1710 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1