上一篇 Nacos服務(wù)發(fā)現(xiàn)-零、導(dǎo)學(xué)
1.1 從單體架構(gòu)到微服務(wù)
1.1.1 單體架構(gòu)
Web應(yīng)用程序發(fā)展的早期空民,大部分web工程師將所有的功能模塊打包到一起并放在一個(gè)Web容器中運(yùn)行刃唐,所有功能模塊使用同一個(gè)數(shù)據(jù)庫(kù)羞迷,同時(shí),它還提供API或者UI訪問(wèn)的Web模塊等画饥。
盡管也是模塊化邏輯衔瓮,但是最終還是會(huì)打包并部署為單體式應(yīng)用,這種將所有功能部署在一個(gè)Web容器中運(yùn)行的系統(tǒng)就叫做單體架構(gòu)(也稱:巨石型應(yīng)用)
優(yōu)點(diǎn):
- 開發(fā)效率高:模塊之間交互采用本地方法調(diào)用抖甘,并節(jié)省微服務(wù)之間的交互討論時(shí)間與開發(fā)成本热鞍。
- 容易測(cè)試:IDE都是為開發(fā)單個(gè)應(yīng)用設(shè)計(jì)的、容易測(cè)試---在本地就可以啟動(dòng)完整的系統(tǒng)衔彻。
- 容易部署:運(yùn)維成本小薇宠,直接打包為一個(gè)完整的包,拷貝到Web容器的某個(gè)目錄下即可運(yùn)行米奸。
但是昼接,上述的好處是有條件的,它適用于小型簡(jiǎn)單應(yīng)用悴晰,對(duì)于大規(guī)模的復(fù)雜應(yīng)用,就會(huì)展現(xiàn)出來(lái)以下的不足:
缺點(diǎn):
- 復(fù)雜性逐漸變高逐工,可維護(hù)性逐漸變差 :所有業(yè)務(wù)模塊部署在一起铡溪,復(fù)雜度越來(lái)越高,修改時(shí)牽一發(fā)動(dòng)全身泪喊。
- 版本迭代速度逐漸變慢:修改一個(gè)地方就要將整個(gè)應(yīng)用全部編譯棕硫、部署、啟動(dòng)時(shí)間過(guò)長(zhǎng)袒啼、回歸測(cè)試周期過(guò)長(zhǎng)哈扮。
- 阻礙技術(shù)創(chuàng)新:若更新技術(shù)框架,除非你愿意將系統(tǒng)全部重寫蚓再,無(wú)法實(shí)現(xiàn)部分技術(shù)更新滑肉。
- 無(wú)法按需伸縮:通過(guò)冗余部署完整應(yīng)用的方式來(lái)實(shí)現(xiàn)水平擴(kuò)展,無(wú)法針對(duì)某業(yè)務(wù)按需伸縮摘仅。
1.1.2 微服務(wù)
許多大型公司靶庙,通過(guò)采用微服務(wù)架構(gòu)解決了上述問(wèn)題。其思路不是開發(fā)一個(gè)巨大的單體式的應(yīng)用娃属,而是將應(yīng)用分解為小的六荒、互相連接的微服務(wù)。
一個(gè)微服務(wù)一般完成某個(gè)特定的功能矾端,比如訂單服務(wù)掏击、用戶服務(wù)等等。每一個(gè)微服務(wù)都是完整應(yīng)用秩铆,都有自己的業(yè)務(wù)邏輯和數(shù)據(jù)庫(kù)砚亭。一些微服務(wù)還會(huì)發(fā)布API給其它微服務(wù)和應(yīng)用客戶端使用。
比如,根據(jù)前面描述系統(tǒng)可能的分解如下:
每一個(gè)業(yè)務(wù)模塊都使用獨(dú)立的服務(wù)完成钠惩,這種微服務(wù)架構(gòu)模式也影響了應(yīng)用和數(shù)據(jù)庫(kù)之間的關(guān)系柒凉,不像傳統(tǒng)多個(gè)業(yè)務(wù)模塊共享一個(gè)數(shù)據(jù)庫(kù),微服務(wù)架構(gòu)每個(gè)服務(wù)都有自己的數(shù)據(jù)庫(kù)篓跛。
優(yōu)點(diǎn):
- 分而治之膝捞,職責(zé)單一;易于開發(fā)愧沟、理解和維護(hù)蔬咬、方便團(tuán)隊(duì)的拆分和管理
- 可伸縮;能夠單獨(dú)的對(duì)指定的服務(wù)進(jìn)行伸縮
- 局部容易修改沐寺,容易替換林艘,容易部署,有利于持續(xù)集成和快速迭代
- 不會(huì)受限于任何技術(shù)棧
1.2 什么是服務(wù)發(fā)現(xiàn)
在微服務(wù)架構(gòu)中混坞,整個(gè)系統(tǒng)會(huì)按職責(zé)能力劃分為多個(gè)服務(wù)狐援,通過(guò)服務(wù)之間協(xié)作來(lái)實(shí)現(xiàn)業(yè)務(wù)目標(biāo)。這樣在我們的代碼中免不了要進(jìn)行服務(wù)間的遠(yuǎn)程調(diào)用究孕,服務(wù)的消費(fèi)方要調(diào)用服務(wù)的生產(chǎn)方啥酱,為了完成一次請(qǐng)求,消費(fèi)方需要知道服務(wù)生產(chǎn)方的網(wǎng)絡(luò)位置(IP地址和端口號(hào))厨诸。
我們的代碼可以通過(guò)讀取配置文件的方式讀取服務(wù)生產(chǎn)方網(wǎng)絡(luò)位置镶殷,如下:
我們通過(guò)Spring Boot技術(shù)很容易實(shí)現(xiàn):
(1) 新建Maven項(xiàng)目
父工程pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.zhi365</groupId>
<artifactId>nacosdiscover</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<modules>
<module>service-provider</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<!-- 這個(gè)問(wèn)題太坑了,是boot-maven中間那個(gè)【-】的問(wèn)題微酬,重新輸入就好用了 -->
<!--<artifactId>spring-boot‐maven-plugin</artifactId>-->
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
(2) ServiceProvider(服務(wù)生產(chǎn)者)
ServiceProvider是服務(wù)的生產(chǎn)方绘趋,暴露/service服務(wù)地址,實(shí)現(xiàn)代碼如下:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nacosdiscover</artifactId>
<groupId>net.zhi365</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-provider</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
ServiceProviderApplication.java
/**
* @author Xin.li
* @date 2021-01-14 23:38
*/
@SpringBootApplication
public class ServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProviderApplication.class, args);
}
}
ServiceController.java
package net.zhi365.serviceprovider.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Xin.li
* @date 2021-01-14 23:44
*/
@RestController
public class ServiceController {
@GetMapping( value = "/service")
public String service() {
return "provider invoke";
}
}
application.yml
server:
port: 56010
(3) Service A(服務(wù)消費(fèi)者)
實(shí)現(xiàn)代碼:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nacosdiscover</artifactId>
<groupId>net.zhi365</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-rest-consumer-bootstrap</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
SpringRestConsumerBootStrapApplication.java
package net.zhi365.springrestconsumerbootstrap;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Xin.li
* @date 2021-01-15 0:03
*/
@SpringBootApplication
public class SpringRestConsumerBootStrapApplication {
public static void main(String[] args) {
SpringApplication.run(SpringRestConsumerBootStrapApplication.class, args);
}
}
ConsumerController.java
package net.zhi365.springrestconsumerbootstrap.web;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @author Xin.li
* @date 2021-01-15 0:08
*/
@RestController
public class ConsumerController {
@Value("${provider.address}")
private String providerAddress;
@GetMapping(value = "/service")
public String service(){
RestTemplate restTemplate = new RestTemplate();
// 調(diào)用服務(wù)
String providerResult = restTemplate.getForObject("http://" + providerAddress +"/service", String.class );
return "consumer invoke | " + providerResult;
}
}
application.yml
server:
port: 56020
# 服務(wù)生產(chǎn)方地址
provider:
address: 127.0.0.1:56010
看上去很完美颗管,但是陷遮,仔細(xì)考慮以下,此方案對(duì)于微服務(wù)應(yīng)用而言行不通忙上。
首先拷呆,微服務(wù)可能是部署在云環(huán)境的,服務(wù)實(shí)例的網(wǎng)絡(luò)位置或許是動(dòng)態(tài)分配的疫粥。另外茬斧,每一個(gè)服務(wù)一般會(huì)有多個(gè)實(shí)例來(lái)做負(fù)載均衡,由于宕機(jī)或升級(jí)梗逮,服務(wù)實(shí)例網(wǎng)絡(luò)地址會(huì)經(jīng)常動(dòng)態(tài)改變项秉。再者,每一個(gè)服務(wù)也可能應(yīng)對(duì)臨時(shí)訪問(wèn)壓力增加新的服務(wù)節(jié)點(diǎn)慷彤。
如下圖所示:
基于以上的問(wèn)題参滴,服務(wù)之間如何相互感知?服務(wù)如何管理失乾?這就是服務(wù)發(fā)現(xiàn)的問(wèn)題了。如下圖:
上圖中服務(wù)實(shí)例本身并不記錄服務(wù)生產(chǎn)方的網(wǎng)絡(luò)地址锚沸,所有服務(wù)實(shí)例內(nèi)部都會(huì)包含服務(wù)發(fā)現(xiàn)客戶端。
(1)在每個(gè)服務(wù)啟動(dòng)時(shí)會(huì)向服務(wù)發(fā)現(xiàn)中心上報(bào)自己的網(wǎng)絡(luò)位置涕癣。這樣哗蜈,在服務(wù)發(fā)現(xiàn)中心內(nèi)部會(huì)形成一個(gè)服務(wù)注冊(cè)表,服務(wù)注冊(cè)表是服務(wù)發(fā)現(xiàn)的核心部分坠韩,是包含所有服務(wù)實(shí)例的網(wǎng)絡(luò)地址的數(shù)據(jù)庫(kù)距潘。
(2)服務(wù)發(fā)現(xiàn)客戶端會(huì)定期從服務(wù)發(fā)現(xiàn)中心同步服務(wù)注冊(cè)表 ,并緩存在客戶端只搁。
(3)當(dāng)需要對(duì)某服務(wù)進(jìn)行請(qǐng)求時(shí)音比,服務(wù)實(shí)例通過(guò)該注冊(cè)表,定位目標(biāo)服務(wù)網(wǎng)絡(luò)地址氢惋。若目標(biāo)服務(wù)存在多個(gè)網(wǎng)絡(luò)地址洞翩,則使用負(fù)載均衡算法從多個(gè)服務(wù)實(shí)例中選擇出一個(gè),然后發(fā)出請(qǐng)求焰望。
總結(jié)一下菱农,在微服務(wù)環(huán)境中,由于服務(wù)運(yùn)行實(shí)例的網(wǎng)絡(luò)地址是不斷動(dòng)態(tài)變化的柿估,服務(wù)實(shí)例數(shù)量的動(dòng)態(tài)變化 ,因此無(wú)法使用固定的配置文件來(lái)記錄服務(wù)提供方的網(wǎng)絡(luò)地址陷猫,必須使用動(dòng)態(tài)的服務(wù)發(fā)現(xiàn)機(jī)制用于實(shí)現(xiàn)微服務(wù)間的相互感知秫舌。各服務(wù)實(shí)例會(huì)上報(bào)自己的網(wǎng)絡(luò)地址,這樣服務(wù)中心就形成了一個(gè)完整的服務(wù)注冊(cè)表绣檬,各服務(wù)實(shí)例會(huì)通過(guò)服務(wù)發(fā)現(xiàn)中心來(lái)獲取訪問(wèn)目標(biāo)服務(wù)的網(wǎng)絡(luò)地址足陨,從而實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)的機(jī)制。