Ente là một dịch vụ lưu trữ hình ảnh mã hóa đầu cuối (E2EE) đòi hỏi tính bảo mật cao và sự đồng bộ tức thì giữa các thiết bị. Hệ thống thông báo đẩy của Ente được thiết kế để hoạt động ổn định trên Android, iOS và Web, đồng thời đảm bảo quyền riêng tư tối đa cho người dùng.
Tổng quan về kiến trúc hệ thống
Hệ thống sử dụng mô hình kết hợp giữa dịch vụ trung gian (FCM/APNs) và máy chủ điều phối viết bằng ngôn ngữ Go.
| Thành phần | Giải pháp kỹ thuật | Vai trò chính |
|---|---|---|
| Android | Firebase Cloud Messaging (FCM) | Nhận thông báo qua plugin Flutter |
| iOS | Apple Push Notification Service (APNs) | Quản lý token và thông báo nền |
| Backend | Go + Gin Framework | Xử lý logic, quản lý hàng đợi và SDK Firebase Admin |
| Database | PostgreSQL | Lưu trữ và lập chỉ mục token thiết bị |
Triển khai phía Client (Flutter)
Ứng dụng di động của Ente sử dụng Flutter để đồng nhất mã nguồn. Việc quản lý token được thực hiện thông qua lớp dịch vụ chuyên biệt.
class NotificationManager {
static const String KEY_FCM_TOKEN = "device_fcm_token";
static const String KEY_LAST_SYNC_TIME = "last_token_sync_timestamp";
Future<void> setup() async {
await Firebase.initializeApp();
// Lắng nghe thông báo khi ứng dụng đang mở
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
_onMessageReceived(message);
});
}
Future<void> _refreshDeviceToken() async {
final fcmToken = await FirebaseMessaging.instance.getToken();
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (fcmToken != null) {
await _registerTokenWithServer(fcmToken, apnsToken);
}
}
void _onMessageReceived(RemoteMessage msg) {
if (msg.data['action'] == 'trigger_sync') {
SyncCoordinator.instance.startDataSync();
}
}
}
Xử lý tại máy chủ (Golang)
Phía Backend chịu trách nhiệm điều phối việc gửi thông báo dựa trên trạng thái của người dùng và thiết bị.
Cấu trúc bộ điều phối thông báo
type NotificationDispatcher struct {
TokenStore *repository.TokenRepo
LockManager *repository.LockRepo
MessagingAgent *messaging.Client
}
const (
PushFrequencyLimit = 60 // phút
MaxBatchSize = 500
TokenLifetimeDays = 61
)
Logic gửi thông báo đa nền tảng
Hệ thống sử dụng cơ chế Multicast để gửi thông báo đến nhiều thiết bị cùng lúc, tối ưu hóa hiệu suất mạng.
func (d *NotificationDispatcher) DispatchToDevices(tokens []string, payload map[string]string) error {
msg := &messaging.MulticastMessage{
Tokens: tokens,
Data: payload,
Android: &messaging.AndroidConfig{
Priority: "high",
},
APNS: &messaging.APNSConfig{
Headers: map[string]string{
"apns-push-type": "background",
"apns-priority": "5",
},
Payload: &messaging.APNSPayload{
Aps: &messaging.Aps{ContentAvailable: true},
},
},
}
response, err := d.MessagingAgent.SendMulticast(context.Background(), msg)
if err != nil {
return err
}
log.Printf("Gửi thành công: %d, Thất bại: %d", response.SuccessCount, response.FailureCount)
return nil
}
Thiết kế cơ sở dữ liệu
Bảng lưu trữ token được thiết kế để truy vấn nhanh chóng và quản lý vòng đời thiết bị.
CREATE TABLE device_notifications (
uid BIGINT NOT NULL,
fcm_identifier TEXT NOT NULL,
apns_identifier TEXT,
ts_created BIGINT NOT NULL,
ts_updated BIGINT NOT NULL,
ts_last_notified BIGINT NOT NULL,
PRIMARY KEY (fcm_identifier)
);
Cơ chế bảo mật và tối ưu hóa
Mã hóa đầu cuối (E2EE)
Nội dung nhạy cảm không bao giờ được gửi trực tiếp qua thông báo đẩy. Thay vào đó, hệ thống chỉ gửi một "tín hiệu" (action trigger). Khi nhận được tín hiệu này, ứng dụng khách sẽ tự động kết nối với máy chủ qua kênh bảo mật để tải dữ liệu đã mã hóa và giải mã cục bộ.
Kiểm soát lưu lượng và khóa tác vụ
Để tránh tình trạng gửi thông báo trùng lặp hoặc quá tải, Ente sử dụng cơ chế khóa tác vụ (Task Locking):
isAcquired, err := d.LockManager.TryLock(
"push_sync_task",
time.Duration(10) * time.Minute,
d.WorkerID,
)
if err == nil && isAcquired {
// Thực hiện logic quét và gửi thông báo
defer d.LockManager.ReleaseLock("push_sync_task", d.WorkerID)
}
Các kịch bản ứng dụng
- Đồng bộ ảnh mới: Khi người dùng tải ảnh lên từ Web, điện thoại sẽ nhận thông báo ngầm để bắt đầu quá trình tải về và lập chỉ mục.
- Phối hợp đa thiết bị: Trạng thái đọc thông báo hoặc thay đổi cài đặt được cập nhật tức thì trên toàn bộ hệ sinh thái thiết bị của người dùng.
- Duy trì phiên làm việc: Tự động làm mới các token bảo mật khi có yêu cầu từ máy chủ mà không cần sự can thiệp của người dùng.
Chiến lược xử lý lỗi
| Tình huống | Giải pháp xử lý |
|---|---|
| Token không hợp lệ | Xóa ngay lập tức khỏi DB để tránh gửi lặp lại vào địa chỉ rác. |
| Lỗi kết nối FCM/APNs | Sử dụng cơ chế Exponential Backoff để thử lại sau một khoảng thời gian. |
| Thiết bị ngoại tuyến | Thông báo sẽ được hàng đợi của dịch vụ đẩy (FCM) lưu giữ theo thời gian sống (TTL) đã cấu hình. |