一、Nacos 簡介
Nacos 致力於幫助您發現、配置和管理微服務。Nacos 提供了一組簡單易用的特性集,幫助您快速實現動態服務發現、服務配置、服務元數據及流量管理。Nacos 幫助您更敏捷和容易地構建、交付和管理微服務平台。 Nacos 是構建以“服務”為中心的現代應用架構 (例如微服務范式、雲原生范式) 的服務基礎設施。
nacos官網:https://nacos.io/zh-cn/docs/what-is-nacos.html
二、Nacos 安裝
2.1 Nacos 環境依賴
Nacos 基於 java 開發的,運行依賴於64 bit JDK 1.8+環境。
前往官網下載JDK: https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
2.2 Nacos 服務端安裝
由於Nacos是開源的,可以從github下載源碼編譯后安裝,也可以直接從github下載官方編譯好的安裝包進行安裝,本文直接使用官方編譯好的包進行安裝,基於目前官方最新release2.0.3版本。請勿使用2.x以下的版本,之前爆出過漏洞。2.x版本相對於1.x版本,性能也提升了10倍。
下載地址:https://github.com/alibaba/nacos/releases
三、Nacos 部署
3.1 單實例部署
單實例部署不適合生產環境,單點故障是致命的。
Linux 單實例非集群模式啟動命令:
startup.sh -m standalone
Linux 單實例非集群模式關閉命令:
shutdown.sh
訪問 nacos 管理頁面,初始化用戶名密碼均為 nacos
訪問地址:http://127.0.0.1:8848/nacos/index.html
端口默認為8848,ip需要替換為部署服務器實例的ip
3.2 集群部署
3.2.1 集群架構
- 高可用 Nginx 集群
- Nacos 集群(至少三個實例)
- 高可用數據庫集群(取代 Nacos 內嵌數據庫)
3.2.2 模擬部署
環境信息:
系統版本 |
機器IP |
部署應用 |
應用版本 |
CentOS8.5 |
192.168.230.1 |
Nginx |
nginx-1.18.0 |
CentOS8.5 |
192.168.230.129 |
Nacos |
2.0.3 |
CentOS8.5 |
192.168.230.130 |
Nacos |
2.0.3 |
CentOS8.5 |
192.168.230.131 |
Nacos |
2.0.3 |
CentOS8.5 |
192.168.230.1 |
MySQL |
5.7.32 |
使用vmware虛擬機模擬以上不同機器環境。
搭建步驟:
1)初始化 nacos數據庫
解壓下載的安裝包nacos-server-2.0.3.tar.gz至/usr/local目錄,找到/nacos/conf下的nacos-mysql.sql腳本
在MySQL實例創建 nacos庫並執行sql腳本
修改修改 Nacos 配置文件,指向MySQL實例,替換其內嵌數據庫
在application.properties中找到如下配置,該配置默認為注釋掉的,取消注釋即可,修改數據庫信息為實際的數據庫信息后保存。其他nacos服務實例也需要做同樣的修改
為了達到高可用,通常會有多個MySQL數據庫實例,nacos的配置文件也需要指定每一個MySQL實例的信息,例如:
2)集群配置
在/nacos/conf下找到cluster.conf.example文件,將其重命名為cluster.conf
修改cluster.conf文件,將其中的默認的ip信息刪除,將nacos集群中每一台nacos實例的ip添加進去。集群中所有nacos實例都需要做以上集群配置,至此關於 nacos 的配置結束了,可以嘗試以集群模式啟動三個nacos實例了。
切換到/nacos/bin目錄下,執行./startup.sh & tail -f /usr/local/nacos/logs/start.out
啟動nacos,並查看啟動日志,出現下面提示說明啟動成功
此時可以通過瀏覽器訪問每個nacos實例的控制台,例如:http://192.168.230.131:8848/nacos
說明:如果三個實例以集群模式正常啟動,那么分別訪問三個實例的管理頁就是展示以上登錄頁了。如果不能訪問,則可能防火牆未開放 nacos 服務的端口,可執行如下命令。
[root@localhost bin]# firewall-cmd --add-port=8848/tcp --permanent
success
[root@localhost bin]# firewall-cmd --reload
success
如果nacos啟動時報已下異常,有以下三種原因:
原因一:連不上MySQL數據庫,通常是由於MySQL數據庫所在的宿主機沒有開放3306端口導致的,在MySQL宿主機執行以下命令即可
[root@localhost bin]# firewall-cmd --add-port=3306/tcp --permanent
success
[root@localhost bin]# firewall-cmd --reload
Success
原因二:由於nacos配置文件application.properties中默認的數據庫連接超時時間設置較短,如下圖,因為網絡延時等原因,MySQL可能會連接超時導致nacos啟動報錯,因此只需要將超時時間適當設置長一些即可
原因三:虛擬機內存不足,由於在vmvare創建虛擬機時,只給每個虛擬分配了1G的內存,從nacos的啟動腳本startup.sh中可知,nacos以集群模式啟動時,默認分配的java堆內存空間為2G,因此可判斷是由於虛擬機內存不足導致nacos啟動報錯,修改虛擬機內存為2G后可以正常啟動。
3)Nginx 配置
修改 Nginx 配置文件 nginx.conf如下:
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
#nacos集群負載均衡
upstream nacos-cluster {
server 192.168.230.129:8848;
server 192.168.230.130:8848;
server 192.168.230.131:8848;
}
server {
listen 80;
server_name 192.168.230.1;
location / {
#root html;
#index index.html index.htm;
proxy_pass http://nacos-cluster;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
4)訪問控制台
在瀏覽器輸入Nginx服務所在宿主機的ip即可訪問:
http://192.168.230.1/nacos/#/login
默認用戶名/密碼:nacos/nacos
切換至【集群管理】-【節點列表】,可查看nacos集群節點信息:
四、微服務集成Nacos
4.1 依賴組件版本選型
由於Spring Cloud Alibaba與Spring Boot 及Spring Cloud版本的兼容性要求非常嚴格,如果依賴選錯版本,很可能會導致服務啟動報錯從而導致啟動失敗。不過alibaba官方已經給我們整理出了推薦的版本依賴關系:
由於我們使用的nacos是最新的2.0.3版本,從以上表格可以查詢到依賴的Spring Cloud Alibaba的版本為2.2.7.RELEASE。繼續根據Spring Cloud Alibaba的版本查詢畢業版本依賴關系表,如下圖,可以找到依賴的Spring Cloud版本為Hoxton.SR12,依賴的Spring Boot版本為2.3.12.RELEASE
因此,通過上述查詢,最終確定微服務依賴版本選型如下:
Spring Cloud Version |
Spring Cloud Alibaba Version |
Spring Boot Version |
Nacos Version |
Hoxton.SR12 |
2.2.7.RELEASE |
2.3.12.RELEASE |
2.0.3 |
官方版本說明:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
4.2 注冊中心
首先新建demo-project-parent父工程,根據4.1 依賴組件版本選型選型確定的版本,在父pom文件統一定義好依賴的組件以及版本,如下:
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.7.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- spring boot 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- spring cloud 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- spring cloud alibaba 依賴 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
4.2.1 服務提供者
在父工程下新建服務提供者子模塊micro-service-provider,並在pom文件引入依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
修改provider配置文件application.yml:
server:
port: 8081
spring:
application:
name: service-provider
cloud:
nacos:
discovery:
#nacos集群配置(Nginx)
server-addr: 192.168.230.1:80
provider啟動類:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
Provider測試Controller類:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProviderController {
@Value("${server.port}")
String port;
@GetMapping("/hi")
public String hi(@RequestParam(value = "name", defaultValue = "zmx",required = false) String name) {
return "hello " + name + ", i'm provider, my port:" + port;
}
}
4.2.2 服務消費者
在父工程下新建服務消費者子模塊micro-service-consumer,並在pom文件引入以下依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 需要注意的是引入openfeign,必須要引入loadbalancer,否則無法啟動。-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
修改consumer配置文件:
server:
port: 8082
spring:
application:
name: service-consumer
cloud:
nacos:
discovery:
#nacos集群配置(Nginx)
server-addr: 192.168.230.1:80
在工程的啟動類加上@EnableFeignClient注解,以開啟FeignClient的功能:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
寫一個feign客戶端FeignClient,去調用provider服務的接口:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
//此處的value值對應於provider服務application配置文件中的spring.application.name
@FeignClient(value = "service-provider" )
public interface ProviderClient {
@GetMapping("/hi")
String hi(@RequestParam(value = "name", defaultValue = "zmx", required = false) String name);
}
寫一個測試接口,讓consumer去調用provider服務的接口:
import net.myibc.client.ProviderClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConsumerController {
@Autowired
ProviderClient providerClient;
@GetMapping("/hi-feign")
public String hiFeign(){
return providerClient.hi("feign");
}
}
啟動provider和consumer兩個工程,在nacos的【服務管理】-【服務列表】頁面查看,可見2個服務都已經注冊成功:
注意:這里啟動所有服務后,可能在控制台只能查看到某一個服務,或者查不到注冊的服務,查看nacos.log日志,發現報如下異常:
出現該異常的原因:當nacos客戶端升級為2.x版本后,新增了gRPC的通信方式,新增了兩個端口。這兩個端口在nacos原先的端口上(默認8848),進行一定偏移量自動生成。
端口與主端口的偏移量描述:
9848:客戶端gRPC請求服務端端口,用於客戶端向服務端發起連接和請求
9849:服務端gRPC請求服務端端口,用於服務間同步等
7848:Nacos 集群通信端口,用於Nacos 集群間進行選舉,檢測等
因此,不難猜出,出現該問題的原因是由於nacos2.x版本新增的這兩個端口沒有在宿主機開啟防火牆白名單,導致nacos實例之間無法進行服務間數據同步,因而在控制台無法查看到所有的服務注冊實例信息。只需要在nacos宿主機執行以下命令即可:
[root@localhost bin]# firewall-cmd --add-port=9848/tcp --permanent
success
[root@localhost bin]# firewall-cmd --add-port=9849/tcp --permanent
success
[root@localhost bin]# firewall-cmd --add-port=7848/tcp --permanent
success
[root@localhost bin]# firewall-cmd --reload
Success
注意:如果是采用VIP/nginx代理集群的話,需要在nginx配置9848和9849這兩個端口的TCP請求轉發,否則客戶端服務在啟動時因為無法連接到服務端這兩個端口,從而導致啟動失敗。如果不使用任何代理,在宿主機執行以上開啟端口白名單命令后,只需要在客戶端服務application配置文件里配置naocs集群的節點的ip和端口即可,配置如下:
Nginx配置nacos TCP轉發配置:
#配置nacos TCP轉發
stream {
upstream nacos1 {
server 192.168.230.129:9848;
server 192.168.230.130:9848;
server 192.168.230.131:9848;
}
server {
listen 9848;
proxy_pass nacos1;
}
upstream nacos2 {
server 192.168.230.129:9849;
server 192.168.230.130:9849;
server 192.168.230.131:9849;
}
server {
listen 9849;
proxy_pass nacos2;
}
}
4.2.3 服務調用
在瀏覽器上輸入http://127.0.0.1:8082/hi-feign,瀏覽器返回響應:
hello feign, i'm provider ,my port:8081
可見瀏覽器的請求成功調用了consumer服務的接口,consumer服務也成功地通過feign成功的調用了provider服務的接口。
4.2.4 負載均衡
Feign使用了Spring Cloud Loadbanlancer作為負載均衡器。可以通過修改provider的端口,再在本地啟動一個新的provider服務,那么本地有2個provider 服務,端口分別為8081 和8083。在瀏覽器上多次調用http://127.0.0.1:8082/hi-feign,瀏覽器會交替顯示:
hello feign, i'm provider ,my port:8081
hello feign, i'm provider ,my port:8083
此時控制台provider的實例數會變為2:
4.3 配置中心
4.3.1 配置中心客戶端
在父工程下新建nacos配置客戶端子模塊nacos-config-client,並在pom文件引入以下依賴:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
在bootstrap.yml(一定是bootstrap.yml文件,不是application.yml文件)文件配置以下內容:
spring:
application:
name: nacos-config-client
cloud:
nacos:
config:
server-addr: 192.168.230.1:80
file-extension: yaml
prefix: nacos-config-client
profiles:
active: dev
在上面的配置中,配置了nacos config server的地址,配置的擴展名是ymal(目前僅支持ymal和properties)。注意是沒有配置server.port的,sever.port的屬性在nacos中配置。上面的配置是和Nacos中的dataId 的格式是對應的,nacos的完整格式如下:
${prefix}-${spring.profile.active}.${file-extension}
- prefix 默認為 spring.application.name 的值,也可以通過配置項 spring.cloud.nacos.config.prefix來配置。
- spring.profile.active 即為當前環境對應的 profile,詳情可以參考 Spring Boot文檔。 注意:當 spring.profile.active 為空時,對應的連接符 - 也將不存在,dataId 的拼接格式變成 ${prefix}.${file-extension}
- file-exetension 為配置內容的數據格式,可以通過配置項 spring.cloud.nacos.config.file-extension 來配置。目前只支持 properties 和 yaml 類型。
啟動nacos,登陸控制台http://192.168.230.1/nacos,創建一個Data ID ,完整的配置如圖所示:
寫一個RestController,在Controller上添加@RefreshScope注解實現配置的熱加載。該注解只有加載需要動態更新配置的類上才會生效,如果將該注解加在啟動類上,其他類中的動態更新是不會生效的,代碼如下:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope
public class ConfigController {
@Value("${username:lily}")
private String username;
@RequestMapping("/username")
public String get() {
return username;
}
}
啟動工程nacos-provider,在瀏覽器上訪問http://127.0.0.1:8084/username,可以返回在nacos控制台上配置的username的值zhangsan3。在nacos 控制台上更改username的配置為lisi,在不重啟nacos-provider工程的情況下,重新訪問http://127.0.0.1:8084/username,返回的是修改后的值lisi,可見nacos作為配置中心實現了熱加載功能。
修改nacos中username配置前返回:
將nacos中username的值修改為lisi后重新請求返回:
注意:使用某些高版本的SpringCloud組件時,在啟動nacos配置客戶端服務的時候,可能會找不到bootstrap.yml配置文件,會報如下錯誤,只需要在系統環境變量中配置一下環境變量即可:spring.cloud.bootstrap.enabled=true
4.3.2 開啟權限認證
通常情況下,為了安全起見,客戶端服務在訪問nacos的時候都需要通過用戶名和密碼認證,nacos默認是沒有開啟認證的,如果需要開啟認證,首先需要編輯/nacos/conf/application.properties配置文件,找到nacos.core.auth.enabled=false配置項,如下圖,默認是false,表示權限認證是關閉的,修改為true即可開啟認證。修改完成后需要重啟所有nacos實例。
在application配置文件中配置nacos的用戶名和密碼,如下圖,首先將服務用戶名和密碼配置為一個不存在的用戶,啟動服務驗證權限配置是否生效:
啟動服務,發現報了以下異常,code=403和unknown user,說明權限驗證失敗:
將用戶名和密碼修改為正確的之后,再次啟動服務,可發現服務啟動正常:
至此,說明nacos的權限認證配置已生效。