Spring Boot整合Nacos與Gateway實作前後端分離微服務
架構選擇nacos作為服務注冊,GateWay作為網關兩者都是ali。那nacos咱們都知道可作為配置中心來使用且可動态,那做一個動态路由我覺得還是有些必要性的,不至于每次增加路由都要重新部署服務。
Spring Boot整合Gateway與阿裡系微服務內建Maven配置如下:
<?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>
<!--nacos 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>
一、代碼整合
@Data
@Component
@ConfigurationProperties(prefix = "broad.dynamic.route")//yml配置資訊,下文給出
@ConditionalOnBean(DynamicRouteConfiguration.class)
public class DynamicRouteProperties {
/**
* nacos 配置管理 dataId
*/
private String dataId;
* nacos 配置管理 group
private String group;
* nacos 服務位址
private String ipAddr;
* 啟動動态路由的标志,預設關閉
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名稱
private final static String NAMESPACE="8359a8d9-03ab-4574-9627-9456351c0c01";
//nacos賬号密碼
private final static String USERNAME_PASSWORD="nacos";
//截取字元
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 擷取動态路由配置和監聽異常", e);
private void updateConfig(String content) {
String contentReally=replaceContext(content);
log.info("nacos 動态路由更新: {}", contentReally);
getRouteDefinitions(contentReally).forEach(routeDefinition -> {
log.info("動态路由配置: {}", routeDefinition);
writer.delete(Mono.just(routeDefinition.getId()));
writer.save(Mono.just(routeDefinition)).subscribe();
publisher.publishEvent(new RefreshRoutesEvent(this));
} catch (Exception e) {
log.error("更新動态路由配置異常: ", e);
* 因在nacos配置路由攜帶 spring字首,會導緻yaml解析失敗,是以需截取
* @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轉換
* @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;
二、配置檔案參數
server:
port: 8085
spring:
application:
name:gongfu
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: apiconsumer ##路由 ID(不是網關ID),保持唯一
#目标服務位址 (加 lb 根據注冊中心服務名比對)
uri: lb://provider1
predicates:
# #路由條件,Predicate 接受一個輸入參數,傳回一個布爾值結果。該接口包含多種預設方法來将 Predicate 組合成其他複雜的邏輯(比如:與,或,非)
- Path=/provider/**
#必須加上StripPrefix=1,否則通路服務時會帶上provider
filters:
- StripPrefix=1
locator:
enabled: true #表明gateway開啟服務注冊和發現的功能,并且spring cloud gateway自動根據服務發現為每一個服務建立了一個router,這個router将以服務名開頭的請求路徑轉發到對應的服務
lower-case-service-id: true #是将請求路徑上的服務名配置為小寫(因為服務注冊的時候,向注冊中心注冊時将服務名轉成大寫的了
三、啟動類
啟動類增加@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);
四、前後端分離解決跨域問題
(1)利用nginx反向代理,解決跨域問題
所有的請求都通過nginx,讓 nginx 來差別請求是發送到前端還是網關。發送到網關的請求,讓網關轉發到相應的微服務。
(2)添加響應頭
因為所有的請求都會經過 gateway 網關,在請求響應的過程中,經 gateway 網關給請求添加響應頭,解決跨域問題。
public class GongfuCorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter() {
UrlBasedCorsConfigurationSource ubcs = new UrlBasedCorsConfigurationSource();
// 對于任意路徑(/**)都要配置跨域
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 配置跨域
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);
這樣就解決了Gateway+Nacos前後端分離中的問題了。