服務(wù)注冊(cè)、發(fā)現(xiàn)和遠(yuǎn)程調(diào)用
早期文章

? ? ? ? 本篇文章介紹如何完成一個(gè)簡(jiǎn)單的服務(wù)注冊(cè)、發(fā)現(xiàn)和遠(yuǎn)程調(diào)用的 Demo,通過該 Demo 來(lái)學(xué)習(xí)和了解關(guān)于 Spring Cloud 相關(guān)的知識(shí)。
項(xiàng)目結(jié)構(gòu)
? ? ? ? 創(chuàng)建一個(gè) Maven 的聚合項(xiàng)目,使用 SpringBoot 作為其父項(xiàng)目,然后通過在其下添加子模塊來(lái)構(gòu)建一個(gè)簡(jiǎn)單的微服務(wù)的項(xiàng)目。
創(chuàng)建一個(gè) service_user 服務(wù)
? ? ? ? 在 Maven 項(xiàng)目下創(chuàng)建一個(gè) Module 作為子模塊,在項(xiàng)目的 POM 文件中加入 web 依賴,依賴如下:
<dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-webartifactId>dependency>
? ? ? ? 引入依賴后,創(chuàng)建 controller、service、impl 三個(gè)包,然后分別創(chuàng)建 UserController、UserService 和 UserServiceImpl 三個(gè)類文件,以及一個(gè)啟動(dòng)類。項(xiàng)目結(jié)構(gòu)如下圖。

? ? ? ??該項(xiàng)目來(lái)模擬通過輸入用戶的 id 來(lái)返回用戶名。UserController 代碼如下:
public class UserController{private UserService userService;public String getUser( Long id){return userService.getUser(id);}}
? ? ? ? UserServiceImpl 的代碼如下:
public class UserServiceImpl implements UserService{public String getUser(Long id) {????????String?name?=?null;????????????????if?(id?==?1)?{name = "admin";} else {name = "user";}return name;}}
? ? ? ? application.yml 配置文件如下:
server:port: 8001spring:application:name: service-user
? ? ? ? 注意,這里的 spring.application.name 的名字不能使用 下劃線,否則后面使用 OpenFeign 進(jìn)行遠(yuǎn)程調(diào)用時(shí)會(huì)報(bào)錯(cuò)。
? ? ? ? 因?yàn)閯?chuàng)建的子模塊是 Maven 類型的項(xiàng)目,因此需要手動(dòng)添加啟動(dòng)類,代碼如下:
public class ServiceUserApplication{public static void main(String[] args)??? {SpringApplication.run(ServiceUserApplication.class, args);}}
? ? ? ? 啟動(dòng)項(xiàng)目,訪問 8001 端口來(lái)請(qǐng)求我們的接口,如下所示。


創(chuàng)建 service_dict 項(xiàng)目
? ? ? ? 接著再創(chuàng)建一個(gè)子模塊,同樣引入 web 依賴,創(chuàng)建 controller、service 和 impl 的包,并創(chuàng)建 DictController、DictService 和 DictImpl 三個(gè)類,以及啟動(dòng)類和 application.yml 配置文件,項(xiàng)目結(jié)構(gòu)如下:

? ? ? ? 分別來(lái)寫 DictController 和 DictServiceImpl 兩個(gè)類的代碼,DictController 代碼如下:
public class DictController{private DictService dictService;public String getDict( Long id){return dictService.getDict(id);}}
? ? ? ? DictServiceImpl 代碼如下:
public class DictServiceImpl implements DictService{public String getDict(Long id) {String name = null;if (id == 1) {name = "管理員";} else {name = "普通用戶";}return name;}}
? ? ? ? application.yml 配置文件如下:
server:port: 8002spring:application:name: service-dict
? ? ? ??該項(xiàng)目的端口號(hào)為 8002,在添加了該項(xiàng)目的啟動(dòng)類之后,啟動(dòng)該項(xiàng)目,訪問 8002 接口,如下圖所示。


服務(wù)注冊(cè)
? ? ? ??可以看到,我們完成了兩個(gè)服務(wù),第一個(gè)服務(wù)通過 id 來(lái)獲取用戶名,第二個(gè)服務(wù)通過 id 來(lái)判斷用戶的類型。為了提高服務(wù)的可用性,可以將每個(gè)服務(wù)進(jìn)行水平擴(kuò)展,當(dāng)調(diào)用這些服務(wù)的時(shí)候,服務(wù)有不同的 IP 地址,或者有不同的端口號(hào)。想要調(diào)用服務(wù),就需要知道服務(wù)的 IP 地址和端口號(hào)。但是,這樣做有一定的問題,比如某個(gè)服務(wù)下線了,或者新增加了服務(wù),那么調(diào)用方需要?jiǎng)討B(tài)的了解服務(wù)的變化,而服務(wù)的地址直接在調(diào)用方里進(jìn)行管理,那么就不方便了。
? ? ? ? 在這種情況下,可以通過服務(wù)注冊(cè)中心來(lái)動(dòng)態(tài)的管理服務(wù),服務(wù)中心有 Zookeeper、Eureka、Nacos 等。這里我們使用 Nacos 來(lái)管理服務(wù)的注冊(cè)。這里,我們?cè)?Windows 下啟動(dòng) Nacos,啟動(dòng)畫面如下:

