feat: implement real-time Pub/Sub alert broadcast system and update docs/GRPC.md

This commit is contained in:
2026-07-17 19:24:25 +09:00
parent 8bb11fe205
commit 20a818520c
6 changed files with 444 additions and 24 deletions
+68
View File
@@ -31,6 +31,28 @@ var (
storeMu sync.RWMutex
)
type AlertSubscriber struct {
ClientId string
Channel chan *protoapi.AlertMessage
}
var (
subscribers = make(map[string]*AlertSubscriber)
subMu sync.Mutex
)
func publishAlert(alert *protoapi.AlertMessage) {
subMu.Lock()
defer subMu.Unlock()
for _, sub := range subscribers {
select {
case sub.Channel <- alert:
default:
fmt.Printf("Alert channel blocked for client %s, dropping event\n", sub.ClientId)
}
}
}
func random(min, max int, src rand.Source) int {
return rand.New(src).Intn(max-min) + min
}
@@ -81,6 +103,17 @@ func (IoTServer) GetDate(ctx context.Context, r *protoapi.RequestDateTime) (*pro
func (IoTServer) UpdateSensingData(ctx context.Context, r *protoapi.SensingData) (*protoapi.SensingResponse, error) {
fmt.Printf("Received sensing data - Device: %s, Temp: %.2f°C, Humid: %.2f%%\n", r.GetDeviceId(), r.GetTemperature(), r.GetHumidity())
// 임계값 초과(40도 초과) 시 실시간 Pub/Sub 경보 메시지 발행
if r.GetTemperature() > 40.0 {
fmt.Printf("⚠️ Critical temperature detected: %.2f°C! Publishing warning...\n", r.GetTemperature())
publishAlert(&protoapi.AlertMessage{
AlertId: fmt.Sprintf("alert-%d", time.Now().UnixNano()),
DeviceId: r.GetDeviceId(),
Message: fmt.Sprintf("Critical high temperature: %.2f°C (Humidity: %.2f%%)", r.GetTemperature(), r.GetHumidity()),
Timestamp: time.Now().Unix(),
})
}
response := &protoapi.SensingResponse{
Success: true,
Message: fmt.Sprintf("Sensing data updated successfully for device %s", r.GetDeviceId()),
@@ -185,6 +218,41 @@ func (IoTServer) DownloadFile(r *protoapi.DownloadRequest, stream protoapi.IoTSe
return nil
}
func (IoTServer) SubscribeAlerts(r *protoapi.AlertSubscription, stream protoapi.IoTService_SubscribeAlertsServer) error {
clientId := r.GetClientId()
ch := make(chan *protoapi.AlertMessage, 10)
sub := &AlertSubscriber{
ClientId: clientId,
Channel: ch,
}
subMu.Lock()
subscribers[clientId] = sub
subMu.Unlock()
fmt.Printf("Client %s subscribed to alerts on topic '%s'\n", clientId, r.GetTopic())
for {
select {
case alert := <-ch:
err := stream.Send(alert)
if err != nil {
subMu.Lock()
delete(subscribers, clientId)
subMu.Unlock()
fmt.Printf("Client %s alert subscription disconnected: %v\n", clientId, err)
return err
}
case <-stream.Context().Done():
subMu.Lock()
delete(subscribers, clientId)
subMu.Unlock()
fmt.Printf("Client %s unsubscribed (context done)\n", clientId)
return nil
}
}
}
func ServerRun(addr string) {
server := grpc.NewServer()
var iotServer IoTServer