Remove deprecated studio package and refactor agent integration - Deleted the studio package, which is no longer in use, to streamline the codebase. - Updated references in the agent and chat modules to utilize the new agent package instead of the deprecated neo package. - Ensured that all related middleware and routing functionalities are now aligned with the agent architecture, enhancing overall system coherence.
153 lines
3.3 KiB
Go
153 lines
3.3 KiB
Go
package message
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/gin-gonic/gin"
|
|
jsoniter "github.com/json-iterator/go"
|
|
"github.com/yaoapp/kun/log"
|
|
)
|
|
|
|
// AsyncMessageQueue represents a queue for handling message writes
|
|
type AsyncMessageQueue struct {
|
|
queue chan *AsyncTask
|
|
workers int
|
|
wg sync.WaitGroup
|
|
shutdown chan struct{}
|
|
}
|
|
|
|
// AsyncTask represents a task to write a message
|
|
type AsyncTask struct {
|
|
message *Message
|
|
writer gin.ResponseWriter
|
|
done chan bool
|
|
}
|
|
|
|
var (
|
|
defaultQueue *AsyncMessageQueue
|
|
queueOnce sync.Once
|
|
)
|
|
|
|
// GetQueue returns the default message queue instance
|
|
func GetQueue() *AsyncMessageQueue {
|
|
queueOnce.Do(func() {
|
|
defaultQueue = NewAsyncQueue(10) // Initialize with 10 workers
|
|
defaultQueue.Start()
|
|
})
|
|
return defaultQueue
|
|
}
|
|
|
|
// NewAsyncQueue creates a new message queue with the specified number of workers
|
|
func NewAsyncQueue(workers int) *AsyncMessageQueue {
|
|
return &AsyncMessageQueue{
|
|
queue: make(chan *AsyncTask, 1000), // Buffer size of 1000
|
|
workers: workers,
|
|
shutdown: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// Start starts the message queue workers
|
|
func (mq *AsyncMessageQueue) Start() {
|
|
for i := 0; i < mq.workers; i++ {
|
|
mq.wg.Add(1)
|
|
go mq.worker()
|
|
}
|
|
}
|
|
|
|
// Stop stops the message queue workers
|
|
func (mq *AsyncMessageQueue) Stop() {
|
|
close(mq.shutdown)
|
|
mq.wg.Wait()
|
|
}
|
|
|
|
// worker processes messages from the queue
|
|
func (mq *AsyncMessageQueue) worker() {
|
|
defer mq.wg.Done()
|
|
|
|
for {
|
|
select {
|
|
case task := <-mq.queue:
|
|
if task == nil {
|
|
continue
|
|
}
|
|
success := writeMessageToResponse(task.message, task.writer)
|
|
if task.done != nil {
|
|
task.done <- success
|
|
}
|
|
case <-mq.shutdown:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// WriteMessageAsync writes the message to response writer using the message queue
|
|
func WriteMessageAsync(m *Message, w gin.ResponseWriter) bool {
|
|
done := make(chan bool, 1)
|
|
task := &AsyncTask{
|
|
message: m,
|
|
writer: w,
|
|
done: done,
|
|
}
|
|
|
|
// Try to send the task to the queue with a timeout
|
|
select {
|
|
case GetQueue().queue <- task:
|
|
// Wait for the message to be processed with a longer timeout
|
|
select {
|
|
case success := <-done:
|
|
return success
|
|
case <-time.After(5 * time.Second): // Increased timeout to 5 seconds
|
|
log.Error("Message processing timeout")
|
|
return false
|
|
}
|
|
case <-time.After(1 * time.Second): // Increased queue timeout to 1 second
|
|
log.Error("Queue is full, message dropped")
|
|
return false
|
|
}
|
|
}
|
|
|
|
// writeMessageToResponse writes the message directly to the response writer
|
|
func writeMessageToResponse(m *Message, w gin.ResponseWriter) bool {
|
|
// Sync write to response writer
|
|
locker.Lock()
|
|
defer locker.Unlock()
|
|
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
// Ignore if done is true
|
|
if m.IsDone {
|
|
return
|
|
}
|
|
|
|
message := "Write Response Exception: (if client close the connection, it's normal) \n %s\n\n"
|
|
color.Red(message, r)
|
|
|
|
// Print the message
|
|
raw, _ := jsoniter.MarshalToString(m)
|
|
color.White("Message:\n %s", raw)
|
|
}
|
|
}()
|
|
|
|
// Ignore silent messages
|
|
if m.Silent {
|
|
return true
|
|
}
|
|
|
|
data, err := jsoniter.Marshal(m)
|
|
if err != nil {
|
|
log.Error("%s", err.Error())
|
|
return false
|
|
}
|
|
|
|
data = append([]byte("data: "), data...)
|
|
data = append(data, []byte("\n\n")...)
|
|
|
|
if _, err := w.Write(data); err != nil {
|
|
color.Red("Write JSON Message Error: %s", err.Error())
|
|
return false
|
|
}
|
|
w.Flush()
|
|
return true
|
|
}
|