? ? ? ? 然后通過瀏覽器訪問 http://localhost:8848/nacos 來(lái)訪問 Nacos 的管理頁(yè)面,登錄后的管理頁(yè)面如下圖所示:

? ? ? ? 有了 Nacos 后我們修改上面的兩個(gè)子模塊,讓兩個(gè)子模塊將自己注冊(cè)到 Nacos 中。分別在 service_user 和 service_dict 的 POM 文件中增加如下的依賴。
<dependency><groupId>com.alibaba.cloudgroupId><artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId><version>2.2.6.RELEASEversion>dependency>
? ? ? ? 并在啟動(dòng)類上增加注解,注解如下:
@EnableDiscoveryClient? ? ? ??在 application.yml 中將注冊(cè)中心的地址配置一下,配置如下:
server:port: 8002spring:application:name: service-dictcloud:nacos:discovery:: 127.0.0.1:8848
? ? ? ? 然后重新啟動(dòng)這兩個(gè)子模塊,這樣,兩個(gè)服務(wù)就被注冊(cè)到了 Nacos 中,可以在 Nacos 中查看服務(wù)列表,如下圖所示。

? ? ? ? 通過上圖可以看到,已經(jīng)將自己注冊(cè)到了 Nacos 中。
服務(wù)發(fā)現(xiàn)與遠(yuǎn)程服務(wù)調(diào)用
? ? ? ??現(xiàn)在,我們讓 service_user 去調(diào)用 service_dict 來(lái)模擬一次遠(yuǎn)程調(diào)用的過程,也就是在調(diào)用 getUser 接口時(shí),在 getUser 接口的內(nèi)部去調(diào)用 getDict 接口。
? ? ? ? 創(chuàng)建一個(gè)子模塊,命名為 client,引入如下依賴:
<dependency><groupId>org.springframework.cloudgroupId><artifactId>spring-cloud-starter-openfeignartifactId><version>2.2.9.RELEASEversion>dependency>
? ? ? ? 有了該依賴以后,在項(xiàng)目中添加一個(gè)接口,通過該接口調(diào)用 service_dict 下的 getDict 接口。項(xiàng)目結(jié)構(gòu)如下:

? ? ? ? 這里只有 DictFeignClient 接口文件,該文件的代碼如下:
public interface DictFeignClient{String getDict( Long id);}
? ? ? ? 在注解 @FeignClient 中定義服務(wù)的名稱,在 @GetMapping 注解中寫入接口地址的 uri。將該子模塊引入 service_user 中,即可通過 OpenFeign 來(lái)完成遠(yuǎn)程調(diào)用。修改 service_user 中的 UserServiceImpl 文件,代碼如下:
public class UserServiceImpl implements UserService{private DictFeignClient dictFeignClient;public String getUser(Long id) {String name = null;String dictName = dictFeignClient.getDict(id);if (id == 1) {name = "admin";} else {name = "user";}return name + " " + dictName;}}
? ? ? ? 使用 @Autowired 來(lái)注入 DictFeignClient,調(diào)用 DictFeignClient 接口中定義的 getDict 來(lái)調(diào)用 service_dict 中的 getDict 接口。這樣就完成了遠(yuǎn)程調(diào)用。在 service_user 的啟動(dòng)類上加如下注解:
@EnableFeignClients(basePackages = "com.coderup2u")? ? ? ? 重新啟動(dòng) service_user 項(xiàng)目,并在瀏覽器中進(jìn)行訪問 getUser 接口,如下圖所示。


? ? ? ??從上面的代碼中可以看出,進(jìn)行遠(yuǎn)程調(diào)用時(shí),只定義了服務(wù)的名稱,也就是 通過 @FeignClient("service-dict"),但是并沒有給出該服務(wù)實(shí)際的地址和端口號(hào)。由此,我們可以想到,服務(wù)的 IP 地址和端口號(hào)是由服務(wù)中心獲取到的,即服務(wù)發(fā)現(xiàn)。服務(wù)發(fā)現(xiàn)會(huì)動(dòng)態(tài)的根據(jù)注冊(cè)中心服務(wù)列表的變化而變化。當(dāng)服務(wù)增加、下線,注冊(cè)中心管理的地址列表都會(huì)發(fā)生變化,從而提供給服務(wù)調(diào)用方,這樣就避免了調(diào)用了已經(jīng)下線的服務(wù),從而導(dǎo)致服務(wù)調(diào)用的失敗。他們大致的關(guān)系如下圖所示。

總結(jié)
? ? ? ? 本文通過簡(jiǎn)單的 Demo 介紹 Spring Cloud Alibaba 中 Nacos 的使用,服務(wù)注冊(cè)、服務(wù)發(fā)現(xiàn),及遠(yuǎn)程服務(wù)的調(diào)用。可以通過在簡(jiǎn)單的 Demo 上來(lái)完成 Nacos 集群的搭建,服務(wù)集群的搭建來(lái)進(jìn)行測(cè)試。

公眾號(hào)內(nèi)回復(fù)?【mongo】 下載 SpringBoot 整合操作 MongoDB 的文檔。
更多文章
