Spring Cloud学习(五):Zuul服务网关

2019-08-09 00:00:00 学习 网关 服务

1. Zuul介绍

Zuul在微服务中主要是做路由转发功能和请求过滤功能。

  • 路由转发:根据一定的规则,区分请求并转发到对应的服务商,比如两个请求/api/user/getUser,/api/blog/getBlog在zuul服务中分别转发到用户服务和博客服务。
  • 请求过滤:很多服务请求都有会身份验证,就可以直接在zuul中去对身份验证的token去做校验,而不用在每个服务中都去做一次校验那么麻烦

2. 准备工作

准备一个用户服务,一个博客服务,一个注册中心

2.1. 用户服务

  • application.yml
server:
  port: 4003
spring:
  application:
    name: service-user
eureka:
  client:
    service-url:
        defaultZone: http://eureka-server1:3001/eureka/
  instance:
    instance-id: 用户服务-4003
  • UserController.java
/**
 * @author 吕梁山
 * @date 2019/7/19
 */
@RestController
@RequestMapping("/api/user")
public class UserController {

    @RequestMapping(value = "/getUser", method = RequestMethod.GET)
    public String getUser(String userId){
        return "查找用户:" + userId;
    }

    @RequestMapping(value = "/addUser", method = RequestMethod.POST)
    public String addUser(String userName){
        return "新增用户姓名:" + userName;
    }

    @RequestMapping(value = "/updateUser", method = RequestMethod.PUT)
    public String updateUser(String userName){
        return "更新用户姓名:" + userName;
    }

    @RequestMapping(value = "/deleteUser", method = RequestMethod.DELETE)
    public String deleteUser(String userId){
        return "删除用户:" + userId;
    }
}

2.2. 博客服务

  • application.yml
server:
  port: 4004
spring:
  application:
    name: service-blog
eureka:
  client:
    service-url:
        defaultZone: http://eureka-server1:3001/eureka/
  instance:
    instance-id: 博客服务-4004
  • UserController.java
/**
 * @author 吕梁山
 * @date 2019/7/19
 */
@RestController
@RequestMapping("/api/blog")
public class BlogController {

    @RequestMapping(value = "/getBlog", method = RequestMethod.GET)
    public String getBlog(String blogId){
        return "查找博客:" + blogId;
    }

    @RequestMapping(value = "/addBlog", method = RequestMethod.POST)
    public String addBlog(String blogName){
        return "新增博客标题:" + blogName;
    }

    @RequestMapping(value = "/updateBlog", method = RequestMethod.PUT)
    public String updateBlog(String blogName){
        return "更新博客标题:" + blogName;
    }

    @RequestMapping(value = "/deleteBlog", method = RequestMethod.DELETE)
    public String deleteBlog(String blogId){
        return "删除博客:" + blogId;
    }
}

2.3. 注册中心

  • application.yml
server:
  port: 3001
eureka:
  instance:
    # 在服务中心显示的名称
    hostname: eureka-server1
  client:
    # false表示不向注册中心注册自己
    register-with-eureka: false
    # false声明是服务端
    fetch-registry: false
    service-url:
        # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
        defaultZone: http://eureka-server1:3001/eureka/
  server:
    enable-self-preservation: false

3. 创建Zuul服务

  • 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 http://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.6.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.pikaqiu.springcloud</groupId>
        <artifactId>zuul</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>SpringCloud-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.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>
  • SpringCloudZuulApplication.java
    @SpringCloudApplication
    @EnableEurekaClient
    @EnableZuulProxy
    public class SpringCloudZuulApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringCloudZuulApplication.class, args);
        }
        
    }
  • application.yml
    server:
      port: 5005
    spring:
      application:
        name: zuul
    eureka:
      client:
        service-url:
            defaultZone: http://eureka-server1:3001/eureka/
      instance:
        instance-id: Zuul路由服务-5005

启动后查看注册中心

《Spring Cloud学习(五):Zuul服务网关》

 

4. 配置Zuul

配置zuul之前先看用户服务和博客服务能否正常访问,分别访问

http://127.0.0.1:4003/api/user/getUser?userId=1
http://127.0.0.1:4004/api/blog/getBlog?blogId=1

可以看到分别返回

查找用户:1
查找博客:1

Zuul的路由转发配置主要在配置文件去配置

zuul:
  routes:
    service-user:
        path: /api/user/**
        serviceId: service-user
        stripPrefix: false
    service-blog:
        path: /api/blog/**
        serviceId: service-blog
        stripPrefix: false
  • path:匹配的规则
  • serviceId:要转发至的服务id
  • stripPrefix: 是否去掉前缀(为true的时候访问/api/user/getUser在转发到用户服务时会去除path配置的/api/user/变成/getUser)

配置好Zuul后,分别访问

http://127.0.0.1:5005/api/user/getUser?userId=1
http://127.0.0.1:5005/api/blog/getBlog?blogId=1

可以看到分别返回

查找用户:1
查找博客:1

这样就说明Zuul的路由转发成功了。

5. 请求过滤

请求过滤即将那些不符合要求的请求过滤掉,这里演示就只以请求是否带token这个参数为例

5.1. 新建一个过滤器(新建一个类去继承ZuulFilter类)

/**
 * @author 吕梁山
 * @date 2019/7/19
 */
@Component
public class MyZuulFilter extends ZuulFilter {

    Logger logger = LoggerFactory.getLogger(MyZuulFilter.class);

    /**
     * pre:路由之前
     * routing:路由之时
     * post: 路由之后
     * error:发送错误调用
     */
    @Override
    public String filterType() {
        logger.info("准备进入路由-------------->");
        return "pre";
    }

    /**
     * 过滤的顺序
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 这里可以写逻辑判断,是否要过滤,true为永远过滤。
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤器的具体逻辑
     */
    @Override
    public Object run() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        HttpServletResponse response = currentContext.getResponse();
        response.setHeader("Content-Type","text/html;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        String token = request.getParameter("token");
        if(StringHelper.isNullOrEmptyString(token)){
            logger.warn("token is empty");
            currentContext.setSendZuulResponse(false);
            currentContext.setResponseStatusCode(401);
            currentContext.setResponseBody("token为空");
            return null;
        }
        return null;
    }
}

自定义过滤器的实现,继承ZuulFilter后需要重写实现下面四个方法

  • filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型
    • pre:可以在请求被路由之前调用
    • routing:在路由请求时候被调用
    • post:在routing和error过滤器之后被调用
    • error:处理请求时发生错误时被调用
  • filterOrder:通过int值来定义过滤器的执行顺序
  • shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效。
  • run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,当然我们也可以进一步优化我们的返回,比如,通过ctx.setResponseBody(body)对返回body内容进行编辑等。

5.2. 进行测试

  • 首先不带token进行一次访问
http://127.0.0.1:5005/api/blog/getBlog?blogId=1

返回结果为

token为空
  • 然后带上token进行访问
http://127.0.0.1:5005/api/blog/getBlog?blogId=1&token=test

返回结果为

查找博客:1

至此Zuul过滤器也成功了

 欢迎留言:http://pikaqiu.vip/article/2370.html
示例代码:https://github.com/Liangshan1994/SpringCloud

相关文章