Spring Cloud之路由网关Zuul

一:Zuul简单介绍

  1.  简介
            这是Netflix构建微服务的另一个组件—>智能路由网关组件Zuul。它一般用于构建
    边界服务(Edge Service),主要用于动态路由,过滤,监控,弹性伸缩和安全。
  2.  Zuul的主要作用
            ⑴Zuul,Ribbon以及Euraka相结合,可以实现智能路由和负载均衡的功能,Zuul能够
                将请求流量按照某种策略分发到集群状态的多个服务实例。
            ⑵网关将所有服务的API接口统一聚合,并且统一对外暴露。外界系统调用API接口
                时,都是由路由对外暴露的API接口,外界系统不需要知道微服务系统中各个服务
                相互调用的复杂性。微服务系统也保护了其内部微服务单元的API接口,防止其被
                外界直接调用,导致服务的敏感信息对外暴露。
            ⑶网关服务可以用来做用户身份认证和权限认证,防止非法请求操作API接口,对服
                务器起到保护作用。
            ⑷网关可以实现监控功能,实时日志输出,对请求进行记录。
            ⑸网关可以用来实现流量监控,在高流量的情况下,对服务进行降级。
            ⑹API接口从内部服务分离出来,方便做测试。  
  3. Zuul的工作原理      
            Zuul是通过Servlet来实现的,Zuul通过自定义的ZuulServlet(类似于Spring MVC的
     DispatchServlet)来对请求进行控制,ZuulServlet是Zuul的核心Servlet,它的作用是初始
    化ZuulFilter,并且去编排这些ZuulFilter的执行顺序,这个类中存在一个service()方法,执
    行了过滤器执行的逻辑。Zuul的核心是一系列的过滤器,可以在Http请求的发起和响应返
     回期间执行一系列的过滤器。Zuul包含了以下4种过滤器:
             a:PRE过滤器—它是在请求路由到具体的服务之前执行的,这种类型的过滤器可以
                   做安全验证,例如身份验证,参数验证等。
             b:ROUTING过滤器—它用于将请求路由到具体的微服务实例。在默认情况下,它
                   使用Http Client进行网络请求。
             c:POST过滤器—它是在请求已经被路由到具体的微服务之后执行的。一般情况下
                   用作收集统计信息,指标,以及将响应传输到客户端。
             d:EEOR过滤器—它是在其它过滤器发生错误时执行的。
  4. Zuul过滤器的关键特性
              ⑴简介
                         Zuul采取了动态读取,编译和运行这些过滤器。过滤器之间不能直接相互通信,
                  而是通过RequestContext对象来共享数据,每个请求都会创建一个RequestContext
                  对象。
              ⑵特性如下
                         a:Type(类型)—Zuul过滤器的类型,这个类型决定了过滤器在请求的哪个阶段
                               起作用,例如PRE,POST阶段等。
                         b:Execution Order(执行顺序)—这个参数规定了过滤器的执行顺序,order的值
                               越小,越先执行。
                         c:Criteria(标准)—表示Filter执行所需的条件
                         d:Action(行动)—表示如果符合执行条件,则执行Action(即逻辑代码)
  5. Zuul请求的生命周期
             ⑴流程图如下
                    
             ⑵流程图的逻辑如下
                        当一个客户端Request请求进入Zuul网关服务时,会先进入到PRE Filters过滤器,进行
                 一系列的验证,操作或者判断,当然我们也可以使用自定义放入过滤器。后面再交给过滤器
                 ROUTING Filters去进行请求的路由转发,将请求路由到具体的服务实例进行逻辑处理,最后
                 返回处理结果。当具体的服务处理完后,最后由POST Filters过滤器进行处理,这个会将结果
                 返回给客户端。当然PRE,ROUTING,POST这3个处理器处理请求的过程中一旦处理任何问题,
                 都会调用ERROR Filters进行处理,最后将问题返回到客户端。

