一,在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统。一个简答的微服务系统如下图:

Spring cloud学习之路(六,路由网关 ZUUL)

在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服。,服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理(下一篇文章讲述),配置服务的配置文件放在git仓库,方便开发人员随时改配置。

二,Zuul简介:

由于微服务过多,可能某一个小业务就需要调各种微服务的接口,不可避免的就会需要负载均衡和反向代理了,以确保ui不直接与所有的微服务接口接触,所以我们需要使用一个组件来做分发,跨域等各种请求。

  ZUUL是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,它主要用作反向代理、Filter扩展、动态加载、动态路由、压力测试、弹性扩展、审查监控、安全检查等。

Spring cloud学习之路(六,路由网关 ZUUL)

通过网关的方式,提供致对外的服务,具体的服务调用分发由网关根据注册中心进行分发。

三,继续使用上一节的工程。在原有的工程上,创建一个新的工程。

3.1 可以在创建service-zuul模块时勾选依赖:勾选 Eureka Discovery Client ,Zuul和Web或者也可以走默认,然后在pom.xml文件下添加依赖:

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zhanghq</groupId>
    <artifactId>service_zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>service_zuul</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR2</spring-cloud.version>
    </properties>

    <dependencies>
        <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.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3.2 在其入口applicaton类加上注解@EnableZuulProxy,开启zuul的功能:

@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@EnableDiscoveryClient
public class ServiceZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceZuulApplication.class, args);
    }

}

3.3 加上配置文件application.yml加上以下的配置代码:

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

server:
  port: 8765

spring:
  application:
    name: service-zuul
      #第二种方式配置zuul地址映射(服务ID)
      #zuul:
      #  routes:
      #    api-a:
      #      path: /api-a/**
      #      service-id: service-consumer-feign
      #    api-b:
      #      path: /api-b/**
      #      service-id: service-consumer-ribbon
      #第一种方式配置url映射
      #zuul:
      #  routes:
      #    feign:
      #      path: /feign/**
    #      url: http://localhost:8085/
#第三中方式zuul.routes.服务名称: 访问路径
zuul:
  routes:
    consumer-feign: /feign/**
    consumer-ribbon: /ribbon/**
  #若忽略某个微服务不走网关,则这样设置:
  #ignored-services: service-consumer-ribbon
  #路由前缀
  #prefix: /zuul
  #默认不生效,要想生效,zuul.stripPrefix=false 把忽略前缀设置成false
  #strip-prefix: false

先指定服务注册中心的地址为http://localhost:8761/eureka/,服务的端口为8765,服务名为service-zuul;以/feign/ 开头的请求都转发给consumer-ribbon服务;以/ribbon/开头的请求都转发给consumer-feign服务;

3.4 依次运行这四个工程;打开浏览器访问:http://localhost:8765/feign/hi?name=jackson ;浏览器显示:

Spring cloud学习之路(六,路由网关 ZUUL)

四,服务过滤

4.1 zuul不仅只是路由,并且还能过滤,做一些安全验证。继续改造工程;

@Component
public class AccessFilter extends ZuulFilter {

    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
    /**
     *     pre:可以在请求被路由之前调用
     *     route:在路由请求时候被调用
     *     post:在route和error过滤器之后被调用
     *     error:处理请求时发生错误时被调用
     * @return
     */

    @Override
    public String filterType() {
        return "pre";  //请求前被调用;
    }

    /*
        filterOrder:过滤的顺序
        shouldFilter:这里可以写逻辑判断,是否要过滤,本文true,永远过滤。
        run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。
     */
    @Override
    public int filterOrder() {
        return 0; //越小越先执行
    }

    /**
     * 是否执行过滤器,过滤器是否执行的开关
     * 如果有多个过滤器时,上一个过滤器返回为true,下一个过滤器需要执行,如果为false就没必要执行了
     *
     */
    @Override
    public boolean shouldFilter() {
        return true;//此过滤器始终执行
        // return (boolean)RequestContext.getCurrentContext().get("isSuccess");
    }

    /**
     * 具体执行的逻辑
     * @return
     * @throws ZuulException
     */
    @Override
    @ResponseBody
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest(); //获取request

        System.out.println(String.format("%s AccessPasswordFilter request to %s", request.getMethod(), request.getRequestURL().toString()));
        String author = request.getHeader("author"); //得到请求头的用户信息
        //author = "jackson";  //添加一个临时数据测试
        if(StringUtils.isNotEmpty(author)){ //假设的逻辑
            requestContext.setSendZuulResponse(true);  //对该请求进行路由
            requestContext.setResponseStatusCode(HttpStatus.OK.value());
            requestContext.set("isSuccess",true); //设置一个状态,下一个过滤器可能会用到的
            return null;
        }else {
            requestContext.setSendZuulResponse(false); //过滤该请求,不进行路由
            requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
            Map json = new HashMap();
            json.put("msg","用户令牌错误");
            requestContext.setResponseBody(json.toString());  //返回前端内容
            HttpServletResponse response = requestContext.getResponse();
            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            requestContext.set("isSuccess",false);
            return null;
        }
    }
}

4.2 通过实现接口可以实现Zuul的容错与回退功能,下面这个例子来自Zuul的源码DefaultFallbackProvider,这里我稍微修改了下:

@Component
public class ZuulFallBack implements FallbackProvider {
    @Override
    public String getRoute() {
        return "consumer-feign"; //匹配微服务名字,可以用*或者null 代表所有服务的都过滤
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;//请求网关成功了,所以是ok
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.OK.value();
            }

            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.OK.getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            @ResponseBody
            public InputStream getBody() throws IOException {
                Map json=new HashMap();
                json.put("state","501");
                json.put("msg","后台接口错误");
                return new ByteArrayInputStream(json.toString().getBytes("UTF-8")); //返回前端的内容
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders=new HttpHeaders();
                httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
                return httpHeaders;
            }
        };
    }
}

上一篇:Spring cloud学习之路(五,断路器 Hystrix)https://blog.csdn.net/Zhang_Jackson/article/details/103293354

下一篇:

相关文章: