Nacos Spring
本文將介紹 nacos-spring-context
中的一些關(guān)鍵的特性:
- 注解驅(qū)動(dòng)
- 依賴注入
- 外部化配置
- 事件驅(qū)動(dòng)
1. 注解驅(qū)動(dòng)
1.1. 啟用 Nacos
@EnableNacos
是一個(gè)模塊驅(qū)動(dòng)的注解花沉,它支持 Nacos Spring 的所有功能哮针,包括服務(wù)發(fā)現(xiàn)和配置管理。它等于 @EnableNacosDiscovery
加上 @EnableNacosConfig
定页,可以單獨(dú)配置并在不同場(chǎng)景中使用。
1.2. 配置監(jiān)聽(tīng)
假設(shè)在 Nacos 服務(wù)中有一個(gè)配置板丽,其 dataId
是 "testDataId" 而 groupId
是默認(rèn)組("DEFAULT_GROUP")檩禾。 現(xiàn)在,您可以使用 ConfigService#publishConfig
方法更改其內(nèi)容:
@NacosInjected
private ConfigService configService;
@Test
public void testPublishConfig() throws NacosException {
configService.publishConfig(DATA_ID, DEFAULT_GROUP, "9527");
}
然后您可以添加一個(gè)監(jiān)聽(tīng)器苞氮,它將監(jiān)聽(tīng)配置的變化。 您可以通過(guò)在 Spring Bean 中添加配置變更監(jiān)聽(tīng)器方法來(lái)執(zhí)行此操作:
@NacosConfigListener(dataId = DATA_ID)
public void onMessage(String config) {
assertEquals("mercyblitz", config); // asserts true
}
下面的代碼具有相同的效果:
configService.addListener(DATA_ID, DEFAULT_GROUP, new AbstractListener() {
@Override
public void receiveConfigInfo(String config) {
assertEquals("9527", config); // asserts true
}
});
另外瓤逼,@NacosConfigListener
支持更豐富的類型轉(zhuǎn)換笼吟。
- 請(qǐng)參看: Simple Sample of
@NacosConfigListener
1.2.1. 類型
@NacosConfigListener
的類型轉(zhuǎn)換包括內(nèi)置和自定義實(shí)現(xiàn)。 默認(rèn)情況下霸旗,內(nèi)置類型轉(zhuǎn)換基于 Spring DefaultFormattingConversionService
贷帮,這意味著它包好了大多數(shù)情況以及 Spring 框架更高級(jí)版本的豐富功能。
例如诱告,前面示例中的內(nèi)容 "9527" 也可以通過(guò)帶 "int" 或 "Integer" 參數(shù)的方法進(jìn)行監(jiān)聽(tīng)::
@NacosConfigListener(dataId = DATA_ID)
public void onInteger(Integer value) {
assertEquals(Integer.valueOf(9527), value); // asserts true
}
@NacosConfigListener(dataId = DATA_ID)
public void onInt(int value) {
assertEquals(9527, value); // asserts true
}
當(dāng)然, nacos-spring-context
為開(kāi)發(fā)人員提供彈性擴(kuò)展撵枢。 如果定義名為nacosConfigConversionService
的Spring Bean,其類型為ConversionService
蔬啡,則將忽略DefaultFormattingConversionService
诲侮。 此外,您可以自定義NacosConfigConverter
接口的實(shí)現(xiàn)箱蟆,以指定類型轉(zhuǎn)換的偵聽(tīng)器方法:
public class UserNacosConfigConverter implements NacosConfigConverter<User> {
@Override
public boolean canConvert(Class<User> targetType) {
return true;
}
@Override
public User convert(String source) {
return JSON.parseObject(source, User.class);
}
}
UserNacosConfigConverter
類綁定在 @NacosConfigListener.converter()
屬性上,如下:
@NacosInjected
private ConfigService configService;
@Test
public void testPublishUser() throws NacosException {
configService.publishConfig("user", DEFAULT_GROUP, "{\"id\":1,\"name\":\"mercyblitz\"}");
}
@NacosConfigListener(dataId = "user", converter = UserNacosConfigConverter.class)
public void onUser(User user) {
assertEquals(Long.valueOf(1L), user.getId());
assertEquals("mercyblitz", user.getName());
}
1.2.2. 超時(shí)時(shí)間
由于運(yùn)行自定義的 NacosConfigConverter
可能需要一些時(shí)間刮便,因此您可以在 @NacosConfigListener.timeout()
屬性中設(shè)置最大執(zhí)行時(shí)間空猜,以防止它阻塞其他偵聽(tīng)器:
@Configuration
public class Listeners {
private Integer integerValue;
private Double doubleValue;
@NacosConfigListener(dataId = DATA_ID, timeout = 50)
public void onInteger(Integer value) throws Exception {
Thread.sleep(100); // timeout of execution
this.integerValue = value;
}
@NacosConfigListener(dataId = DATA_ID, timeout = 200)
public void onDouble(Double value) throws Exception {
Thread.sleep(100); // normal execution
this.doubleValue = value;
}
public Integer getIntegerValue() {
return integerValue;
}
public Double getDoubleValue() {
return doubleValue;
}
}
Listeners
Bean 的 integerValue
總是為null
,不會(huì)改變恨旱。 因此辈毯,以下斷言都將是 true
:
@Autowired
private Listeners listeners;
@Test
public void testPublishConfig() throws NacosException {
configService.publishConfig(DATA_ID, DEFAULT_GROUP, "9527");
assertNull(listeners.getIntegerValue()); // asserts true
assertEquals(Double.valueOf(9527), listeners.getDoubleValue()); // asserts true
}
- 請(qǐng)參看:Timeout Sample of
@NacosConfigListener
1.3. 全局和自定義 Nacos 屬性
globalProperties
是任何 @EnableNacos
,@EnableNacosDiscovery
或 @EnableNacosConfig
中的必選屬性搜贤,其類型為 @NacosProperties
谆沃。
globalProperties
將初始化為其他注解或組件的 "全局 Nacos 屬性",例如:@NacosInjected
仪芒。
換句話說(shuō)唁影,全局 Nacos 屬性 定義全局和默認(rèn)屬性。它設(shè)置為具有最低優(yōu)先級(jí)掂名,并且也可以被覆蓋据沈。覆蓋優(yōu)先級(jí)如下表所示:
Precedence Order | Nacos Annotation | Required |
---|---|---|
1 | *.properties() |
N |
2 |
@EnableNacosConfig.globalProperties() or @EnableNacosDiscovery.globalProperties()
|
Y |
3 | @EnableNacos.globalProperties() |
Y |
*.properties()
定義來(lái)自以下之一的自定義 Nacos 屬性:
@NacosInjected.properties()
@NacosConfigListener.properties()
@NacosPropertySource.properties()
@NacosConfigurationProperties.properties()
自定義的 Nacos 屬性也由 @NacosProperties
配置。 不過(guò)饺蔑,它們是可選的锌介,用于在特殊情況下覆蓋全局 Nacos 屬性。 如果沒(méi)有定義,Nacos 屬性將嘗試從 @EnableNacosConfig.globalProperties()
或 @EnableNacosDiscovery.globalProperties()
或 @EnableNacos.globalProperties()
中查找屬性孔祸。
1.4. @NacosProperties
@NacosProperties
是全局和自定義 Nacos 屬性的統(tǒng)一注解隆敢。 它充當(dāng)Java Properties
和 NacosFactory
類之間的中介。NacosFactory
負(fù)責(zé)創(chuàng)建 ConfigService
或 NamingService
實(shí)例崔慧。
@NacosProperties
的屬性完全支持占位符筑公,它的源是Spring Environment
抽象中的各種 PropertySource
,通常是Java System Properties
和操作系統(tǒng)環(huán)境變量尊浪。 所有占位符的前綴都是 nacos.
匣屡。@NacosProperties
和 Nacos 屬性的屬性之間的映射如下所示:
Attribute | Property | Placeholder | Description | Required |
---|---|---|---|---|
endpoint() |
endpoint |
${nacos.endpoint:} |
N | |
namespace() |
namespace |
${nacos.namespace:} |
N | |
accessKey() |
access-key |
${nacos.access-key:} |
N | |
secretKey() |
secret-key |
${nacos.secret-key:} |
N | |
serverAddr() |
server-addr |
${nacos.server-addr:} |
Y | |
contextPath() |
context-path |
${nacos.context-path:} |
N | |
clusterName() |
cluster-name |
${nacos.cluster-name:} |
N | |
encode() |
encode |
${nacos.encode:UTF-8} |
N |
請(qǐng)注意,@EnableNacosDiscovery
和 @EnableNacosConfig
之間 globalProperties()
的占位符存在一些差異:
Attribute |
@EnableNacosDiscovery 's Placeholder |
@EnableNacosConfig 's Placeholder |
---|---|---|
endpoint() |
${nacos.discovery.endpoint:${nacos.endpoint:}} |
${nacos.config.endpoint:${nacos.endpoint:}} |
namespace() |
${nacos.discovery.namespace:${nacos.namespace:}} |
${nacos.config.namespace:${nacos.namespace:}} |
accessKey() |
${nacos.discovery.access-key:${nacos.access-key:}} |
${nacos.config.access-key:${nacos.access-key:}} |
secretKey() |
${nacos.discovery.secret-key:${nacos.secret-key:}} |
${nacos.config.secret-key:${nacos.secret-key:}} |
serverAddr() |
${nacos.discovery.server-addr:${nacos.server-addr:}} |
${nacos.config.server-addr:${nacos.server-addr:}} |
contextPath() |
${nacos.discovery.context-path:${nacos.context-path:}} |
${nacos.config.context-path:${nacos.context-path:}} |
clusterName() |
${nacos.discovery.cluster-name:${nacos.cluster-name:}} |
${nacos.config.cluster-name:${nacos.cluster-name:}} |
encode() |
${nacos.discovery.encode:${nacos.encode:UTF-8}} |
${nacos.config.encode:${nacos.encode:UTF-8}} |
這些 @EnableNacosDiscovery
和 @EnableNacosConfig
的占位符用于隔離不同的 Nacos 服務(wù)拇涤,在大多數(shù)情況下都是不必要的捣作。默認(rèn)情況下,將使用常規(guī)占位符鹅士。
2. 依賴注入
@NacosInjected
是一個(gè)核心注解券躁,用于在Spring Beans 中注入 ConfigService
或 NamingService
實(shí)例,并使這些實(shí)例可緩存掉盅。 這意味著如果它們的 @NacosProperties
相等也拜,則實(shí)例將是相同的,無(wú)論屬性是來(lái)自全局還是自定義的 Nacos 屬性:
@NacosInjected
private ConfigService configService;
@NacosInjected(properties = @NacosProperties(encode = "UTF-8"))
private ConfigService configService2;
@NacosInjected(properties = @NacosProperties(encode = "GBK"))
private ConfigService configService3;
@NacosInjected
private NamingService namingService;
@NacosInjected(properties = @NacosProperties(encode = "UTF-8"))
private NamingService namingService2;
@NacosInjected(properties = @NacosProperties(encode = "GBK"))
private NamingService namingService3;
@Test
public void testInjection() {
Assert.assertEquals(configService, configService2);
Assert.assertNotEquals(configService2, configService3);
Assert.assertEquals(namingService, namingService2);
Assert.assertNotEquals(namingService2, namingService3);
}
屬性 configService
使用 @EnableNacos#globalProperties()
或 @EnableNacosConfig#globalProperties()
趾痘,因?yàn)?encode
屬性的默認(rèn)值是 "UTF-8"慢哈,因此 configService
實(shí)例和由 @NacosProperties(encode ="UTF-8")
注解的 configService2
實(shí)例是相同的。 namingService
和 namingService2
也是如此永票。
值得注意的是卵贱,與 NacosFactory.createConfigService()
方法創(chuàng)建的 ConfigService
實(shí)例不同,@NacosInjected
注解創(chuàng)建的 ConfigService
實(shí)例支持 Nacos Spring 事件侣集。 例如键俱,在增強(qiáng)的 ConfigService
調(diào)用 publishConfig()
方法之后會(huì)有一個(gè) NacosConfigPublishedEvent
。 有關(guān)更多詳細(xì)信息世分,請(qǐng)參閱"事件驅(qū)動(dòng)"部分编振。
- 請(qǐng)參看:Dependency Injection Sample
3. 外部化配置
外部化配置是 Spring Boot 引入的概念,它允許應(yīng)用程序接收外部屬性源以控制運(yùn)行時(shí)行為臭埋。 Nacos Server 在應(yīng)用程序外部運(yùn)行單獨(dú)的進(jìn)程以維護(hù)應(yīng)用程序配置踪央。 nacos-spring-context
提供了對(duì)象綁定,動(dòng)態(tài)配置(自動(dòng)刷新)等功能斋泄。
這里有 nacos-spring-context
和 Spring Stack 之間的簡(jiǎn)單比較:
Spring Stack | Nacos Spring | Highlight |
---|---|---|
@Value |
@NacosValue |
auto-refreshed |
@ConfigurationProperties |
@NacosConfigurationProperties |
auto-refreshed,@NacosProperty ,@NacosIgnore
|
@PropertySource |
@NacosPropertySource |
auto-refreshed, precedence order control |
@PropertySources |
@NacosPropertySources |
- 請(qǐng)參看:Auto-Refreshed Sample of
@NacosConfigurationProperties
- 請(qǐng)參看:Sample of
@NacosPropertySources
and@NacosPropertySource
4. 事件驅(qū)動(dòng)
Nacos 事件驅(qū)動(dòng) 基于標(biāo)準(zhǔn)的 Spring Event / Listener 機(jī)制杯瞻。 Spring 的 ApplicationEvent
是所有 Nacos Spring 事件的抽象超類:
Nacos Spring Event | Trigger |
---|---|
NacosConfigPublishedEvent |
After ConfigService.publishConfig()
|
NacosConfigReceivedEvent |
AfterListener.receiveConfigInfo()
|
NacosConfigRemovedEvent |
After configService.removeConfig()
|
NacosConfigTimeoutEvent |
ConfigService.getConfig() on timeout |
NacosConfigListenerRegisteredEvent |
After ConfigService.addListner() or ConfigService.removeListener()
|
NacosConfigurationPropertiesBeanBoundEvent |
After @NacosConfigurationProperties binding |
NacosConfigMetadataEvent |
After Nacos Config operations |
- 請(qǐng)參看:Event/Listener Sample