SpringCloud学习--- Zuul详解(附代码包)

Posted 小样5411

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud学习--- Zuul详解(附代码包)相关的知识,希望对你有一定的参考价值。

前言

上一篇:Hystrix详解
Zuul(网关)在SpringCloud中非常重要,非常重要,Zuul网关的作用类似nginx的作用,Nginx可以做请求转发,Nginx来管理服务的ip地址,客户端只要访问Nginx的端口即可,具体转发访问哪个服务又Nginx转发,Zuul也有这种功能,并Zuul能认证与授权,如果Zuul认为一个请求有权限,才会进行转发给对应服务。除此之外,如果服务有迭代更改,也只需要改Zuul配置,客户端无需改动,还可限流,优点多多,使用起来却很简单。
优点
1、请求转发,客户端无需再维护大量服务端口
2、认证和授权操作
3、可做限流与安全
4、项目迭代,导致项目拆分或更改,不用动客户端,只要修改Zuul配置

一、Zuul快速入门

创建一个新module,maven项目,命名为zuul_01
常见几步:导入依赖、配置yml、配置启动类+启动Zuul注解、最后测试

注意:zuul是也是通过在Eureka中注册从而访问其他服务

第一步:导入依赖

<dependencies>
        <!--zuul是也是通过在EurekaClient中注册访问其他服务,所以也需要导入Eureka_Client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

第二步:配置application.yml

# 指定往哪个EurekaServer服务中注册
eureka:
  client:
    serviceUrl:
      defaultZone: http://root:root@localhost:8761/eureka/,http://root:root@localhost:8762/eureka/

# 指定服务名称,为了区分创建的服务
spring:
  application:
    name: zuul

server:
  port: 8001

第三步:启动类,注解开@EnableEurekaClient和@EnableZuulProxy,因为Zuul要在Eureka中注册

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class,args);
    }
}

启动ZuulApplication,输入localhost:8761看看已经启动的服务,这里你要把我前几篇案例也看了,拿代码运行,都是有关联的

通过Zuul的端口8001,输入服务名+接口就可以访问到对应的服务,过程就是Zuul->Customer->Pay调用

二、Zuul常用配置

2.1 监控界面与忽略服务配置

