SpringCloud微服務基礎1:Eureka註冊中心
1.Eureka基本概念
Spring Cloud Eureka是Spring Cloud Netflix專案下的服務治理模組。而Spring Cloud Netflix專案是Spring Cloud的子專案之一,主要內容是對Netflix公司一系列開源產品的包裝,它為Spring Boot應用提供了自配置的Netflix OSS整合。通過一些簡單的註解,開發者就可以快速的在應用中配置一下常用模組並構建龐大的分散式系統。它主要提供的模組包括:服務發現(Eureka),斷路器(Hystrix),智慧路由(Zuul),客戶端負載均衡(Ribbon)等。
Eureka架構圖
-
Eureka:就是服務註冊中心(可以是一個叢集),對外暴露自己的地址;
-
提供者:啟動後向Eureka註冊自己資訊(地址,提供什麼服務);
-
消費者:向Eureka訂閱服務,Eureka會將對應服務的所有提供者地址列表傳送給消費者,並且定期更新;
-
心跳(續約):提供者定期通過http方式向Eureka重新整理自己的狀態。
Eureka架構中的三個核心角色:
- 服務註冊中心:
Eureka的服務端應用,提供服務註冊和發現功能,就是剛剛我們建立的eureka-demo
- 服務提供者
提供服務的應用,可以是SpringBoot應用,也可以是其它任意技術實現,只要對外提供的是Rest風格服務即可。本例中就是我們實現的user-service-demo
- 服務消費者
消費應用從註冊中心獲取服務列表,從而得知每個服務方的資訊,知道去哪裡呼叫服務方。本例中就是我們實現的consumer-demo
2.建立Eureka工程
2.1、pom檔案配置
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <!-- SpringCloud版本,是最新的F系列 --> <spring-cloud.version>Finchley.RC1</spring-cloud.version> </properties> <dependencies> <!-- Eureka服務端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <!-- SpringCloud依賴,一定要放到dependencyManagement中,起到管理版本的作用即可 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <!-- SpringCloud版本,是最新的F系列 --> <spring-cloud.version>Finchley.RC1</spring-cloud.version> </properties> <dependencies> <!-- Eureka服務端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <!-- SpringCloud依賴,一定要放到dependencyManagement中,起到管理版本的作用即可 --> <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> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
2.2、啟動類@EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer // 宣告這個應用是一個EurekaServer
public class EurekaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaDemoApplication.class, args);
}
}
2.3、配置檔案application.yml
server:
port: 10086 # 埠
spring:
application:
name: eureka-server # 應用名稱,會在Eureka中顯示
eureka:
client:
register-with-eureka: false # 是否註冊自己的資訊到EurekaServer,預設是true
fetch-registry: false # 是否拉取其它服務的資訊,預設是true
service-url: # EurekaServer的地址,現在是自己的地址,如果是叢集,需要加上其它Server的地址。
defaultZone: http://127.0.0.1:${server.port}/eureka
3.服務提供方工程
伺服器提供方需要將給服務註冊到Eureka註冊中心。
3.1、服務方pom檔案
(1)先新增SpringCloud依賴
<!-- SpringCloud的依賴 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Spring的倉庫地址 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
(2)然後是Eureka客戶端
<!-- Eureka客戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.2、服務方application.yml配置檔案
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/wanzhuang
username: root
password: 86505002
hikari:
maximum-pool-size: 20
minimum-idle: 10
application:
name: user-service # 應用名稱
mybatis:
type-aliases-package: cn.jun.userservice.pojo
eureka:
client:
service-url: # EurekaServer地址
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true # 當呼叫getHostname獲取例項的hostname時,返回ip而不是host名稱
ip-address: 127.0.0.1 # 指定自己的ip資訊,不指定的話會自己尋找
3.3、服務方啟動類@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient // 開啟EurekaClient功能
public class UserServiceDemoApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceDemoApplication.class, args);
}
}
【注意事項】
(1) 這裡我們添加了spring.application.name屬性來指定應用名稱,將來會作為應用的id使用;
(2)不用指定register-with-eureka(表示是否自己註冊到Eureka)和fetch-registry(是否拉去其他服務資訊),因為預設是true。
4、服務消費方工程
服務消費方也就消費者,它需要從Eureka註冊中獲取服務
4.1、pom檔案
<!-- SpringCloud的依賴 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Spring的倉庫地址 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<!-- Eureka客戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
4.2、application.yml配置檔案
server:
port: 8080
spring:
application:
name: consumer # 應用名稱
eureka:
client:
service-url: # EurekaServer地址
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true # 當其它服務獲取地址時提供ip而不是hostname
ip-address: 127.0.0.1 # 指定自己的ip資訊,不指定的話會自己尋找
4.3、啟動類@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient // 開啟Eureka客戶端
public class UserConsumerDemoApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
public static void main(String[] args) {
SpringApplication.run(UserConsumerDemoApplication.class, args);
}
}
4.4、消費者service層獲取服務提供方的服務例項
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;// Eureka客戶端,可以獲取到服務例項資訊
public List<User> queryUserByIds(List<Long> ids) {
List<User> users = new ArrayList<>();
// String baseUrl = "http://localhost:8081/user/";
// 根據服務名稱,獲取服務例項
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
// 因為只有一個UserService,因此我們直接get(0)獲取
ServiceInstance instance = instances.get(0);
// 獲取ip和埠資訊
String baseUrl = "http://"+instance.getHost() + ":" + instance.getPort()+"/user/";
ids.forEach(id -> {
// 我們測試多次查詢,
users.add(this.restTemplate.getForObject(baseUrl + id, User.class));
// 每次間隔500毫秒
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
return users;
}
}
5、高可用的Eureka Server
多個Eureka Server之間也會互相註冊為服務,當服務提供者註冊到Eureka Server叢集中的某個節點時,該節點會把服務的資訊同步給叢集中的每個節點,從而實現資料同步。因此,無論客戶端訪問到Eureka Server叢集中的任意一個節點,都可以獲取到完整的服務列表資訊。
我們假設要搭建兩條EurekaServer的叢集,埠分別為:10086和10087
5.1、Eureka叢集配置
1、10086埠的Eureka中心配置
server:
port: 10086 # 埠
spring:
application:
name: eureka-server # 應用名稱,會在Eureka中顯示
eureka:
client:
service-url: # 配置其他Eureka服務的地址,而不是自己,比如10087
defaultZone: http://127.0.0.1:10087/eureka
2、10087埠的Eureka中心配置
server:
port: 10087 # 埠
spring:
application:
name: eureka-server # 應用名稱,會在Eureka中顯示
eureka:
client:
service-url: # 配置其他Eureka服務的地址,而不是自己,比如10087
defaultZone: http://127.0.0.1:10086/eureka
所謂的高可用註冊中心,其實就是把EurekaServer自己也作為一個服務進行註冊,這樣多個EurekaServer之間就能互相發現對方,從而形成叢集。因此我們做了以下修改:
(1) 刪除了register-with-eureka=false和fetch-registry=false兩個配置。因為預設值是true,這樣就會吧自己註冊到註冊中心了。
(2)把service-url的值改成了另外一臺EurekaServer的地址,而不是自己;
5.2、服務提供者
eureka:
client:
service-url: # EurekaServer地址,多個地址以','隔開
defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
instance:
lease-expiration-duration-in-seconds: 10 # 10秒即過期
lease-renewal-interval-in-seconds: 5 # 5秒一次心跳
instance-id: ${spring.application.name}:${server.port}
其中有兩個重要配置專案
(1)ease-renewal-interval-in-seconds:服務續約(renew)的間隔,預設為30秒;
(2)lease-expiration-duration-in-seconds:服務失效時間,預設值90秒。
也就是說,預設情況下每個30秒服務會向註冊中心傳送一次心跳,證明自己還活著。如果超過90秒沒有傳送心跳,EurekaServer就會認為該服務宕機,會從服務列表中移除,這兩個值在生產環境不要修改,預設即可。
【注意】instance-id屬性來修改它的構成
eureka:
instance:
instance-id: ${spring.application.name}:${server.port}
預設格式是:${hostname} + ${spring.application.name} + ${server.port}
;instance-id是區分同一服務的不同例項的唯一標準,因此不能重複。
5.3、消費者
當服務消費者啟動是,會檢測eureka.client.fetch-registry=true
引數的值,如果為true,則會從Eureka Server服務的列表只讀備份,然後快取在本地。並且每隔30秒
會重新獲取並更新資料。 我們可以通過修改eureka.client.registry-fetch-interval-seconds引數的時間。
eureka:
server:
enable-self-preservation: false # 關閉自我保護模式(預設為開啟)
eviction-interval-timer-in-ms: 1000 # 掃描失效服務的間隔時間(預設為60*1000ms)
有些時候,我們的服務提供方並不一定會正常下線,可能因為記憶體溢位、網路故障等原因導致服務無法正常工作。Eureka Server需要將這樣的服務剔除出服務列表。因此它會開啟一個定時任務,每隔60秒對所有失效的服務(超過90秒未響應)進行剔除。可以通過eureka.server.eviction-interval-timer-in-ms引數對其進行修改,單位是毫秒,生成環境不要修改。這個會對我們開發帶來極大的不變,你對服務重啟,隔了60秒Eureka才反應過來。開發階段可以適當調整,比如10S。