javascript
Spring Cloud Alibaba——Nacos实现服务治理
引言
本博客總結(jié)微服務(wù)開發(fā)中各個(gè)微服務(wù)調(diào)用的實(shí)現(xiàn),并使用 Nacos 完成服務(wù)注冊(cè)和發(fā)現(xiàn)。
文章中會(huì)涉及到 maven 的使用,以及 spring boot 的一些知識(shí)。開發(fā)工具采用 IDEA 2020.2。
設(shè)計(jì)一個(gè)電商訂單和商品購(gòu)買微服務(wù),實(shí)現(xiàn)微服務(wù)的注冊(cè)發(fā)現(xiàn)與調(diào)用。
一、模塊設(shè)計(jì)
本案例采用電商網(wǎng)站作為展示,涉及到的三個(gè)微服務(wù)有:shop-user、shop-product、shop-order,還有一個(gè)公共依賴模塊shop-common。他們的依賴、調(diào)用關(guān)系如下所示:
shop-user 是用戶微服務(wù),端口是807x
shop-product 是商品微服務(wù),端口是808x
shop-order 是訂單微服務(wù),端口是809x
三個(gè)微服務(wù)之間可以通過HTTP請(qǐng)求相互調(diào)用業(yè)務(wù)邏輯。
二、創(chuàng)建Maven父工程
為了便于依賴的管理,和項(xiàng)目維護(hù),在實(shí)際生產(chǎn)中,往往通過父工程來管理各個(gè) maven 微服務(wù)模塊,和maven 依賴模塊。
(在這里我需要簡(jiǎn)單說明一下這個(gè)大的maven 工程下面,如何理解各個(gè)子模塊的關(guān)系。案例中包含了三個(gè)微服務(wù)(shop-user/shop-product/shop-order),和一個(gè)公共依賴模塊(shop-common),它們都會(huì)作為一個(gè) maven 子模塊存放到父工程目錄下,但實(shí)際上,在實(shí)際部署的時(shí)候,三個(gè)微服務(wù)是分開部署的,因?yàn)?strong>三個(gè)微服務(wù)之間的關(guān)系,除了通過父工程來統(tǒng)一維護(hù)一些依賴版本之外,沒有什么在代碼層面的耦合關(guān)系。而公共依賴模塊則在代碼層面耦合到各個(gè)模塊中,部署之后,也是你中有我的概念。)
首先 New ——> Project ——> Maven ,選擇好JDK 版本后,直接Next,跳過 archetype 選項(xiàng)。
填寫必要的項(xiàng)目名稱和存儲(chǔ)位置,maven坐標(biāo)等信息,點(diǎn)擊finish:
idea可以快速為我們創(chuàng)建并打開新項(xiàng)目,由于? Maven 父工程只做版本管理,不需要寫任何代碼,因此一般都會(huì)直接刪除 src 目錄:
緊接著,我們需要修改父工程 pom 文件。它主要需要負(fù)責(zé)兩件事:1、指定父工程? ?2、依賴版本的鎖定
<?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>org.morty</groupId><artifactId>shop</artifactId><version>1.0-SNAPSHOT</version><!-- 指定父工程--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.5.RELEASE</version></parent><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><!-- 版本鎖定--><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Greenwich.SR5</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.1.1.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement> </project>下表展示了 Spring Cloud Alibaba & Spring Cloud & Spring Boot 兼容關(guān)系:
| ------- | ------- | ------- |
| Spring Cloud Hoxton | 2.2.x.RELEASE | 2.2.x.RELEASE |
| Spring Cloud Greenwich | 2.1.x.RELEASE | 2.1.x.RELEASE |
| Spring Cloud Finchley | 2.0.x.RELEASE | 2.0.x.RELEASE |
| Spring Cloud Edgware | 1.5.x.RELEASE | 1.5.x.RELEASE |
三、創(chuàng)建基礎(chǔ)依賴模塊
new——>Module...
添加必要的依賴:
<?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>shop</artifactId><groupId>org.morty</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>shop-common</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.58</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies> </project>創(chuàng)建domain實(shí)體類,User、Product、Order,這樣,其他三個(gè)微服務(wù)可以依賴使用:
package com.morty.domain;import lombok.Data;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id;@Entity(name = "shop_user") @Data public class User {@Id// 數(shù)據(jù)庫自增@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer uid;private String username;private String password;private String telephone; } package com.morty.domain;import lombok.Data;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id;@Data @Entity(name = "shop_product") public class Product {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer pid;private String pname;// 商品價(jià)格private Double pprice;// 庫存private Integer stock; } package com.morty.domain;import lombok.Data;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id;@Data @Entity(name = "shop_order") public class Order {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer oid;private Integer uid;private String username;private Integer pid;private String pname;private Double pprice;/** 購(gòu)買數(shù)量*/private Integer number; }四、創(chuàng)建微服務(wù)模塊
依次創(chuàng)建shop-user、shop-product、shop-order 三個(gè)微服務(wù),并依賴 shop-common。篇幅有限,以 shop-product 為例。
1、和shop-common的創(chuàng)建方式一樣,新建一個(gè) Module,并命名 shop-product,修改pom文件,添加 shop-common依賴和 web starter:
<dependencies><!-- 依賴基礎(chǔ)模塊--><dependency><groupId>org.morty</groupId><artifactId>shop-common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>2、創(chuàng)建spring boot 啟動(dòng)類:
@SpringBootApplication @Slf4j public class ProductApplication {public static void main(String[] args) {SpringApplication.run(ProductApplication.class);log.info("-----------啟動(dòng)成功------------");} }3、修改配置文件:
server:port: 8081 spring:application:name: service-productdatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=trueusername: rootpassword: 123456jpa:properties:hibernate:hbm2ddl:auto: update# InnoDB方言dialect: org.hibernate.dialect.MySQL5InnoDBDialect然后就是創(chuàng)建 controller、service、dao:
@Slf4j @RestController @RequestMapping("/product") public class ProductController {@Autowiredprivate ProductService productService;/*** 查詢訂單信息* @param pid* @return*/@GetMapping("/{pid}")public Product getProduct(@PathVariable("pid") Integer pid) {log.info("收到查詢商品信息請(qǐng)求,商品編號(hào):{}", pid);Product product = productService.getProduct(pid);log.info("商品信息查詢成功:{}", JSON.toJSONString(product));return product;} } @Service public class ProductService {@Autowiredprivate ProductDao productDao;public Product getProduct(Integer productId) {return productDao.findById(productId).get();} } public interface ProductDao extends JpaRepository<Product, Integer> { }最后,手動(dòng)創(chuàng)建 shop 數(shù)據(jù)庫,然后啟動(dòng)服務(wù),可以看到表已經(jīng)自動(dòng)創(chuàng)建好了,向 shop_product 表插入一條商品信息:
INSERT INTO `shop_product`(pname, pprice, stock) VALUES('皮大衣', '120', '20');打開瀏覽器,訪問接口,可以正常返回:
五、微服務(wù)調(diào)用
按照類似的步驟創(chuàng)建好了三個(gè)微服務(wù)之后,我們來實(shí)現(xiàn)訂單到商品的微服務(wù)調(diào)用。需要說明的是,任何兩個(gè)服務(wù)之間都是可以通過http請(qǐng)求進(jìn)行調(diào)用,而不完全需要服務(wù)治理功能,也就是說,如果我們指定了ip和端口號(hào),實(shí)際上就可以實(shí)現(xiàn)微服務(wù)的調(diào)用。
為了演示方便,這里只列出關(guān)鍵代碼,并去掉了Service的接口層。
提供必要的 restTemplate:
@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}DAO:
public interface OrderDao extends JpaRepository<Order, Integer> { }Service:
@Service public class OrderService {@Autowiredprivate OrderDao orderDao;public void createOrder(Order order) {orderDao.save(order);} }Controller:
@Slf4j @RestController @RequestMapping("/order") public class OrderController {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate OrderService orderService;/**** 下單* @param pid* @return*/@GetMapping("/prod/{pid}")public Order order(@PathVariable("pid") Integer pid) {log.info("接收到{}號(hào)商品的下單請(qǐng)求,準(zhǔn)備調(diào)用商品微服務(wù)", pid);// 調(diào)用商品微服務(wù),查詢商品信息Product prod = restTemplate.getForObject("http://localhost:8081/product/" + pid, Product.class);log.info("查詢到{}號(hào)商品信息,內(nèi)容是:{}", pid, JSON.toJSONString(prod));// 下單(即創(chuàng)建訂單并保存)Order order = new Order();order.setUid(1);order.setUsername("測(cè)試用戶");order.setPid(pid);order.setPname(prod.getPname());order.setPprice(prod.getPprice());order.setNumber(1);// 訂單入庫orderService.createOrder(order);log.info("創(chuàng)建訂單成功,訂單信息為:{}", JSON.toJSONString(order));return order;} }然后在配置文件中指定 8091 端口號(hào),以及數(shù)據(jù)庫地址等必要信息。啟動(dòng) OrderApplication 和 ProductApplication,調(diào)用 /order/prod/{pid} 接口:
檢查控制臺(tái)打印的日志:
訂單微服務(wù):
商品微服務(wù):
同時(shí)數(shù)據(jù)庫也出現(xiàn)了剛才添加的訂單記錄:
六、服務(wù)治理
在前面的微服務(wù)調(diào)用案例中,我們通過 restTemplate 對(duì)象,配合 ip + port 的形式,實(shí)現(xiàn)了最簡(jiǎn)單的訂單微服務(wù)到商品微服務(wù)的調(diào)用邏輯。
但這在實(shí)際生產(chǎn)中會(huì)存在較大的問題:
1、一旦服務(wù)提供者的地址發(fā)生變化,就不得不去修改服務(wù)調(diào)用者的代碼,即便是使用配置文件也治標(biāo)不治本。
2、在高并發(fā)場(chǎng)景中,服務(wù)一般需要進(jìn)行集群部署,會(huì)有多個(gè)服務(wù)提供者實(shí)例。那么就需要通過負(fù)載均衡調(diào)用不同的服務(wù)提供者,來分散單個(gè)服務(wù)實(shí)例的訪問壓力,上面這種調(diào)用方式顯然無法滿足負(fù)載均衡的要求。
3、一旦微服務(wù)變得越來越多,如果管理服務(wù)清單將會(huì)是一個(gè)大問題。
基于以上幾點(diǎn),就有了服務(wù)治理的概念:
服務(wù)治理是微服務(wù)架構(gòu)中最核心、最基本的模塊。用于實(shí)現(xiàn)各個(gè)微服務(wù)的自動(dòng)化注冊(cè)和發(fā)現(xiàn)。
服務(wù)注冊(cè):在服務(wù)治理框架中,都會(huì)構(gòu)建一個(gè)注冊(cè)中心。每個(gè)服務(wù)單元向注冊(cè)中心登記自己提供服務(wù)的詳細(xì)信息。注冊(cè)中心會(huì)基于這些微服務(wù)的詳細(xì)信息,生成一張服務(wù)清單。注冊(cè)中心需要以心跳的方式檢測(cè)清單中服務(wù)是否可用,如果發(fā)現(xiàn)心跳異常的服務(wù),會(huì)從服務(wù)清單中剔除不可用的服務(wù)。
服務(wù)發(fā)現(xiàn):服務(wù)消費(fèi)者向注冊(cè)中心咨詢服務(wù),并獲取所有服務(wù)的實(shí)例清單,實(shí)現(xiàn)對(duì)具體服務(wù)的訪問。
常用的服務(wù)治理框架有:
ZooKeeper:是一個(gè)分布式服務(wù)框架,是Apache Hadoop 的一個(gè)子項(xiàng)目,它主要用來解決分布式應(yīng)用中經(jīng)常遇到的一些數(shù)據(jù)管理問題,如統(tǒng)一命名服務(wù)、狀態(tài)同步服務(wù)、集群管理、分布式應(yīng)用配置項(xiàng)管理等。
Eureka:是Spring Cloud Netfix 中的重要組件,主要作用是做服務(wù)注冊(cè)和發(fā)現(xiàn),但現(xiàn)在已經(jīng)閉源。
Nacos:是一個(gè)更易于構(gòu)建云原生應(yīng)用的動(dòng)態(tài)服務(wù)發(fā)現(xiàn)、配置管理和服務(wù)管理平臺(tái)。它是 Spring cloud Alibaba 的組件之一,負(fù)責(zé)服務(wù)注冊(cè)發(fā)現(xiàn)和服務(wù)配置,可以認(rèn)為是 Eureka + Config 的組合升級(jí)版服務(wù)治理框架。
七、Nacos-discovery 實(shí)現(xiàn)微服務(wù)調(diào)用
7.1 啟動(dòng) Nacos 服務(wù)
首先,如果想使用 Nacos 注冊(cè)中心服務(wù),必須到官網(wǎng)上下載啟動(dòng)壓縮包。值得一提的是,原來的 Eureka 是通過 Spring boot 構(gòu)建一個(gè)專門用于實(shí)現(xiàn)注冊(cè)發(fā)現(xiàn)的微服務(wù),這需要我們手動(dòng)去構(gòu)建這樣一個(gè)重要的架構(gòu)組件,但是 Nacos 則提供了獨(dú)立的啟動(dòng)程序,讓開發(fā)者可以開箱即用,進(jìn)一步提高了微服務(wù)部署的效率。
nacos 下載地址:https://nacos.io/zh-cn/docs/quick-start.html
不論你是在 Windows 環(huán)境上學(xué)習(xí)和練習(xí),還是在 Linux 服務(wù)器上安裝部署,都只需要簡(jiǎn)單的一鍵啟動(dòng)即可。
啟動(dòng)成功后,我們通過瀏覽器訪問控制臺(tái),默認(rèn)用戶名和密碼都是 nacos,下圖登錄成功后進(jìn)入首頁:
7.2 將微服務(wù)注冊(cè)到 Nacos
以 shop-product 為例,演示如何將微服務(wù)注冊(cè)到 Nacos。
1、微服務(wù)中添加 nacos 客戶端依賴:
<!-- nacos 客戶端--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-nacos-discovery</artifactId></dependency>2、為主類添加@EnableDiscoveryClient 注解:
@Slf4j @EnableDiscoveryClient @SpringBootApplication public class ProductApplication {public static void main(String[] args) {SpringApplication.run(ProductApplication.class);log.info("-----------啟動(dòng)成功------------");} }3、配置 Nacos Server 地址:
spring:cloud:nacos:discovery:server-addr: localhost:88484、啟動(dòng)微服務(wù),查看 Nacos 控制臺(tái):
7.3 Nacos 實(shí)現(xiàn)微服務(wù)調(diào)用
針對(duì)前面第五節(jié)訂單到商品的微服務(wù)調(diào)用方式,我們調(diào)整一下restTemplate 代碼,以服務(wù)治理推薦的方式來實(shí)現(xiàn)微服務(wù)調(diào)用。
引入服務(wù)發(fā)現(xiàn)客戶端對(duì)象:
@Autowired private DiscoveryClient discoveryClient;修改代碼:
啟動(dòng) shop-order 、shop-product 微服務(wù),它們會(huì)自動(dòng)注冊(cè)到 Nacos 中。
重新調(diào)用下單接口,可以看到接口依然調(diào)用成功:
總結(jié)
微服務(wù)注冊(cè)中心的主要功能是負(fù)責(zé)服務(wù)注冊(cè)和發(fā)現(xiàn),它會(huì)生成一張注冊(cè)服務(wù)清單,可以簡(jiǎn)單理解為一個(gè)服務(wù)名稱和對(duì)應(yīng)服務(wù)地址的對(duì)照表,服務(wù)消費(fèi)者使用服務(wù)名稱調(diào)用服務(wù)提供者的接口時(shí),會(huì)直接發(fā)送到對(duì)應(yīng)地址:
微服務(wù)如果想要注冊(cè)到 Nacos Server,需要完成三件事:
1、添加 nacos-discovery 依賴
2、啟動(dòng)服務(wù)發(fā)現(xiàn)客戶端,即添加 @EnableDiscoveryClient 注解到主類
3、配置 Nacos server 注冊(cè)中心地址和端口號(hào)
?
總結(jié)
以上是生活随笔為你收集整理的Spring Cloud Alibaba——Nacos实现服务治理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java so jnienv_JNI初步
- 下一篇: Maven的作用