Zuul监控界面没有Hystrix好看,比较简单,效果如下,配置好的服务可以到Zuul这里看看是否生效,如地址含/customer/**就会到customer服务找,遇到遇到/eureka/**就会到eureka服务中找,和nginx相似

那我们怎么配置呢?老生常谈的几步

第一步:导入依赖
新增

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

第二步:配置yml
新增

# zuul的监控界面,开发时配置为*,上线无需配置
management:
  endpoints:
    web:
      exposure:
        include: "*"

然后重启,输入http://localhost:8001/actuator/routes即可

如果想移出某个服务呢?application.yml增加如下配置

重启程序,刷新界面,会发现eureka服务直接移除了,customer服务还在,但是无法访问它

#zuul配置
zuul:
  ignored-services: eureka  #基于服务名忽略,如要忽略全部服务用*
  ignored-patterns: /**/search/** #监控界面还是看得到,但是无法访问,会404

自定义配置方式:如果你想把这个下面路径换个其他名字,可以用自定义配置

#zuul配置
zuul:
  ignored-services: eureka  #基于服务名忽略,如要忽略全部服务用*
  ignored-patterns: /**/pay/** #监控界面还是看得到,但是无法访问,会404
  routes:
    payClient:  /pay/** #服务名:新路径
    customer: /user/**

这样就创建了新的可以访问的路径,当然旧的也可以使用,两种等价

不想用旧的,可以忽略,比如我们这里写ignored-services: "*"试试,重启,刷新,以前的访问方式就忽略了,所以这样就可以按自己所需改访问名字

如下输入http://localhost:8001/user/customer/2得到下面结果,以前是http://localhost:8001/customer/customer/2,现在改了下名字而已

我们暂时不用这个忽略,先注释

2.2 灰度发布

什么是灰度发布?
灰度发布就是在版本迭代过程,如app发布了一个新版本,更新的用户用心的接口调用功能,而老用户还是用老接口调用功能,那么采用灰度发布,就能平衡好新老版本,怎么做?两步即可

第一步:Zuul启动类增加@Bean代码块,Zuul是通过版本的方式,新版本Zuul会让对应服务用新版接口,老版本用老的

@Bean
    public PatternServiceRouteMapper serviceRouteMapper(){
        return new PatternServiceRouteMapper(
                "(?<name>^.+)-(?<version>v.+$)",
                "${version}/${name}"
        		);
    }

第二步:准备一个服务,弄出两个版本

先做如下操作


将新旧Customer都重启,ZuulApplication也重启,改动的都重启,然后查看Zuul的监控界面,那么新的就会以/v1/customer出现,如果再加customer就会变成/v2/customer,zuul都会自动进行

如果你没有得到v1和v2,只有一个v1,没有v2则证明你的有问题,一般是注册的问题,我就碰到了,最后我思考认为应该让所以服务都注册,然后Eureka中有全部服务,Zuul再注册,它就能获取到所有服务了


启动要按照这个顺序启动就没问题

于是就能根据不同版本进行访问

三、Zuul过滤器

过滤器是Zuul的核心组件,我们先看看它的执行流程,如下图

客户端请求发送到Zuul服务上,首先通过PreFilter过滤器链,这个过滤器可以做限流、权限验证等操作,如果请求无误,则正常放行,请求再次转发给RoutingFilter,RouteFilter会将请求转发到指定服务得到响应结果,再将结果传递回RoutingFilter,RouteFilter再将结果传递给PostFilter过滤器链,最终再将响应信息交给客户端。

提供四种过滤器

3.1 Zuul过滤器快速入门

在zuul模块创建两个自定义过滤器类,继承ZuulFilter,重写四个方法

@Component
public class TestZuulFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;//设置过滤器类型,前置过滤器
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; //设置过滤器执行顺序,数字越小会先执行,这里比前置小,就会在前置过滤器之前执行
    }

    @Override
    public boolean shouldFilter() {
        return true;   //是否开启当前过滤器
    }

    @Override
    public Object run() throws ZuulException {
        System.out.println("prefix过滤器已经执行");
        return null;
    }
}

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

@Component
public class TestZuulFilter2 extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;//设置过滤器类型,前置过滤器
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1; //设置过滤器执行顺序,数字越小会先执行,这里比前置小,就会在前置过滤器之前执行
    }

    @Override
    public boolean shouldFilter() {
        return true;   //是否开启当前过滤器
    }

    @Override
    public Object run() throws ZuulException {
        System.out.println("prefix过滤器222已经执行");
        return null;
    }
}

重启ZuulApplication,然后随机访问一个地址,比如这个http://localhost:8001/v1/customer/version,控制台就会打印下面结果

FilterConstants.PRE_DECORATION_FILTER_ORDER - 1的TestZuulFilter先执行,因为越小的数字会先执行,源码中默认为5

3.2 PreFilter实现token校验

创建一个AuthenticFilter,继承ZuulFilter

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.http.HttpStatus;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import javax.servlet.http.HttpServletRequest;

@Component	//一定记得加component交给SPring管理
public class AuthenticFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 2;//最前进行校验
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //1、获取request对象
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();

        //2、获取token参数
        String token = request.getParameter("token");
        //3、对比token
        if(token == null || !("123".equals(token))){//假设token查到为123
            //4、token校验失败,直接响应数据
            currentContext.setSendZuulResponse(false);//不响应
            currentContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);//401

        }
        return null;
    }
}

上面的token实际通过Redis查找、也可以是其他查出token,注意自己定义的Filter要用@Component,交给Spring管理

重启ZuulApplication,浏览器刷新,返回401不可访问HttpStatus.SC_UNAUTHORIZED就是401状态码,表示未授权,校验失败

token正确则放行

3.3 Zuul的降级

Zuul默认会包括Hystrix依赖,所以不用再导入关于降级的Hystrix依赖,本质是Zuul整合Hystrix实现降级,返回托底数据

创建ZuulFallBack实现降级

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;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
public class ZuulFallBack implements FallbackProvider {
    @Override
    public String getRoute() {
        return "*"; //指定全部出现问题的服务都走这个降级方法
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {//route表示服务名
        System.out.println("降级的服务:"+route);
        cause.printStackTrace();//问题打印
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR;    //INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.value(); //500状态码
            }

            @Override
            public String getStatusText() throws IOException {
                //指定错误信息
                return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                String msg = "当前服务:"+ route + ",出现问题";
                return new ByteArrayInputStream(msg.getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                //指定响应头信息
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

阅读上面代码,有注释
新增睡眠

重启Customer有关服务(两个),再重启ZuulApplication服务,输入地址http://localhost:8001/v2/customer/version?token=123

控制台会打印原因,读超时Read timed out,因为睡眠了

代码:
链接:https://pan.baidu.com/s/1U3ebAqfIS-s_7gqqyuOSGQ
提取码:233x

以上是关于SpringCloud学习--- Zuul详解(附代码包)的主要内容,如果未能解决你的问题,请参考以下文章

Zuul 详解,带视频

SpringCloud学习--- Hystrix详解(附代码包)

SpringCloud学习--- Hystrix详解(附代码包)

SpringCloud学习--- Feign详解(附代码压缩包)

SpringCloud学习--- Feign详解(附代码压缩包)

springCloud(14):使用Zuul构建微服务网关-路由端点与路由配置详解