feat(chat): асинхронная обработка входящих — отзывчивый интерфейс
Router теперь обрабатывает входящие в воркер-горутине (FIFO-очередь с буфером 256) вместо синхронного вызова onUserMsg из long-poll цикла канала. Долгий вызов аналитика (Decide) больше не блокирует приём новых сообщений от Telegram: цикл getUpdates продолжает работать. - router.go: NewRouter запускает processLoop; handleIncoming кладёт событие в канал и возвращается; маршрутизация + pending по-прежнему обновляются синхронно под мьютексом. - router_test.go: fakeOnMsg стал потокобезопасным с ожиданием числа входящих (wait), т.к. обработка теперь асинхронная. Преимущества: интерфейс не замирает на время анализа; порядок входящих сохраняется (FIFO). Ограничение: воркер один — при очень долгом аналитике следующие сообщения ждут в очереди, но канал их продолжает принимать.
This commit is contained in:
@@ -18,6 +18,13 @@ type Router struct {
|
||||
|
||||
// Hook, вызываемый на каждое входящее событие (обычно → process_turn).
|
||||
onUserMsg func(Incoming)
|
||||
|
||||
// Асинхронная обработка входящих: handleIncoming кладёт событие в канал,
|
||||
// воркер-горутина последовательно вызывает onUserMsg. Благодаря этому
|
||||
// long-poll цикл канала (Telegram) не блокируется на время долгого
|
||||
// вызова аналитика и продолжает принимать новые сообщения.
|
||||
incoming chan Incoming
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewRouter создаёт роутер. onUserMsg — колбэк обработки входящего.
|
||||
@@ -25,11 +32,22 @@ func NewRouter(onUserMsg func(Incoming)) *Router {
|
||||
if onUserMsg == nil {
|
||||
onUserMsg = func(Incoming) {}
|
||||
}
|
||||
return &Router{
|
||||
r := &Router{
|
||||
sessions: map[UserID]any{},
|
||||
routes: map[UserID]Route{},
|
||||
pending: map[UserID]PendingQ{},
|
||||
onUserMsg: onUserMsg,
|
||||
incoming: make(chan Incoming, 256),
|
||||
}
|
||||
go r.processLoop()
|
||||
return r
|
||||
}
|
||||
|
||||
// processLoop — воркер асинхронной обработки входящих (FIFO).
|
||||
func (r *Router) processLoop() {
|
||||
for inc := range r.incoming {
|
||||
r.onUserMsg(inc)
|
||||
r.wg.Done()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +77,11 @@ func (r *Router) handleIncoming(inc Incoming) {
|
||||
delete(r.pending, inc.UserID)
|
||||
}
|
||||
r.mu.Unlock()
|
||||
r.onUserMsg(inc)
|
||||
|
||||
// Асинхронная обработка: кладём событие в очередь воркера и сразу
|
||||
// возвращаемся, не блокируя вызывающий long-poll цикл канала.
|
||||
r.wg.Add(1)
|
||||
r.incoming <- inc
|
||||
}
|
||||
|
||||
// Send уведомляет пользователя через текущий маршрут. M1 (нет маршрута) — no-op,
|
||||
|
||||
Reference in New Issue
Block a user