一、服務發現-搭建Nacos服務
1、基本概念
(1)Nacos 是阿里巴巴推出來的一個新開源項目,是一個更易於構建雲原生應用的動態服務發現、配置管理和服務管理平台。Nacos 致力於幫助您發現、配置和管理微服務。Nacos 提供了一組簡單易用的特性集,幫助您快速實現動態服務發現、服務配置、服務元數據及流量管理。Nacos 幫助您更敏捷和容易地構建、交付和管理微服務平台。 Nacos 是構建以“服務”為中心的現代應用架構 (例如微服務范式、雲原生范式) 的服務基礎設施。
(2)常見的注冊中心:
-
Eureka(原生,2.0遇到性能瓶頸,停止維護)
-
Zookeeper(支持,專業的獨立產品。例如:dubbo)
-
Consul(原生,GO語言開發)
-
Nacos
相對於 Spring Cloud Eureka 來說,Nacos 更強大。Nacos = Spring Cloud Eureka + Spring Cloud Config
Nacos 可以與 Spring, Spring Boot, Spring Cloud 集成,並能代替 Spring Cloud Eureka, Spring Cloud Config
- 通過 Nacos Server 和 spring-cloud-starter-alibaba-nacos-discovery 實現服務的注冊與發現。
(3)Nacos是以服務為主要服務對象的中間件,Nacos支持所有主流的服務發現、配置和管理。
Nacos主要提供以下四大功能:
\1. 服務發現和服務健康監測
\2. 動態配置服務
\3. 動態DNS服務
\4. 服務及其元數據管理
(4)Nacos結構圖
2、Nacos下載和安裝
(1)下載地址和版本
下載地址:https://github.com/alibaba/nacos/releases
下載版本:nacos-server-1.1.4.tar.gz或nacos-server-1.1.4.zip,解壓任意目錄即可
(2)啟動nacos服務
- Linux/Unix/Mac
啟動命令(standalone代表着單機模式運行,非集群模式)
啟動命令:sh startup.sh -m standalone
- Windows
啟動命令:cmd startup.cmd 或者雙擊startup.cmd運行文件。
訪問:http://localhost:8848/nacos
用戶名密碼:nacos/nacos
3、服務注冊
配置Nacos客戶端的pom依賴
<!--服務注冊-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
添加服務配置信息
配置application.properties,在客戶端微服務中添加注冊Nacos服務的配置信息
# nacos服務地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
添加Nacos客戶端注解
在客戶端微服務啟動類中添加注解
@EnableDiscoveryClient
4、啟動客戶端微服務
啟動注冊中心
啟動已注冊的微服務,可以在Nacos服務列表中看到被注冊的微服務
二、服務調用 - Feign
1、基本概念
- Feign是Netflix開發的聲明式、模板化的HTTP客戶端, Feign可以幫助我們更快捷、優雅地調用HTTP API。
- Feign支持多種注解,例如Feign自帶的注解或者JAX-RS注解等。
- Spring Cloud對Feign進行了增強,使Feign支持了Spring MVC注解,並整合了Ribbon和Eureka,從而讓Feign的使用更加方便。
- Spring Cloud Feign是基於Netflix feign實現,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,除了提供這兩者的強大功能外,還提供了一種聲明式的Web服務客戶端定義的方式。
- Spring Cloud Feign幫助我們定義和實現依賴服務接口的定義。在Spring Cloud feign的實現下,只需要創建一個接口並用注解方式配置它,即可完成服務提供方的接口綁定,簡化了在使用Spring Cloud Ribbon時自行封裝服務調用客戶端的開發量。
2、實現服務調用
在service模塊添加pom依賴
<!--服務調用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在調用端的啟動類添加注解
@EnableFeignClients
創建包和接口
創建client包
@FeignClient注解用於指定從哪個服務中調用功能 ,名稱與被調用的服務名保持一致。
@GetMapping注解用於對被調用的微服務進行地址映射。
@PathVariable注解一定要指定參數名稱,否則出錯
@Component注解防止,在其他位置注入CodClient時idea報錯
package com.edu.client;
@FeignClient("service-vod")
@Component
public interface VodClient {
@DeleteMapping(value = "/eduvod/vod/video/{videoId}")
public R removeVideo(@PathVariable("videoId") String videoId);
}
調用微服務
在調用端的VideoServiceImpl中調用client中的方法
@Override
public boolean removeVideoById(String id) {
//查詢雲端視頻id
Video video = baseMapper.selectById(id);
String videoSourceId = video.getVideoSourceId();
//刪除視頻資源
if(!StringUtils.isEmpty(videoSourceId)){
vodClient.removeVideo(videoSourceId);
}
Integer result = baseMapper.deleteById(id);
return null != result && result > 0;
}
三、熔斷器 - Hystrix
Spring Cloud 在接口調用上,大致會經過如下幾個組件配合:
Feign ----->Hystrix —>Ribbon —>Http Client(apache http components 或者 Okhttp) 具體交互流程上,如:
- 接口化請求調用當調用被@FeignClient注解修飾的接口時,在框架內部,將請求轉換成Feign的請求實例feign.Request,交由Feign框架處理。
- Feign :轉化請求Feign是一個http請求調用的輕量級框架,可以以Java接口注解的方式調用Http請求,封裝了Http調用流程。
- Hystrix:熔斷處理機制 Feign的調用關系,會被Hystrix代理攔截,對每一個Feign調用請求,Hystrix都會將其包裝成HystrixCommand,參與Hystrix的流控和熔斷規則。如果請求判斷需要熔斷,則Hystrix直接熔斷,拋出異常或者使用FallbackFactory返回熔斷Fallback結果;如果通過,則將調用請求傳遞給Ribbon組件。
- Ribbon:服務地址選擇 當請求傳遞到Ribbon之后,Ribbon會根據自身維護的服務列表,根據服務的服務質量,如平均響應時間,Load等,結合特定的規則,從列表中挑選合適的服務實例,選擇好機器之后,然后將機器實例的信息請求傳遞給Http Client客戶端,HttpClient客戶端來執行真正的Http接口調用;
- HttpClient :Http客戶端,真正執行Http調用根據上層Ribbon傳遞過來的請求,已經指定了服務地址,則HttpClient開始執行真正的Http請求
Hystrix概念
Hystrix 是一個供分布式系統使用,提供延遲和容錯功能,保證復雜的分布系統在面臨不可避免的失敗時,仍能有其彈性。
比如系統中有很多服務,當某些服務不穩定的時候,使用這些服務的用戶線程將會阻塞,如果沒有隔離機制,系統隨時就有可能會掛掉,從而帶來很大的風險。SpringCloud使用Hystrix組件提供斷路器、資源隔離與自我修復功能。下圖表示服務B觸發了斷路器,阻止了級聯失敗
feign 結合Hystrix使用
1、在service的pom中添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!--hystrix依賴,主要是用 @HystrixCommand -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--服務注冊-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--服務調用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、在配置文件中添加hystrix配置
#開啟熔斷機制
feign.hystrix.enabled=true
# 設置hystrix超時時間,默認1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
3、在service的client包里面創建熔斷器的實現類
@Component
public class VodFileDegradeFeignClient implements VodClient {
@Override
public R removeVideo(String videoId) {
return R.error().message("time out");
}
@Override
public R removeVideoList(List videoIdList) {
return R.error().message("time out");
}
}
4、修改VodClient接口的注解
@FeignClient(name = "service-vod", fallback = VodFileDegradeFeignClient.class)
@Component
public interface VodClient {
@DeleteMapping(value = "/eduvod/vod/{videoId}")
public R removeVideo(@PathVariable("videoId") String videoId);
@DeleteMapping(value = "/eduvod/vod/delete-batch")
public R removeVideoList(@RequestParam("videoIdList") List videoIdList);
}
5、測試熔斷器效果
四、GATEWAY網關
一、網關基本概念
1、API網關介紹
API 網關出現的原因是微服務架構的出現,不同的微服務一般會有不同的網絡地址,而外部客戶端可能需要調用多個服務的接口才能完成一個業務需求,如果讓客戶端直接與各個微服務通信,會有以下的問題:
(1)客戶端會多次請求不同的微服務,增加了客戶端的復雜性。
(2)存在跨域請求,在一定場景下處理相對復雜。
(3)認證復雜,每個服務都需要獨立認證。
(4)難以重構,隨着項目的迭代,可能需要重新划分微服務。例如,可能將多個服務合並成一個或者將一個服務拆分成多個。如果客戶端直接與微服務通信,那么重構將會很難實施。
(5)某些微服務可能使用了防火牆 / 瀏覽器不友好的協議,直接訪問會有一定的困難。
以上這些問題可以借助 API 網關解決。API 網關是介於客戶端和服務器端之間的中間層,所有的外部請求都會先經過 API 網關這一層。也就是說,API 的實現方面更多的考慮業務邏輯,而安全、性能、監控可以交由 API 網關來做,這樣既提高業務靈活性又不缺安全性
2、Spring Cloud Gateway
Spring cloud gateway是spring官方基於Spring 5.0、Spring Boot2.0和Project Reactor等技術開發的網關,Spring Cloud Gateway旨在為微服務架構提供簡單、有效和統一的API路由管理方式,Spring Cloud Gateway作為Spring Cloud生態系統中的網關,目標是替代Netflix Zuul,其不僅提供統一的路由方式,並且還基於Filer鏈的方式提供了網關基本的功能,例如:安全、監控/埋點、限流等。
3、Spring Cloud Gateway核心概念
網關提供API全托管服務,豐富的API管理功能,輔助企業管理大規模的API,以降低管理成本和安全風險,包括協議適配、協議轉發、安全策略、防刷、流量、監控日志等貢呢。一般來說網關對外暴露的URL或者接口信息,我們統稱為路由信息。如果研發過網關中間件或者使用過Zuul的人,會知道網關的核心是Filter以及Filter Chain(Filter責任鏈)。Sprig Cloud Gateway也具有路由和Filter的概念。下面介紹一下Spring Cloud Gateway中幾個重要的概念。
(1)路由。路由是網關最基礎的部分,路由信息有一個ID、一個目的URL、一組斷言和一組Filter組成。如果斷言路由為真,則說明請求的URL和配置匹配
(2)斷言。Java8中的斷言函數。Spring Cloud Gateway中的斷言函數輸入類型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的斷言函數允許開發者去定義匹配來自於http request中的任何信息,比如請求頭和參數等。
(3)過濾器。一個標准的Spring webFilter。Spring cloud gateway中的filter分為兩種類型的Filter,分別是Gateway Filter和Global Filter。過濾器Filter將會對請求和響應進行修改處理
Spring cloud Gateway發出請求。然后再由Gateway Handler Mapping中找到與請求相匹配的路由,將其發送到Gateway web handler。Handler再通過指定的過濾器鏈將請求發送到我們實際的服務執行業務邏輯,然后返回。
二、創建api-gateway模塊(網關服務)
1、在infrastructure模塊下創建api_gateway模塊
2、在pom.xml引入依賴
<dependencies>
<dependency>
<groupId>com.royal</groupId>
<artifactId>common_utils</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!--服務調用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
3、編寫application.properties配置文件
# 服務端口
server.port=8222
# 服務名
spring.application.name=service-gateway
# nacos服務地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#使用服務發現路由
spring.cloud.gateway.discovery.locator.enabled=true
#服務路由名小寫
#spring.cloud.gateway.discovery.locator.lower-case-service-id=true
#設置路由id
spring.cloud.gateway.routes[0].id=service-acl
#設置路由的uri
spring.cloud.gateway.routes[0].uri=lb://service-acl
#設置路由斷言,代理servicerId為auth-service的/auth/路徑
spring.cloud.gateway.routes[0].predicates= Path=/*/acl/**
#配置service-edu服務
spring.cloud.gateway.routes[1].id=service-edu
spring.cloud.gateway.routes[1].uri=lb://service-edu
spring.cloud.gateway.routes[1].predicates= Path=/eduservice/**
#配置service-ucenter服務
spring.cloud.gateway.routes[2].id=service-ucenter
spring.cloud.gateway.routes[2].uri=lb://service-ucenter
spring.cloud.gateway.routes[2].predicates= Path=/ucenterservice/**
#配置service-ucenter服務
spring.cloud.gateway.routes[3].id=service-cms
spring.cloud.gateway.routes[3].uri=lb://service-cms
spring.cloud.gateway.routes[3].predicates= Path=/cmsservice/**
spring.cloud.gateway.routes[4].id=service-msm
spring.cloud.gateway.routes[4].uri=lb://service-msm
spring.cloud.gateway.routes[4].predicates= Path=/edumsm/**
spring.cloud.gateway.routes[5].id=service-order
spring.cloud.gateway.routes[5].uri=lb://service-order
spring.cloud.gateway.routes[5].predicates= Path=/orderservice/**
spring.cloud.gateway.routes[6].id=service-order
spring.cloud.gateway.routes[6].uri=lb://service-order
spring.cloud.gateway.routes[6].predicates= Path=/orderservice/**
spring.cloud.gateway.routes[7].id=service-oss
spring.cloud.gateway.routes[7].uri=lb://service-oss
spring.cloud.gateway.routes[7].predicates= Path=/eduoss/**
spring.cloud.gateway.routes[8].id=service-statistic
spring.cloud.gateway.routes[8].uri=lb://service-statistic
spring.cloud.gateway.routes[8].predicates= Path=/staservice/**
spring.cloud.gateway.routes[9].id=service-vod
spring.cloud.gateway.routes[9].uri=lb://service-vod
spring.cloud.gateway.routes[9].predicates= Path=/eduvod/**
spring.cloud.gateway.routes[10].id=service-edu
spring.cloud.gateway.routes[10].uri=lb://service-edu
spring.cloud.gateway.routes[10].predicates= Path=/eduuser/**
yml文件:
server:
port: 8222
spring:
application:
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: SERVICE-ACL
uri: lb://SERVICE-ACL
predicates:
- Path=/*/acl/** # 路徑匹配
- id: SERVICE-EDU
uri: lb://SERVICE-EDU
predicates:
- Path=/eduservice/** # 路徑匹配
- id: SERVICE-UCENTER
uri: lb://SERVICE-UCENTER
predicates:
- Path=/ucenter/** # 路徑匹配
nacos:
discovery:
server-addr: 127.0.0.1:8848
4、編寫啟動類
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
三、網關相關配置
1、網關解決跨域問題
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
2、全局Filter,統一處理會員登錄與外部不允許訪問的服務
import com.google.gson.JsonObject;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* <p>
* 全局Filter,統一處理會員登錄與外部不允許訪問的服務
* </p>
*
* @author qy
* @since 2019-11-21
*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
//谷粒學院api接口,校驗用戶必須登錄
if(antPathMatcher.match("/api/**/auth/**", path)) {
List<String> tokenList = request.getHeaders().get("token");
if(null == tokenList) {
ServerHttpResponse response = exchange.getResponse();
return out(response);
} else {
// Boolean isCheck = JwtUtils.checkToken(tokenList.get(0));
// if(!isCheck) {
ServerHttpResponse response = exchange.getResponse();
return out(response);
// }
}
}
//內部服務接口,不允許外部訪問
if(antPathMatcher.match("/**/inner/**", path)) {
ServerHttpResponse response = exchange.getResponse();
return out(response);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
private Mono<Void> out(ServerHttpResponse response) {
JsonObject message = new JsonObject();
message.addProperty("success", false);
message.addProperty("code", 28004);
message.addProperty("data", "鑒權失敗");
byte[] bits = message.toString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
//response.setStatusCode(HttpStatus.UNAUTHORIZED);
//指定編碼,否則在瀏覽器中會中文亂碼
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
}
}
3、自定義異常處理
服務網關調用服務時可能會有一些異常或服務不可用,它返回錯誤信息不友好,需要我們覆蓋處理
ErrorHandlerConfig:
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import java.util.Collections;
import java.util.List;
/**
* 覆蓋默認的異常處理
*
*/
@Configuration
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class ErrorHandlerConfig {
private final ServerProperties serverProperties;
private final ApplicationContext applicationContext;
private final ResourceProperties resourceProperties;
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public ErrorHandlerConfig(ServerProperties serverProperties,
ResourceProperties resourceProperties,
ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer,
ApplicationContext applicationContext) {
this.serverProperties = serverProperties;
this.applicationContext = applicationContext;
this.resourceProperties = resourceProperties;
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
JsonExceptionHandler exceptionHandler = new JsonExceptionHandler(
errorAttributes,
this.resourceProperties,
this.serverProperties.getError(),
this.applicationContext);
exceptionHandler.setViewResolvers(this.viewResolvers);
exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
return exceptionHandler;
}
}
JsonExceptionHandler:
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.server.*;
import java.util.HashMap;
import java.util.Map;
/**
* 自定義異常處理
*
* <p>異常時用JSON代替HTML異常信息<p>
*
*/
public class JsonExceptionHandler extends DefaultErrorWebExceptionHandler {
public JsonExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
ErrorProperties errorProperties, ApplicationContext applicationContext) {
super(errorAttributes, resourceProperties, errorProperties, applicationContext);
}
/**
* 獲取異常屬性
*/
@Override
protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
Map<String, Object> map = new HashMap<>();
map.put("success", false);
map.put("code", 20005);
map.put("message", "網關失敗");
map.put("data", null);
return map;
}
/**
* 指定響應處理方法為JSON處理的方法
* @param errorAttributes
*/
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
/**
* 根據code獲取對應的HttpStatus
* @param errorAttributes
*/
@Override
protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
return HttpStatus.OK;
}
}
Nacos配置中心
1、Spring Cloud Config
Spring Cloud Config 為分布式系統的外部配置提供了服務端和客戶端的支持方案。在配置的服務端您可以在所有環境中為應用程序管理外部屬性的中心位置。客戶端和服務端概念上的Spring Environment 和 PropertySource 抽象保持同步, 它們非常適合Spring應用程序,但是可以與任何語言中運行的應用程序一起使用。當應用程序在部署管道中從一個開發到測試直至進入生產時,您可以管理這些環境之間的配置,並確保應用程序在遷移時具有它們需要運行的所有內容。服務器存儲后端的默認實現使用git,因此它很容易支持標記版本的配置環境,並且能夠被管理內容的各種工具訪問。很容易添加替代的實現,並用Spring配置將它們插入。
Spring Cloud Config 包含了Client和Server兩個部分,server提供配置文件的存儲、以接口的形式將配置文件的內容提供出去,client通過接口獲取數據、並依據此數據初始化自己的應用。Spring cloud使用git或svn存放配置文件,默認情況下使用git。
2、Nacos替換Config
Nacos 可以與 Spring, Spring Boot, Spring Cloud 集成,並能代替 Spring Cloud Eureka, Spring Cloud Config。通過 Nacos Server 和 spring-cloud-starter-alibaba-nacos-config 實現配置的動態變更。
應用場景
在系統開發過程中,開發者通常會將一些需要變更的參數、變量等從代碼中分離出來獨立管理,以獨立的配置文件的形式存在。目的是讓靜態的系統工件或者交付物(如 WAR,JAR 包等)更好地和實際的物理運行環境進行適配。配置管理一般包含在系統部署的過程中,由系統管理員或者運維人員完成。配置變更是調整系統運行時的行為的有效手段。
如果微服務架構中沒有使用統一配置中心時,所存在的問題:
- 配置文件分散在各個項目里,不方便維護
- 配置內容安全與權限
- 更新配置后,項目需要重啟
nacos配置中心:系統配置的集中管理(編輯、存儲、分發)、動態更新不重啟、回滾配置(變更管理、歷史版本管理、變更審計)等所有與配置相關的活動。
3、讀取Nacos配置中心的配置文件
1、在Nacos創建統一配置文件
(1)點擊創建按鈕
(2)輸入配置信息
Data ID 的完整規則格式如下
${prefix}-${spring.profile.active}.${file-extension}
-
- prefix 默認為所屬工程配置spring.application.name 的值(即:nacos-provider),也可以通過配置項 spring.cloud.nacos.config.prefix來配置。
-
- spring.profiles.active=dev 即為當前環境對應的 profile。 注意:當 spring.profiles.active 為空時,對應的連接符 - 也將不存在,dataId 的拼接格式變成 \({prefix}.\){file-extension}
-
- file-exetension 為配置內容的數據格式,可以通過配置項 spring.cloud.nacos.config.file-extension 來配置。目前只支持 properties 和 yaml 類型。
2、以service-statistics模塊為例
(1)在service中引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
(2)創建bootstrap.properties配置文件
#配置中心地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
#spring.profiles.active=dev
# 該配置影響統一配置中心中的dataId
spring.application.name=service-statistics
(3)把項目之前的application.properties內容注釋,啟動項目查看效果
springboot配置文件加載順序
其實yml和properties文件是一樣的原理,且一個項目上要么yml或者properties,二選一的存在。推薦使用yml,更簡潔。
bootstrap與application
(1)加載順序
-
這里主要是說明application和bootstrap的加載順序。
-
bootstrap.yml(bootstrap.properties)先加載
-
application.yml(application.properties)后加載
-
bootstrap.yml 用於應用程序上下文的引導階段。
-
bootstrap.yml 由父Spring ApplicationContext加載。
-
父ApplicationContext 被加載到使用 application.yml 的之前。
(2)配置區別
- bootstrap.yml 和application.yml 都可以用來配置參數。
- bootstrap.yml 可以理解成系統級別的一些參數配置,這些參數一般是不會變動的。
- application.yml 可以用來定義應用級別的。
4、名稱空間切換環境
在實際開發中,通常有多套不同的環境(默認只有public),那么這個時候可以根據指定的環境來創建不同的 namespce,例如,開發、測試和生產三個不同的環境,那么使用一套 nacos 集群可以分別建以下三個不同的 namespace。以此來實現多環境的隔離。
1、創建命名空間
默認只有public,新建了dev、test和prod命名空間
2、克隆配置
(1)切換到配置列表:
- 可以發現有四個名稱空間:public(默認)以及我們自己添加的3個名稱空間(prod、dev、test),可以點擊查看每個名稱空間下的配置文件,當然現在只有public下有一個配置。
- 默認情況下,項目會到public下找 服務名.properties文件。
- 接下來,在dev名稱空間中也添加一個nacos-provider.properties配置。
- 這時有兩種方式:
- 第一,切換到dev名稱空間,添加一個新的配置文件。缺點:每個環境都要重復配置類似的項目
- 第二,直接通過clone方式添加配置,並修改即可。推薦
在項目模塊中,修改bootstrap.properties添加如下配置
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.profiles.active=dev
# 該配置影響統一配置中心中的dataId,之前已經配置過
spring.application.name=service-statistics
spring.cloud.nacos.config.namespace=13b5c197-de5b-47e7-9903-ec0538c9db01
namespace的值為:配置列表項里面的ID
重啟服務提供方服務,測試修改之后是否生效
5、多配置文件加載
在一些情況下需要加載多個配置文件。假如現在dev名稱空間下有三個配置文件:service-statistics.properties、redis.properties、jdbc.properties
添加配置,加載多個配置文件
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.profiles.active=dev
# 該配置影響統一配置中心中的dataId,之前已經配置過
spring.application.name=service-statistics
spring.cloud.nacos.config.namespace=13b5c197-de5b-47e7-9903-ec0538c9db01
spring.cloud.nacos.config.ext-config[0].data-id=redis.properties
# 開啟動態刷新配置,否則配置文件修改,工程無法感知
spring.cloud.nacos.config.ext-config[0].refresh=true
spring.cloud.nacos.config.ext-config[1].data-id=jdbc.properties
spring.cloud.nacos.config.ext-config[1].refresh=true