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 practical combat series. Through the previous article, we learned that the core of Spring Cloud Gateway is routing configuration, and then a route is configured in the local application.yml. However, this way of modifying the local configuration file is inflexible and may not meet the flexible business requirements. 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 code writing configuration;
- In addition, there is a more flexible configuration method: dynamic agent. Because it involves a lot of code, it will be introduced in detail in a separate article
Source download
- The complete source code in this actual combat can be downloaded from GitHub. The address and link information are shown in the table below( https://github.com/zq2599/blog_demos):
name | link | remarks |
---|---|---|
Project Home | https://github.com/zq2599/blog_demos | The project is on the GitHub home page |
git warehouse address (https) | https://github.com/zq2599/blog_demos.git | The warehouse address of the source code of the project, https protocol |
git warehouse address (ssh) | git@github.com:zq2599/blog_demos.git | The project source code warehouse address, ssh protocol |
- There are multiple folders in this git project. The source code of this article is in the spring cloud tutorials folder, as shown in the red box below:
preparation
-
A little more preparation needs to be done before the official start. In the whole Spring Cloud Gateway actual combat series, all requests will finally be routed to the provider Hello web. At present, the service has only one web interface / hello/str. now let's add another one to it, which will be used in the 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 string type * @return */ @GetMapping("/str") public String helloStr() { return Constants.LB_PREFIX + ", " + dateStr(); } }
- The Constants.LB_PREFIX in the above code comes from the subproject common:
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 is started
-
After the provider Hello project is started successfully, go to see nacos and confirm that it has been registered:
- Ready to start the actual combat
The destination address supports the service name (replacing the previous IP + port)
- Let's start with the simplest. Let's look at the routing configuration above, as shown in the red box below. The target address is IP + port:
-
After playing Spring Cloud, you naturally see the problem: no registration is found. Indeed, it is inappropriate to write the address and port in the configuration file. Let's solve this problem first;
-
A new sub project named gateway by loadbalance is added. The dependencies in pom.xml are as follows. It can be seen that the focus is spring cloud starter loadbalance:
<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> <!-- Routing policy usage lb The way is that this dependency must have --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> <!--nacos:Registration Center--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
-
The code of the startup class is omitted (as before)
-
The configuration information is as follows. The key point is that the uri value lb: / / provider Hello is prefixed with lb:, and the following provider Hello is the service name registered in nacos:
server: #Service port port: 8085 spring: application: name: gateway-by-loadbalance cloud: nacos: # Configuration of registry 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() // Verification status .expectStatus().isOk() // Verify the result. Note that the result is in string format .expectBody(String.class).consumeWith(result -> assertTrue(result.getResponseBody().contains(Constants.LB_PREFIX))); } }
- Run the unit test. It can be seen that the above configuration can accurately find the service through the prefix lb:
Support configuration on nacos
-
There is a problem in 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 of remote configuration. After the application is started, you can get your own configuration information from nacos. Let's try
-
A new subproject named gateway Nacos config is added. The dependencies in pom.xml are as follows. Please note the Chinese notes inside, indicating 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> <!-- use bootstrap.yml This dependence must have --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> <!-- Routing policy usage lb The way is that this dependency must have --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> <!--nacos:Configuration center--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!--nacos:Registration Center--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
- The local configuration file bootstrap.yml is very simple, which is the address and remote configuration information of nacos:
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, as shown in the red box below:
- Add a configuration. The points to note are as follows (the text of the configuration information will be given later for easy replication):
- The complete configuration information in the figure above 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, which are no different from the previous ones:
@Test void testHelloPredicates() { webClient.get() .uri("/hello/str") .accept(MediaType.APPLICATION_JSON) .exchange() // Verification status .expectStatus().isOk() // Verify the result. Note that the result is in string format .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() // Verification status .expectStatus().isOk() // Verify the result. Note that the result is in string format .expectBody(String.class).consumeWith(result -> assertTrue(result.getResponseBody().contains(Constants.LB_PREFIX))); }
- Run the unit test class and pass the test, which proves that the configuration file obtained from nacos is successful:
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 the routing. You can write your own code to configure it, which is more flexible
-
Add a new sub project named gateway by code, and its pom.xml file can refer to the previous project
-
Next, the focus of this example is to add a RouteLocator type bean in the configuration class. You can add a route 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( // The first parameter is the unique identity of the route "path_route_lb", // The second parameter is a lambda implementation, // The matching conditions are set to match according to the request path and the forwarding address, // Note that lb: / / indicates that this is a service name, which should be from r -> r.path("/lbtest/**").uri("lb://provider-hello") ) .build(); } }
- Only one route is configured for the above code, and the other is in the configuration file, so you can verify whether the code and the configuration file can take effect at the same time:
server: #Service port port: 8084 spring: application: name: gateway-by-code cloud: nacos: discovery: # nacos service address 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 as like as two peas in the previous project, and it does not occupy space. It is still the two test method testHelloPredicates and testLoadBalance.
-
The execution unit test can be passed successfully, which proves that there is no problem with the code configuration Routing:
- So far, we have tried 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 we can give you some reference
Defects and Solutions
- Although there are many configuration methods mentioned above, there is a common problem: after the configuration changes, the Gateway application needs to be restarted to take effect, which is difficult to accept in the continuous production environment
- In order to make the latest routing configuration take effect without restarting the Gateway application, let's explore how to implement dynamic routing in the next article
You're not alone. Xinchen's original accompanies you all the way
- Java series
- Spring collection
- Docker series
- kubernetes series
- Database + middleware series
- DevOps series
Welcome to the official account: programmer Xin Chen
Wechat search "programmer Xinchen", I'm Xinchen, looking forward to traveling with you in the Java World
https://github.com/zq2599/blog_demos