Spring Boot integrates Nacos with Gateway to implement front-end and back-end decoupled microservices
The architecture chooses nacos as the service registration and GateWay as the gateway, both of which are ali. That nacos we all know can be used as a configuration center and can be dynamic, then to do a dynamic route I think there is still some necessity, not to add routes every time to redeploy the service.
Spring Boot Integration Gateway and Alibaba Microservices Integration Maven are configured as follows:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bins.springboot.java</groupId>
<artifactId>gongfu</artifactId>
<version>0.0.1</version>
<name>gongfu</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--gateway-->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<!--dicovery-->
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependencies>
<dependencyManagement>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.RELEASE</version>
</dependencyManagement>
<build>
<plugins>
<plugin>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1. Code integration
@Data
@Component
@ConfigurationProperties (prefix = "broad.dynamic.route") //yml configuration information, given below
@ConditionalOnBean(DynamicRouteConfiguration.class)
public class DynamicRouteProperties {
/**
* nacos configuration management dataId
*/
private String dataId;
* Nacos configuration management group
private String group;
* Nacos service address
private String ipAddr;
* Flags that enable dynamic routing, off by default
private boolean enabled = false;
}
DynamicRouteConfiguration:
@Configuration
@ConditionalOnProperty(name = "broad.dynamic.route.enabled", matchIfMissing = true)
public class DynamicRouteConfiguration implements ApplicationEventPublisherAware {
Logger log= LoggerFactory.getLogger(DynamicRouteConfiguration.class);
Namespace name
private final static String NAMESPACE="8359a8d9-03ab-4574-9627-9456351c0c01";
Nacos account password
private final static String USERNAME_PASSWORD="nacos";
Intercept characters
private final static String REPLACE="routes:";
private DynamicRouteProperties bean;
private RouteDefinitionWriter writer;
private ApplicationEventPublisher publisher;
public DynamicRouteConfiguration(DynamicRouteProperties bean, RouteDefinitionWriter writer, ApplicationEventPublisher publisher) {
this.bean = bean;
this.writer = writer;
this.publisher = publisher;
}
@PostConstruct
private void init() {
Assert.notNull(bean.getDataId(), "broad.dynamic.route.dataId null异常");
Assert.notNull(bean.getGroup(), "broad.dynamic.route.group is null异常");
Assert.notNull(bean.getIpAddr(), "broad.dynamic.route.ipAddr is null异常");
dynamicRouteByNacosListener();
private void dynamicRouteByNacosListener() {
try {
Properties properties = new Properties();
properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE);
properties.put(PropertyKeyConst.SERVER_ADDR, bean.getIpAddr());
properties.put("username",USERNAME_PASSWORD);
properties.put("password",USERNAME_PASSWORD);
ConfigService configService = NacosFactory.createConfigService(properties);
String content = configService.getConfigAndSignListener(
bean.getDataId(),
bean.getGroup(),
5000,
new AbstractListener() {
@Override
public void receiveConfigInfo(String configInfo) {
updateConfig(configInfo);
});
updateConfig(content);
} catch (NacosException e) {
log.error("nacos get dynamic routing configuration and listening exceptions", e);
private void updateConfig(String content) {
String contentReally=replaceContext(content);
log.info ("nacos dynamic route update: {}", contentReally);
getRouteDefinitions(contentReally).forEach(routeDefinition -> {
log.info ("Dynamic Routing Configuration: {}", routeDefinition);
writer.delete(Mono.just(routeDefinition.getId()));
writer.save(Mono.just(routeDefinition)).subscribe();
publisher.publishEvent(new RefreshRoutesEvent(this));
} catch (Exception e) {
log.error("Update dynamic routing configuration exception: ", e);
* Because the route configured in nacos carries a spring prefix, yaml parsing fails, so interception is required
* @return
private String replaceContext(String content){
String contentDelete=content.replaceAll("\r|\n", "").substring(0, content.indexOf(REPLACE)+1);
String contentReally=content.substring(contentDelete.length()+7, content.length());
return contentReally;
* yaml conversion
* @param content
private List<RouteDefinition> getRouteDefinitions(String content) {
return JSON.parseArray(JSON.toJSONString(
YamlHelper.getInstance().load(content)
), RouteDefinition.class);
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
Second, the configuration file parameters
server:
port: 8085
spring:
application:
name:gongfu
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: apiconsumer ##路由 ID (not gateway ID) and remains unique
#目标服务地址 (plus lb matches based on registry service name)
uri:lb://provider1
predicates:
# #路由条件, Predicate accepts an input parameter and returns a Boolean result. The interface contains several default methods to combine Predicate into other complex logic (e.g., and, or, non)
- Path=/provider/**
#必须加上StripPrefix = 1, otherwise you will bring a provideder when you access the service
filters:
- StripPrefix=1
locator:
Enabled:true #表明gateway开启服务注册和发现的功能, and spring cloud gateway automatically creates a router for each service based on service discovery, which will forward the request path that begins with the service name to the corresponding service
lower-case-service-id: true #是将请求路径上的服务名配置为小写 (because when the service is registered, the service name is changed to uppercase when it registers with the registry.)
Third, the start-up class
The startup class increases the @EnableDiscoverClient
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class GongfuApplication {
public static void main(String[] args) {
SpringApplication.run(GongfuApplication.class, args);
Fourth, the separation of front and back end solves cross-domain problems
(1) Use nginx reverse proxy to solve cross-domain problems
All requests go through nginx, leaving nginx to distinguish whether the request is sent to the frontend or the gateway. Requests sent to the gateway that are forwarded by the gateway to the appropriate microservice.
(2) Add response headers
Because all requests will go through the gateway gateway, during the request response process, the gateway gateway adds response headers to the request to solve the cross-domain problem.
public class GongfuCorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter() {
UrlBasedCorsConfigurationSource ubcs = new UrlBasedCorsConfigurationSource();
Cross-domains are configured for any path (/**).
CorsConfiguration corsConfiguration = new CorsConfiguration();
Configure cross-domain
corsConfiguration.addAllowedHeader("*"); // Access-Control-Expose-Headers
corsConfiguration.addAllowedMethod("*"); // Access-Control-Allow-Methods
corsConfiguration.addAllowedOriginPattern("*"); // Access-Control-Allow-origin
corsConfiguration.setAllowCredentials(true); // Access-Control-Allow-Credentials
ubcs.registerCorsConfiguration("/**", corsConfiguration);
return new CorsWebFilter(ubcs);
This solves the problem in the separation of the front and back ends of Gateway+Nacos.