1、微服務架構中關鍵組件:服務提供者、服務消費者、服務發現組件
其中,服務提供者的變化依賴於服務發現組件,對服務消費者來說透明
2、服務發現組件的功能
1)服務註冊表:用來記錄各個微服務的信息,名稱、IP、端口等。並提供查詢API(用於查詢可用的微服務實例)和管理API(用於服務的註冊和註銷)
2)服務註冊:指微服務在啓動時,將自己的信息註冊到服務發現組件上的過程
3)服務發現:指查詢可用微服務列表及其網絡地址的機制
4)服務檢查:服務發現組件使用一定機制定時檢測已註冊的服務,如發現某實例長時間無法訪問,就會從服務註冊表中移除該實例
3、Eureka:是Netflix開源的服務發現組件,本身是一個REST的服務,包含Server和Client兩部分
4、Eureka元數據
1)標準元數據:主機名、IP地址、端口號、狀態頁、健康檢查等
2)自定義元數據:使用eureka.instance.metadata-map配置
5、REST端點
Eureka Server提供了一些REST端點,服務可使用這些端點操作Eureka,從而實現註冊與發現,前面使用的Eureka Client就是java編寫的操作REST端點的類庫。可以使用XML或JSON與端點通信,默認XML
操作 | 請求方式 | 端點URL | 返回值說明 |
---|---|---|---|
註冊新服務 | POST | /eureka/apps/{appID} | 傳遞JSON或者XML格式參數內容,HTTP code爲204時表示成功 |
註銷服務 | DELETE | /eureka/apps/{appID}/{instanceID} | HTTP code爲200時表示成功 |
心跳檢測 | PUT | /eureka/apps/{appID}/{instanceID} | HTTP code爲200時表示成功 |
查詢所有服務 | GET | /eureka/apps | HTTP code爲200時表示成功,返回XML/JSON數據內容 |
查詢appID下的服務列表 | GET | /eureka/apps/{appID} | HTTP code爲200時表示成功,返回XML/JSON數據內容 |
查詢指定appID下指定instanceID的服務 | GET | /eureka/apps/{appID}/{instanceID} | 獲取指定appID以及InstanceId的服務信息,HTTP code爲200時表示成功,返回XML/JSON數據內容 |
查詢指定instanceID服務列表 | GET | /eureka/apps/instances/{instanceID} | 獲取指定instanceID的服務列表,HTTP code爲200時表示成功,返回XML/JSON數據內容 |
變更服務狀態 | PUT | /eureka/apps/{appID}/{instanceID}/status?value=DOWN | 服務上線、服務下線等狀態變動,HTTP code爲200時表示成功 |
變更元數據 | PUT | /eureka/apps/{appID}/{instanceID}/metadata?key=value | HTTP code爲200時表示成功 |
查詢指定IP下的服務列表 | GET | /eureka/vips/{vipAddress} | HTTP code爲200時表示成功 |
查詢指定安全IP下的服務列表 | GET | /eureka/svips/{svipAddress} | HTTP code爲200時表示成功 |
其中appID是應用程序名稱即微服務集羣名稱,instanceID是實例相關聯的唯一ID即某一個服務節點的ID
6、Eureka的自我保護模式
默認EurekaServer在90秒沒有接收到某個微服務實例的心跳,將會註銷該實例。當EurekaServer節點在短時間內丟失過多客戶端時(可能發生了網絡分區故障),那麼這個節點就會進入自我保護模式,即保護服務註冊表中的信息,不再刪除服務註冊表中的數據(也就是不會註銷任何微服務),當網絡故障恢復後,該EurekaServer節點會自動退出自我保護模式
注:可食用eureka.server.enable-self-preservation = false 禁用自我保護模式
7、多網卡環境下的IP選擇
1)忽略指定名稱的網卡:spring.cloud.inetutils.ignored-interfaces = - docker0
2)使用正則表達式,指定使用的網絡地址:spring.cloud.inetutils.preferredNetworks: -192.168
3)只使用站點本地地址:spring.cloud.inetutils.useOnlySiteLocalInterfaces = true
4)手動指定IP地址:eureka.instance.ip-address = 127.0.0.1
8、開啓Eureka的健康檢查
eureka.client.healthcheck.enabled = true
注:該配置只能配置在application.yml中,不能配置在bootstrap.yml中
9、競品對比
SpringCloud的服務註冊發現組件可使用:Eureka、Zookeeper、Consul
組件名 | 語言 | CAP | 一致性算法 | 服務健康檢查 | 對外暴露接口 | SpringCloud集成 |
---|---|---|---|---|---|---|
Eureka | Java | AP | 無 | 可配支持 | HTTP | 已集成 |
Consul | Go | CP | Raft | 支持 | HTTP/DNS | 已集成 |
Zookeeper | Java | CP | Paxos | 支持 | 客戶端 | 已集成 |
10、Demo
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.springcloud.demo</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-demo</name>
<description>Demo project for Spring Boot</description>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<modules>
<module>eureka1</module>
<module>eureka2</module>
<module>movie</module>
<module>user</module>
</modules>
<dependencies>
<!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 其他依賴 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1)Eureka1(集羣節點1)
<?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>
<parent>
<groupId>com.springcloud.demo</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.springcloud.demo</groupId>
<artifactId>eureka1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- eureka server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!-- spring boot 權限 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
server:
port: 8761
eureka:
client:
# 是否將自己註冊到EurekaServer,默認true
# registerWithEureka: false
# 是否從EurekaServer獲取信息,默認true,eureka集羣同步信息時需要開啓
# fetchRegistry: false
serviceUrl:
# 設置與EurekaServer交互的地址,多個地址可用英文逗號分隔
# defaultZone: http://peer2:8762/eureka/
defaultZone: http://user:123qwe@peer2:8762/eureka/
instance:
hostname: peer1
spring:
application:
name: eureka1
security:
basic:
# 開啓基於HTTP basic的認證
enabled: true
# 設置登錄用戶名密碼
user:
name: user
password: 123qwe
package com.springcloud.demo.eureka1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class Eureka1Application {
public static void main(String[] args) {
SpringApplication.run(Eureka1Application.class, args);
}
}
2)Eureka2(集羣節點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>
<parent>
<groupId>com.springcloud.demo</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.springcloud.demo</groupId>
<artifactId>eureka2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- eureka server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!-- spring boot 權限 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
server:
port: 8762
eureka:
client:
# # 是否將自己註冊到EurekaServer,默認true
# registerWithEureka: false
# # 是否從EurekaServer獲取信息,默認true,eureka集羣同步信息時需要開啓
# fetchRegistry: false
serviceUrl:
# 設置與EurekaServer交互的地址,多個地址可用英文逗號分隔
# defaultZone: http://peer1:8761/eureka/
defaultZone: http://user:123qwe@peer1:8761/eureka/
instance:
hostname: peer2
spring:
application:
name: eureka2
security:
basic:
# 開啓基於HTTP basic的認證
enabled: true
# 設置登錄用戶名密碼
user:
name: user
password: 123qwe
package com.springcloud.demo.eureka2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class Eureka2Application {
public static void main(String[] args) {
SpringApplication.run(Eureka2Application.class, args);
}
}
3)User(用戶服務)
<?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>
<parent>
<groupId>com.springcloud.demo</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.springcloud.demo</groupId>
<artifactId>user</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>user</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- eureka client -->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-netflix-eureka-client</artifactId>-->
<!--<version>RELEASE</version>-->
<!--</dependency>-->
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
server:
port: 8000
spring:
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: none
datasource:
platform: h2
schema: classpath:schema.sql
data: classpath:data.sql
application:
# 用於指定註冊到EurekaServer上的應用名稱
name: user
logging:
level:
root: INFO
org.hibernate: INFO
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
eureka:
client:
serviceUrl:
# 設置與EurekaServer交互的地址,多個地址可用英文逗號分隔
# defaultZone: http://peer1:8761/eureka/, http://peer2:8762/eureka/
defaultZone: http://user:123qwe@peer1:8761/eureka/, http://user:123qwe@peer2:8762/eureka/
instance:
# 表示將自己的IP註冊到EurekaServer,默認false表示註冊微服務所在操作系統到hostname到EurekaServer
prefer-ip-address: true
package com.springcloud.demo.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient // 聲明這是一個EurekaClient,@EnableDiscoverClient是一個抽象聲明,組件實現可以使用eureka、zookeeper、consul
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
package com.springcloud.demo.user;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler" })
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
private String username;
@Column
private String name;
@Column
private Integer age;
@Column
private BigDecimal balance;
}
package com.springcloud.demo.user;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
package com.springcloud.demo.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/{id}")
public User findById(@PathVariable Long id) {
return userRepository.getOne(id);
}
}
4)Movie(電影服務)
<?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>
<parent>
<groupId>com.springcloud.demo</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.springcloud.demo</groupId>
<artifactId>movie</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>movie</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.57</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
server:
port: 8010
spring:
application:
# 用於指定註冊到EurekaServer上的應用名稱
name: movie
eureka:
client:
serviceUrl:
# 設置與EurekaServer交互的地址,多個地址可用英文逗號分隔
# defaultZone: http://peer1:8761/eureka/, http://peer2:8762/eureka/
defaultZone: http://user:123qwe@peer1:8761/eureka/, http://user:123qwe@peer2:8762/eureka/
instance:
# 表示將自己的IP註冊到EurekaServer,默認false表示註冊微服務所在操作系統到hostname到EurekaServer
prefer-ip-address: true
package com.springcloud.demo.movie;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient // 聲明這是一個EurekaClient,@EnableDiscoverClient是一個抽象聲明,組件實現可以使用eureka、zookeeper、consul
public class MovieApplication {
public static void main(String[] args) {
SpringApplication.run(MovieApplication.class, args);
}
}
package com.springcloud.demo.movie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class MovieConfiguration {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
package com.springcloud.demo.movie;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler" })
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
private String username;
@Column
private String name;
@Column
private Integer age;
@Column
private BigDecimal balance;
}
package com.springcloud.demo.movie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
// 此處暫未通過微服務調用
return restTemplate.getForObject("http://127.0.0.1:8000/" + id, User.class);
}
}