Bối cảnh và nhu cầu trong kiến trúc Microservices
Kiến trúc vi mô (Microservices) chia tách hệ thống lớn thành nhiều dịch vụ nhỏ độc lập. Để đảm bảo tính sẵn sàng cao, mỗi dịch vụ thường được triển khai trên nhiều node khác nhau. Điều này dẫn đến việc quản lý số lượng lớn các instance dịch vụ trở nên phức tạp, đặc biệt khi chúng cần gọi lẫn nhau. Do đó, một thành phần trung tâm để đăng ký và phát hiện dịch vụ là bắt buộc.
- Đăng ký dịch vụ: Instance của nhà cung cấp gửi thông tin (tên dịch vụ, địa chỉ IP, cổng) tới trung tâm quản lý.
- Phát hiện dịch vụ: Người tiêu dùng truy vấn trung tâm để lấy danh sách các node khả dụng của dịch vụ mục tiêu.
- Kiểm tra sức khỏe: Trung tâm cần loại bỏ các instance lỗi để tránh chuyển lưu lượng đến nơi không hoạt động.
Tổng quan về Eureka
Eureka là giải pháp mã nguồn mở từ Netflix dành cho việc quản lý danh sách dịch vụ. Nó đóng vai trò là trung tâm đăng ký, cung cấp khả năng đăng ký, tìm kiếm, kiểm tra trạng thái và cập nhật thông tin dịch vụ theo thời gian thực.
Trong hệ sinh thái Eureka, có hai vai trò chính:
- Máy chủ Eureka (Eureka Server): Cung cấp giao diện quản lý và lưu trữ dữ liệu dịch vụ.
- Khách hàng Eureka (Eureka Client): Tích hợp vào ứng dụng dịch vụ, có thể đóng vai trò là người cung cấp hoặc người tiêu dùng.
Các chức năng cốt lõi bao gồm:
- Đăng ký (Register): Gửi metadata như tên app, IP, cổng, trạng thái chạy lên server.
- Gửi lại tín hiệu sống (Renew): Client gửi nhịp tim (heartbeat) mỗi 30 giây. Nếu Server không nhận được trong 90 giây, nó sẽ coi instance đó đã chết.
- Lấy danh sách (Fetch Registries): Client tải toàn bộ danh sách dịch vụ từ Server về cache tại chỗ. Cache này được làm mới mỗi 30 giây.
- Hủy đăng ký (Cancel): Khi tắt ứng dụng bình thường, Client sẽ thông báo để Server xóa khỏi danh sách.
- Vứt bỏ (Eviction): Server tự động dọn dẹp các instance không gửi nhịp tim sau 90 giây.
Lưu ý kỹ thuật:
- Khởi tạo chậm: Client thường trễ khoảng 40 giây sau khi khởi động mới gửi yêu cầu đăng ký đầu tiên.
- Cơ chế cache: Server duy trì bản sao cache và đồng bộ định kỳ.
- Độ trễ tổng thể: Từ lúc một dịch vụ khởi động đến khi khả dụng bởi dịch vụ khác có thể lên tới 70 giây do cộng dồn độ trễ đăng ký và cache.
- Tự bảo vệ: Nếu tỷ lệ nhịp tim nhận được dưới 85% trong 15 phút, Server sẽ tạm ngừng việc hủy bỏ các instance để tránh xóa nhầm khi mạng gặp sự cố.
Hướng dẫn triển khai thực tế
Xây dựng máy chủ đăng ký
Bước đầu tiên là tạo một dự án chuyên biệt làm trung tâm quản lý.
1. Thêm thư viện Maven
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2. Cấu hình Application.yml
Tắt việc tự đăng ký để server này không tham gia vào danh sách dịch vụ.
server:
port: 8761
spring:
application:
name: registry-center
eureka:
client:
register-with-eureka: false
fetch-registry: false
instance:
prefer-ip-address: true
hostname: localhost
3. Điểm khởi động
@SpringBootApplication
@EnableEurekaServer // Kích hoạt vai trò Server
public class RegistryMain {
public static void main(String[] args) {
SpringApplication.run(RegistryMain.class, args);
}
}
Truy cập http://localhost:8761 để xem bảng điều khiển quản trị.
Tích hợp dịch vụ người dùng (Client)
Tạo một module dịch vụ cụ thể, ví dụ quản lý đơn hàng.
1. Phụ thuộc cần thiết
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2. Thiết lập cấu hình kết nối
server:
port: 8082
spring:
application:
name: order-service # Tên duy nhất của dịch vụ
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka/
3. Khởi tạo ứng dụng
@SpringBootApplication
@EnableDiscoveryClient // Hoặc @EnableEurekaClient
public class OrderServiceApp {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApp.class, args);
}
}
Sau khi chạy, dịch vụ order-service sẽ xuất hiện trên giao diện web của Server.
Nguyên lý hoạt động bên trong
Cơ chế phía Client (DiscoveryClient)
Khi sử dụng chú thích @EnableEurekaClient, Spring sẽ khởi tạo đối tượng DiscoveryClient. Quá trình khởi tạo này thiết lập ba luồng xử lý chính:
- Tim mạch (Heartbeat): Sử dụng
heartbeatExecutorđể gửi gói tin续约 (renew) định kỳ. - Nâng cấp Cache (Cache Refresh): Dùng
cacheRefreshExecutorđể pull dữ liệu dịch vụ mới nhất từ Server về. - Đồng bộ hóa Instance: Quản lý trạng thái đăng ký ban đầu.
Quá trình khởi chạy diễn ra như sau:
- Check cấu hình: Xác định xem có cần đăng ký hay không, có cần lấy danh sách không.
- Tạo ThreadPool: Khởi tạo bộ xử lý luồng bất đồng bộ cho các tác vụ nền.
- Lập lịch công việc: Gọi
initScheduledTasks()để gắn các tác vụ定时 vào Scheduler. Một tác vụ kéo dữ liệu registry (mặc định 30s), một tác vụ gửi nhịp tim (mặc định 30s). - Thực hiện đăng ký: Đợi một khoảng trễ (default 40s), sau đó gọi phương thức
register()để gửi POST request lên endpoint/apps/{appName}.
Cơ chế phía Server (EurekaServer)
Việc kích hoạt @EnableEurekaServer sẽ nạp class EurekaServerAutoConfiguration nếu phát hiện marker bean tương ứng. Khung xương cốt bao gồm:
- Jersey Framework: Xử lý các API RESTful để phục vụ đăng ký và tìm kiếm.
- PeerAwareInstanceRegistry: Thành phần chịu trách nhiệm lưu trữ danh sách instance. Dữ liệu được giữ trong bộ nhớ với cấu trúc Map lồng nhau (Group by AppName -> List of Instances).
Quy trình ghi chép (Registration):
- Nhận yêu cầu từ Client chứa
InstanceInfo. - Kiểm tra trùng lặp: Nếu ID đã tồn tại, so sánh timestamp dirty để quyết định cập nhật phiên bản mới hay cũ.
- Thêm vào tập hợp
registry(ConcurrentHashMap). - Broadcast: Thông báo sự kiện này sang các Node Peer khác trong cluster Eureka để đồng bộ hóa.
Xử lý hết hạn (Eviction):
Server không chờ Client báo lỗi mà chủ động dọn dẹp. Một tác vụ EvictionTask được lên lịch chạy mỗi 60 giây.
- Duyệt qua tất cả các lease trong registry.
- Kiểm tra thời gian gửi nhịp tim cuối cùng (
LastRenewedTime) cộng với thời gian thuê bao tối đa (thường 90s). - Nếu thời gian hiện tại vượt quá ngưỡng, instance bị đánh dấu là hết hạn.
- Tùy thuộc vào cơ chế tự bảo vệ, Server sẽ thực hiện xóa bỏ hoặc giữ lại.
Cơ chế Tự bảo vệ (Self-Preservation)
Trong môi trường phân tán, mạng lưới không phải lúc nào cũng ổn định. Nếu Eureka Server mất kết nối với Client nhưng Client vẫn chạy tốt, Server có thể hiểu nhầm rằng Client đã chết và xóa nó khỏi danh sách. Đây là rủi ro lớn.
Mục đích: Bảo vệ danh sách dịch vụ khỏi việc bị xóa sai lầm khi Server gặp sự cố mạng.
Logic hoạt động:
- Server theo dõi số lượng nhịp tim nhận được trong vòng 1 phút gần nhất (
Renews(last min)). - Tính toán ngưỡng dự kiến dựa trên số lượng Client đang active (
Renews threshold= Số Client * 2 lần/phút * 0.85). - Nếu thực tế thu thập được thấp hơn ngưỡng 85%, Server chuyển sang chế độ tự bảo vệ.
- Ở chế độ này, mặc dù các lease đã hết hạn, Server vẫn KHÔNG xóa chúng khỏi danh sách.
Hệ quả: Có thể tồn tại các dịch vụ thực tế đã chết nhưng vẫn hiển thị là online. Do đó, phía người tiêu dùng dịch vụ cần kết hợp với Cbreaker (Fusion/Fault Tolerance) để tự xử lý khi gọi đến địa chỉ lỗi.
Bảng so sánh các giải pháp Registry
| Đặc điểm | Eureka | Zookeeper | Nacos | Consul |
|---|---|---|---|---|
| Giao thức truyền thông | HTTP | TCP | HTTP/DNS | HTTP/gRPC |
| Đảm bảo tính nhất quán | AP (Sẵn sàng cao) | CP (Nhất quán mạnh) | Hỗ trợ CP/AP | CP |
| Hỗ trợ cân bằng tải | Có (Ribbon) | Không tích hợp sẵn | Có | Có |
| Chống sét sụp (Cascading Failure) | Có (Cơ chế tự bảo vệ) | Không | Có | Không |
| Tích hợp với Dubbo | Không hỗ trợ trực tiếp | Hỗ trợ tốt | Hỗ trợ tốt | Hỗ trợ |
| Tích hợp với Kubernetes | Hạn chế | Hạn chế | Hỗ trợ tốt | Hỗ trợ tốt |
Eureka nổi bật trong hệ sinh thái Spring Cloud nhờ tính đơn giản và khả năng chịu lỗi cao (AP), phù hợp cho các kịch bản ưu tiên tính khả dụng liên tục thay vì tính nhất quán tức thời.