Welcome to my GitHub

https://github.com/zq2599/blog_demos

Content: Classification and summary of all original articles and supporting source code, involving Java, Docker, Kubernetes, DevOPS, etc.;

Overview of this article

  • This article is the second in the "Spring Cloud Gateway Actual Combat" series. Through the previous article, we learned that the core of Spring Cloud Gateway is routing configuration, and then configure a route in the local application.yml, but this way of modifying the local configuration file is lacking Flexibility may not be able to meet flexible and changeable business needs. Therefore, the purpose of this article is to find out other configuration methods other than local configuration to meet various actual needs;
  • In general, the following three methods are commonly used:
  • The target address supports the service name (replacing the previous IP+port);
  • Support configuration on nacos;
  • Support the configuration of writing code;
  • There is also a more flexible configuration method: <font color="blue">dynamic proxy</font>, because a lot of code is involved, a separate article will be introduced in detail

Source download

nameLinkRemark
Project homepagehttps://github.com/zq2599/blog_demosThe project's homepage on GitHub
git warehouse address (https)https://github.com/zq2599/blog_demos.gitThe warehouse address of the source code of the project, https protocol
git warehouse address (ssh)git@github.com:zq2599/blog_demos.gitThe warehouse address of the source code of the project, ssh protocol
  • There are multiple folders in this git project. The source code of this article is under the <font color="blue">spring-cloud-tutorials</font> folder, as shown in the red box below:

在这里插入图片描述

Ready to work

  • Before the official start, you need to do a little more preparation work. In the entire "Spring Cloud Gateway Actual Combat" series, all requests will eventually be routed to the web <font color="blue">provider-hello</font>. The service currently only has A web interface <font color="blue">/hello/str</font>, now let’s add another to it, which will be used in actual combat later
  • The newly added web interface comes from LBTest.java, which is very simple:
package com.bolingcavalry.provider.controller;

import com.bolingcavalry.common.Constants;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;

@RestController
@RequestMapping("/lbtest")
public class LBTest {

    private String dateStr(){
        return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
    }

    /**
     * 返回字符串类型
     * @return
     */
    @GetMapping("/str")
    public String helloStr() {
        return Constants.LB_PREFIX + ", " + dateStr();
    }
}
  • The <font color="blue">Constants.LB_PREFIX</font> in the above code comes from the sub-project <font color="red">common</font>:
package com.bolingcavalry.common;

public interface Constants {
    String HELLO_PREFIX = "Hello World";
    String LB_PREFIX = "Load balance";
}
  • After writing the code, make sure that nacos has been started
  • After starting the <font color="blue">provider-hello</font> project, go to nacos after the startup is successful, and confirm that it has been registered:

在这里插入图片描述

  • Ready, you can start the actual combat

The target address supports the service name (replacing the previous IP+port)

  • Let's start with the simplest, first look at the routing configuration in the previous article, as shown in the red box in the figure below, the target address is IP+port:

在这里插入图片描述

  • If you have played Spring Cloud, you can naturally see the problem: no registration is found, indeed, it is not appropriate to write the address and port in the configuration file in this way, let's solve this problem first;
  • Add a new sub-project named <font color="blue">gateway-by-loadbalance</font>. The dependencies in its pom.xml are as follows. It can be seen that the focus is on <font color="red"> spring-cloud -starter-loadbalancer </font>:
<dependencies>
        <dependency>
            <groupId>com.bolingcavalry</groupId>
            <artifactId>common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 路由策略使用lb的方式是,这个依赖一定要有 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <!--nacos:注册中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
  • The code for the startup class is omitted (the same as the previous one)
  • The configuration information is as follows, focusing on the value of uri<font color="red"> lb://provider-hello </font>, using the prefix <font color="blue">lb:</font>, followed by The <font color="blue">provider-hello</font> is the service name registered in nacos:
server:
  #服务端口
  port: 8085
