转载

springcloud微服务系列——服务网关组件Zuul

前言

通过前几篇教程的介绍,我们基本上认识了构建一个微服务系统所需要的组件,通过这些组件的结合,我们已经可以搭建一个较为简略的微服务系统了,比如下面的架构图:

https://img-blog.csdn.net/20180803175652889?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lleWF6aGlzaGFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70

可以看出,该系统架构图包含了服务注册中心eureka-server作为服务注册中心,config-server作为配置中心获取远程Git地址的配置文件信息,在服务的请求处还做了负载均衡,服务之间做了集群分布,实现了高可用,服务之间还可以实现相互调用,由此可见,一个简易的微服务系统就搭建成了。

引出Zuul

上面的架构已经包含了微服务系统的基础功能,但是仍有不足之处:

首先是服务路由的不确定性,在之前的学习中,我们通过restTemplate+ribbon 以及 feign客户端的方式实现了负载均衡,可以实现对高可用服务的消费,但那种直接基于服务名来调用的负载处理并不完全,我们需要有一个更强大的网关控制工具。

其次是缺乏校验功能,当我们需要对一个即有的集群内访问接口,实现外部服务访问时,我们不得不通过在原有接口上增加校验逻辑,这样跟服务之间就存在耦合了,不符合开发的宗旨。

针对上述的不足之处,spring cloud 提供了 Zuul组件来加以完善。

Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能,加入了Zuul后的微服务架构将变得更加完善,架构图如下:

https://img-blog.csdn.net/20180803175708387?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lleWF6aGlzaGFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70

下面用实例带大家领略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

br/>@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:

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 {

br/>@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服务需要做集群分布,避免服务挂了网关失效的情况。
原文  http://blog.51cto.com/13792737/2156966
正文到此结束
Loading...