寫在前面
本文參照Spring官方文檔,寫了相關代碼 (源碼位址),在此做下筆記;
官網關于
Nacos
的介紹是這樣的:“
Nacos
是一個易于使用的動态服務發現、配置和服務管理平台,用于建構雲本地應用程式”。一句話描述了
Nacos
能夠實作的功能:服務發現、配置、服務治理。
那麼,我們先從基礎的服務發現開始吧!
入門
因為後續我們将陸續使用到
Spring-Cloud-Alibaba
中的其它元件,是以,我們在頂層子產品中引入如下的
POM
管理:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
當然,不要忘了這個項目需要是
SpringBoot
項目,是以,我們之間讓這個頂層子產品歸于
Spring-Boot
下,添加如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
以上是整個大項目的 POM 檔案描述,以後各子子產品均在該項目下建立。現在,讓我們按學習的進度來建立各個子項目(子子產品)。
啟動 Nacos 服務
首先,我們需要 下載下傳 Nacos 服務,進入
/bin
目錄下,通過
startup.cmd
啟動它。啟動成功之後,通路
http://<ip>:8848/nacos
可檢視控制台:
官網在這裡描述為,但你通路這個位址會 404。我看了
http://ip:8848
包中的配置檔案,發現位址應為如上的形式。
nacos-server.jar
啟動 Provider 應用
建立一個
provider_app
子子產品,引入如下依賴:
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
并添加
application.yml
配置檔案:
application.yml
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: provider-app
server:
port: 18083
新增啟動類:
package com.duofei;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* 啟動類
* @author duofei
* @date 2020/7/22
*/
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderDemoApplication {
public static void main(String[] args) {
SpringApplication.run(NacosProviderDemoApplication.class, args);
}
@RestController
public class EchoController{
@GetMapping("/echo/{string}")
public String echo(@PathVariable String string){
return "Hello Nacos Discovery " + string;
}
}
}
啟動應用程式,觀察
Nacos
的控制台:
可以看出,我們剛運作的服務成功注冊。如果仔細留意應用程式的啟動日志,也能看見列印的注冊完成的資訊。
啟動 Consumer 應用
調用者服務稍微複雜一點,因為它需要調用提供者的
RESTful
服務。這裡使用最原始的
LoadBalanceClient
和
RestTemplate
來通路提供者提供的服務。
建立
consumer-app
項目,依賴不變,配置檔案稍作修改:
application.yml
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: consumer-app
server:
port: 18084
啟動類如下:
package com.duofei;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.sql.SQLOutput;
/**
* 啟動類
* @author duofei
* @date 2020/7/22
*/
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApp {
public static void main(String[] args) {
SpringApplication.run(NacosConsumerApp.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
@RestController
public class NacoController{
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
@Value("${spring.application.name}")
private String appName;
@GetMapping("/echo/app-name")
public String echoAppName(){
ServiceInstance serviceInstance = loadBalancerClient.choose("provider-app");
String path = String.format("http://%s:%s/echo/%s", serviceInstance.getHost(), serviceInstance.getPort(), appName);
System.out.println("request path: " + path);
return restTemplate.getForObject(path, String.class);
}
}
}
通路
http://192.168.3.18:18084/echo/app-name
,但在擷取服務執行個體時,發現擷取到的服務執行個體位址并不是
192.168.3.18
, 是以無法通路到注冊的服務。這是因為服務提供者的配置檔案并未配置 ip 位址,我們所拿到的 ip 位址是在啟動過程中通過 Java 原生的
NetworkInterface
擷取的。具體的底層執行代碼則是在
NacosDiscoveryProperties
類的
init
方法。
是以,我們修改服務提供者類的配置檔案,新增
ip
配置:
spring:
cloud:
nacos:
discovery:
ip: 192.168.3.18
然後重新通路
http://192.168.3.18:18084/echo/app-name
,就可以得到
Hello Nacos Discovery consumer-app
的響應了。
Nacos
的服務注冊與發現流程很簡單,但唯一讓我有點困惑的便是這
ip
擷取了。原因可能是因為我本地有建立過虛拟機,然後有比較多的虛拟網絡接口,而
nacos
的篩選方式也比較簡單粗暴,直接取了第一個符合條件的網絡接口。
不過,在後續發現,可以在配置檔案中指定網絡接口的名稱:
spring:
cloud:
nacos:
discovery:
network-interface: eth9
上面這個網口名稱對應了我的
192.168.3.18
的 ip 位址,這樣也可以正常注冊 ip 位址。
服務發現端點
Nacos Discovery
在内部提供了一個具有
nacos-discovery
端點 id 的端點。
端點暴露的
json
内容為:
- subscribe: 展示了目前的服務訂閱資訊;
-
: 展示了目前服務的NacosDiscoveryProperties
配置;Nacos
想要通路
/nacos-discovery
這個端點路徑,需要引入
spring-boot-starter-actuator
元件,并在配置檔案中新增如下内容:
management:
endpoints:
web:
exposure:
include: nacos-discovery
如上的修改,我們将它應用到了前面的服務提供者和服務調用者子子產品中,然後,通路
http://192.168.3.18:18083/actuator/nacos-discovery
路徑或者是将端口改為服務調用者的端口路徑:
/actuator 這個基礎路徑,可以通過 management.endpoints.web.base-path
修改。
可以看到暴露的内容正是
NacosDiscoveryProperties
和
subscribe
。
但為什麼
subscribe
的内容為空呢?這是因為,訂閱資訊需要主動去訂閱。在我們的
consumer-app
的啟動類中增加如下代碼:
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
// 實作 InitializingBean 接口
@Override
public void afterPropertiesSet() throws Exception {
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
namingService.subscribe("provider-app", event -> {
System.out.println(event);
});
}
需要注意的是,注冊好該監聽器,會在啟動後直接觸發一次。現在,讓我們重新通路端點,擷取到如下資訊:
我們可以在
nacos
的控制台,修改訂閱的服務資訊,也會觸發該事件。但檢視該事件的實作類隻有一個,是以很難确定具體的事件行為是什麼。
服務發現的配置
服務發現的配置可以參考
NacosDiscoveryProperties
類。大多配置都見名知意,可一切還是要以代碼的執行結果為準,多思考為什麼,切記不要先入為主。
因為我自己在這裡就犯了一個先入為主的錯誤。我打算配置一個
namespace
,第一步是在
nacos
控制台上建立了一個命名空間,命名空間名為
demo
,意為測試代碼運作時使用的命名空間。緊接着,第二步,我在
consumer-app
配置檔案中新增了如下配置:
spring:
cloud:
nacos:
discovery:
namespace: demo
可結果并不如我所願,
consumer-app
始終無法注冊到"demo"的命名空間。然後,我開始跟蹤源碼,結果發現在底層,
namespace
最終對應的是
namespaceId
。是以,這裡其實是需要一個命名空間 id 的。而該 id 值在你建立好命名空間後就可以得到。這個地方的值修改為命名空間 id 後,一切就正常了。
consumer-app
項目修改完命名空間後,記得将
provider-app
也修改為對應的命名空間,否則
consumer-app
無法通路到
provider-app
。
總結
nacos
的服務發現與注冊真的比較簡單。我這裡整個配置項也就使用了三個,這可能是因為還沒有內建
Dubbo
或者其它的
RPC
架構。
這裡還有一點沒有提到,那就是負載均衡。不過,你可以發現
nacos-discovery
使用了
Ribbon
。那麼,
nacos-discovery
自然會使用
ribbon
來實作負載均衡政策。想了解更多的可以去檢視
ribbon
文檔,還有其它疑問的也可以看下
nacos-discovery
下
ribbon
包中的源碼。
文檔中的内容也需要持懷疑的态度,代碼才是檢驗“真理”的标準!
并不是指文檔中的内容有錯,而是說文檔中的内容簡潔,容易讓我們剛入門的小白造成了解上的誤差!
我與風來
認認真真學習,做思想的産出者,而不是文字的搬運工。
但行善事,莫問前程。