二:案例实战

  1.  首先我们搭建Zuul服务
      
     ⑴创建启动类EurekaZuulClientApplication.java文件
            
     ⑵创建配置文件application.yml文件
            
            由上面的配置我们能够知道程序名称为service-zuul
            服务中心的注册地址为:http://peer1:8761/eureka/
            程序的端口为5000
            并且我们这里配置了zuul.routs.eurekaClient.path为/eurekaClientApi/**,
            zuul.routs.eurekaClient.service-id为eureka-client,那么这两个配置表示
            的就是将以/eurekaClientApi开头的Url路由到eureka-client服务。当然其中
            zuul.routs.eurekaClient中的eurekaClient是自定义的。同理下面的两个服务
            eureka-ribbon-client和eureka-feign-client也是一样。
     ⑶在pom.xml文件中添加依赖
           
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
        <groupId>com.kgf</groupId>
        <artifactId>chapter5-2</artifactId>
        <version>1.0-SNAPSHOT</version>
      </parent>
      <artifactId>eureka-zuul-client</artifactId>
      <name>eureka-zuul-client</name>
      <dependencies>
    	  <dependency>
    	  	<groupId>org.springframework.cloud</groupId>
    	  	<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    	  </dependency>
    	  <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    		 <dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
      </dependencies>
    </project>
     
  2. 测试效果
       ⑴ 依次启动工程eureka-server,eureka-client(注意这里需要启动两个实例,端口分别为8762,8763),eureka-ribbon-client,
            eureka-feign-client和eureka-zuul-client
       ⑵ 效果
                  
            a:那么现在我们首先通过zuul服务直接访问eureka-client的接口
                  路径:http://peer1:5000/eurekaClientApi/hi?name=aaa
                  第一次:
                       
                  第二次:
                       
                  通过上面的效果我们是可以发现zuul默认和Ribbon结合实现了负载均衡的功能。
                  这说明zuul起到了路由的作用。
           b:当然如果我们不需要Ribbon去做这个负载均衡,我们可以指定服务实例的Url,一旦指定
                 了url,这时候就不需要service-id了,如下:
                 
                效果如下,不管请求几次都是8762这个服务的实例了。
                
         c:当然,如果我们既想要去指定Url,又想做负载均衡,那么我们就需要自己维护
              负载均衡的服务注册列表。
              
              再次看看效果:
              第一次:
                  
              第二次:
                  

三:我们在Zuul的配置上可以添加一个前缀

  1.  在application.yml配置文件中添加一个属性prefix即可
        
  2. 效果
               

四:在Zuul上配置熔断器

  1. 简介
          在Zuul中实现熔断功能需要实现FallbackProvider的接口。实现该接口的两个方法,
    一个是getRoute()方法,用于指定熔断功能应用于哪些路由的服务,另一个方法fallbackResponse()
    为进入熔断功能时执行的逻辑。
  2.   创建接口FallbackProvider的实现类MyFallbackProvider.java   
      
    package com.kgf.eureka.zuul.hystrix;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
    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 MyFallbackProvider implements FallbackProvider  {
    
    	@Override
    	public String getRoute() {
    		return "eureka-client";//实现对eureka-client服务的熔断
    	}
    
    	@Override
    	public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
    		return new ClientHttpResponse() {
    
    			@Override
    			public InputStream getBody() throws IOException {
    				return new ByteArrayInputStream("Error,I am the fallback".getBytes());
    			}
    
    			@Override
    			public HttpHeaders getHeaders() {
    				HttpHeaders headers = new HttpHeaders();
    				headers.setContentType(MediaType.APPLICATION_JSON);
    				return headers;
    			}
    
    			@Override
    			public HttpStatus getStatusCode() throws IOException {
    				return HttpStatus.OK;
    			}
    
    			@Override
    			public int getRawStatusCode() throws IOException {
    				return 200;
    			}
    
    			@Override
    			public String getStatusText() throws IOException {
    				return "OK";
    			}
    
    			@Override
    			public void close() {
    				
    			}
    			
    		};
    	}
    
    }
    

  3. 依次启动工程eureka-server,eureka-client(注意这里需要启动两个实例,端口分别为8762,8763),eureka-ribbon-client,
    eureka-feign-client和eureka-zuul-client
  4. 效果

     访问eureka-client接口正常展示
     
     下面我们将eureka-client关掉效果:
     
  5. 如果我们将getRoute()方法的返回改为”*”,那么将对所有的路由服务都加熔断功能
      

五:在Zuul中使用过滤器

  1. 简介
          zuul不仅只是路由,并且还能过滤,做一些安全验证,实现过滤器很简单,只要继承ZuulFilter,并且实现
    ZuulFilter中的抽象方法,包括filterType()和filterOrder(),以及IZuulFilter的shouldFilter()和Object run()的两个方法。
  2. 实例代码
          ⑴创建自定义的过滤器MyFilter.java
                  
                  
    package com.kgf.eureka.zuul.filter;
    
    import java.io.IOException;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.lang.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    
    /**
     *      自定义过滤器
     * @author 86136
     *
     */
    @Component
    public class MyFilter extends ZuulFilter {
    
    	private static Logger log = LoggerFactory.getLogger(MyFilter.class);
    	
    	/**
    	 * filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
    	 * 1:pre:路由之前
    	 * 2:routing:路由之时
    	 * 3:post: 路由之后
    	 * 4:error:发送错误调用
    	 */
    	@Override
    	public String filterType() {
    		return "pre";//这里必须小写
    	}
    	/**
    	 * 这个方法表示该过滤器是否过滤逻辑,如果为true,则执行run方法,如果为false,则不执行run方法
    	 */
    	@Override
    	public boolean shouldFilter() {
    		return true;
    	}
    
    	/**
    	 * 这个run方法中是主要写具体过滤的逻辑的。
    	 * 这里我们测试的例子是判断用户是否在请求参数中传递了token这个参数,
    	 * 如果没有传递这个参数,那么请求就不会被传递到具体的服务实例,直接返回相应状态码401
    	 */
    	@Override
    	public Object run() throws ZuulException {
    		//获取RequestContext对象,因为过滤器直接不能直接通信,必须通过RequestContext来共享数据
    		RequestContext context = RequestContext.getCurrentContext();
    		//获取HttpServletRequest对象
    		HttpServletRequest request = context.getRequest();
    		//获取我们的参数标识
    		String accessToken = request.getParameter("token");
    		if(StringUtils.isBlank(accessToken)) {
    			log.warn("token is empty!");
    			//如果参数为空,那么我们设置网关返回直接不路由到具体服务
    			context.setSendZuulResponse(false);
    			//返回状态码为401
    			context.setResponseStatusCode(401);
    			try {
    				//将错误信息返回到前台
    				context.getResponse().getWriter().write("token is empty!");
    			} catch (IOException e) {
    				return null;
    			}
    		}
    		log.info("ok");
    		return null;
    	}
    
    	
        /**
                  * 过滤的顺序 ,值越小越先执行该过滤器
         */
    	@Override
    	public int filterOrder() {
    		return 0;
    	}
    
    }
    

          ⑵依次启动工程eureka-server,eureka-client(注意这里需要启动两个实例,端口分别为8762,8763),eureka-ribbon-client,
              eureka-feign-client和eureka-zuul-client服务
              a:访问浏览器路径:http://peer1:5000/v1/eurekaClientApi/hi?name=aaa
                    
             b: 访问浏览器路径:http://peer1:5000/v1/eurekaClientApi/hi?name=aaa&&token=nihao
                    
             c:总结
                    我们定义的pre类型的过滤器在请求被zuul路由转发之前,会对请求进行安全校验。            
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值