Về mặt lý thuyết
Ta cần chú ý đến 3 cache ở các thành phần sau : 1 ở gateway Zuul - đóng vai cổng vào để nhận các truy vấn đến; 1 ở load balancer Ribbon - đóng vai trò tác nhân cân bằng tải để điều hướng các truy vấn đến những dịch vụ web trong hệ thống; 1 của service discovery Eureka - dịch vụ chủ Eureka lưu dữ và cập nhật danh sách các dịch vụ web có trong hệ thống bao gồm các microservices. Mặc định mỗi cache của từng thành phần này sẽ tự cập nhật mỗi 30 giây. Vậy nên phải chờ khoảng 30+30+30 = 90 giây để tất cả các cache được cập nhật ở mọi lớp này.
Ta xét trường hợp một hệ thống HA (high availability) gồm hai replicas - mỗi một replica bao gồm một tập hợp hoàn chỉnh các dịch vụ web cho phép hệ thống hoạt động bình thường. Trong trường hợp này ta có thể tiến hành việc cài đặt như sau :
- Hủy đăng ký của tất cả các dịch vụ web trong instance đầu tiên của Eureka server (dùng enpoint pause của actuator hoặc Eureka API PUT OUT_OF_SERVICE)
- Đợi 90 giây.
- Dừng tất cả các service, rồi cài đặt lại chúng cho instance thứ nhất này.
- Đợi 90 giây.
- Hủy đăng ký của tất cả các dịch vụ web trong instance thứ hai của Eureka server (dùng enpoint pause của actuator hoặc Eureka API PUT OUT_OF_SERVICE)
- Đợi 90 giây.
- Dừng tất cả các service, rồi cài đặt lại chúng cho instance thứ hai này.
- sau khoảng 90 giây, các dịch vụ web của instance thứ hai sẽ hoạt động trở lại.
Ngoài ra, cần phải cài đặt retry cho gateway để tránh trường hợp một dịch vụ web có vấn đề thì truy vấn vẫn sẽ được thực hiện trong một dịch web tương đương khác có sẵn.
Cài đặt
1. Cấu hình để mở các endpoints actuator /pause và /shutdown
Trong tập tin application.yml
management:
endpoint:
pause:
enabled: true
restart:
enabled: true
resume:
enabled: true
shutdown:
enabled: true
Ví dụ dùng các endpoints này như sau :
$ curl -X POST http://localhost:1234/actuator/pause
$ curl -X POST http://localhost:1234/actuator/shutdown
2. Cài đặt retry
Trong tập tin pom.xml thêm Spring Retry vào dự án như sau :
<!-- enable retry for non-reactive version (load-balanced RestTemplate) -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
Trong tập tin cấu hình application.xml của zuul, ta cần kích hoạt loadbalancer và retry như sau :
spring:
cloud:
# enable retry for reactive version (with WebClient)
loadbalancer:
retry:
enabled: true
ribbon:
enabled: true
zuul:
retryable: true
ribbon:
OkToRetryOnAllOperations: true
MaxAutoRetries: 3
MaxAutoRetriesNextServer: 3
# 500 (Internal Server Error), 503 (Service Unavailable), 408 (Request Timeout)
retryableStatusCodes: 500, 503, 408
Trong các tập tin cấu hình application.xml của các services, ta cần cấu hình cho retry như sau :
ribbon:
OkToRetryOnAllOperations: true
MaxAutoRetries: 3
MaxAutoRetriesNextServer: 3
# 500 (Internal Server Error), 503 (Service Unavailable), 408 (Request Timeout)
retryableStatusCodes: 500, 503, 408
Xem thêm tại đây
https://openspacevn.blogspot.com/2023/07/cau-hinh-e-dung-auto-retry-cho-spring.html
3. Cài đặt cho các instance của HA
a. Cài đặt chung cho các instance trong tập tin application.xml
eureka:
instance:
prefer-ip-address: true
# Indicates the interval of heartbeats that the client sends to the server, in seconds (The default value is 30 seconds)
lease-renewal-interval-in-seconds: 1
# The time in seconds that the Eureka server waits since it received the last heartbeat from a client before it can remove that client from its registry (the default value is 90 seconds)
lease-expiration-duration-in-seconds: 1
client:
fetch-registry: true
register-with-eureka: true
healthcheck:
enabled: true
server:
# This property tells the Eureka server to run a job at this frequency to evict the expired clients. (the default value is 60 seconds)
eviction-interval-timer-in-ms: 1000
enableSelfPreservation: false
ribbon:
# in second
NFLoadBalancerPingInterval: 1
# in millisecond
ServerListRefreshInterval: 1000
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 2m
b. Cài đặt cho instance 1 trong tập tin application_ha_1.xml
eureka:
client:
service-url:
defaultZone: http://${app1.address.ip}:8761/eureka/,http://${app2.address.ip}:8761/eureka/
preferSameZoneEureka: true
transport:
applicationsResolverUseIp: true
instance:
metadata-map:
zone: zone1
c. Cài đặt cho instance 2 trong tập tin application_ha_2.xml
eureka:
client:
service-url:
defaultZone: http://${app2.address.ip}:8761/eureka/,http://${app1.address.ip}:8761/eureka/
preferSameZoneEureka: true
transport:
applicationsResolverUseIp: true
instance:
metadata-map:
zone: zone2
d. Cấu hình cho từng server Eureka ở HA
eureka:
client:
fetch-registry: true
register-with-eureka: true
e. Dùng Eureka API để đặt dịch vụ web ở chế độ OUT_OF_SERVICE
Cú pháp :
PUT /eureka/apps/appId/instanceId/status?value=OUT_OF_SERVICE
Ví dụ :
curl -X PUT http://localhost:8761/eureka/apps/DEMO-SERVICE/demo-service:f42adf31bb76e181988fc292a1344be9/status?value=OUT_OF_SERVICE
Tham khảo
Ngoài ra có thể tham khảo thêm các properties trong class org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean
(chẳng hạn như : peerEurekaNodesUpdateIntervalMs, peerEurekaStatusRefreshTimeIntervalMs ...)
Các đường dẫn tham khảo :
https://www.credera.com/insights/zero-downtime-rolling-deployments-netflixs-eureka-zuul
https://cloud.spring.io/spring-cloud-netflix/2.0.x/multi/multi__service_discovery_eureka_clients.html
https://github.com/spring-cloud/spring-cloud-netflix/issues/1571
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#graceful-shutdown
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.files.profile-specific