※POM
※父POM
<packaging>pom</packaging>
<modules>
<module>provider</module>
<module>consumer</module>
<module>api</module>
<module>eureka</module>
</modules>
<groupId>scp</groupId>
<artifactId>SpringCloudPractice</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-cloud-release.version>Greenwich.RELEASE</spring-cloud-release.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud-release.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
※生產者和消費者向註冊中心註冊,api 為生產者和消費者共用的方法
※consumer pom
<parent>
<artifactId>SpringCloudPractice</artifactId>
<groupId>scp</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>consumer</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<artifactId>api</artifactId>
<groupId>scp</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
※provider pom
<parent>
<artifactId>SpringCloudPractice</artifactId>
<groupId>scp</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>provider</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<artifactId>api</artifactId>
<groupId>scp</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
※api pom
<parent>
<artifactId>SpringCloudPractice</artifactId>
<groupId>scp</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>api</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<finalName>cyc</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
※因為 api 有程式碼給 consumer 和 provider 使用,maven install 會失敗,所以要在 api 加上 plugin 才可以
※eureka pom
<parent>
<artifactId>SpringCloudPractice</artifactId>
<groupId>scp</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>eureka</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<!--<artifactId>spring-cloud-netflix-eureka-server</artifactId>没有 starter 是错的-->
</dependency>
</dependencies>
※java 類別
※provider
package controller;
@RestController
public class TestController {
@GetMapping("/testGet")
public ApiBean get() {
System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
ApiBean ab = new ApiBean();
ab.setId(1);
ab.setName("xxx");
return ab;
}
}
※consumer
package controller;
@RestController
@Import(RestTemplate.class)
public class consumerController {
@Resource
private RestTemplate restTemplate;
@GetMapping("/xxx")
public ApiBean get() {
return restTemplate.getForObject("http://localhost:9001" + "/testGet", ApiBean.class);
}
}
※因為 RestTemplate 是內鍵的 class,並沒有類似 @Component 這種註解,spring 不知道,所以用了 @Import 將它註冊到 spring
※api
package scp;
@Data
public class ApiBean implements Serializable {
private int id;
private String name;
}
※eureka
package main;
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain {
public static void main(String[] args) {
SpringApplication.run(EurekaMain.class, args);
}
}
※application.yml
※provider
server:
port: 9001
eureka:
client:
service-url:
defaultZone: http://localhost:9051/eureka
※consumer
server:
port: 80
eureka:
client:
service-url:
defaultZone: http://localhost:9051/eureka
※eureka
server:
port: 9051
eureka:
client:
register-with-eureka: false # 不註冊自己
fetch-registry: false # 取得註冊資訊,因為自己就是註冊中心,不需要取得註冊資訊
service-url:
defaultZone: http://localhost:${server.port}/eureka/ # Eureka Server 的位址
※ fetch-registry 一定要 false 才有辦法啟動,否則會報 Cannot execute request on any known server
※ register-with-eureka 如果註冊自己可以順利啟動成功,只是沒什麼意義,感覺就像麥當勞的員工自己也在排隊買漢堡一樣
※server.port 如果給 0,會隨機產生一個,看控製台即可知道
※遇到的問題
invalid LOC header
jar 檔簽名壞了,從倉庫刪除後重新下載即可
Make sure your own configuration does not rely on that class. This can also happen if you are @ComponentScanning a springframework package
main 方法要有 package,還有另外一個錯誤是要有 main 方法,但錯誤訊息很明顯,所以一個專案放一個有 package 的 main 方法就可以了,方法裡什麼都不寫也行
※Eureka 註冊中心
啟動後會有現都是 UNKNOWN,所以 consumer 和 provider 還要在 application.yml 加上 spring.application.name,這樣 eureka 才會分辯的出來,修改後如下,eureka 會變成全部大寫英文
如果想改右邊的 Status 顯示的名稱,還得在 consumer 和 provider 加上 eureka.instance.instance-id,修改後如下,這裡 eureka 不會變成大寫
上上一張圖的左下角有網址,是滑鼠在 status 的 consumer 或 provider 上時顯示的,我記得不改是 localhost 之類的,但不知哪一版改了,如果是 localhost 想改成 ip,還可以加上 eureka.instance.prefer-ip-address: true 就可以了
還有一個很像的設定 eureka.instance.ip-address: ,因為有可能有多張網卡,可以寫死其中一張網卡的 IP,如果兩個都設定,最終註冊在 eureka 會是這裡的設定
※啟動順序
必需先啟動 eureka,再啟動 provider,最後是 consumer
如果 eureka 在啟動前有其他的專案先啟動了,eureka 抓不到
如果 consumer 在 provider 之前,我試的結果沒問題,但理論上是先有提供者,這樣消費者才抓的到
如果已經照順序啟動好了,將 provider 或 consumer 的 spring.application.name 名字換掉,只重啟改掉的專案,會發現 eureka 會有紅色的警告,因為有個 CAP 理論
Consistency(一致性)
Availability(可用性)
Partition tolerance(分区容错性)
目前不可能全部做到,會犧牲一個
eureka 保證 AP
zookeeper 保證 CP
長時間沒有人連 eureka 會報這個警告,或者我將名稱換掉也會,因為他要保證可用性,有可能只是網路不順,寧可記錄錯的資料也不要刪掉,而預設過了 90 秒後,真的都沒有人連了,才會刪除,如果不喜歡這個功能,可以關掉
eureka.server.enable-self-preservation: false
※actuator/info
上面的 Status 一點進去就掛了,這裡是設定裡面的資訊
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
-----------------------------
<build>
<finalName>SpringCloudPractice</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<delimiters>
<delimit>@</delimit>
</delimiters>
</configuration>
</plugin>
</plugins>
</build>
※這裡的 pom 是全域的
※delimit 裡面的符號是用在註冊進 eureka 的專案要用到的,前後用這個符號包起來即可
※以下以 provider 為例,在 application.yml 增加如下的設定
info:
xxx.ooo: xxoo.aa
name: provider.info
jdk_version: @java.version@
version: @version@
chi_test: 梅山小路用
※info 下的 key 和 value 都可以隨便亂打,但 @ 包起來的部分會去 pom 抓,有自己的就抓自己的,沒有就抓父 pom 的,雖然 provider 有 api,但不會去抓,如果都沒有,啟動會報錯
※服務發現
使用 DiscoveryClient 可以發現 Eureka 上註冊的服務
@Resource
private DiscoveryClient client;
@GetMapping("/discovery")
public DiscoveryClient discoveryTest() {
List<String> services = client.getServices();
System.out.println("所有服務名稱=" + services);
// List<ServiceInstance> instances = client.getInstances("provider1".toUpperCase()); // 一定要大寫
services.forEach(serviceName -> {
List<ServiceInstance> instances = client.getInstances(serviceName);
instances.forEach(s -> {
System.out.println("host=" + s.getHost()); // 192.168.254.104
System.out.println("instanceId=" + s.getInstanceId()); // bruceProvider1
System.out.println("scheme=" + s.getScheme()); // null
System.out.println("serviceId=" + s.getServiceId()); // PROVIDER1
System.out.println("port=" + s.getPort()); // 9001
System.out.println("uri=" + s.getUri()); // http://192.168.254.104:9001
Map<String, String> metadata = s.getMetadata();
metadata.forEach((k, v) -> {
System.out.print("key=" + k); // management.port
System.out.println(", value=" + v); // 9001
});
});
System.out.println("------------------------------------");
});
return client;
}
※在 provider 加上上面的程式碼
※注意 DiscoveryClient是
org.springframework.cloud.client.discovery 的包
※在 provider 的啟動類上加上 @EnableDiscoveryClient,但我試的結果,不加也可以
private static final String PROVIDER_URI = "http://localhost:9001";
@GetMapping("/consumer/discovery")
public Object discovery() {
return restTemplate.getForObject(PROVIDER_URI + "/discovery", Object.class);
}
※provider 最後是要給 consumer 的,所以這裡新增這一段程式碼,但回傳值我給 DiscoveryClient 後,打上網址 localhost/consumer/discovery 會報錯
※Eureka 增加帳號密碼
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
※在 Eureka server 增加 security 的 POM
※application.yml 增加 spring.security.user,下一層有 name 和 password 可以設定帳號密碼
※如果有集群,每個都要加 pom 和設定密碼,也可以設定不一樣的帳密
※如果只有加 pom,連網址就會出現打帳密的視窗,但因為沒有帳密進不去
※
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain extends WebSecurityConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(EurekaMain.class, args);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.csrf().disable(); // 不處理 CSRF 攻擊
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}
※繼承 WebSecurityConfigurerAdapter 後,覆寫 configure,參數是 HttpSecurity 的
※也可以自己寫一個類別繼承 WebSecurityConfigurerAdapte
※不繼承,server 端不會有問題,差在 client 端要連的時候會報 Cannot execute request on any known server 的錯
※用戶端連 eureka 時,只要在 application.yml 的 defaultZone 改一下就可以,如
http://xxx:ooo@xxx.ooo9051:9051/eureka
就是在原本的網址加上「帳號:密碼@」