spring:
  application:
    name: gateway-by-loadbalance
  cloud:
    nacos:
      # 注册中心的配置
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: path_route_lb
          uri: lb://provider-hello
          predicates:
          - Path=/lbtest/**
  • Unit test class:
package com.bolingcavalry.gateway;

import com.bolingcavalry.common.Constants;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.junit.jupiter.api.Assertions.assertTrue;

@SpringBootTest
@ExtendWith(SpringExtension.class)
@AutoConfigureWebTestClient
public class HelloTest {

    @Autowired
    private WebTestClient webClient;

    @Test
    void testLoadBalance() {
        webClient.get()
                .uri("/lbtest/str")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                // 验证状态
                .expectStatus().isOk()
                // 验证结果,注意结果是字符串格式
                .expectBody(String.class).consumeWith(result  -> assertTrue(result.getResponseBody().contains(Constants.LB_PREFIX)));
    }
}
  • Run the unit test and pass, it can be seen that the above configuration can accurately find the service through the prefix <font color="blue">lb:</font>:

在这里插入图片描述

Support configuration on nacos

  • There is a problem with writing all configuration information in application.yml: remote configuration is not possible, which is inconvenient in scenarios with a large number of applications. Fortunately, nacos provides the ability to configure remotely. After the application is started, you can get your own from nacos. Configuration information, let's try
  • Add a new sub-project named <font color="blue">gateway-nacos-config</font>. The dependencies in the pom.xml are as follows. Please pay attention to the Chinese comments inside, each specifying the role of each dependency :
<dependencies>
        <dependency>
            <groupId>com.bolingcavalry</groupId>
            <artifactId>common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 使用bootstrap.yml的时候,这个依赖一定要有 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <!-- 路由策略使用lb的方式是,这个依赖一定要有 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <!--nacos:配置中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--nacos:注册中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
  • The local configuration file <font color="blue">bootstrap.yml</font> is very simple, it is the address of nacos and remote configuration information:
spring:
  application:
    name: gateway-nacos-config
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yml
        group: DEFAULT_GROUP
  • Next, add a configuration file to nacos, and operate the red box as shown in the figure below:

在这里插入图片描述

  • To add a configuration, the places to pay attention to are as follows (the text of the configuration information is given later for easy copying):

在这里插入图片描述

  • The complete configuration information in the above figure is as follows:
server:
  port: 8083
spring:
  cloud:
    gateway:
      routes:
        - id: path_route_addr
          uri: http://127.0.0.1:8082
          predicates:
          - Path=/hello/**
        - id: path_route_lb
          uri: lb://provider-hello
          predicates:
          - Path=/lbtest/**
  • The two test methods in the test class are as follows, without any difference from the previous ones:
@Test
    void testHelloPredicates() {
        webClient.get()
                .uri("/hello/str")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                // 验证状态
                .expectStatus().isOk()
                // 验证结果,注意结果是字符串格式
                .expectBody(String.class).consumeWith(result  -> assertTrue(result.getResponseBody().contains(Constants.HELLO_PREFIX)));
    }

    @Test
    void testLoadBalance() {
        webClient.get()
                .uri("/lbtest/str")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                // 验证状态
                .expectStatus().isOk()
                // 验证结果,注意结果是字符串格式
                .expectBody(String.class).consumeWith(result  -> assertTrue(result.getResponseBody().contains(Constants.LB_PREFIX)));
    }
  • Run the unit test class, and the test passes, which proves that the configuration file is successfully obtained from nacos:

在这里插入图片描述

How to write code configuration

  • In the previous examples, the routing information is written in the configuration file. In fact, there is another way: write code to configure routing, and you can write your own code to configure, which is more flexible.
  • Add a new subproject named <font color="blue">gateway-by-code</font>, and its pom.xml file can refer to the previous project
  • The focus of the next example is to add a RouteLocator type bean to the configuration class, and a route can be added through the following code:
package com.bolingcavalry.gateway.cofig;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RouteConfig {

    @Bean
    public RouteLocator customizeRoute(RouteLocatorBuilder builder) {
        return builder
                .routes()
                .route(
                            // 第一个参数是路由的唯一身份
                        "path_route_lb",
                            // 第二个参数是个lambda实现,
                            // 设置了配套条件是按照请求路径匹配,以及转发地址,
                            // 注意lb://表示这是个服务名,要从
                            r -> r.path("/lbtest/**").uri("lb://provider-hello")
                )
                .build();
    }
}
  • The above code only configures one route, and there is one in the configuration file, so that you can verify whether the code and the configuration file can take effect at the same time:
server:
  #服务端口
  port: 8084
spring:
  application:
    name: gateway-by-code
  cloud:
    nacos:
      discovery:
        # nacos服务地址
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: path_route_addr
          uri: http://127.0.0.1:8082
          predicates:
          - Path=/hello/**
  • The test class is exactly the same as the previous project, so it does not take up space. It is still two test methods <font color="blue">testHelloPredicates</font> and <font color="blue">testLoadBalance</font>
  • Executing the unit test can pass smoothly, proving that there is no problem with the code configuration routing:

在这里插入图片描述

  • So far, we have tried the examples of load balancing, nacos configuration, and code configuration. Together, they will bring great convenience to the configuration of the actual living environment. I hope to give you some reference.

Defects and solutions

  • Although there are many configuration methods mentioned above, they have a common problem: every time the configuration changes, the Gateway application needs to be restarted to take effect, which is unacceptable in a production environment with uninterrupted requests.
  • In order to make the latest routing configuration take effect without restarting the Gateway application, let’s explore how <font color="blue">dynamic routing</font> is implemented in the next article.

You are not lonely, Xinchen is with you all the way

  1. Java series
  2. Spring series
  3. Docker series
  4. kubernetes series
  5. database + middleware series
  6. DevOps series

Welcome to pay attention to the public account: programmer Xin Chen

Search "Programmer Xin Chen" on WeChat, I am Xin Chen, and I look forward to traveling the Java world with you...
https://github.com/zq2599/blog_demos

程序员欣宸
147 声望24 粉丝

热爱Java和Docker