SpringCloud Zuul网关实现路由和自动发现路由

我们做分布式系统,为了不暴露具体的服务,以及实现各种统一处理,常常使用网关来管理接口。SpringCloud分布式系统中常用zuul来实现网关功能。zuul最基本的功能,就是把所有的接口都收到自己这里,按照规则和负载均衡的配置分发。

zuul实现路由最常用的方法是在属性文件properties或者 yml中进行配置。

我们首先创建几个必要的服务:

1. eureka注册中心

按照正常方式创建,无需多余内容。application.yml配置:

server:
  port: 8101
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
spring:
  application:
    name: eureka

2. 两个微服务user和 student

依赖


      org.springframework.cloud
      spring-cloud-starter-netflix-eureka-client

application.yml

server:
  port: 8107
spring:
  application:
    name: user
eureka:
  instance:
    hostname: studio.chris.com
  client:
    service-url:
      defaultZone: http://${eureka.instance.hostname}:8101/eureka

server:
  port: 8108
spring:
  application:
    name: student
eureka:
  instance:
    hostname: studio.chris.com
  client:
    service-url:
      defaultZone: http://${eureka.instance.hostname}:8101/eureka

并没有什么差别,只是服务名不同而已。这里的服务名设计的比较简单,主要为方便进行服务发现路由的时候使用简洁。要注意避免歧义,也就是说解析的时候不要出现歧义,就是一个微服务名称不能是另一个微服务名称头部的子字符串,否则可能在某些情况下会导致路由错误。比如stu和student就不合适。

在每个微服务下面写个接口,如

@RestController
@RequestMapping("/api")
public class StudentController {
    @Value("${server.port}")
    String port;

    @GetMapping("/test")
    public String test() {
        return "Called test api from student module.port: " + port;
    }
}

我们用port做标记来区别不同的实例,以验证zuul给我们做了负载均衡。

3. zuul

pom依赖


    org.springframework.cloud
    spring-cloud-starter-netflix-eureka-client


    org.springframework.cloud
    spring-cloud-starter-netflix-zuul

application.yml

server:
  port: 8080
spring:
  application:
    name: zuul
eureka:
  instance:
    hostname: studio.chris.com
  client:
    service-url:
      defaultZone: http://${eureka.instance.hostname}:8101/eureka

可以看出yml的配置基本上都是一样的,无非区别了个端口和服务名称。而那个端口,在我们进行多实例部署的时候又是需要更改的。

启动类注解

@EnableEurekaClient
@EnableDiscoveryClient
@EnableZuulProxy

好了,所有服务我们已经构建好,现在需要配置zuul

zuul路由的最常见方式实在application.yml文件中配置

添加:

zuul:
  routes:
    user-manage:
      path: /user/**
      serviceId: user
    student-manage:
      path: /student/**
      serviceId: student

user-manage和student-manage是两个路由,这个名称只要区别开就好了。serviceId和path是配对的,比如user-manage的配置,凡是/user开头的接口调用都会被分发到在注册中心注册为user的服务中去均衡调用。

如:请求localhost:8080/user/api/test,结果会出现类似以下这样的结果

Called test api from user module.port: 8107

这是的zuul也算是配置完成了,我们把各个服务启动起来测试。注册中心和zuul启动起来,其他两个微服务要启动多个实例,我们在Idea中打开多个终端,运行以下命令:

mvn spring-boot:run -e -Dmaven.test.skip=true -Dserver.port=8211

最后的端口号每次运行的时候都要修改。

我们在浏览器中打开注册中心的监控页面:

http://localhost:8101/

可以看到我们启动起来的微服务,其中user和student是多实例的。

我们通过网关8080调用接口,会发现返回的字符串端口号在变化,这就是因为负载均衡分发到了不同的实例。

其实这种路由还可以这样配置

zuul:
    user: /user/**
    student: /student/**

这种键值对的方式更简洁,key为注册服务名称,value为匹配的路径表达式。

注意:这种方法最常见,但是也很笨拙,如果我们增加了一个微服务,都要在yml文件中配置,也就要重启网关。但是一般我们不会这样做,网关不要经常重启,配置文件也不需要经常修改。我们把yml中关于zuul的配置全部删除干净。

接着,我们在pom中依赖一个包


    org.springframework.cloud
    spring-cloud-starter-eureka
    1.3.5.RELEASE

好了,这就完了,既不用配置,也不用写代码,直接启动就好了。就算新添加一个微服务,也就是照常从网关调用就好了。zuul会根据注册中心中的发现的服务来自动进行路由。我们的zuul就再也不用关了。

最后需要补充的一个点,网关就是个关,大鬼小鬼都要走这里。它的作用除了路由、负载均衡之外,还有统一处理。由于网关在接口方法调用之前,所以可以做一些路由前的预处理。比如,我们分布式架构中最重视权限,一般的权限验证都是在微服务中根据自己的需求去做验证逻辑,但是架构层面统一要求的逻辑可以在网关处理,以节省资源。比如,我们要求所有的网络请求都要携带身份验证信息,没有就不允许进行任何接口调用,我们就可以在zuul过滤器中来实现。

实现方式就是写一个过滤器,来继承ZuulFilter

package com.chris.zuul;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by Chris Chan
 * 2020/3/14 12:52
 * Use for:
 * Explain:
 */
@Component
@WebFilter(urlPatterns = "/*")
public class ChrisZuulFilter 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 context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        HttpServletResponse response = context.getResponse();

        //授权过滤 不带身份证不让进场 立即退出
        String token = request.getHeader("Authorization");
        if (StringUtils.isEmpty(token)) {
            try {
                response.sendError(401, "Authorization is empty");
            } catch (Exception e) {

            }
            return null;
        }

        return null;
    }
}

这样我们请求的时候就一定要带一个Basic或者Bearer类似的Authorization头信息,不带的不再进行路由分发。至于这些头信息对不对,可以在这里进行检测,也可以放行到微服务中自己进行处理。

SpringCloud Zuul网关实现路由和自动发现路由_第1张图片

 

你可能感兴趣的:(Java,架构,SpringCloud)