springcloud微服务系列教程 (九) 服务网关组件Zuul

前言

通过前几篇教程的介绍,我们基本上认识了构建一个微服务系统所需要的组件,通过这些组件的结合,我们已经可以搭建一个较为简略的微服务系统了,比如下面的架构图:
这里写图片描述
可以看出,该系统架构图包含了服务注册中心eureka-server作为服务注册中心,config-server作为配置中心获取远程Git地址的配置文件信息,在服务的请求处还做了负载均衡,服务之间做了集群分布,实现了高可用,服务之间还可以实现相互调用,由此可见,一个简易的微服务系统就搭建成了。

引出Zuul

上面的架构已经包含了微服务系统的基础功能,但是仍有不足之处:
首先是服务路由的不确定性,在之前的学习中,我们通过restTemplate+ribbon 以及 feign客户端的方式实现了负载均衡,可以实现对高可用服务的消费,但那种直接基于服务名来调用的负载处理并不完全,我们需要有一个更强大的网关控制工具。
其次是缺乏校验功能,当我们需要对一个即有的集群内访问接口,实现外部服务访问时,我们不得不通过在原有接口上增加校验逻辑,这样跟服务之间就存在耦合了,不符合开发的宗旨。
针对上述的不足之处,spring cloud 提供了 Zuul组件来加以完善。
Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能,加入了Zuul后的微服务架构将变得更加完善,架构图如下:
这里写图片描述
下面用实例带大家领略Zuul的魅力。

创建工程

在创建Zuul工程之前,我们需要先引用之前的工程,读者有疑问可以参考https://blog.csdn.net/yeyazhishang/article/details/81392085

在原有的工程创建新新工程zuul-service,其pom文件如下:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yeya</groupId>
    <artifactId>zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>zuul</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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-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>

在程序的启动类上加入注解 @EnableDiscoveryClient 和 @EnableZuulProxy,开启服务注册和服务网关的功能,代码如下:

@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulApplication {

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

在配置文件application.yml配置如下信息:

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:1111/eureka/
server:
  port: 1116
spring:
  application:
    name: zuul-service
zuul:
  routes:
    api-a:
      path: /clientapi/**
      serviceId: eureka-client
    api-b:
      path: /ribbonapi/**
      serviceId: eureka-ribbon

其中,eureka.client.serviceUrl.defaultZone是服务注册中心的地址,服务的名称为zuul-service,以/clientapi/ 开头的请求都转发给eureka-client服务;以/ribbonapi/开头的请求都转发给eureka-ribbon服务;

配置好信息后,依次启动这几个工程,记得eureka-client要改端口 (1112,1113) 启动两次,然后打开浏览器,多次访问 http://localhost:1116/clientapi/hello,会发现浏览器反复显示

hello : 1112 
hello : 1113

同样,多次访问 http://localhost:1116/ribbonapi/consumer ,浏览器会显示相同的结果,说明zuul的确起到了路由转发的作用。

服务过滤

zuul除了能做路由转发之外,还能实现过滤作用,做一些安全的验证功能,下面我们来改造一下zuul-service工程。在工程中创建一个过滤器继承ZuulFilter ,并实现类中的方法,具体实现如下功能;

如果请求地址中有带token,那么过滤器就不做拦截,否则拦截并输出 token is empty,下面是具体代码:

@Component
public class MyFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

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

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        Object accessToken = request.getParameter("token");
        if(accessToken == null) {
            ctx.setSendZuulResponse(false);
            try {
                ctx.getResponse().getWriter().write("token is empty");
            }catch (Exception e){}

            return null;
        }
        return null;
    }
}

filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:

  • pre:路由之前
  • routing:路由之时
  • post: 路由之后
  • error:发送错误调用
  • filterOrder:过滤的顺序
  • shouldFilter:这里可以写逻辑判断,是否要过滤,本文true,永远过滤。
  • run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。

写好代码后,重启服务,访问 http://localhost:1116/ribbonapi/consumer ,发现浏览器返回

token is empty

加上参数token,重新访问 http://localhost:1116/ribbonapi/consumer?token=11,发现浏览器正常输出

hello : 1112 
hello : 1113

至此,zuul的过滤工程成功实现。

总结

zuul作为微服务系统的服务网关,起到了至关重要的作用,所以,一般来说,在生产上的微服务系统中,zuul服务需要做集群分布,避免服务挂了网关失效的情况。

本文源码地址:

https://github.com/Taoxj/SpringCloudDemo/tree/master/zuul

猜你喜欢

转载自blog.csdn.net/yeyazhishang/article/details/81392215