服務註冊與發現---Eureka
- 1. Eureka系統架構
- 2. Eureka編碼舉例
- 3. Eureka集羣
- 3.1 配置hosts
- 3.2 創建Eureka Server的application.yml
- 3.3 打Eureka Server的jar包
- 3.4 運行 eureka-server.jar
- 3.5 修改Provider和Consumer的配置
- 3.5 測試結果:
- 4. Provider集羣驗證
- 5. Eureka Server註冊信息優化
- 6. Eureka Server註冊表微服務信息
- 7. Eureka的自我保護
Eureka是美國Netflix公司出品的服務註冊與發現組件,Spring Cloud集成了該組件,然而時代發展,技術革新,Eureka已經停止更新了,但部分老的Spring Cloud的服務註冊與發現組件還是用的Eureka。
學學瞭解瞭解,也有裨益,畢竟思想沒有變化。
1. Eureka系統架構
Eureka採用了CS的設計架構,Eureka Server 作爲服務的註冊功能的服務器,它是服務註冊中心。而系統的其他服務,使用Eureka的客戶端連接到Eureka Server 並維持心跳鏈接。這樣系統的維護人員可以通過Eureka Server來監控系統中各個微服務是否正常運行。
服務註冊與發現中,有一個註冊中心(Eureka Server)。當服務器啓動的時候,會把當前自己的服務器的信息,例如服務IP地址端口等以別名的方式註冊到註冊中心上(Eureka Server)。另一方服務以別名的方式去註冊中心上獲取到的實際通信地址(IP+Port),然後在實現本地的遠程調用,調用方式可以是Restful風格的接口(例如Spring boot 的 Controller)或者 RPC(例如Alibaba 的 Dubbo)。
核心設計思想是使用服務中心管理每個服務之間的依賴關係(服務治理概念),這樣可以在部署時方便的擴容或遷移服務,而不用去維護服務之間的依賴關係,說的直白一點就是,服務部署的IP和端口發生變化,註冊中心的數據同時發生變化(服務啓動的時候會向中心自動註冊),服務的消費者,可以從註冊中心通過約定的別名(一般是服務名)獲取變更後服務的IP和端口,然後去調用這個服務。
Eureka包含兩個組件:
Eureka Server提供服務來註冊服務
各個微服務節點通過配置啓動後,會在Eureka Server中進行註冊,這樣EurekaServer中的服務註冊表中將會存儲所有的可用節點的信息,服務節點的信息可以在界面中直觀看到。
Eureka Client通過註冊中心進行訪問
是一個java客戶端,用於簡化Eureka Server的交互,客戶端同時也具備一個內置的、使用輪詢(round-robin)負載算法的負載均衡器。在應用啓動後,將會向Eureka Server發送心跳(默認週期30秒)。如果Eureka Server在多個心跳週期沒有接收到某個節點的心跳,Eureka Server將會從服務註冊表中把這個服務節點移除(默認90秒)
2. Eureka編碼舉例
2.1 先創建Eureka Server
創建一個Spring boot應用,導入一下依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.yml:
server:
port: 7001
# eureka 配置
eureka:
instance:
# eureka 的服務器實例名稱
hostname: localhost
client:
# false 表示不向註冊中心註冊自己
register-with-eureka: false
# false 表示本服務就是註冊中心,本服務的職責就是維護服務實例,並不需要去檢索服務
fetch-registry: false
service-url:
# 設置於Eureka Server交互的地址,註冊服務和查詢服務都需要依賴這個地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
在主類上加@EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
啓動服務,訪問localhost:7001
2.2 編寫一個Provider
pom中添加如下依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置application.yml
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123
http:
encoding:
charset: UTF-8
force: true
# eureka client配置
eureka:
client:
# 表示是否將自己註冊進 EurekaServer, 默認true
register-with-eureka: true
# 是否從Eureka Server抓取自己的註冊信息,默認true。單節點無所謂,集羣必須設置爲true才能配合ribbon使用負載均衡
fetch-registry: true
service-url:
# 註冊中心地址
defaultZone: http://localhost:7001/eureka
mybatis:
mapper-locations: classpath:mapper/*.xml
# 所有Entity別名類所在包
type-aliases-package: com.yp.entity
在主類上添加@EnableEurekaClient
@SpringBootApplication
@EnableEurekaClient
public class PaymentApplication {
public static void main(String[] args) {
SpringApplication.run(PaymentApplication.class, args);
}
}
啓動服務,訪問localhost:7001
會發現有一個服務cloud-payment-service(大寫)已經註冊上來了
2.2 編寫一個Consumer
pom中添加如下依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置application.yml
server:
port: 80
spring:
application:
name: cloud-order-service
http:
encoding:
charset: UTF-8
force: true
eureka:
client:
# 表示是否將自己註冊進 EurekaServer, 默認true
register-with-eureka: true
# 是否從Eureka Server抓取自己的註冊信息,默認true。單節點無所謂,集羣必須設置爲true才能配合ribbon使用負載均衡
fetch-registry: true
service-url:
# 註冊中心地址
defaultZone: http://localhost:7001/eureka
在主類上添加@EnableEurekaClient
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
啓動服務,訪問localhost:7001
2.3 測試Consumer訪問Provider
2.31 Consumer編寫
在Consumer中寫一個RestTemplete配置,主要是實現負載均衡,和Restful接口訪問
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
Consumer寫Controller
@RestController
@Slf4j
public class OrderController {
// 直接訪問地址
private static final String PAYMENT_URL = "http://localhost:8001";
// eureka上註冊的微服務
private static final String SERVICE_NAME = "http://CLOUD-PAYMENT-SERVICE";
@Autowired
private RestTemplate restTemplate;
@GetMapping("consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
return restTemplate.getForObject(SERVICE_NAME + "/payment/" + id, CommonResult.class);
}
}
2.3.2 編寫Provider
在Provider中寫一個Controller,爲了簡化,後續service、dao不再列出
@RestController
@Slf4j
public class PaymentController {
// 僅僅是爲了驗證負載均衡,因爲本地驗證只能由端口區分
@Value("${server.port}")
private int servicePort;
@Autowired
private PaymentService paymentService;
@GetMapping("/payment/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
Payment payment = paymentService.getPaymentById(id);
if (payment != null) {
return new CommonResult<>(200, "獲取數據成功! servicePort: " + servicePort, payment);
} else {
return new CommonResult<>(500, "獲取數據失敗! servicePort: " + servicePort, null);
}
}
}
2.3.3 運行測試
3. Eureka集羣
目的是提高容錯,原理就是互相註冊
3.1 配置hosts
因爲本地實驗,要區分多個Eureka Server,同一IP映射多個域名
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
3.2 創建Eureka Server的application.yml
創建application7001.yml
server:
port: 7001
# eureka 配置
eureka:
instance:
# eureka 的服務器實例名稱
hostname: eureka7001.com
client:
# false 表示不向註冊中心註冊自己
register-with-eureka: false
# false 表示本服務就是註冊中心,本服務的職責就是維護服務實例,並不需要去檢索服務
fetch-registry: false
service-url:
# 設置於Eureka Server交互的地址,註冊服務和查詢服務都需要依賴這個地址
defaultZone: http://eureka7002.com:7002/eureka
創建application7002.yml
server:
port: 7002
# eureka 配置
eureka:
instance:
# eureka 的服務器實例名稱
hostname: eureka7002.com
client:
# false 表示不向註冊中心註冊自己
register-with-eureka: false
# false 表示本服務就是註冊中心,本服務的職責就是維護服務實例,並不需要去檢索服務
fetch-registry: false
service-url:
# 設置於Eureka Server交互的地址,註冊服務和查詢服務都需要依賴這個地址
defaultZone: http://eureka7001.com:7001/eureka
3.3 打Eureka Server的jar包
在pom中加入plugin
<build>
<finalName>eureka-server</finalName>
<resources>
<resource>
<directory>src/main/java</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**.*</include>
<include>**/*.*</include><!-- i18n能讀取到 -->
<include>**/*/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork> <!-- 如果沒有該配置,devtools不會生效 -->
<!-- 指定該Main Class爲全局的唯一入口 -->
<mainClass>com.yp.EurekaServerApplication</mainClass>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal><!--可以把依賴的包都打包到生成的Jar包中-->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
3.4 運行 eureka-server.jar
在兩個cmd窗口獨立運行
java -jar eureka-server.jar --spring.config.location=D:\spring-cloud-demo\application7001.yml
java -jar eureka-server.jar --spring.config.location=D:\spring-cloud-demo\application7002.yml
3.5 修改Provider和Consumer的配置
Provider和Consumer的application.yml defaultZone作同樣的修改
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
eureka:
client:
# 表示是否將自己註冊進 EurekaServer, 默認true
register-with-eureka: true
# 是否從Eureka Server抓取自己的註冊信息,默認true。單節點無所謂,集羣必須設置爲true才能配合ribbon使用負載均衡
fetch-registry: true
service-url:
# 註冊中心地址
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
3.5 測試結果:
4. Provider集羣驗證
啓動兩個cloud-payment-service進程,一個端口8001,另一個8002,打包與Eureka Server一樣
java -jar payment-server.jar --spring.config.location=.\application8001.yml
java -jar payment-server.jar --spring.config.location=.\application8002.yml
端口輪詢:
5. Eureka Server註冊信息優化
instance:
# 主機名
instance-id: payment_8001
# 訪問路徑顯示IP
prefer-ip-address: true
eureka:
client:
# 表示是否將自己註冊進 EurekaServer, 默認true
register-with-eureka: true
# 是否從Eureka Server抓取自己的註冊信息,默認true。單節點無所謂,集羣必須設置爲true才能配合ribbon使用負載均衡
fetch-registry: true
service-url:
# 註冊中心地址
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
# 主機名
instance-id: payment_8001
# 訪問路徑顯示IP
prefer-ip-address: true
6. Eureka Server註冊表微服務信息
寫一個Controller用於相應註冊中心註冊表服務實例
@RestController
@RequestMapping("/consumer")
@Slf4j
public class ServiceInstancesController {
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/payment/serviceInstances")
public List<ServiceVo> discovery() {
List<ServiceVo> serviceVoList = new ArrayList<>();
List<String> services = discoveryClient.getServices();
for (String serviceName : services) {
ServiceVo serviceVo = new ServiceVo();
List<ServiceInstanceVo> instanceVoList = new ArrayList<>();
serviceVo.setServiceName(serviceName);
List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
for (ServiceInstance serviceInstance : instances) {
ServiceInstanceVo instanceVo = new ServiceInstanceVo();
instanceVo.setServiceId(serviceInstance.getServiceId());
instanceVo.setHost(serviceInstance.getHost());
instanceVo.setPort(serviceInstance.getPort());
instanceVo.setUri(serviceInstance.getUri().toString());
instanceVoList.add(instanceVo);
}
serviceVo.setInstances(instanceVoList);
serviceVoList.add(serviceVo);
}
return serviceVoList;
}
}
在主類上加 @EnableDiscoveryClient
請求:
http://localhost/consumer/payment/serviceInstances
格式化:
[
{
"serviceName":"cloud-payment-service",
"instances":[
{
"host":"192.168.110.1",
"port":8001,
"uri":"http://192.168.110.1:8001",
"serviceId":"CLOUD-PAYMENT-SERVICE"
},
{
"host":"192.168.110.1",
"port":8002,
"uri":"http://192.168.110.1:8002",
"serviceId":"CLOUD-PAYMENT-SERVICE"
}
]
},
{
"serviceName":"cloud-order-service",
"instances":[
{
"host":"192.168.110.1",
"port":80,
"uri":"http://192.168.110.1:80",
"serviceId":"CLOUD-ORDER-SERVICE"
}
]
}
]
7. Eureka的自我保護
某時刻某一服務不可用了,Eureka不會立刻清理,依舊會對該微服務的信息進行保存,提高系統可用性,這主要是考慮網絡阻塞,擁堵對健康檢查的影響。
Eureka Service配置
server:
# 關閉自我保護
enable-self-preservation: false
# 時間間隔,單位毫秒
eviction-interval-timer-in-ms: 3000
server:
port: 7001
# eureka 配置
eureka:
instance:
# eureka 的服務器實例名稱
hostname: eureka7001.com
client:
# false 表示不向註冊中心註冊自己
register-with-eureka: false
# false 表示本服務就是註冊中心,本服務的職責就是維護服務實例,並不需要去檢索服務
fetch-registry: false
service-url:
# 設置於Eureka Server交互的地址,註冊服務和查詢服務都需要依賴這個地址
defaultZone: http://eureka7002.com:7002/eureka
server:
# 關閉自我保護
enable-self-preservation: false
# 時間間隔,單位毫秒
eviction-interval-timer-in-ms: 3000
註冊的服務配置
lease-renewal-interval-in-seconds: 2
# Eureka服務端在接收最後一次心跳等待時間上限,單位秒(默認90秒),超時將剔除服務
lease-expiration-duration-in-seconds: 3
eureka:
client:
# 表示是否將自己註冊進 EurekaServer, 默認true
register-with-eureka: true
# 是否從Eureka Server抓取自己的註冊信息,默認true。單節點無所謂,集羣必須設置爲true才能配合ribbon使用負載均衡
fetch-registry: true
service-url:
# 註冊中心地址
defaultZone: http://eureka7001.com:7001/eureka
instance:
# 主機名
instance-id: payment_8001
# 訪問路徑顯示IP
prefer-ip-address: true
# Eureka客戶端向服務端發送心跳的時間間隔,單位秒(默認30秒)
lease-renewal-interval-in-seconds: 2
# Eureka服務端在接收最後一次心跳等待時間上限,單位秒(默認90秒),超時將剔除服務
lease-expiration-duration-in-seconds: 3
測試: