這一節開始,我們將通過代碼逐步解決我們上一節中關於分佈式和集羣的幾個問題!實際的編碼會讓我們對SpringCloud、微服務、分佈式、集羣瞭解的更深刻。
一、父子項目
接下來就要開始做 springcloud 項目了。 springcloud 比較特別,它由多個微服務組成, 所謂的微服務,就是 springboot,。
所以可以說 springcloud 由多個 springboot 項目組成, 而這些 springboot 之間又是圍繞一個共同目的而存在的。
所以,爲了便於組織這些 springboot 項目,我們會採用 maven 父子-聚合 項目的方式來開發。下面 我們來看一下如何使用IDEA創建maven父子聚合項目。
通過 maven 可以創建父子-聚合項目。 所謂的父子項目,即有一個父項目,有多個子項目。
這些子項目,在業務邏輯上,都歸納在這個父項目下,並且一般來說,都會有重複的jar包共享。
所以常用的做法會把重複的 jar 包都放在父項目下進行依賴,那麼子項目就無需再去依賴這些重複的 jar 包了。
1、新建一個父maven項目
這裏我們給它命名位父maven項目。
2、修改pom文件
idea 自動生成的 pom.xml 有一大堆東西,很多都用不着。 修改爲如下的內容。
2.1. 默認是 jar, 修改爲pom。 這樣纔可以作爲父項目存在。
<packaging>pom</packaging>
2.2. 增加 hutool jar 和 junit 包的依賴,用於後來子項目裏觀察對其的調用。
pom文件代碼如下:
<?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>com.topmap</groupId>
<artifactId>parentMavenProject</artifactId>
<version>1.0-SNAPSHOT</version>
<name>parentMavenProject</name>
<description>parentMavenProject</description>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.3.1</version>
</dependency>
</dependencies>
</project>
3、創建一個子項目
所謂的子項目,其實是maven module.
右鍵點擊 parentMavenProject->New->Module.
這樣我們就在父maven項目下面新建了子maven項目。
子項目的pom文件中多了一個<parent>,這是對父項目的依賴。
父maven項目多了一個<modules>,這表示對子項目的關聯。
下面我們來測試一下子項目是否已經跟父項目取得關聯(這裏在子項目中使用父項目依賴的jar包進行測試)
在 childMavenProject 下新建 TestHutool類,並運行。
這裏我們在子項目中使用了父項目中調價的依賴中的方法,並且運行成功了!這說明子項目,能夠使用 父項目中的 jar 包了。
4、父子項目整體架構
這就是我們的整體目錄結構,可以發現 childMavenProject 是位於 parentMavenProject下面的。 所以如果將來有 childMavenProject1, childMavenProject2, childMavenProject3 也會放在這麼一個目錄下,就方便管理了。
二、服務註冊中心
下面我們根據上面新建父子項目的流程,再新建父項目---springcloud,子項目---eureka-server,這裏eureka-server就是服務註冊中心。
上面的父子項目創建好了之後把父項目springcloud的 src 目錄刪了,因爲父項目裏用不到,看一下父項目的pom:
<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>com.topmap</groupId>
<artifactId>springcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud</name>
<description>springcloud</description>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.3.1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
這個pom裏有幾點重要信息:
1. 依賴 springboot 版本是 2.0.3
2. 有基於 hutool 的依賴, hutool 是一個工具類,用起來很方便
3. springcloud 用的版本是 Finchley
再新建子項目 eureka-server
再來看一下子項目的pom文件:
<?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>springcloud</artifactId>
<groupId>com.topmap</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
這裏添加了springcloud的spring-cloud-starter-netflix-eureka-server jar 包,就是用來做服務註冊的一個jar包。
然後我們新建一個EurekaServer 啓動類。
EurekaServer ,它扮演的角色是註冊中心,用於註冊各種微服務,以便於其他微服務找到和訪問。
EurekaServer 本身就是個 Springboot 微服務, 所以它有 @SpringBootApplication 註解。
@EnableEurekaServer 表示這是個 EurekaServer 。
啓動的時候,端口號沒有在配置文件裏,而是直接放在代碼裏,這麼做是爲了提示同學這個端口號是否被佔用了,否則有時候端口號被佔用了,老是啓動不了,搞得自己暈頭轉向的。
NetUtil 是 Hutool 的工具,在父項目的 pom.xml 裏已經依賴了。
最後我們來配置yml文件,提供 eureka 的相關信息。
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
spring:
application:
name: eureka-server
來解釋一下上面的信息:
hostname: localhost 表示主機名稱。
registerWithEureka:false. 表示是否註冊到服務器。 因爲它本身就是服務器,所以就無需把自己註冊到服務器了。
fetchRegistry: false. 表示是否獲取服務器的註冊信息,和上面同理,這裏也設置爲 false。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ 自己作爲服務器,公佈出來的地址。 比如後續某個微服務要把自己註冊到 eureka server, 那麼就要使用這個地址: http://localhost:8761/eureka/
name: eurka-server 表示這個微服務本身的名稱是 eureka-server
最後啓動EurekaServerApplication:
這就是註冊中心的管理界面,主要看 :Instances currently registered with Eureka, 可以發現信息是:No instances available。
這表示 暫時還沒有微服務註冊進來。
下面我們會進行微服務的註冊!
三、註冊數據微服務
創建子項目 product-data-service。
修改 pom.xml 爲如下:
spring-cloud-starter-netflix-eureka-client 表示這是個 eureka 客戶端。
spring-boot-starter-web: 表示這是個web服務,會提供控制層
<?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>springcloud</artifactId>
<groupId>com.topmap</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>product-data-service</artifactId>
<dependencies>
<!-- eureka 客戶端依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--web服務依賴,會提供控制層-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
然後我們編寫product的實體類:
package com.topmap.pojo;
/**
* @Author: MaHuadong
* @Date: 2019/12/30 15:22
* @Version 1.0
*/
public class Product {
private int id;
private String name;
private int price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public Product(){
}
public Product(int id,String name,int price){
super();
this.id=id;
this.name = name;
this.price = price;
}
}
再新建service:
service類提供一個 Product 集合。
package com.topmap.service;
import com.topmap.pojo.Product;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: MaHuadong
* @Date: 2019/12/30 15:39
* @Version 1.0
*/
@Service
public class ProductService {
@Value("${server.port}")
String port;
public List<Product> listProducts(){
List<Product> ps = new ArrayList<>();
ps.add(new Product(1,"product a from port:"+port, 50));
ps.add(new Product(2,"product b from port:"+port, 150));
ps.add(new Product(3,"product c from port:"+port, 250));
return ps;
}
}
需要注意的是,這裏把 端口號 放進了產品信息裏。 這個數據服務會做成集羣,那麼訪問者爲了分辨到底是從哪個數據微服務取的數據,就需要提供個端口號,才能意識到是從不同的微服務得到的數據。
再新建Controller:
控制類,把 Product 集合轉換成 json 數組。
下面我們再來寫這個子項目product-data-service的啓動類:
啓動類, 考慮到要做集羣。 所以讓用戶自己輸入端口,推薦 8001,8002,8003.
但是每次測試都要輸入端口號又很麻煩,所以做了個 Future類,如果5秒不輸入,那麼就默認使用 8001端口。
package com.topmap;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NetUtil;
import cn.hutool.core.util.NumberUtil;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @Author: MaHuadong
* @Date: 2019/12/30 15:54
* @Version 1.0
*/
@SpringBootApplication
//代表這是一個Eureka客戶端
@EnableEurekaClient
public class ProductDataServiceApplication {
public static void main(String[] args){
int port=0;
int defaultPort=8001;
Future<Integer> future= ThreadUtil.execAsync(() ->{
int p=0;
System.out.println("請於5秒鐘內輸入端口號, 推薦 8001 、 8002 或者 8003,超過5秒將默認使用 " + defaultPort);
Scanner scanner=new Scanner(System.in);
while(true){
String strPort=scanner.nextLine();
if (!NumberUtil.isInteger(strPort)){
System.err.println("只能是數字");
continue;
}else {
p = Convert.toInt(strPort);
scanner.close();
break;
}
}
return p;
});
try {
port=future.get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e){
port = defaultPort;
}
if (!NetUtil.isUsableLocalPort(port)){
System.err.printf("端口%d被佔用了,無法啓動%n", port );
System.exit(1);
}
new SpringApplicationBuilder(ProductDataServiceApplication.class).properties("server.port=" + port).run(args);
}
}
最後我們來配置子項目product-data-service的配置文件yml:
# server:
# port: 因爲會啓動多個 product-data-service, 所以端口號由用戶自動設置,推薦 8001,8002,8003
spring:
application:
# 微服務的名稱
name: product-data-service
eureka:
client:
serviceUrl:
# 註冊中心的地址
defaultZone: http://localhost:8761/eureka/
好了,我們現在父子項目都寫好了,即微服務的註冊中心和客戶端應用都寫好了。
現在再來看一下http://localhost:8761/中的Instances currently registered with Eureka:
仍然還未有微服務進行註冊,因爲我們的微服務 product-data-service還未啓動,下面我們進行啓動!
啓動兩次 ProductDataServiceApplication, 分別輸入 8001和8002.
可以在註冊中心 http://127.0.0.1:8761/ 看到, product-data-service 這個微服務,有兩個實例,分別是8001和8002端口。
在上面給了兩行紅色警示,下面我們設置一下去掉它:
把註冊中心重新啓動一下或者在配置裏把自我保護模式關了
|
這樣就不會再有那個紅色提醒了。
可以如此訪問: http://127.0.0.1:8001/products ,http://127.0.0.1:8002/products,並看到如圖所示的數據。
但是這種方式是通過 http 協議 訪問微服務本身,和註冊中心沒有關係,也觀察不到集羣的效果,接下來我們就會講如何用微服務,訪問另一個微服務。
這一節就先到這兒!