Spring Cloud Gateway整合Nacos实现服务路由及集群负载均衡

目录

https://www.bilibili.com/read/cv17234910/

一、序言

我们都知道 Spring Cloud Gateway 是一个基于 Spring BootSpring WebFluxProject Reactor 构建的高性能网关,旨在提供简单、高效的API路由。

Spring Cloud Gateway基于 Netty 运行,因此在传统Servlet容器中或者打成war包是不能正常运行的。

二、代码示例

这里我们注册中心选型的是 Nacos ,如果还没有安装Nacos,请参考:Nacos快速安装部署。

1、父工程spring-cloud-gateway-learning

<pre class="prettyprint hljs dust"><modules>
        <module>spring-cloud-api-gateway</module>
        <module>spring-cloud-user-service</module>
        <module>spring-cloud-message-service</module>
</modules>

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.boot.version>2.3.7.RELEASE</spring.boot.version>
    <spring.cloud.version>Hoxton.SR12</spring.cloud.version>
    <spring.cloud.alibaba.version>2.2.6.RELEASE</spring.cloud.alibaba.version>
    <commons.lang3.version>3.12.0</commons.lang3.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>${commons.lang3.version}</version>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>${spring.boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring.cloud.alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement></pre>

备注:具体Spring Cloud各版本说明请参考 Spring Cloud Alibaba版本说明 。

2、子工程spring-cloud-api-gateway

(1) pom.xml

<pre class="prettyprint hljs xml"><parent>
   <groupId>com.universe</groupId>
   <artifactId>spring-cloud-gateway-learning</artifactId>
   <version>1.0-SNAPSHOT</version>
  </parent>

<dependencies>
   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-gateway</artifactId>
   </dependency>
   <dependency>
       <groupId>com.alibaba.cloud</groupId>
       <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
   </dependency>
 </dependencies></pre>

(2) 配置文件和代码示例bootstrap.yml

<pre class="prettyprint hljs less">spring:
application:
name: api-gateway
server:
port: 9000</pre>

application.yml

<pre class="prettyprint hljs yaml">spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**
- id: message-service
uri: lb://message-service
predicates:
- Path=/message/**
nacos:
discovery:
server-addr: localhost:8848</pre>

如果URI以lb开头,比如如上配置中的 lb://user-serviceSpring Cloud Gateway 会用 ReactiveLoadBalancerClientFilter 解析服务名为 user-service 的实例对应的实际host和端口,并做集群负载均衡。

这项功能通过全局过滤器 ReactiveLoadBalancerClientFilter 实现,官网描述如下:

img

RouteRecordGlobalFilter

<pre class="prettyprint hljs java">@Slf4j
  @Component
  public class  RouteRecordGlobalFilter  implements  GlobalFilter,  Ordered  {

@Override
public  Mono<Void>  filter(ServerWebExchange exchange,  GatewayFilterChain chain)  {
// RouteToRequestUrlFilter会把实际路由的URL通过该属性保存
URI proxyRequestUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
long start = System.currentTimeMillis();

return chain.filter(exchange).then(Mono.fromRunnable(() -> {
    long end = System.currentTimeMillis();
    log.info("实际调用地址为:{},调用耗时为:{}ms", proxyRequestUri, (end - start));
}));
}

@Override
public  int  getOrder()  {
// 优先级设为最低,先让RouteToRequestUrlFilter先调用
return Ordered.LOWEST_PRECEDENCE;
}
  }</pre>

RouteRecordGlobalFilter这个全局过滤器我们主要用来记录路由后的实际代理地址,以及调用耗时。

我们看下 RouteToRequestUrlFilter 的描述会发现实际路由地址会通过 ServerWebExchange 中名为 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的属性保存。

img

关于 RouteToRequestUrlFilter 的部分源码如下:

<pre class="prettyprint hljs kotlin">@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
    if (route == null) {
        return chain.filter(exchange);
    }
    log.trace("RouteToRequestUrlFilter start");
    URI uri = exchange.getRequest().getURI();
    boolean encoded = containsEncodedParts(uri);
    URI routeUri = route.getUri();

    if (hasAnotherScheme(routeUri)) {
        // this is a special url, save scheme to special attribute
        // replace routeUri with schemeSpecificPart
        exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR,
                routeUri.getScheme());
        routeUri = URI.create(routeUri.getSchemeSpecificPart());
    }

    if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {
        // Load balanced URIs should always have a host. If the host is null it is
        // most
        // likely because the host name was invalid (for example included an
        // underscore)
        throw new IllegalStateException("Invalid host: " + routeUri.toString());
    }

    URI mergedUrl = UriComponentsBuilder.fromUri(uri)
            // .uri(routeUri)
            .scheme(routeUri.getScheme()).host(routeUri.getHost())
            .port(routeUri.getPort()).build(encoded).toUri();
    exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);
    return chain.filter(exchange);
}</pre>

备注:更多关于全局过滤器的介绍请参考 Spring Cloud Gateway全局过滤器 。

3、子工程spring-cloud-user-service

(1) pom.xml

<pre class="prettyprint hljs xml"><parent>
    <groupId>com.universe</groupId>
    <artifactId>spring-cloud-gateway-learning</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies></pre>

(2) 配置文件bootstrap.yml

<pre class="prettyprint hljs yaml">spring:
application:
name: user-service
server:
servlet:
context-path: /user

---
spring:
profiles: user-service-master
server:
port: 9091

---
spring:
profiles: user-service-slave
server:
port: 9092</pre>

application.yml

<pre class="prettyprint hljs less">spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848</pre>

UserController

@RestController
 public class UserController {


@GetMapping("/info")
 public Map<String, Object> getUserInfo() {

Random random = new Random();
int waitTime = random.nextInt(1000);
LockSupport.parkNanos(1000 * 1000 * waitTime);

Map<String, Object> result = new HashMap<>();
result.put("name", "Nick");
result.put("age", 25);
return result;
}
}

4、子工程spring-cloud-message-service

(1) pom.xml

<pre class="prettyprint hljs xml"><parent>
    <groupId>com.universe</groupId>
    <artifactId>spring-cloud-gateway-learning</artifactId>
    <version>1.0-SNAPSHOT</version>
   </parent>

  <dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
   </dependencies></pre>

(2) 配置文件和代码示例

botstrap.yml

spring:
application:
name: message-service
server:
servlet:
context-path: /message

---
spring:
profiles: message-service-master
server:
port: 9093

---
spring:
profiles: message-service-slave
server:
port: 9094

application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

MessageController

@RestController
public class MessageController {


    @GetMapping("/info")
    public Map<String, Object> getMessageInfo() {

    Random random = new Random();
        int waitTime = random.nextInt(1000);
        LockSupport.parkNanos(1000 * 1000 * waitTime);

        Map<String, Object> result = new HashMap<>();
        result.put("id", 1);
        result.put("title", "我爱中国");
        return result;
    }
}

三、测试结果

分别启动api-gateway、指定概要文件启动两个user-service服务实例、和两个message-service服务实例,查看Nacos控制台。

img

可以看到,api-gateway启动了一个服务实例,user-service和message-service都启动了两个服务实例。

备注:IDEA运行时指定 Active Profiles 即可。

1、集群负载均衡测试

连续访问 http://localhost:9000/user/info ,可以看到user-service集群服务实例被轮询调用。

img

2、服务路由测试

分别访问 http://localhost:9000/user/infohttp://localhost:9000/message/info ,我们可以看到基于路径匹配的服务路由分发是成功的。

img

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值