16、路由配置

142 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情

16、路由配置

一、路由配置

1、简单路由

Spring Cloud 在 Zuul 的 routing 阶段实现了几个过滤器, 这些过滤器决定如何进行路由工作。其中, 最基本的就是 SimpleHostRoutingFilter,该过滤器运行后,会将 HTTP 请求全部转发到“源服务”( HTTP 服务), 本书将其称为简单路由,本章 7.2 节的例子实际上就是使用了简单路由进行请求转发。以下为简单路由的配置,同时使用了 path 与 url:

#配置简单路由
#配置简单路由
zuul.routes.routeTest.path=/routeTest/163
zuul.routes.routeTest.url=http://www.baidu.com
#配置简单路由 简化配置--只使用url
zuul.routes.routeTest163.url=http://www.baidu.com

为了简化配置,可以省略path,默认情况下使用routeId 作为 path

访问http://localhost:8080/routeTest/163 http://localhost:8080/routeTest163 都可以路由到163网站。

实际上,要触发简单路由,配置的 url 值需要以 http:或者 https:字符串开头。以下的配置不能触发简单路由: zuul.routes.noRoute163.url=www.baidu.com

简单路由的过滤器 SimpleHostRoutingFilter 使用 HttpClient 进行转发,该过滤器会将HttpServletRequest 的相关数据(HTTP 方法、参数、请求头等)转换为 HttpClient 的请求 实例(HttpRequest), 再使用 CloseableHttpClient 进行转发。 在此过程中,为了保证转发的性能,使用了 HttpClient 的连接池功能。涉及连接池,就需要对其进行配置。在使用简单路由时,可以配置以下两项,修改 HttpClient 连接池的属性。

  • zuul.host.maxTotalConnections:目标主机的最大连接数,默认值为200,配置该项,相当于调用了PoolingHttpClientConnectionManager的setMaxTotal方法
  • zuul.host.maxPerRouteConnections:每个主机的初始连接数,默认值为20,配置该项,相当于调用了PoolingHttpClientConnectionManager的setDefaultMaxPerRoute方法
# 简单路由配置HttpClient转发连接池 。目标主机的最大连接数,默认值为200
zuul.host.maxTotalConnections=200
# 简单路由配置HttpClient转发连接池 。每个主机的初始连接数,默认值为20
zuul.host.maxPerRouteConnections=20

浏览SimpleHostRoutingFilter的run() -- 简单路由相当于请求转发

@Override
public Object run() {
    RequestContext context = RequestContext.getCurrentContext();
    HttpServletRequest request = context.getRequest();//获取request
    MultiValueMap<String, String> headers = this.helper
        .buildZuulRequestHeaders(request);
    MultiValueMap<String, String> params = this.helper
        .buildZuulRequestQueryParams(request);
    String verb = getVerb(request);
    InputStream requestEntity = getRequestBody(request);
    if (request.getContentLength() < 0) {
        context.setChunkedRequestBody();
    }

    String uri = this.helper.buildZuulRequestURI(request);
    this.helper.addIgnoredHeaders();

    try {
        //最终调用httpclient进行转发,为了保证转发的性能,会使用httpclient的连接池功能,如果涉及连接池则需要进行配置,上面有所提及
        CloseableHttpResponse response = forward(this.httpClient, verb, uri, request,
                                                 headers, params, requestEntity);
        setResponse(response);
    }
    catch (Exception ex) {
        throw new ZuulRuntimeException(ex);
    }
    return null;
}

2、跳转路由

除了简单路由外,也支持跳转路由。当外部访问网关的A地址时,会跳转到B地址, 处理跳转路由 的过滤器为 SendForwardFilter 。接 下来进行简单测试,为网关项目 ( zuul-gateway )添加一个控制器

//跳转路由 - 测试控制器
@RestController
public class SourceController {

    @RequestMapping(value = "/source/hello/{name}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public String hello(@PathVariable("name") String name) {
        return "Hello " + name;
    }
}

控制器中提供了一个最简单的 hello 服务,用来当作“源服务”,在 application.yml 进行转发配置,配置项如下: 这里只是本地跳转 -- 不通端口的跳转不知道怎么配置,没找到

#跳转路由
zuul.routes.helloRoute.path=/test/**
zuul.routes.helloRoute.url=forward:/source/hello

当外部访问 /test 地址时,将会自动跳转型 /source/hello 地址。 打开浏览器,输入 http: // localhost: 8080/test/anugs ,可以看到浏览器会返回字符串 hello angus ,可见源服务被调用。跳转路由实现较为简单, 实际上是调用了 RequestDispatcher的 forwar 方法进行跳转。

浏览SendForwardFilter的run() -- 相当于重定向

@Override
public Object run() {
    try {
        RequestContext ctx = RequestContext.getCurrentContext();
        String path = (String) ctx.get(FORWARD_TO_KEY);
        //访问A,实际跳转B
        //使用RequestDispatcher
        RequestDispatcher dispatcher = ctx.getRequest().getRequestDispatcher(path);
        if (dispatcher != null) {
            ctx.set(SEND_FORWARD_FILTER_RAN, true);
            if (!ctx.getResponse().isCommitted()) {
                //直接forward跳转,过程中没有其他复杂处理
                //简单路由是由网关响应,跳转路由由转向后的目标响应
                dispatcher.forward(ctx.getRequest(), ctx.getResponse());
                ctx.getResponse().flushBuffer();
            }
        }
    }
    catch (Exception ex) {
        ReflectionUtils.rethrowRuntimeException(ex);
    }
    return null;
}

3、Ribbon 路由

我们己经接触过 Ribbon 路由。当网关作为 Eureka 客户端注册到 Eureka服务器时,可以通过配置 serviceId 将请求转发到集群的服务中。 使用以下配置,可以执行Ribbon 路由过滤器:

#配置 ribbon 路由规则
#zuul.routes.sale.path=/sale/**
#zuul.routes.sale.service-id=invoker-server
#配置 ribbon 路由规则  -- 省略配置
#zuul.routes.invoker-server.path=/sale/**
#配置 ribbon 路由规则
zuul.routes.sale.path=/sale/**
zuul.routes.sale.url=invoker-server

与简单路由类似, serviceId 也可以被省略。当省略时,将会使用 routeld 作为 serviceld 下面的配置片断,效果等同于上面的配置:

#配置 ribbon 路由规则  -- 省略配置
#zuul.routes.invoker-server.path=/sale/**

需要注意的是,如果提供的url配置项不是简单路由格式(不以 http :或 https;开头), 也不是跳转路由格式( forward:开头),那么将会执行 Ribbon 路由过滤器,将url看作 serviceId。 下面的配置片断,效果也等同于前面的配置:

#配置 ribbon 路由规则
zuul.routes.sale.path=/sale/**
zuul.routes.sale.url=invoker-server

4、自定义路由规则

如果上面的路由配置无法满足实际需求,可以考虑使用自定义的路由规则 。实现方式 较为简单,在配置类中创建一个 PatternServiceRouteMapper 即可

@Configuration
public class MyConfig {
    
    @Bean
    public PatternServiceRouteMapper patternServiceRouteMapper() {
        // 通过一个正则表达式来匹配
        return new PatternServiceRouteMapper("(zuul)-(?<module>.+)-(service)",
                "${module}/**");
    }
}

创建了 PatternServiceRouteMapper 实例,构造器的第一个参数为 serviceId 的正则表达 式,第二个参数为路由的 path。 访问 module/** 的请求,将会被路由到 zuul-module-service的微服务

更进一步,以上的路由规则,如果想让一个或多个服务不被路由,可以使用zuul .ignoredServices 属性。 例如在代码清单 7-6 的基础上,想排除 zuul-sale-service、 zuul-book-service 这两个模块,可以配置 zuul.ignoredServices: zuul-sale-service, zuul-bookservice。 zuul.ignoredServices: zuul-sale-service, zuul-bookservice

5、忽略路由

除了 上面提到的 zuul.ignoredServices 配置可以忽略路由外,还可以使用zuul.i gnoredPattens 来设置不进行路由的 URL, 请见以下配置片断:

#配置 ribbon 路由规则
zuul.routes.sale.path=/sale/**
zuul.routes.sale.service-id=invoker-server

#配置忽略路由
zuul.ignored-patterns=/sale/router/3

zuul.ignoredPatterns=/sale/noRoute

访问/sale路径的请求都会被路由到 invoker-server 进行处理 但是 /sale/noRoute 和 /sale/router/3 除外, http://localhost:8080/sale/router/2 就可以正常返回

通配符

详细请见:www.jb51.net/article/127…

image.png