diff --git a/server/actor_srv/server/conn.go b/server/actor_srv/server/conn.go deleted file mode 100644 index 69279e3c..00000000 --- a/server/actor_srv/server/conn.go +++ /dev/null @@ -1,46 +0,0 @@ -package server - -import ( - "errors" - "github.com/kercylan98/minotaur/vivid" - "github.com/kercylan98/minotaur/vivid/vivids" - "net" -) - -type ConnectionWriter func(packet Packet) error - -type ConnProps struct { - Conn net.Conn - Writer ConnectionWriter - MessageChan chan Packet -} - -type Conn struct { - vivid.BasicActor - ConnProps -} - -func (c *Conn) OnPreStart(ctx vivids.ActorContext) (err error) { - var ok bool - c.ConnProps, ok = ctx.GetProps().(ConnProps) - if !ok { - return errors.New("invalid ConnProps") - } - - return -} - -func (c *Conn) OnReceived(ctx vivids.MessageContext) (err error) { - switch e := ctx.GetMessage().(type) { - case error: - ctx.NotifyTerminated() // 已关闭,销毁 Actor 即可 - case NetworkConnectionReceivedMessage: - return c.ConnProps.Writer(e.Packet) - } - - return -} - -func (c *Conn) OnDestroy(ctx vivids.ActorContext) (err error) { - return c.ConnProps.Conn.Close() // 关闭连接 -} diff --git a/server/actor_srv/server/network.go b/server/actor_srv/server/network.go deleted file mode 100644 index 9601f432..00000000 --- a/server/actor_srv/server/network.go +++ /dev/null @@ -1,31 +0,0 @@ -package server - -import ( - "github.com/kercylan98/minotaur/vivid/vivids" -) - -type Network interface { - vivids.Actor -} - -type ( - NetworkConnectionOpenedEvent struct { - vivids.ActorRef - } - - NetworkConnectionClosedEvent struct { - vivids.ActorRef - } - - NetworkConnectionReceivedMessage struct { - Packet Packet - } - - ConnectionWriteMessage struct { - Packet Packet - } - - ConnectionAsyncWriteErrorEvent struct { - Error error - } -) diff --git a/server/actor_srv/server/network/gnet.go b/server/actor_srv/server/network/gnet.go deleted file mode 100644 index 13313464..00000000 --- a/server/actor_srv/server/network/gnet.go +++ /dev/null @@ -1,225 +0,0 @@ -package network - -import ( - "context" - "errors" - "fmt" - "github.com/gobwas/ws" - "github.com/gobwas/ws/wsutil" - "github.com/kercylan98/minotaur/server/actor_srv/server" - "github.com/kercylan98/minotaur/toolkit/collection" - "github.com/kercylan98/minotaur/vivid" - "github.com/kercylan98/minotaur/vivid/vivids" - "github.com/panjf2000/gnet/v2" - "time" -) - -var ( - schemaWebSocket = "ws" - schemaTcp = "tcp" - schemaTcp4 = "tcp4" - schemaTcp6 = "tcp6" - schemaUdp = "udp" - schemaUdp4 = "udp4" - schemaUdp6 = "udp6" - schemaUnix = "unix" -) - -func newGnetEngine(schema, addr string, pattern ...string) server.Network { - g := &gnetEngine{ - addr: addr, - schema: schema, - pattern: collection.FindFirstOrDefaultInSlice(pattern, "/"), - } - return g -} - -type gnetEngine struct { - vivid.BasicActor - addr string - schema string - pattern string - eng gnet.Engine - upgrader ws.Upgrader - ctx vivids.ActorContext -} - -func (g *gnetEngine) OnPreStart(ctx vivids.ActorContext) (err error) { - g.ctx = ctx - var addr string - switch g.schema { - case schemaTcp, schemaWebSocket: - addr = fmt.Sprintf("tcp://%s", g.addr) - if g.schema == schemaWebSocket { - g.initWebSocketUpgrader() - } - case schemaTcp4: - addr = fmt.Sprintf("tcp4://%s", g.addr) - case schemaTcp6: - addr = fmt.Sprintf("tcp6://%s", g.addr) - case schemaUdp: - addr = fmt.Sprintf("udp://%s", g.addr) - case schemaUdp4: - addr = fmt.Sprintf("udp4://%s", g.addr) - case schemaUdp6: - addr = fmt.Sprintf("udp6://%s", g.addr) - case schemaUnix: - addr = fmt.Sprintf("unix://%s", g.addr) - default: - return fmt.Errorf("unsupported schema: %s", g.schema) - } - - ctx.Future(func() vivids.Message { - return gnet.Run(g, addr) - }) - - return -} - -func (g *gnetEngine) OnReceived(ctx vivids.MessageContext) (err error) { - switch v := ctx.GetMessage().(type) { - case error: - ctx.NotifyTerminated(v) - } - - return -} - -func (g *gnetEngine) OnDestroy(ctx vivids.ActorContext) (err error) { - return g.eng.Stop(context.TODO()) -} - -func (g *gnetEngine) OnBoot(eng gnet.Engine) (action gnet.Action) { - g.eng = eng - return -} - -func (g *gnetEngine) OnShutdown(eng gnet.Engine) { - -} - -func (g *gnetEngine) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) { - if g.schema == schemaWebSocket { - c.SetContext(newWebsocketWrapper(c)) - } else { - connActor, err := g.ctx.ActorOf(new(server.Conn), vivids.NewActorOptions(). - WithName(c.RemoteAddr().String()). - WithProps(server.ConnProps{ - Conn: c, - Writer: func(packet server.Packet) error { - return c.AsyncWrite(packet.GetBytes(), func(c gnet.Conn, err error) error { - return g.ctx.GetParentActor().Tell(server.ConnectionAsyncWriteErrorEvent{ - Error: err, - }) - }) - }, - })) - if err != nil { - action = gnet.Close - return - } - - g.ctx.PublishEvent(server.NetworkConnectionOpenedEvent{ - ActorRef: connActor, - }) - - c.SetContext(connActor) - } - return -} - -func (g *gnetEngine) OnClose(c gnet.Conn, err error) (action gnet.Action) { - var conn vivids.ActorRef - if g.schema == schemaWebSocket { - conn = c.Context().(*websocketWrapper).ref - } else { - conn = c.Context().(vivids.ActorRef) - } - - _ = g.ctx.GetParentActor().Tell(server.NetworkConnectionClosedEvent{ - ActorRef: conn, - }, vivids.WithMessageSender(g.ctx)) - return -} - -func (g *gnetEngine) OnTraffic(c gnet.Conn) (action gnet.Action) { - if g.schema == schemaWebSocket { - wrapper := c.Context().(*websocketWrapper) - - if err := wrapper.readToBuffer(); err != nil { - return gnet.Close - } - - if err := wrapper.upgrade(g.upgrader, func() { - // 协议升级成功后视为连接建立 - conn, err := g.ctx.ActorOf(new(server.Conn), vivids.NewActorOptions(). - WithName(c.RemoteAddr().String()). - WithProps(server.ConnProps{ - Conn: c, - Writer: func(packet server.Packet) error { - return wsutil.WriteServerMessage(c, ws.OpText, packet.GetBytes()) - }, - })) - if err != nil { - action = gnet.Close - return - } - - wrapper.ref = conn - g.ctx.PublishEvent(server.NetworkConnectionOpenedEvent{ - ActorRef: conn, - }) - - }); err != nil { - return gnet.Close - } - wrapper.active = time.Now() - - // decode - messages, err := wrapper.decode() - if err != nil { - return gnet.Close - } - - for _, message := range messages { - packet := server.NewPacket(message.Payload) - packet.SetContext(message.OpCode) - if err = wrapper.ref.Tell(server.NetworkConnectionReceivedMessage{ - Packet: packet, - }, vivids.WithMessageSender(g.ctx)); err != nil { - action = gnet.Close - break - } - } - } else { - buf, err := c.Next(-1) - if err != nil { - return gnet.Close - } - - var clone = make([]byte, len(buf)) - copy(clone, buf) - - if err = c.Context().(vivids.ActorRef).Tell(server.NetworkConnectionReceivedMessage{ - Packet: server.NewPacket(clone), - }, vivids.WithMessageSender(g.ctx)); err != nil { - action = gnet.Close - } - } - return -} - -func (g *gnetEngine) OnTick() (delay time.Duration, action gnet.Action) { - return -} - -func (g *gnetEngine) initWebSocketUpgrader() { - g.upgrader = ws.Upgrader{ - OnRequest: func(uri []byte) (err error) { - if string(uri) != g.pattern { - err = errors.New("bad request") - } - return - }, - } -} diff --git a/server/actor_srv/server/network/gnet_logger.go b/server/actor_srv/server/network/gnet_logger.go deleted file mode 100644 index f1f7fa0a..00000000 --- a/server/actor_srv/server/network/gnet_logger.go +++ /dev/null @@ -1,24 +0,0 @@ -package network - -type gNetLogger struct { -} - -func (l *gNetLogger) Debugf(format string, args ...interface{}) { - -} - -func (l *gNetLogger) Infof(format string, args ...interface{}) { - -} - -func (l *gNetLogger) Warnf(format string, args ...interface{}) { - -} - -func (l *gNetLogger) Errorf(format string, args ...interface{}) { - -} - -func (l *gNetLogger) Fatalf(format string, args ...interface{}) { - -} diff --git a/server/actor_srv/server/network/http.go b/server/actor_srv/server/network/http.go deleted file mode 100644 index b348911e..00000000 --- a/server/actor_srv/server/network/http.go +++ /dev/null @@ -1,46 +0,0 @@ -package network - -import ( - "context" - "errors" - "github.com/kercylan98/minotaur/server" - "github.com/kercylan98/minotaur/vivid" - "github.com/kercylan98/minotaur/vivid/vivids" - "net/http" -) - -type HttpServe struct { - *http.ServeMux -} - -type httpCore[H http.Handler] struct { - vivid.BasicActor - addr string - handler H - srv *http.Server - controller server.Controller -} - -func (h *httpCore[H]) OnPreStart(ctx vivids.ActorContext) (err error) { - ctx.Future(func() vivids.Message { - return h.srv.ListenAndServe() - }) - - return -} - -func (h *httpCore[H]) OnReceived(ctx vivids.MessageContext) (err error) { - switch v := ctx.GetMessage().(type) { - case error: - switch { - case errors.Is(v, http.ErrServerClosed): - ctx.NotifyTerminated() - } - } - - return -} - -func (h *httpCore[H]) OnDestroy(ctx vivids.ActorContext) (err error) { - return h.srv.Shutdown(context.TODO()) -} diff --git a/server/actor_srv/server/network/kcp.go b/server/actor_srv/server/network/kcp.go deleted file mode 100644 index cb00c240..00000000 --- a/server/actor_srv/server/network/kcp.go +++ /dev/null @@ -1,108 +0,0 @@ -package network - -import ( - "github.com/kercylan98/minotaur/server/actor_srv/server" - "github.com/kercylan98/minotaur/vivid" - "github.com/kercylan98/minotaur/vivid/vivids" - "github.com/xtaci/kcp-go/v5" - "runtime" - "sync" - "sync/atomic" -) - -var kcpInitOnce sync.Once - -func init() { - kcp.SystemTimedSched.Close() // 默认禁用 KCP 系统定时器 - kcp.SystemTimedSched = nil -} - -func newKcpCore(addr string) server.Network { - kcpInitOnce.Do(func() { - if kcp.SystemTimedSched == nil { - kcp.SystemTimedSched = kcp.NewTimedSched(runtime.NumCPU()) - } - }) - return &kcpCore{ - addr: addr, - } -} - -type kcpCore struct { - vivid.BasicActor - addr string - closed atomic.Bool - ctx vivids.ActorContext -} - -// -//func (k *kcpCore) OnPreStart(ctx vivids.ActorContext) (err error) { -// k.ctx = ctx -// ctx.Future(func() vivids.Message { -// lis, err := kcp.ListenWithOptions(k.addr, nil, 0, 0) -// if err != nil { -// return err -// } -// defer func(lis *kcp.Listener) { -// _ = lis.Close() -// }(lis) -// for !k.closed.Load() { -// var conn *kcp.UDPSession -// var srvConn server.Conn -// if conn, err = lis.AcceptKCP(); err != nil { -// continue -// } -// -// // 注册连接 -// srvConn, err = k.ctx.GetParentActor().Ask(server.ServerNetworkConnectionOpenedEvent{ -// Conn: conn, -// ConnectionWriter: func(packet server.Packet) (err error) { -// if _, err = conn.Write(packet.GetBytes()); err != nil { -// return k.ctx.GetParentActor().Tell(server.NetworkConnectionAsyncWriteErrorMessage{ -// Conn: srvConn, -// Packet: packet, -// Error: err, -// }) -// } -// return -// }, -// }) -// -// // 处理连接数据 -// go func(ctx context.Context, conn *kcp.UDPSession, srvConn server.Conn) { -// var buf = make([]byte, 1024) -// var n int -// for { -// select { -// case <-ctx.Done(): -// return -// default: -// if n, err = conn.Read(buf); err != nil { -// srvConn.Close() -// return -// } -// k.controller.ReactPacket(conn, server.NewPacket(buf[:n])) -// } -// } -// }(ctx, conn, srvConn) -// } -// -// return nil -// }) -// -// return -//} -// -//func (k *kcpCore) OnReceived(ctx vivids.MessageContext) (err error) { -// switch v := ctx.GetMessage().(type) { -// case error: -// ctx.NotifyTerminated(v) -// } -// -// return -//} -// -//func (k *kcpCore) OnDestroy(ctx vivids.ActorContext) (err error) { -// k.closed.Store(true) -// return nil -//} diff --git a/server/actor_srv/server/network/networks.go b/server/actor_srv/server/network/networks.go deleted file mode 100644 index 5c014717..00000000 --- a/server/actor_srv/server/network/networks.go +++ /dev/null @@ -1,80 +0,0 @@ -package network - -import ( - "github.com/kercylan98/minotaur/server/actor_srv/server" - "net/http" -) - -// Http 创建一个基于 http.ServeMux 的 HTTP 的网络 -func Http(addr string) server.Network { - return HttpWithHandler(addr, &HttpServe{ServeMux: http.NewServeMux()}) -} - -// HttpWithHandler 创建一个基于 http.Handler 的 HTTP 的网络 -func HttpWithHandler[H http.Handler](addr string, handler H) server.Network { - c := &httpCore[H]{ - addr: addr, - handler: handler, - srv: &http.Server{ - Addr: addr, - Handler: handler, - DisableGeneralOptionsHandler: false, - }, - } - return c -} - -// WebSocket 创建一个基于 TCP 的 WebSocket 网络 -// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 -// - pattern 期望为 WebSocket 的路径,如果为空则默认为 / -func WebSocket(addr string, pattern ...string) server.Network { - return newGnetEngine(schemaWebSocket, addr, pattern...) -} - -// Tcp 创建一个 TCP 网络 -// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 -func Tcp(addr string) server.Network { - return newGnetEngine(schemaTcp, addr) -} - -// Tcp4 创建一个 IPv4 TCP 网络 -// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 -func Tcp4(addr string) server.Network { - return newGnetEngine(schemaTcp4, addr) -} - -// Tcp6 创建一个 IPv6 TCP 网络 -// - addr 期望为类似于 [::1]:1234 的地址 -func Tcp6(addr string) server.Network { - return newGnetEngine(schemaTcp6, addr) -} - -// Udp 创建一个 UDP 网络 -// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 -func Udp(addr string) server.Network { - return newGnetEngine(schemaUdp, addr) -} - -// Udp4 创建一个 IPv4 UDP 网络 -// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 -func Udp4(addr string) server.Network { - return newGnetEngine(schemaUdp4, addr) -} - -// Udp6 创建一个 IPv6 UDP 网络 -// - addr 期望为类似于 [::1]:1234 的地址 -func Udp6(addr string) server.Network { - return newGnetEngine(schemaUdp6, addr) -} - -// Unix 创建一个 Unix Domain Socket 网络 -// - addr 期望为类似于 /tmp/xxx.sock 的文件地址 -func Unix(addr string) server.Network { - return newGnetEngine(schemaUnix, addr) -} - -// Kcp 创建一个 KCP 网络 -// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 -func Kcp(addr string) server.Network { - return newKcpCore(addr) -} diff --git a/server/actor_srv/server/network/read_writer.go b/server/actor_srv/server/network/read_writer.go deleted file mode 100644 index 58f10db1..00000000 --- a/server/actor_srv/server/network/read_writer.go +++ /dev/null @@ -1,8 +0,0 @@ -package network - -import "io" - -type ReadWriter struct { - io.Reader - io.Writer -} diff --git a/server/actor_srv/server/network/wsbsocket_wrapper.go b/server/actor_srv/server/network/wsbsocket_wrapper.go deleted file mode 100644 index fe9a5532..00000000 --- a/server/actor_srv/server/network/wsbsocket_wrapper.go +++ /dev/null @@ -1,160 +0,0 @@ -package network - -import ( - "bytes" - "errors" - "fmt" - "github.com/gobwas/ws" - "github.com/gobwas/ws/wsutil" - "github.com/kercylan98/minotaur/vivid/vivids" - "github.com/panjf2000/gnet/v2" - "io" - "time" -) - -// newWebsocketWrapper 创建 websocket 包装器 -func newWebsocketWrapper(conn gnet.Conn) *websocketWrapper { - wrapper := &websocketWrapper{ - conn: conn, - upgraded: false, - active: time.Now(), - } - return wrapper -} - -// websocketWrapper websocket 包装器 -type websocketWrapper struct { - ref vivids.ActorRef // 引用 - conn gnet.Conn // 连接 - upgraded bool // 是否已经升级 - hs ws.Handshake // 握手信息 - active time.Time // 活跃时间 - buf bytes.Buffer // 缓冲区 - - header *ws.Header // 当前头部 - cache bytes.Buffer // 缓存的数据 -} - -// readToBuffer 将数据读取到缓冲区 -func (w *websocketWrapper) readToBuffer() error { - size := w.conn.InboundBuffered() - buf := make([]byte, size) - n, err := w.conn.Read(buf) - if err != nil { - return err - } - if n < size { - return fmt.Errorf("incomplete data, read buffer bytes failed! size: %d, read: %d", size, n) - } - w.buf.Write(buf) - return nil -} - -// upgrade 升级 -func (w *websocketWrapper) upgrade(upgrader ws.Upgrader, upgradedHandler func()) (err error) { - if w.upgraded { - return - } - - buf := &w.buf - reader := bytes.NewReader(buf.Bytes()) - n := reader.Len() - - w.hs, err = upgrader.Upgrade(ReadWriter{ - Reader: reader, - Writer: w.conn, - }) - skip := n - reader.Len() - if err != nil { - if err == io.EOF || errors.Is(err, io.ErrUnexpectedEOF) { //数据不完整,不跳过 buf 中的 skipN 字节(此时 buf 中存放的仅是部分 "handshake data" bytes),下次再尝试读取 - return - } - buf.Next(skip) - return err - } - buf.Next(skip) - w.upgraded = true - upgradedHandler() - return -} - -// decode 解码 -func (w *websocketWrapper) decode() (messages []wsutil.Message, err error) { - if messages, err = w.read(); err != nil { - return - } - var result = make([]wsutil.Message, 0, len(messages)) - for _, message := range messages { - if message.OpCode.IsControl() { - err = wsutil.HandleClientControlMessage(w.conn, message) - if err != nil { - return - } - continue - } - if message.OpCode == ws.OpText || message.OpCode == ws.OpBinary { - result = append(result, message) - } - } - return result, nil -} - -// decode 解码 -func (w *websocketWrapper) read() (messages []wsutil.Message, err error) { - var buf = &w.buf - for { - // 从缓冲区中读取 header 信息并写入到缓存中 - if w.header == nil { - if buf.Len() < ws.MinHeaderSize { - return // 不完整的数据,不做处理 - } - var header ws.Header - if buf.Len() >= ws.MaxHeaderSize { - header, err = ws.ReadHeader(buf) - if err != nil { - return - } - } else { - // 使用新的 reader 尝试读取 header,避免 header 不完整 - reader := bytes.NewReader(buf.Bytes()) - currLen := reader.Len() - header, err = ws.ReadHeader(reader) - skip := currLen - reader.Len() - if err != nil { - if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { - return messages, nil - } - buf.Next(skip) - return nil, err - } - buf.Next(skip) - } - - w.header = &header - if err = ws.WriteHeader(&w.cache, header); err != nil { - return nil, err - } - } - - // 将缓冲区数据读出并写入缓存 - if n := int(w.header.Length); n > 0 { - if buf.Len() < n { - return // 不完整的数据,不做处理 - } - - if _, err = io.CopyN(&w.cache, buf, int64(n)); err != nil { - return - } - } - - // 消息已完整,读取数据帧,否则数据将被分割为多个数据帧 - if w.header.Fin { - messages, err = wsutil.ReadClientMessage(&w.cache, messages) - if err != nil { - return - } - w.cache.Reset() - } - w.header = nil - } -} diff --git a/server/actor_srv/server/packet.go b/server/actor_srv/server/packet.go deleted file mode 100644 index c720e19a..00000000 --- a/server/actor_srv/server/packet.go +++ /dev/null @@ -1,45 +0,0 @@ -package server - -// NewPacket 创建一个新的数据包 -func NewPacket(data []byte) Packet { - return new(packet).init(data) -} - -// Packet 写入连接的数据包接口 -type Packet interface { - // GetBytes 获取数据包字节流 - GetBytes() []byte - // SetContext 设置数据包上下文,上下文通常是受特定 Network 实现所限制的 - // - 在内置的 network.WebSocket 实现中,上下文被用于指定连接发送数据的操作码 - SetContext(ctx any) Packet - // GetContext 获取数据包上下文 - GetContext() any -} - -type packet struct { - ctx any - data []byte -} - -func (m *packet) init(data []byte) Packet { - m.data = data - return m -} - -func (m *packet) reset() { - m.ctx = nil - m.data = m.data[:0] -} - -func (m *packet) GetBytes() []byte { - return m.data -} - -func (m *packet) SetContext(ctx any) Packet { - m.ctx = ctx - return m -} - -func (m *packet) GetContext() any { - return m.ctx -} diff --git a/server/actor_srv/server/server.go b/server/actor_srv/server/server.go deleted file mode 100644 index 7fc71c47..00000000 --- a/server/actor_srv/server/server.go +++ /dev/null @@ -1,44 +0,0 @@ -package server - -import ( - "github.com/kercylan98/minotaur/toolkit" - "github.com/kercylan98/minotaur/vivid" - "github.com/kercylan98/minotaur/vivid/vivids" -) - -func NewServer(network Network) *Server { - return &Server{ - network: network, - } -} - -type Server struct { - vivid.BasicActor - network Network -} - -func (s *Server) OnPreStart(ctx vivids.ActorContext) error { - networkActor, err := ctx.ActorOf(s.network, vivids.NewActorOptions().WithName("network")) - - networkActor.Subscribe(ctx, NetworkConnectionOpenedMessage{}) - - return err -} - -func (s *Server) OnReceived(ctx vivids.MessageContext) (err error) { - switch e := ctx.GetMessage().(type) { - case NetworkConnectionOpenedMessage: - return s.onNetworkConnectionOpened(ctx, e) - case NetworkConnectionClosedMessage: // 向连接 Actor 转发关闭消息 - return e.Conn.Tell(e, vivids.WithMessageSender(ctx)) - } - - return -} - -func (s *Server) onNetworkConnectionOpened(ctx vivids.MessageContext, e NetworkConnectionOpenedMessage) error { - // 为连接创建一个新的 Actor,当创建失败时回应错误消息 - // 当回复失败时,调用方考虑超时机制来决定本次操作失败 - conn, err := ctx.ActorOf(newConn(e.Conn, e.ConnectionWriter), vivids.NewActorOptions().WithName(e.Conn.RemoteAddr().String())) - return ctx.Reply(toolkit.NotNil(conn, err)) -} diff --git a/server/actor_srv/server/server_test.go b/server/actor_srv/server/server_test.go deleted file mode 100644 index f9bb65b3..00000000 --- a/server/actor_srv/server/server_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package server_test - -import ( - "github.com/kercylan98/minotaur/server/actor_srv/server" - "github.com/kercylan98/minotaur/server/actor_srv/server/network" - "github.com/kercylan98/minotaur/toolkit/chrono" - "github.com/kercylan98/minotaur/vivid" - "github.com/kercylan98/minotaur/vivid/vivids" - "testing" - "time" -) - -func TestNewServer(t *testing.T) { - system := vivid.NewActorSystem("TestServerSystem") - if err := system.Run(); err != nil { - t.Error(err) - } - - srv := server.NewServer(network.WebSocket(":9966")) - if ref, err := system.ActorOf(srv, vivids.NewActorOptions().WithName("server")); err != nil { - t.Error(err) - } else { - if err = ref.Tell("start"); err != nil { - t.Error(err) - } - } - - time.Sleep(chrono.Day) -} diff --git a/toolkit/buffer/ring.go b/toolkit/buffer/ring.go index 2d909290..ebb4a97e 100644 --- a/toolkit/buffer/ring.go +++ b/toolkit/buffer/ring.go @@ -144,5 +144,5 @@ func (b *Ring[T]) Reset() { b.r = 0 b.w = 0 b.size = b.initSize - b.buf = make([]T, b.initSize) + b.buf = b.buf[:b.initSize] } diff --git a/toolkit/dynamic_wait_group.go b/toolkit/dynamic_wait_group.go new file mode 100644 index 00000000..b77bc7c9 --- /dev/null +++ b/toolkit/dynamic_wait_group.go @@ -0,0 +1,53 @@ +package toolkit + +import ( + "sync" +) + +// NewDynamicWaitGroup 创建一个新的 DynamicWaitGroup +func NewDynamicWaitGroup() *DynamicWaitGroup { + return &DynamicWaitGroup{ + wait: sync.NewCond(new(sync.Mutex)), + } +} + +// DynamicWaitGroup 是一个动态的 WaitGroup,允许在等待的过程中动态地添加或减少等待的计数 +type DynamicWaitGroup struct { + c int64 + wait *sync.Cond +} + +// Add 增加等待的计数 +func (d *DynamicWaitGroup) Add(delta int64) { + d.wait.L.Lock() + defer d.wait.L.Unlock() + d.c += delta + if d.c < 0 { + panic("negative counter in DynamicWaitGroup") + } + if d.c == 0 { // 如果计数变为0,唤醒所有等待的 goroutine + d.wait.Broadcast() + } +} + +// Done 减少等待的计数 +func (d *DynamicWaitGroup) Done() { + d.Add(-1) +} + +// Wait 等待所有的计数完成 +// - 当传入 handler 时将会在计数完成后执行 handler,执行时会阻止计数器的变化 +func (d *DynamicWaitGroup) Wait(handler ...func()) { + defer d.wait.L.Unlock() + for { + d.wait.L.Lock() + if d.c == 0 { + for _, f := range handler { + f() + } + break + } + d.wait.Wait() + d.wait.L.Unlock() + } +} diff --git a/vivid/actor.go b/vivid/actor.go new file mode 100644 index 00000000..42e1d403 --- /dev/null +++ b/vivid/actor.go @@ -0,0 +1,17 @@ +package vivid + +type Actor interface { + OnReceive(ctx MessageContext) +} + +type FreeActor[T any] struct { + actor T +} + +func (i *FreeActor[T]) OnReceive(ctx MessageContext) { + +} + +func (i *FreeActor[T]) GetActor() T { + return i.actor +} diff --git a/vivid/actor_context.go b/vivid/actor_context.go new file mode 100644 index 00000000..832e58d2 --- /dev/null +++ b/vivid/actor_context.go @@ -0,0 +1,44 @@ +package vivid + +// ActorContext 针对 Actor 的上下文,该上下文暴露给 Actor 自身使用,但不提供外部自行实现 +// - 上下文代表了 Actor 完整的生命周期,该上下文将在 Actor 的生命周期中一直存在 +type ActorContext interface { + internalActorContext + ActorRef + // GetId 获取当前 ActorContext 的 ID + GetId() ActorId + + // GetSystem 获取当前 ActorContext 所属的 ActorSystem + GetSystem() *ActorSystem + + // GetActor 获取当前 ActorContext 的 Actor 对象 + GetActor() Actor + + // GetParent 获取当前上下文的父 Actor 的引用 + GetParent() ActorRef +} + +type _ActorContext struct { + *_internalActorContext + *_ActorCore +} + +func (c *_ActorContext) actorOf(actor Actor, opt any) ActorRef { + return nil +} + +func (c *_ActorContext) GetId() ActorId { + return c.id +} + +func (c *_ActorContext) GetSystem() *ActorSystem { + return c.system +} + +func (c *_ActorContext) GetActor() Actor { + return c.Actor +} + +func (c *_ActorContext) GetParent() ActorRef { + return c.parent +} diff --git a/vivid2/actor_core.go b/vivid/actor_core.go similarity index 80% rename from vivid2/actor_core.go rename to vivid/actor_core.go index e345d218..d4a11bd6 100644 --- a/vivid2/actor_core.go +++ b/vivid/actor_core.go @@ -1,6 +1,9 @@ package vivid -import "sync" +import ( + "github.com/kercylan98/minotaur/toolkit" + "sync" +) type ActorCore interface { Actor @@ -20,10 +23,12 @@ func newActorCore[T Actor](system *ActorSystem, id ActorId, actor Actor, options _ActorContext: &_ActorContext{ _internalActorContext: &_internalActorContext{}, }, - system: system, - parent: options.Parent, - childrenRW: new(sync.RWMutex), - children: make(map[ActorName]*_ActorCore), + system: system, + parent: options.Parent, + childrenRW: new(sync.RWMutex), + children: make(map[ActorName]*_ActorCore), + messageGroup: toolkit.NewDynamicWaitGroup(), + messageHook: options.MessageHook, } core._LocalActorRef.core = core @@ -46,7 +51,8 @@ type _ActorCore struct { dispatcher Dispatcher // Actor 所绑定的 Dispatcher mailboxFactory MailboxFactory // Actor 所绑定的 MailboxFactory mailbox Mailbox // Actor 所绑定的 Mailbox - group sync.WaitGroup + messageGroup *toolkit.DynamicWaitGroup // 等待消息处理完毕的消息组 + messageHook func(MessageContext) bool // 消息钩子 } func (a *_ActorCore) GetMailboxFactory() MailboxFactory { diff --git a/vivid2/actor_id.go b/vivid/actor_id.go similarity index 95% rename from vivid2/actor_id.go rename to vivid/actor_id.go index 34134390..b43619dd 100644 --- a/vivid2/actor_id.go +++ b/vivid/actor_id.go @@ -201,6 +201,23 @@ func (a ActorId) Name() ActorName { return filepath.Base(a.Path()) } +// IsLocal 检查 ActorId 是否是本地 ActorId +func (a ActorId) IsLocal(system *ActorSystem) bool { + if a.Network() != system.network { + return false + } + if a.Cluster() != system.cluster { + return false + } + if a.Host() != system.host { + return false + } + if a.Port() != system.port { + return false + } + return true +} + // String 获取 ActorId 的字符串表示 func (a ActorId) String() string { if a == "" { diff --git a/vivid2/actor_option.go b/vivid/actor_option.go similarity index 67% rename from vivid2/actor_option.go rename to vivid/actor_option.go index 2a4fca4e..94fa49d6 100644 --- a/vivid2/actor_option.go +++ b/vivid/actor_option.go @@ -4,10 +4,28 @@ type ActorOption[T Actor] func(opts *ActorOptions[T]) type ActorOptions[T Actor] struct { options []ActorOption[T] - Name string // Actor 名称 - Parent ActorContext // 父 actor 上下文 - DispatcherId DispatcherId // 调度器 ID - MailboxFactoryId MailboxFactoryId // 邮箱工厂 ID + Name string // Actor 名称 + Parent ActorContext // 父 actor 上下文 + DispatcherId DispatcherId // 调度器 ID + MailboxFactoryId MailboxFactoryId // 邮箱工厂 ID + Props func(T) // Actor 属性 + MessageHook func(ctx MessageContext) bool // 消息钩子 +} + +// WithMessageHook 设置 Actor 的消息钩子,在消息处理前执行。返回 false 将会阻止消息的处理 +func (o *ActorOptions[T]) WithMessageHook(hook func(ctx MessageContext) bool) *ActorOptions[T] { + o.options = append(o.options, func(opts *ActorOptions[T]) { + opts.MessageHook = hook + }) + return o +} + +// WithProps 设置 Actor 的属性 +func (o *ActorOptions[T]) WithProps(props func(T)) *ActorOptions[T] { + o.options = append(o.options, func(opts *ActorOptions[T]) { + opts.Props = props + }) + return o } // WithMailboxFactory 设置 Actor 使用的邮箱工厂,当邮箱工厂不存在时,将会导致 Actor 创建失败 @@ -46,6 +64,10 @@ func NewActorOptions[T Actor]() *ActorOptions[T] { return &ActorOptions[T]{} } +func NewFreeActorOptions[T any]() *ActorOptions[*FreeActor[T]] { + return &ActorOptions[*FreeActor[T]]{} +} + func (o *ActorOptions[T]) applyOption(opts ...ActorOption[T]) *ActorOptions[T] { for _, opt := range opts { opt(o) diff --git a/vivid/actor_ref.go b/vivid/actor_ref.go new file mode 100644 index 00000000..bd7b9cba --- /dev/null +++ b/vivid/actor_ref.go @@ -0,0 +1,117 @@ +package vivid + +type ActorRef interface { + // Tell 向 Actor 发送一条消息,当消息发送失败时将会进入死信队列中,以下列举一些特殊场景 + // - 当接收人处理消息失败或不存在时,将会进入到接收人所在的 ActorSystem 的死信队列中 + // - 当发送人已经销毁或不存在时,将会进入到发送人所在的 ActorSystem 的死信队列中 + // - 当自己给自己发送消息时,消息不会立即执行,而是会进入到自己的邮箱中,等待下一次消息循环时执行 + Tell(msg Message, opts ...MessageOption) + + // Ask 向 Actor 发送消息并等待回复 + // - 当消息发送失败或等待超时的时候将会返回 nil,并进入死信队列中 + // - 当接收人处理消息失败、回复失败或不存在时,将会进入到接收人所在的 ActorSystem 的死信队列中 + // - 当发送人已经销毁或不存在时,将会进入到发送人所在的 ActorSystem 的死信队列中 + // - 当给自己给自己发送消息时需特别注意,自己的邮箱会收到消息,但是由于不会立即执行,所以回复始终会等待到超时位置,而超时后收到的消息将被执行,回复将无效 + Ask(msg Message, opts ...MessageOption) Message + + // 内部发送消息实现 + send(ctx MessageContext) +} + +// _LocalActorRef 本地 Actor 引用 +type _LocalActorRef struct { + core *_ActorCore // Actor 核心 +} + +func (r *_LocalActorRef) Tell(msg Message, opts ...MessageOption) { + r.core.system.sendMessage(r.core._LocalActorRef, msg, opts...) +} + +func (r *_LocalActorRef) Ask(msg Message, opts ...MessageOption) Message { + return r.core.system.sendMessage(r.core._LocalActorRef, msg, append(opts, func(options *MessageOptions) { + options.reply = true + })...) +} + +func (r *_LocalActorRef) send(ctx MessageContext) { + r.core.messageGroup.Add(1) + if !r.core.dispatcher.Send(r.core, ctx) { + r.core.messageGroup.Done() + } +} + +func newRemoteActorRef(system *ActorSystem, actorId ActorId) *_RemoteActorRef { + return &_RemoteActorRef{ + system: system, + actorId: actorId, + } +} + +// _RemoteActorRef 远程 Actor 引用 +type _RemoteActorRef struct { + system *ActorSystem // Actor 系统 + actorId ActorId // 远程 Actor ID +} + +func (r *_RemoteActorRef) Tell(msg Message, opts ...MessageOption) { + r.system.sendMessage(r, msg, opts...) +} + +func (r *_RemoteActorRef) Ask(msg Message, opts ...MessageOption) Message { + return r.system.sendMessage(r, msg, append(opts, func(options *MessageOptions) { + options.reply = true + })...) +} + +func (r *_RemoteActorRef) send(ctx MessageContext) { + data, err := r.system.codec.Encode(ctx) + if err != nil { + return + } + if err = r.system.client.Exec(data); err != nil { + + } +} + +// newDeadLetterActorRef 创建一个新的死信 Actor 引用 +func newDeadLetterActorRef(system *ActorSystem) *_DeadLetterActorRef { + return &_DeadLetterActorRef{ + system: system, + } +} + +// _DeadLetterActorRef 死信 Actor 引用 +type _DeadLetterActorRef struct { + system *ActorSystem // Actor 系统 +} + +func (r *_DeadLetterActorRef) Tell(msg Message, opts ...MessageOption) { + r.system.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ + Error: ErrActorDeadOrNotExist, + Message: msg, + })) +} + +func (r *_DeadLetterActorRef) Ask(msg Message, opts ...MessageOption) Message { + r.system.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ + Error: ErrActorDeadOrNotExist, + Message: msg, + })) + return nil +} + +func (r *_DeadLetterActorRef) send(ctx MessageContext) { + panic("dead letter actor ref can't send message") +} + +// newNoSenderActorRef 创建一个新的无发送者 Actor 引用 +func newNoSenderActorRef(system *ActorSystem) ActorRef { + return &_NoSenderActorRef{ + _DeadLetterActorRef: newDeadLetterActorRef(system), + } +} + +// _NoSenderActorRef 无发送者 Actor 引用 +type _NoSenderActorRef struct { + *_DeadLetterActorRef +} diff --git a/vivid/actor_system.go b/vivid/actor_system.go new file mode 100644 index 00000000..5c2a8efb --- /dev/null +++ b/vivid/actor_system.go @@ -0,0 +1,366 @@ +package vivid + +import ( + "context" + "fmt" + "github.com/google/uuid" + "github.com/kercylan98/minotaur/toolkit" + "github.com/kercylan98/minotaur/toolkit/charproc" + "path" + "sync" + "sync/atomic" + "time" +) + +func NewActorSystem(name string) ActorSystem { + s := ActorSystem{ + dispatchers: make(map[DispatcherId]Dispatcher), + dispatcherRW: new(sync.RWMutex), + mailboxFactors: make(map[MailboxFactoryId]MailboxFactory), + mailboxFactorRW: new(sync.RWMutex), + actors: make(map[ActorId]*_ActorCore), + actorRW: new(sync.RWMutex), + deadLetters: new(_DeadLetterStream), + codec: new(_GobCodec), + askWaits: make(map[uint64]chan<- Message), + askWaitsLock: new(sync.RWMutex), + messageSeq: new(atomic.Uint64), + waitGroup: toolkit.NewDynamicWaitGroup(), + name: name, + } + s.ctx, s.cancel = context.WithCancel(context.Background()) + s.BindDispatcher(new(_Dispatcher)) // default dispatcher + s.BindMailboxFactory(NewFIFOFactory(func(message MessageContext) { + // received message + core := message.GetReceiver().(*_LocalActorRef).core + defer func() { + core.messageGroup.Done() + if r := recover(); r != nil { + s.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ + Error: fmt.Errorf("%w: %v", ErrActorPanic, r), + To: core.GetId(), + Message: message, + })) + } + }() + if core.messageHook != nil && !core.messageHook(message) { + return + } + core.OnReceive(message) + })) + var err error + s.userGuard, err = generateActor(&s, new(UserGuardActor), parseActorOptions(NewActorOptions[*UserGuardActor]().WithName("user"))) + if err != nil { + panic(err) + } + return s +} + +type ActorSystem struct { + ctx context.Context + cancel context.CancelFunc + dispatchers map[DispatcherId]Dispatcher + dispatcherGuid DispatcherId + dispatcherRW *sync.RWMutex + mailboxFactors map[MailboxFactoryId]MailboxFactory // mailbox factory + mailboxFactorGuid MailboxFactoryId + mailboxFactorRW *sync.RWMutex + actors map[ActorId]*_ActorCore // actor id -> actor core + actorRW *sync.RWMutex + deadLetters *_DeadLetterStream // 死信队列 + userGuard *_ActorCore // 用户使用的顶级 Actor + codec Codec + server Server + client Client + messageSeq *atomic.Uint64 + askWaits map[uint64]chan<- Message + askWaitsLock *sync.RWMutex + waitGroup *toolkit.DynamicWaitGroup + + name string // ActorSystem 名称 + network string // 网络类型 + host string // 主机地址 + port uint16 // 端口 + cluster string // 集群名称 +} + +func (s *ActorSystem) Shutdown() { + defer s.cancel() + s.unbindActor(s.userGuard) + s.waitGroup.Wait() +} + +func (s *ActorSystem) getSystem() *ActorSystem { + return s +} + +func (s *ActorSystem) GetDeadLetters() DeadLetterStream { + return s.deadLetters +} + +type actorOf interface { + getSystem() *ActorSystem + + getContext() *_ActorCore +} + +func (s *ActorSystem) BindMailboxFactory(f MailboxFactory) MailboxFactoryId { + s.mailboxFactorRW.Lock() + defer s.mailboxFactorRW.Unlock() + + s.mailboxFactorGuid++ + s.mailboxFactors[s.mailboxFactorGuid] = f + + return s.mailboxFactorGuid +} + +func (s *ActorSystem) UnbindMailboxFactory(id MailboxFactoryId) { + if id == DefaultMailboxFactoryId { + return + } + s.mailboxFactorRW.Lock() + defer s.mailboxFactorRW.Unlock() + + delete(s.mailboxFactors, id) +} + +func (s *ActorSystem) getMailboxFactory(id MailboxFactoryId) MailboxFactory { + s.mailboxFactorRW.RLock() + defer s.mailboxFactorRW.RUnlock() + + return s.mailboxFactors[id] +} + +func (s *ActorSystem) getDispatcher(id DispatcherId) Dispatcher { + s.dispatcherRW.RLock() + defer s.dispatcherRW.RUnlock() + + return s.dispatchers[id] +} + +func (s *ActorSystem) BindDispatcher(d Dispatcher) DispatcherId { + s.dispatcherRW.Lock() + defer s.dispatcherRW.Unlock() + + s.dispatcherGuid++ + s.dispatchers[s.dispatcherGuid] = d + + return s.dispatcherGuid +} + +func (s *ActorSystem) UnbindDispatcher(id DispatcherId) { + if id == DefaultDispatcherId { + return + } + s.dispatcherRW.Lock() + defer s.dispatcherRW.Unlock() + + delete(s.dispatchers, id) +} + +func (s *ActorSystem) unbindActor(actor ActorContext) { + // 等待消息处理完毕后拒绝新消息 + core := actor.(*_ActorCore) + core.messageGroup.Wait(func() { + core.dispatcher.Detach(core) + s.actorRW.Lock() + delete(s.actors, core.GetId()) + s.actorRW.Unlock() + }) + + actor.getLockable().RLock() + var children = actor.getChildren() + actor.getLockable().RUnlock() + + for _, child := range children { + s.unbindActor(child) + } + + s.waitGroup.Done() + //log.Debug("actor unbind", log.String("actor", core.GetId().String())) +} + +func (s *ActorSystem) getActor(id ActorId) *_ActorCore { + s.actorRW.RLock() + defer s.actorRW.RUnlock() + + return s.actors[id] +} + +func (s *ActorSystem) getContext() *_ActorCore { + return s.userGuard +} + +func (s *ActorSystem) sendMessage(receiver ActorRef, message Message, options ...MessageOption) Message { + var opts = new(MessageOptions).apply(options) + + ctx := newMessageContext(s, message) + switch ref := receiver.(type) { + case *_LocalActorRef: + ctx = ctx.withLocal(ref.core, opts.Sender) + case *_RemoteActorRef: + var senderId ActorId + if sender, ok := opts.Sender.(*_LocalActorRef); ok { + senderId = sender.core.GetId() + } + ctx = ctx.withRemote(ref.actorId, senderId) + } + if opts.ContextHook != nil { + opts.ContextHook(ctx) + } + receiver.send(ctx) + + // 等待回复 + if opts.reply { + // 如果等待回复,需要增加发送人等待计数 + switch sender := opts.Sender.(type) { + case *_ActorCore: + sender.messageGroup.Add(1) + defer sender.messageGroup.Done() + case *_LocalActorRef: + sender.core.messageGroup.Add(1) + defer sender.core.messageGroup.Done() + default: + // 不应存在远程发送者,其他类型增加消息总计数 + s.waitGroup.Add(1) + defer s.waitGroup.Done() + } + + if opts.ReplyTimeout == 0 { + opts.ReplyTimeout = time.Second + } + + waiter := make(chan Message) + timeoutCtx, cancel := context.WithTimeout(s.ctx, opts.ReplyTimeout) + defer func(seq uint64, waiter chan Message, cancel context.CancelFunc) { + cancel() + close(waiter) + s.askWaitsLock.Lock() + delete(s.askWaits, seq) + s.askWaitsLock.Unlock() + }(ctx.GetSeq(), waiter, cancel) + + s.askWaitsLock.Lock() + s.askWaits[ctx.GetSeq()] = waiter + s.askWaitsLock.Unlock() + + select { + case <-timeoutCtx.Done(): + s.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ + Error: ErrMessageReplyTimeout, + })) + case reply := <-waiter: + return reply + } + } + + return nil +} + +func (s *ActorSystem) onProcessServerMessage(bytes []byte) { + var ctx = new(_MessageContext) + if err := s.codec.Decode(bytes, ctx); err != nil { + s.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ + Error: err, + })) + return + } + ctx.system = s + + // 远程回复 + if ctx.RemoteReplySeq != 0 { + s.askWaitsLock.RLock() + wait, existWait := s.askWaits[ctx.RemoteReplySeq] + s.askWaitsLock.RUnlock() + if existWait { + wait <- ctx.Message + return + } + } + + // 查找接收者 + receiver := s.getActor(ctx.ReceiverId) + if receiver == nil { + s.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ + Error: fmt.Errorf("%w: %s", ErrActorDeadOrNotExist, ctx.ReceiverId), + From: ctx.SenderId, + To: ctx.ReceiverId, + })) + return + } + ctx.actorContext = receiver + + // 远程消息增加计数,该计数将在消息处理完毕后减少 + receiver.messageGroup.Add(1) + if !receiver.dispatcher.Send(receiver, ctx) { + receiver.messageGroup.Done() + } +} + +func generateActor[T Actor](system *ActorSystem, actor T, options *ActorOptions[T]) (*_ActorCore, error) { + if options.Name == charproc.None { + options.Name = uuid.NewString() + } + + var actorPath = options.Name + if options.Parent != nil { + actorPath = path.Join(options.Parent.GetId().Path(), options.Name) + } else { + actorPath = path.Clean(options.Name) + } + + // 绝大多数情况均会成功,提前创建资源,减少锁粒度 + var actorId = NewActorId(system.network, system.cluster, system.host, system.port, system.name, actorPath) + var core = newActorCore(system, actorId, actor, options) + + // 检查是否重名 + var parentLock *sync.RWMutex + if options.Parent != nil { + parentLock = options.Parent.getLockable() + parentLock.Lock() + if options.Parent.hasChild(options.Name) { + parentLock.Unlock() + return nil, fmt.Errorf("%w: %s", ErrActorAlreadyExists, options.Name) + } + } + + // 绑定分发器 + core.dispatcher = system.getDispatcher(options.DispatcherId) + if core.dispatcher == nil { + if parentLock != nil { + parentLock.Unlock() + } + return nil, fmt.Errorf("%w: %d", ErrDispatcherNotFound, options.DispatcherId) + } + + // 绑定邮箱 + core.mailboxFactory = system.getMailboxFactory(options.MailboxFactoryId) + if core.mailboxFactory == nil { + if parentLock != nil { + parentLock.Unlock() + } + return nil, fmt.Errorf("%w: %d", ErrMailboxFactoryNotFound, options.MailboxFactoryId) + } + + // 启动 Actor + system.waitGroup.Add(1) + core.dispatcher.Attach(core) + + // 绑定父 Actor 并注册到系统 + system.actorRW.Lock() + if options.Parent != nil { + options.Parent.bindChild(options.Name, core) + } + system.actors[actorId] = core + system.actorRW.Unlock() + if parentLock != nil { + parentLock.Unlock() + } + + if options.Props != nil { + options.Props(actor) + } + core.Tell(OnPreStart{}) + + return core, nil +} diff --git a/vivid/basic_actor.go b/vivid/basic_actor.go deleted file mode 100644 index 175d753a..00000000 --- a/vivid/basic_actor.go +++ /dev/null @@ -1,6 +0,0 @@ -package vivid - -import "github.com/kercylan98/minotaur/vivid/vivids" - -// BasicActor 是 Actor 的基础实现,该实现仅提供了一个空的 Actor -type BasicActor = vivids.BasicActor diff --git a/vivid/behavior.go b/vivid/behavior.go deleted file mode 100644 index 40e7bf57..00000000 --- a/vivid/behavior.go +++ /dev/null @@ -1,8 +0,0 @@ -package vivid - -import "github.com/kercylan98/minotaur/vivid/vivids" - -// RegisterBehavior 注册行为 -func RegisterBehavior[T vivids.Message](ctx vivids.ActorContext, behavior vivids.ActorBehavior[T]) { - ctx.RegisterBehavior((*T)(nil), behavior) -} diff --git a/vivid2/client.go b/vivid/client.go similarity index 100% rename from vivid2/client.go rename to vivid/client.go diff --git a/vivid2/codec.go b/vivid/codec.go similarity index 100% rename from vivid2/codec.go rename to vivid/codec.go diff --git a/vivid2/dead_letter.go b/vivid/dead_letter.go similarity index 100% rename from vivid2/dead_letter.go rename to vivid/dead_letter.go diff --git a/vivid2/dead_letter_event.go b/vivid/dead_letter_event.go similarity index 100% rename from vivid2/dead_letter_event.go rename to vivid/dead_letter_event.go diff --git a/vivid2/dispatcher.go b/vivid/dispatcher.go similarity index 73% rename from vivid2/dispatcher.go rename to vivid/dispatcher.go index 1e2ff6cf..b317b05a 100644 --- a/vivid2/dispatcher.go +++ b/vivid/dispatcher.go @@ -1,7 +1,5 @@ package vivid -import "sync" - const ( DefaultDispatcherId DispatcherId = 1 // 默认调度器 ID ) @@ -17,23 +15,18 @@ type Dispatcher interface { // Detach 将一个 Actor 从调度器中移除 Detach(actor ActorCore) - // Send 向一个 Actor 发送消息 - Send(actor ActorCore, message MessageContext) + // Send 向一个 Actor 发送消息。如果消息发送成功,返回 true;否则返回 false + Send(actor ActorCore, message MessageContext) bool } type _Dispatcher struct { - group sync.WaitGroup } func (d *_Dispatcher) Attach(actor ActorCore) { factory := actor.GetMailboxFactory() mailbox := factory.Get() actor.BindMailbox(mailbox) - d.group.Add(1) - go func(group *sync.WaitGroup, mailbox Mailbox) { - defer group.Done() - mailbox.Start() - }(&d.group, mailbox) + mailbox.Start() } func (d *_Dispatcher) Detach(actor ActorCore) { @@ -43,7 +36,7 @@ func (d *_Dispatcher) Detach(actor ActorCore) { factory.Put(mailbox) } -func (d *_Dispatcher) Send(actor ActorCore, message MessageContext) { +func (d *_Dispatcher) Send(actor ActorCore, message MessageContext) bool { mailbox := actor.GetMailbox() - mailbox.Enqueue(message) + return mailbox.Enqueue(message) } diff --git a/vivid2/errors.go b/vivid/errors.go similarity index 89% rename from vivid2/errors.go rename to vivid/errors.go index a0e64ee3..94200d9e 100644 --- a/vivid2/errors.go +++ b/vivid/errors.go @@ -10,4 +10,5 @@ var ( ErrActorDeadOrNotExist = errors.New("actor dead or not exist") ErrMessageReplyTimeout = errors.New("message reply timeout") ErrAskWaitNotExist = errors.New("ask wait not exist") + ErrActorPanic = errors.New("actor panic") ) diff --git a/vivid2/internal_actor_context.go b/vivid/internal_actor_context.go similarity index 87% rename from vivid2/internal_actor_context.go rename to vivid/internal_actor_context.go index 82f2afa0..9a3d4cf0 100644 --- a/vivid2/internal_actor_context.go +++ b/vivid/internal_actor_context.go @@ -3,6 +3,8 @@ package vivid import "sync" type internalActorContext interface { + actorOf + // getLock 获取当前 ActorContext 的锁 // - 所有函数均不操作锁,应由外部调用者自行操作 getLockable() *sync.RWMutex @@ -25,6 +27,14 @@ type _internalActorContext struct { *_ActorCore // ActorContext } +func (c *_internalActorContext) getSystem() *ActorSystem { + return c.system +} + +func (c *_internalActorContext) getContext() *_ActorCore { + return c._ActorCore +} + func (c *_internalActorContext) getLockable() *sync.RWMutex { return c.childrenRW } diff --git a/vivid2/mailbox.go b/vivid/mailbox.go similarity index 84% rename from vivid2/mailbox.go rename to vivid/mailbox.go index 20108b7d..67bcc0fb 100644 --- a/vivid2/mailbox.go +++ b/vivid/mailbox.go @@ -11,11 +11,12 @@ type MailboxFactory interface { type Mailbox interface { // Start 开始队列的消费逻辑,此刻应保证队列已经允许写入 + // - 该函数应为一个异步函数 Start() // Stop 停止队列,队列的停止方式根据不同的实现可能会有所不同 Stop() // Enqueue 将一个消息放入队列 - Enqueue(message MessageContext) + Enqueue(message MessageContext) bool } diff --git a/vivid2/mailbox_fifo.go b/vivid/mailbox_fifo.go similarity index 79% rename from vivid2/mailbox_fifo.go rename to vivid/mailbox_fifo.go index 42c47438..c8a776a5 100644 --- a/vivid2/mailbox_fifo.go +++ b/vivid/mailbox_fifo.go @@ -55,33 +55,32 @@ func (f *FIFO) Start() { f.cond.L.Unlock() f.closed = make(chan struct{}) - defer func(f *FIFO) { - close(f.closed) - f.buffer.Reset() - }(f) - - for { - f.cond.L.Lock() - elements := f.buffer.ReadAll() - if len(elements) == 0 || (f.opts.StopMode == FIFOStopModeInstantly && f.status == fifoStateStopping) { - // 此刻队列没有消息且处于停止中状态,将会关闭 Actor - if f.status == fifoStateStopping { - f.status = fifoStateStopped - f.cond.L.Unlock() - return + go func(f *FIFO) { + defer func(f *FIFO) { + close(f.closed) + f.buffer.Reset() + }(f) + + for { + f.cond.L.Lock() + elements := f.buffer.ReadAll() + if len(elements) == 0 { + if f.status == fifoStateStopping { + f.status = fifoStateStopped + f.cond.L.Unlock() + return + } + f.cond.Wait() + elements = f.buffer.ReadAll() // 重新读取消息 } - f.cond.Wait() + f.cond.L.Unlock() - // 重新读取消息 - elements = f.buffer.ReadAll() - } - f.cond.L.Unlock() - - for i := 0; i < len(elements); i++ { - elem := elements[i] - f.handler(elem) + for i := 0; i < len(elements); i++ { + elem := elements[i] + f.handler(elem) + } } - } + }(f) } func (f *FIFO) Stop() { @@ -98,13 +97,13 @@ func (f *FIFO) Stop() { <-f.closed } -func (f *FIFO) Enqueue(message MessageContext) { +func (f *FIFO) Enqueue(message MessageContext) bool { f.cond.L.Lock() if f.status != fifoStateNone { if f.status != fifoStateRunning { if f.opts.StopMode != FIFOStopModeDrain { f.cond.L.Unlock() - return + return false } } } @@ -112,6 +111,7 @@ func (f *FIFO) Enqueue(message MessageContext) { f.buffer.Write(message) f.cond.L.Unlock() f.cond.Broadcast() + return true } func (f *FIFO) reset() { diff --git a/vivid2/mailbox_fifo_factory.go b/vivid/mailbox_fifo_factory.go similarity index 100% rename from vivid2/mailbox_fifo_factory.go rename to vivid/mailbox_fifo_factory.go diff --git a/vivid2/mailbox_fifo_options.go b/vivid/mailbox_fifo_options.go similarity index 100% rename from vivid2/mailbox_fifo_options.go rename to vivid/mailbox_fifo_options.go diff --git a/vivid2/message.go b/vivid/message.go similarity index 100% rename from vivid2/message.go rename to vivid/message.go diff --git a/vivid/message_context.go b/vivid/message_context.go new file mode 100644 index 00000000..9774f27b --- /dev/null +++ b/vivid/message_context.go @@ -0,0 +1,170 @@ +package vivid + +type MessageContext interface { + actorOf + + // GetContext 获取 Actor 上下文 + GetContext() ActorContext + + // GetSeq 获取消息的序号 + GetSeq() uint64 + + // GetSender 获取消息的发送者 + GetSender() ActorRef + + // GetReceiver 获取消息的接收者 + GetReceiver() ActorRef + + // GetMessage 获取消息内容 + GetMessage() Message + + // Reply 回复消息 + Reply(Message) + + // GetActor 获取 Actor 对象,该函数是 ActorContext.GetActor 的快捷方式 + GetActor() Actor +} + +func newMessageContext(system *ActorSystem, message Message) *_MessageContext { + return &_MessageContext{ + system: system, + Seq: system.messageSeq.Add(1), + Network: system.network, + Host: system.host, + Port: system.port, + Message: message, + } +} + +// _MessageContext 消息上下文,消息上下文实现了兼容本地及远程消息的上下文 +// - 该结构体中,除开公共信息外,内部字段被用于本地消息,公开字段被用于远程消息,需要保证公共及公开字段的可序列化 +type _MessageContext struct { + system *ActorSystem // 创建上下文的 Actor 系统 + Seq uint64 // 消息序号 + Network string // 产生消息的网络 + Host string // 产生消息的主机 + Port uint16 // 产生消息的端口 + Message Message // 消息内容 + + // 本地消息是直接根据实现了 ActorRef 的 _ActorCore 来投递的,所以可以直接将消息投递到 ActorCore 绑定的 Dispatcher 中 + actorContext ActorContext // 本地接收者的上下文 + sender ActorRef // 本地发送者,通过 WithSender 设置后,由于是本地消息,所以可以直接使用 ActorRef + + // 远程消息是通过网络传输的,所以需要将接收者的 ActorId 传递到远程消息的上下文中,以便在远程消息到达后,能够根据 ActorId 获取到 ActorRef + ReceiverId ActorId // 远程接收者的 ActorId + SenderId ActorId // 远程发送者的 ActorId,通过 WithSender 设置后,由于是远程消息,所以需要将发送者的 ActorId 传递到远程消息的上下文中 + RemoteReplySeq uint64 // 用于远程回复的消息序号 +} + +func (c *_MessageContext) getSystem() *ActorSystem { + return c.system +} + +func (c *_MessageContext) getContext() *_ActorCore { + return c.GetContext().(*_ActorCore) +} + +func (c *_MessageContext) withLocal(receiver *_ActorCore, sender ActorRef) *_MessageContext { + c.actorContext = receiver + c.sender = sender + return c +} + +func (c *_MessageContext) withRemote(receiverId ActorId, senderId ActorId) *_MessageContext { + c.ReceiverId = receiverId + c.SenderId = senderId + return c +} + +func (c *_MessageContext) GetContext() ActorContext { + if c.actorContext != nil { + return c.actorContext + } + + if c.ReceiverId.IsLocal(c.system) { + if receiver := c.system.getActor(c.ReceiverId); receiver != nil { + c.actorContext = receiver + return receiver + } + return nil + } + + return nil +} + +func (c *_MessageContext) GetSeq() uint64 { + return c.Seq +} + +func (c *_MessageContext) GetSender() ActorRef { + if c.sender != nil { + return c.sender + } + + if c.SenderId.Invalid() { + return newNoSenderActorRef(c.system) + } + + if c.SenderId.IsLocal(c.system) { + if c.sender = c.system.getActor(c.SenderId); c.sender == nil { + c.sender = newDeadLetterActorRef(c.system) + } + return c.sender + } + + c.sender = newRemoteActorRef(c.system, c.SenderId) + return c.sender +} + +func (c *_MessageContext) GetReceiver() ActorRef { + if c.actorContext != nil { + return c.actorContext.(*_ActorCore)._LocalActorRef + } + + if c.ReceiverId.IsLocal(c.system) { + if receiver := c.system.getActor(c.ReceiverId); receiver != nil { + c.actorContext = receiver + return receiver._LocalActorRef + } + return newDeadLetterActorRef(c.system) + } + + return newRemoteActorRef(c.system, c.ReceiverId) +} + +func (c *_MessageContext) GetMessage() Message { + return c.Message +} + +func (c *_MessageContext) Reply(message Message) { + // 本地消息回复直接投递到对应 waiter 中,远程消息则通过网络发送 + sender := c.GetSender() + localSender, isLocal := sender.(*_LocalActorRef) + noSender, isNoSender := sender.(*_NoSenderActorRef) + if isLocal || (isNoSender && c.Network == c.system.network && c.Host == c.system.host && c.Port == c.system.port) { + var system *ActorSystem + if isLocal { + system = localSender.core.system + } else { + system = noSender.system + } + system.askWaitsLock.RLock() + waiter, exist := system.askWaits[c.Seq] + system.askWaitsLock.RUnlock() + if exist { + waiter <- message + } + // TODO 死信 + return + } + + // 远程回复 + sender.send(&_MessageContext{ + Message: message, + RemoteReplySeq: c.Seq, + }) +} + +func (c *_MessageContext) GetActor() Actor { + return c.GetContext().GetActor() +} diff --git a/vivid2/message_options.go b/vivid/message_options.go similarity index 51% rename from vivid2/message_options.go rename to vivid/message_options.go index 4de4999d..2b001e84 100644 --- a/vivid2/message_options.go +++ b/vivid/message_options.go @@ -5,11 +5,11 @@ import "time" type MessageOption func(*MessageOptions) type MessageOptions struct { - reply bool - replySeq uint64 + reply bool Sender ActorRef ReplyTimeout time.Duration + ContextHook func(MessageContext) } func (o *MessageOptions) apply(options []MessageOption) *MessageOptions { @@ -19,14 +19,23 @@ func (o *MessageOptions) apply(options []MessageOption) *MessageOptions { return o } +// WithSender 设置消息发送者,发送者可以有利于对消息流向的追踪 func WithSender(sender ActorRef) MessageOption { return func(options *MessageOptions) { options.Sender = sender } } +// WithReplyTimeout 设置消息回复超时时间,当消息发送后等待回复的时间超过此时间时将会返回 nil func WithReplyTimeout(timeout time.Duration) MessageOption { return func(options *MessageOptions) { options.ReplyTimeout = timeout } } + +// WithContextHook 设置消息上下文钩子,用于在消息发送前获取到消息上下文进行特殊处理 +func WithContextHook(hook func(MessageContext)) MessageOption { + return func(options *MessageOptions) { + options.ContextHook = hook + } +} diff --git a/vivid2/server.go b/vivid/server.go similarity index 100% rename from vivid2/server.go rename to vivid/server.go diff --git a/vivid/test/main.go b/vivid/test/main.go new file mode 100644 index 00000000..9d9d214e --- /dev/null +++ b/vivid/test/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + vivid "github.com/kercylan98/minotaur/vivid" + "reflect" +) + +type TestFreeActor struct { + name string +} + +func main() { + system := vivid.NewActorSystem("test-system") + + actorRef := vivid.FreeActorOf[*TestFreeActor](&system, vivid.NewFreeActorOptions[*TestFreeActor](). + WithName("free"). + WithProps(func(f *vivid.FreeActor[*TestFreeActor]) { + f.GetActor().name = "free" + }). + WithMessageHook(func(ctx vivid.MessageContext) bool { + fmt.Println(vivid.GetActor[*TestFreeActor](ctx).name, reflect.TypeOf(ctx.GetMessage()).String(), ctx.GetMessage()) + return false + }), + ) + actorRef.Tell("Hello, World!") + + system.Shutdown() +} diff --git a/vivid/unsafevivid/actor_context.go b/vivid/unsafevivid/actor_context.go deleted file mode 100644 index 42d5772a..00000000 --- a/vivid/unsafevivid/actor_context.go +++ /dev/null @@ -1,156 +0,0 @@ -package unsafevivid - -import ( - "context" - "github.com/kercylan98/minotaur/toolkit/log" - "github.com/kercylan98/minotaur/vivid/vivids" - "reflect" -) - -// ActorContext 是 Actor 的上下文 -type ActorContext struct { - context.Context // 上下文 - System *ActorSystem // Actor 所属的 Actor 系统 - Core *ActorCore // Actor 的核心 - Parent *ActorCore // 父 Actor - Children map[vivids.ActorName]*ActorCore // 子 Actor - Id vivids.ActorId // Actor 的 ID - IsEnd bool // 是否是末级 Actor - Started bool // 是否已经启动 - Behaviors map[reflect.Type]reflect.Value // 消息处理器 -} - -func (c *ActorContext) RegisterBehavior(message vivids.Message, behavior any) { - if c.Started { - panic("register behavior after actor started") - } - msgType := reflect.TypeOf(message) - behaviorValue := reflect.ValueOf(behavior) - if behaviorValue.Kind() != reflect.Func { - panic("behavior must be a function") - } - if _, exist := c.Behaviors[msgType]; exist { - panic("behavior already registered") - } - if behaviorValue.Type().NumIn() != 2 { - panic("behavior must have two parameters") - } - if behaviorValue.Type().In(0) != reflect.TypeOf(c) { - panic("behavior first parameter must be ActorContext") - } - if behaviorValue.Type().In(1) != msgType { - panic("behavior second parameter must be message") - } - c.Behaviors[msgType] = behaviorValue -} - -// GetSystem 获取 Actor 所属的 Actor 系统 -func (c *ActorContext) GetSystem() vivids.ActorSystem { - return c.System -} - -// restart 重启上下文 -func (c *ActorContext) restart(reentry bool) (recovery func(), err error) { - // 尝试获取快照,获取失败则算作重启失败 - actorSnapshot, err := c.Core.Actor.OnSaveSnapshot(c.Core) - if err != nil { - return nil, err - } - - // 重启所有子 Actor - if !reentry { - c.System.actorsRW.RLock() - defer c.System.actorsRW.RUnlock() - } - var recoveries []func() - for _, child := range c.Children { - if recovery, err = child.restart(true); err != nil { - for _, f := range recoveries { - f() - } - return nil, err - } - recoveries = append(recoveries, recovery) - } - - var recoveryFunc = func() { - // TODO: 恢复快照失败如何处理? - if err := c.Core.OnRecoverSnapshot(c, actorSnapshot); err != nil { - log.Error("actor recover snapshot failed", log.Err(err)) - } - } - if err = c.Core.Actor.OnDestroy(c.Core); err != nil { - recoveryFunc() - return nil, err - } - if err = c.Core.onPreStart(); err != nil { - recoveryFunc() - return nil, err - } - return recoveryFunc, nil -} - -func (c *ActorContext) bindChildren(core *ActorCore) { - c.Children[core.GetOptions().Name] = core -} - -func (c *ActorContext) ActorOf(actor vivids.Actor, opts ...*vivids.ActorOptions) (vivids.ActorRef, error) { - var opt *vivids.ActorOptions - if len(opts) > 0 { - opt = opts[0] - if opt == nil { - opt = vivids.NewActorOptions() - } - } else { - opt = vivids.NewActorOptions() - } - opt = opt.WithParent(c) - - return c.System.generateActor(c, actor, opt, !c.Started) -} - -func (c *ActorContext) GetActor() vivids.Query { - return NewQuery(c.System, c.Core) -} - -func (c *ActorContext) NotifyTerminated(v ...vivids.Message) { - terminatedContext := NewActorTerminatedContext(c.Core, v...) - c.Parent.OnChildTerminated(c.Parent, terminatedContext) - if terminatedContext.cancelTerminate { - return - } - c.System.releaseActor(c.Core, !c.Core.Started) -} - -func (c *ActorContext) GetParentActor() vivids.ActorRef { - return c.Parent.ActorRef -} - -func (c *ActorContext) GetActorId() vivids.ActorId { - return c.Id -} - -func (c *ActorContext) Future(handler func() vivids.Message) vivids.Future { - return NewFuture(c, handler) -} - -func (c *ActorContext) PublishEvent(event vivids.Message) { - c.Core.EventRW.RLock() - defer c.Core.EventRW.RUnlock() - - eventType := reflect.TypeOf(event) - if _, exist := c.Core.Events[eventType]; !exist { - return - } - - var err error - for actorId := range c.Core.Events[eventType] { - if err = c.System.Tell(actorId, event); err != nil { - log.Error("publish event failed", log.Err(err)) - } - } -} - -func (c *ActorContext) GetProps() any { - return c.Core.GetOptions().Props -} diff --git a/vivid/unsafevivid/actor_core.go b/vivid/unsafevivid/actor_core.go deleted file mode 100644 index 45e5dbe0..00000000 --- a/vivid/unsafevivid/actor_core.go +++ /dev/null @@ -1,97 +0,0 @@ -package unsafevivid - -import ( - "context" - "fmt" - vivid "github.com/kercylan98/minotaur/vivid/vivids" - "reflect" - "sync" -) - -func NewActorCore(ctx context.Context, system *ActorSystem, actorId vivid.ActorId, actor vivid.Actor, opts *vivid.ActorOptions) *ActorCore { - core := &ActorCore{ - Actor: actor, - Options: opts, - ActorRef: NewActorRef(system, actorId), - Events: map[reflect.Type]map[vivid.ActorId]struct{}{}, - } - core.ActorContext = &ActorContext{ - Context: ctx, - System: system, - Id: actorId, - Core: core, - Children: map[vivid.ActorName]*ActorCore{}, - Behaviors: map[reflect.Type]reflect.Value{}, - } - return core -} - -type ActorCore struct { - vivid.Actor // 外部 Actor 实现 - *ActorRef // Actor 的引用 - *ActorContext // Actor 的上下文 - Options *vivid.ActorOptions // Actor 的配置项 - RestartNum int // 重启次数 - OutsideContext sync.Map // 外部上下文 - Pause chan struct{} // 暂停处理消息 - Events map[reflect.Type]map[vivid.ActorId]struct{} // 事件订阅 - EventRW sync.RWMutex // 事件订阅读写锁 -} - -// onPreStart 在 Actor 启动之前执行的逻辑 -func (a *ActorCore) onPreStart() (err error) { - defer func() { - if r := recover(); r != nil { - a.NotifyTerminated(r) - err = fmt.Errorf("%w: %v", vivid.ErrActorPreStart, r) - } - }() - if err = a.Actor.OnPreStart(a); err != nil { - return err - } - - a.Started = true - return -} - -func (a *ActorCore) GetOptions() *vivid.ActorOptions { - return a.Options -} - -func (a *ActorCore) SetContext(key, value any) { - a.OutsideContext.Store(key, value) -} - -func (a *ActorCore) GetContext(key any) any { - v, _ := a.OutsideContext.Load(key) - return v -} - -func (a *ActorCore) IsPause() <-chan struct{} { - if a.Pause == nil { - return nil - } - return a.Pause -} - -func (a *ActorCore) BindMessageActorContext(ctx vivid.MessageContext) { - ctx.(*MessageContext).ActorContext = a -} - -func (a *ActorCore) OnReceived(ctx vivid.MessageContext) error { - switch v := ctx.GetMessage().(type) { - case SubscribeEventMessage: - eventType := reflect.TypeOf(v.Event) - a.EventRW.Lock() - defer a.EventRW.Unlock() - subscribers, exist := a.Events[eventType] - if !exist { - subscribers = map[vivid.ActorId]struct{}{} - a.Events[eventType] = subscribers - } - subscribers[v.Subscriber] = struct{}{} - return nil - default: - return a.Actor.OnReceived(ctx) - } -} diff --git a/vivid/unsafevivid/actor_ref.go b/vivid/unsafevivid/actor_ref.go deleted file mode 100644 index d8360d4a..00000000 --- a/vivid/unsafevivid/actor_ref.go +++ /dev/null @@ -1,42 +0,0 @@ -package unsafevivid - -import ( - "github.com/kercylan98/minotaur/toolkit/log" - vivid "github.com/kercylan98/minotaur/vivid/vivids" -) - -func NewActorRef(system *ActorSystem, actorId vivid.ActorId) *ActorRef { - return &ActorRef{ - system: system, - actorId: actorId, - } -} - -type ActorRef struct { - system *ActorSystem - actorId vivid.ActorId -} - -func (a *ActorRef) GetId() vivid.ActorId { - return a.actorId -} - -func (a *ActorRef) Tell(msg vivid.Message, opts ...vivid.MessageOption) error { - return a.system.Tell(a.GetId(), msg, opts...) -} - -func (a *ActorRef) Ask(msg vivid.Message, opts ...vivid.MessageOption) (vivid.Message, error) { - return a.system.Ask(a.GetId(), msg, opts...) -} - -func (a *ActorRef) Subscribe(ctx *ActorContext, event vivid.Event) { - // 订阅应该只是一个接口,实际上是通过 Ask 或者 Tell 进行订阅,这样可以实现分布式事件 - err := a.Tell(&SubscribeEventMessage{ - Subscriber: ctx.GetActorId(), - Event: event, - }) - - if err != nil { - log.Error("subscribe event failed", err) - } -} diff --git a/vivid/unsafevivid/actor_system.go b/vivid/unsafevivid/actor_system.go deleted file mode 100644 index 8c43efb5..00000000 --- a/vivid/unsafevivid/actor_system.go +++ /dev/null @@ -1,339 +0,0 @@ -package unsafevivid - -import ( - "context" - "fmt" - "github.com/kercylan98/minotaur/toolkit/convert" - "github.com/kercylan98/minotaur/toolkit/log" - vivid "github.com/kercylan98/minotaur/vivid/vivids" - "github.com/panjf2000/ants/v2" - "path" - "reflect" - "strings" - "sync" - "sync/atomic" -) - -var actorDispatcherContextKey = reflect.TypeOf((*vivid.Dispatcher)(nil)).Elem() - -// NewActorSystem 创建一个 ActorSystem -func NewActorSystem(name string, opts ...*vivid.ActorSystemOptions) *ActorSystem { - s := &ActorSystem{ - opts: vivid.NewActorSystemOptions().Apply(opts...), - name: name, - actors: make(map[vivid.ActorId]*ActorCore), - replyWaiters: make(map[uint64]chan any), - } - s.ActorSystemExternal = new(ActorSystemExternal).init(s) - return s -} - -// ActorSystem 是维护 Actor 的容器,负责 Actor 的创建、销毁、消息分发等 -type ActorSystem struct { - *ActorSystemExternal - opts *vivid.ActorSystemOptions // ActorSystem 的配置项 - name string // ActorSystem 的名称 - actors map[vivid.ActorId]*ActorCore // 可用于精准快查的映射 - actorsRW sync.RWMutex // 用于保护 actors 的读写锁 - user *ActorCore // 用户使用的顶级 Actor - ctx context.Context // 上下文 - cancel context.CancelFunc // 取消函数 - guid atomic.Uint64 // 未命名 Actor 的唯一标识 - seq atomic.Uint64 // 消息序列号 - replyWaiters map[uint64]chan any // 等待回复的消息 - replyWaitersLock sync.Mutex // 等待回复的消息锁 - gp *ants.Pool // goroutine 池 -} - -// GetName 获取 ActorSystem 的名称 -func (s *ActorSystem) GetName() string { - return s.name -} - -// Run 非阻塞的运行 ActorSystem -func (s *ActorSystem) Run() (err error) { - s.ctx, s.cancel = context.WithCancel(context.Background()) - pool, err := ants.NewPool(s.opts.AntsPoolSize, s.opts.AntsOptions...) - if err != nil { - return err - } - s.gp = pool - - defaultDispatcher, exist := s.opts.Dispatchers[""] - if !exist { - defaultDispatcher = NewDispatcher() - s.opts.Dispatchers[""] = defaultDispatcher - } - for _, d := range s.opts.Dispatchers { - d.OnInit(s) - } - - s.user, err = s.generateActor(context.WithoutCancel(s.ctx), new(userGuardianActor), vivid.NewActorOptions().WithName("user"), false) - if err != nil { - return err - } - - if s.opts.Host != "" { - for i := 0; i < int(s.opts.RemoteProcessorNum); i++ { - go s.handleRemoteMessage(s.ctx, s.opts.Server.C()) - } - } - - return nil -} - -// Shutdown 关闭 ActorSystem -func (s *ActorSystem) Shutdown() error { - if err := s.user.OnDestroy(s.user); err != nil { - return err - } - s.actorsRW.Lock() - defer s.actorsRW.Unlock() - s.releaseActor(s.user, true) - delete(s.actors, s.user.GetId()) - - for _, d := range s.opts.Dispatchers { - d.Stop() - } - - s.cancel() - return nil -} - -// ActorOf 创建一个 Actor -// - 推荐使用 ActorOf 函数来创建 Actor,这样可以保证 Actor 的类型安全 -func (s *ActorSystem) ActorOf(actor vivid.Actor, opts ...*vivid.ActorOptions) (vivid.ActorRef, error) { - return s.user.ActorOf(actor, opts...) -} - -// GetActor 获取 ActorRef -func (s *ActorSystem) GetActor() vivid.Query { - return NewQuery(s, s.user) -} - -func (s *ActorSystem) sendCtx(actorId vivid.ActorId, ctx vivid.MessageContext) error { - ref, err := s.GetActor().MustActorId(actorId).One() - if err != nil { - return fmt.Errorf("%w: %s", err, actorId.String()) - } - core := ref.(*ActorCore) - - return core.GetContext(actorDispatcherContextKey).(vivid.Dispatcher).Send(core, ctx) -} - -// send 用于向 Actor 发送消息 -func (s *ActorSystem) send(senderId, receiverId vivid.ActorId, msg vivid.Message, opts ...vivid.MessageOption) (vivid.MessageContext, *vivid.MessageOptions, error) { - opt := new(vivid.MessageOptions).Apply(opts...) - - ctx := NewMessageContext(s, senderId, receiverId, msg, opt) - - // 检查是否为本地 Actor - if isLocal := receiverId.Host() == s.opts.Host && receiverId.Port() == s.opts.Port; isLocal { - return ctx, opt, s.sendCtx(receiverId, ctx) - } - - // 远程消息如果是匿名发送,设置网络信息 - if ctx.GetSenderId() == "" { - ctx.RemoteNetwork = s.opts.Network - ctx.RemoteHost = s.opts.Host - ctx.RemotePort = s.opts.Port - } - - data, err := gob.Encode(ctx) - if err != nil { - return nil, nil, err - } - - cli, err := s.opts.ClientFactory.NewClient(s, receiverId.Network(), receiverId.Host(), receiverId.Port()) - if err != nil { - return nil, nil, err - } - - return ctx, opt, cli.Exec(data) -} - -// Tell 用于向 Actor 发送消息 -func (s *ActorSystem) Tell(receiverId vivid.ActorId, msg vivid.Message, opts ...vivid.MessageOption) error { - opt := new(vivid.MessageOptions).Apply(append(opts, vivid.WithMessageReply(0))...) - _, _, err := s.send(opt.SenderId, receiverId, msg, vivid.WithMessageOptions(opt)) - return err -} - -// Ask 用于向 Actor 发送消息,并等待回复 -func (s *ActorSystem) Ask(receiverId vivid.ActorId, msg vivid.Message, opts ...vivid.MessageOption) (any, error) { - var opt = new(vivid.MessageOptions).Apply(opts...) - var appendOpts = []vivid.MessageOption{vivid.WithMessageOptions(opt)} - if opt.ReplyTimeout <= 0 { - appendOpts = append(appendOpts, vivid.WithMessageReply(s.opts.AskDefaultTimeout)) - } - - ctx, opt, err := s.send(opt.SenderId, receiverId, msg, appendOpts...) - if err != nil { - return nil, err - } - - waiter := make(chan any) - seq := ctx.GetSeq() - s.replyWaitersLock.Lock() - s.replyWaiters[seq] = waiter - s.replyWaitersLock.Unlock() - - timeout, cancel := context.WithTimeout(s.ctx, opt.ReplyTimeout) - defer func(s *ActorSystem, seq uint64, cancel context.CancelFunc, waiter chan any) { - cancel() - close(waiter) - s.replyWaitersLock.Lock() - delete(s.replyWaiters, seq) - s.replyWaitersLock.Unlock() - }(s, seq, cancel, waiter) - - select { - case <-timeout.Done(): - return nil, fmt.Errorf("%w: %s", vivid.ErrReplyTimeout, receiverId.String()) - case reply := <-waiter: - err, ok := reply.(error) - if ok { - return nil, err - } - return reply, nil - } -} - -func (s *ActorSystem) handleRemoteMessage(ctx context.Context, c <-chan []byte) { - for { - select { - case <-ctx.Done(): - return - case data := <-c: - reply, err := NewMessageContextWithBytes(s, data) - if err != nil { - panic(err) - } - - // 处理回复消息 - if reply.ReplyMessage != nil { - s.replyWaitersLock.Lock() - waiter, exist := s.replyWaiters[reply.Seq] - s.replyWaitersLock.Unlock() - if exist { - waiter <- reply.ReplyMessage - } - continue - } - - // 处理请求消息 - if err = s.sendCtx(reply.GetReceiverId(), reply); err != nil { - log.Error(fmt.Sprintf("handle remote message error: %s", err.Error())) - } - } - } -} - -func (s *ActorSystem) releaseActor(core *ActorCore, reenter bool) { - if !reenter { - s.actorsRW.RLock() - } - - for key, child := range core.Children { - if err := child.OnDestroy(child.Core); err != nil { - log.Error(fmt.Sprintf("unregister actor destroy error: %s", err.Error())) - } - s.releaseActor(child, true) - delete(core.Children, key) - delete(s.actors, child.GetId()) - } - - delete(s.actors, core.Id) - if core.Parent != nil { - delete(core.Parent.Children, core.GetOptions().Name) - } - - if !reenter { - s.actorsRW.RUnlock() - } - - if err := core.GetContext(actorDispatcherContextKey).(vivid.Dispatcher).Detach(core); err != nil { - log.Error(fmt.Sprintf("unregister actor detach error: %s", err.Error())) - return - } - - log.Debug(fmt.Sprintf("actor %s released", core.GetId().String())) -} - -func (s *ActorSystem) generateActor(ctx context.Context, actorImpl vivid.Actor, opt *vivid.ActorOptions, reenter bool) (*ActorCore, error) { - // 应用可选项 - var actorPath string - if opt.Name == "" { - var builder strings.Builder - builder.WriteString(opt.Parent.GetActorId().Name()) - builder.WriteByte('-') - builder.WriteString(convert.Uint64ToString(s.guid.Add(1))) - - opt.Name = builder.String() - if opt.Parent != nil { - actorPath = path.Join(opt.Parent.GetActorId().Path(), opt.Name) - } else { - actorPath = path.Clean(opt.Name) - } - } else { - if opt.Parent != nil { - actorPath = path.Join(opt.Parent.GetActorId().Path(), opt.Name) - } else { - actorPath = path.Clean(opt.Name) - } - } - - // 创建 Actor ID - actorId := vivid.NewActorId(s.opts.Network, s.opts.ClusterName, s.opts.Host, s.opts.Port, s.name, actorPath) - - // 检查 Actor 是否已经存在 - if !reenter { - s.actorsRW.Lock() - defer s.actorsRW.Unlock() - } - actorCore, exist := s.actors[actorId] - if exist { - return nil, fmt.Errorf("%w: %s", vivid.ErrActorAlreadyExists, actorId.Name()) - } - - // 创建 Actor - actorCore = NewActorCore(ctx, s, actorId, actorImpl, opt) - - // 绑定分发器 - dispatcher := s.opts.Dispatchers[opt.DispatcherName] - if dispatcher == nil { - return nil, fmt.Errorf("%w: %s", vivid.ErrDispatcherNotFound, opt.DispatcherName) - } - actorCore.SetContext(actorDispatcherContextKey, dispatcher) - if err := dispatcher.Attach(actorCore); err != nil { - s.releaseActor(actorCore, true) - return nil, err - } - - // 绑定父 Actor - if opt.Parent != nil { - actorCore.Parent = opt.Parent.(*ActorContext).Core - opt.Parent.(*ActorContext).bindChildren(actorCore) - } - - // 启动 Actor - if err := actorCore.onPreStart(); err != nil { - s.releaseActor(actorCore, true) - return nil, err - } - - s.actors[actorId] = actorCore - log.Debug(fmt.Sprintf("actor %s created", actorId.String())) - return actorCore, nil -} - -// GetActorIds 获取 Actor ID -func (s *ActorSystem) GetActorIds() []vivid.ActorId { - s.actorsRW.RLock() - defer s.actorsRW.RUnlock() - var ids = make([]vivid.ActorId, 0, len(s.actors)) - for _, actor := range s.actors { - ids = append(ids, actor.GetId()) - } - return ids -} diff --git a/vivid/unsafevivid/actor_system_benchmark_test.go b/vivid/unsafevivid/actor_system_benchmark_test.go deleted file mode 100644 index 95342af4..00000000 --- a/vivid/unsafevivid/actor_system_benchmark_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package unsafevivid_test - -import ( - "github.com/kercylan98/minotaur/vivid" - "testing" -) - -type TestActor struct { - *vivid.BasicActor -} - -func BenchmarkActorSystem_ActorOf(b *testing.B) { - var err error - system := vivid.NewActorSystem("test") - if err = system.Run(); err != nil { - b.Fatal(err) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err = system.ActorOf(new(TestActor)); err != nil { - b.Fatal(err) - } - } - b.StopTimer() - - if err = system.Shutdown(); err != nil { - b.Fatal(err) - } -} diff --git a/vivid/unsafevivid/actor_system_external.go b/vivid/unsafevivid/actor_system_external.go deleted file mode 100644 index e540aebf..00000000 --- a/vivid/unsafevivid/actor_system_external.go +++ /dev/null @@ -1,16 +0,0 @@ -package unsafevivid - -import "github.com/panjf2000/ants/v2" - -type ActorSystemExternal struct { - system *ActorSystem -} - -func (a *ActorSystemExternal) init(system *ActorSystem) *ActorSystemExternal { - a.system = system - return a -} - -func (a *ActorSystemExternal) GetGoroutinePool() *ants.Pool { - return a.system.gp -} diff --git a/vivid/unsafevivid/actor_terminated_context.go b/vivid/unsafevivid/actor_terminated_context.go deleted file mode 100644 index 17f0c462..00000000 --- a/vivid/unsafevivid/actor_terminated_context.go +++ /dev/null @@ -1,55 +0,0 @@ -package unsafevivid - -import vivid "github.com/kercylan98/minotaur/vivid/vivids" - -func NewActorTerminatedContext(c *ActorCore, v ...vivid.Message) *ActorTerminatedContext { - return &ActorTerminatedContext{ - core: c, - terminatedMessages: v, - } -} - -type ActorTerminatedContext struct { - core *ActorCore // Actor 核心 - terminatedMessages []vivid.Message // 销毁消息 - cancelTerminate bool // 是否取消销毁 -} - -func (c *ActorTerminatedContext) GetActorId() vivid.ActorId { - return c.core.GetId() -} - -func (c *ActorTerminatedContext) GetActor() any { - return c.core.Actor -} - -func (c *ActorTerminatedContext) HasTerminatedMessage() bool { - return len(c.terminatedMessages) > 0 -} - -func (c *ActorTerminatedContext) GetTerminatedMessage() vivid.Message { - if len(c.terminatedMessages) == 1 { - return c.terminatedMessages[0] - } else { - return c.terminatedMessages - } -} - -func (c *ActorTerminatedContext) Restart() error { - c.core.RestartNum++ - if _, err := c.core.restart(false); err != nil { - return err - } - c.core.RestartNum = 0 - c.cancelTerminate = true - return nil -} - -func (c *ActorTerminatedContext) Recover() error { - c.cancelTerminate = true - return nil -} - -func (c *ActorTerminatedContext) GetRestartNum() int { - return c.core.RestartNum -} diff --git a/vivid/unsafevivid/codec.go b/vivid/unsafevivid/codec.go deleted file mode 100644 index b3725823..00000000 --- a/vivid/unsafevivid/codec.go +++ /dev/null @@ -1,31 +0,0 @@ -package unsafevivid - -import ( - "bytes" - goGob "encoding/gob" -) - -var gob = new(gobCodec) - -// Codec 用于远程消息的编解码器的接口 -type Codec interface { - // Encode 用于编码一个消息 - Encode(v any) ([]byte, error) - - // Decode 用于解码一个消息 - Decode(data []byte, v any) error -} - -type gobCodec struct{} - -func (c *gobCodec) Encode(v any) ([]byte, error) { - var buf bytes.Buffer - if err := goGob.NewEncoder(&buf).Encode(v); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func (c *gobCodec) Decode(data []byte, v any) error { - return goGob.NewDecoder(bytes.NewReader(data)).Decode(v) -} diff --git a/vivid/unsafevivid/dispatcher.go b/vivid/unsafevivid/dispatcher.go deleted file mode 100644 index 23e1a927..00000000 --- a/vivid/unsafevivid/dispatcher.go +++ /dev/null @@ -1,124 +0,0 @@ -package unsafevivid - -import ( - "github.com/kercylan98/minotaur/toolkit/log" - "github.com/kercylan98/minotaur/toolkit/queues" - vivid "github.com/kercylan98/minotaur/vivid/vivids" - "reflect" - "sync" -) - -var dispatcherActorContextKey = reflect.TypeOf((*DispatcherActorContext)(nil)).Elem() - -type DispatcherActorContext struct { - mailbox vivid.Mailbox - wait chan struct{} -} - -// NewDispatcher 创建一个新的消息调度器 -func NewDispatcher() *Dispatcher { - d := &Dispatcher{} - return d -} - -type Dispatcher struct { - system vivid.ActorSystemExternal - wait sync.WaitGroup // 等待所有 Actor 关闭 -} - -func (d *Dispatcher) OnInit(system vivid.ActorSystemExternal) { - d.system = system -} - -func (d *Dispatcher) Attach(actor vivid.ActorCore) error { - opts := actor.GetOptions() - // 为 Actor 创建一个邮箱 - var mailbox vivid.Mailbox - if opts.Mailbox != nil { - mailbox = opts.Mailbox() - } else { - mailbox = NewMailbox(queues.NewFIFO[vivid.MessageContext](queues.NewFIFOOptions().WithBufferSize(128))) - } - - ctx := &DispatcherActorContext{ - mailbox: mailbox, - wait: make(chan struct{}), - } - actor.SetContext(dispatcherActorContextKey, ctx) - - pool := d.system.GetGoroutinePool() - if err := pool.Submit(func() { - mailbox.Start() - }); err != nil { - return err - } - - if err := pool.Submit(func() { - d.watchReceive(actor, ctx.wait, mailbox.Dequeue()) - }); err != nil { - mailbox.Stop() - return err - } - return nil -} - -func (d *Dispatcher) Detach(actor vivid.ActorCore) error { - ctx, ok := actor.GetContext(dispatcherActorContextKey).(*DispatcherActorContext) - if !ok { - return nil - } - - d.wait.Add(1) - - // 异步等待邮箱关闭,当池无法使用时降级使用内置 goroutine - pool := d.system.GetGoroutinePool() - if err := pool.Submit(func() { - defer d.wait.Done() - <-ctx.wait - }); err != nil { - go func() { - defer d.wait.Done() - <-ctx.wait - }() - } - - ctx.mailbox.Stop() - return nil -} - -func (d *Dispatcher) Send(receiver vivid.ActorCore, msg vivid.MessageContext) error { - ctx, ok := receiver.GetContext(dispatcherActorContextKey).(*DispatcherActorContext) - if !ok { - return vivid.ErrActorTerminated - } - - ctx.mailbox.Enqueue(msg) - log.Debug("send message to actor", log.Uint64("seq", msg.GetSeq()), - log.String("msg", reflect.TypeOf(msg.GetMessage()).String()), - log.String("sender", msg.GetSenderId().String()), - log.String("receiver", msg.GetReceiverId().String()), - ) - return nil -} - -func (d *Dispatcher) Stop() { - d.wait.Wait() -} - -func (d *Dispatcher) watchReceive(actor vivid.ActorCore, wait chan struct{}, dequeue <-chan vivid.MessageContext) { - defer close(wait) - for { - if pause := actor.IsPause(); pause != nil { - <-pause - } - - ctx, ok := <-dequeue - if !ok { - break - } - actor.BindMessageActorContext(ctx) - if err := actor.OnReceived(ctx); err != nil { - log.Error("actor receive message failed", log.Err(err)) - } - } -} diff --git a/vivid/unsafevivid/event.go b/vivid/unsafevivid/event.go deleted file mode 100644 index 0bb2a9ac..00000000 --- a/vivid/unsafevivid/event.go +++ /dev/null @@ -1,8 +0,0 @@ -package unsafevivid - -import "github.com/kercylan98/minotaur/vivid/vivids" - -type SubscribeEventMessage struct { - Subscriber vivids.ActorId - Event vivids.Event -} diff --git a/vivid/unsafevivid/future.go b/vivid/unsafevivid/future.go deleted file mode 100644 index 242a14ef..00000000 --- a/vivid/unsafevivid/future.go +++ /dev/null @@ -1,29 +0,0 @@ -package unsafevivid - -import vivid "github.com/kercylan98/minotaur/vivid/vivids" - -// NewFuture 创建一个新的 Future 对象,该对象用于异步获取 Actor 的返回值 -func NewFuture(ctx vivid.ActorContext, handler func() vivid.Message) vivid.Future { - f := &future{ - ctx: ctx, - } - c := ctx.(*ActorContext) - var h = func() { - m := handler() - f.done = true - c.Core.Tell(m) - } - if err := c.System.gp.Submit(h); err != nil { - go h() - } - return f -} - -type future struct { - ctx vivid.ActorContext - done bool -} - -func (f *future) IsCompleted() bool { - return f.done -} diff --git a/vivid/unsafevivid/mailbox.go b/vivid/unsafevivid/mailbox.go deleted file mode 100644 index e280d71b..00000000 --- a/vivid/unsafevivid/mailbox.go +++ /dev/null @@ -1,18 +0,0 @@ -package unsafevivid - -import ( - "github.com/kercylan98/minotaur/toolkit/queues" - vivid "github.com/kercylan98/minotaur/vivid/vivids" -) - -// NewMailbox 创建一个新的邮箱 -func NewMailbox(queue queues.Queue[vivid.MessageContext]) *Mailbox { - return &Mailbox{ - Queue: queue, - } -} - -// Mailbox 邮箱是 Actor 中用于接收消息的队列,该邮箱接受任意实现了 queues.Queue 接口的队列作为其实现 -type Mailbox struct { - queues.Queue[vivid.MessageContext] -} diff --git a/vivid/unsafevivid/message_context.go b/vivid/unsafevivid/message_context.go deleted file mode 100644 index bb18d463..00000000 --- a/vivid/unsafevivid/message_context.go +++ /dev/null @@ -1,86 +0,0 @@ -package unsafevivid - -import vivid "github.com/kercylan98/minotaur/vivid/vivids" - -func NewMessageContext(system *ActorSystem, senderId, receiverId vivid.ActorId, message vivid.Message, options *vivid.MessageOptions) *MessageContext { - return &MessageContext{ - system: system, - Options: options, - Message: message, - SenderId: senderId, - ReceiverId: receiverId, - Seq: system.seq.Add(1), - } -} - -func NewMessageContextWithBytes(system *ActorSystem, data []byte) (*MessageContext, error) { - ctx := new(MessageContext) - if err := gob.Decode(data, ctx); err != nil { - return nil, err - } - ctx.system = system - return ctx, nil -} - -type MessageContext struct { - vivid.ActorContext // 消息接收者的 ActorContext,不参与序列化 - system *ActorSystem // 生产该消息的 ActorSystem,不参与序列化 - - Options *vivid.MessageOptions // 消息选项 - SenderId vivid.ActorId // 隐式发送者 - ReceiverId vivid.ActorId // 接收者 - Message vivid.Message // 消息 - ReplyMessage vivid.Message // 回复的消息 - Seq uint64 // 消息序号(响应消息 Seq 同请求消息 Seq) - RemoteNetwork string // 远程网络地址 - RemoteHost string // 远程主机地址 - RemotePort uint16 // 远程端口 -} - -func (c *MessageContext) Reply(msg vivid.Message) error { - var clone = &MessageContext{ - Options: c.Options, - SenderId: c.ReceiverId, - ReceiverId: c.SenderId, - Message: nil, - ReplyMessage: msg, - Seq: c.Seq, - } - if clone.ReceiverId == "" && c.RemoteNetwork != "" && c.RemoteHost != "" { - // 匿名远程 Actor,通过网络发送消息 - client, err := c.system.opts.ClientFactory(c.system, c.RemoteNetwork, c.RemoteHost, c.RemotePort) - if err != nil { - return err - } - data, err := gob.Encode(clone) - if err != nil { - return err - } - return client.Exec(data) - } - - // 本地回复 - c.system.replyWaitersLock.Lock() - waiter, exist := c.system.replyWaiters[clone.Seq] - c.system.replyWaitersLock.Unlock() - if exist { - waiter <- clone.ReplyMessage - } - return nil -} - -func (c *MessageContext) GetSeq() uint64 { - return c.Seq -} - -func (c *MessageContext) GetSenderId() vivid.ActorId { - return c.Options.SenderId -} - -func (c *MessageContext) GetReceiverId() vivid.ActorId { - return c.ReceiverId -} - -func (c *MessageContext) GetMessage() vivid.Message { - return c.Message -} diff --git a/vivid/unsafevivid/query.go b/vivid/unsafevivid/query.go deleted file mode 100644 index 5f8b54cf..00000000 --- a/vivid/unsafevivid/query.go +++ /dev/null @@ -1,170 +0,0 @@ -package unsafevivid - -import ( - "github.com/kercylan98/minotaur/toolkit/collection" - vivid "github.com/kercylan98/minotaur/vivid/vivids" - "path" - "path/filepath" -) - -// NewQuery 创建一个 Actor 查询器 -func NewQuery(system *ActorSystem, core *ActorCore) *Query { - return &Query{ - system: system, - core: core, - resultIds: map[vivid.ActorId]struct{}{}, - lock: true, - } -} - -// Query 是 Actor 查询器 -type Query struct { - system *ActorSystem - core *ActorCore - - results []*ActorCore // 查询结果 - resultIds map[vivid.ActorId]struct{} // 去重查询结果 ID - - actions []func() - lock bool -} - -func (q *Query) ActorId(actorIds ...vivid.ActorId) vivid.Query { - q.actions = append(q.actions, func() { - if q.resultIds == nil { - q.resultIds = make(map[vivid.ActorId]struct{}) - } - for _, actorId := range actorIds { - if _, exist := q.resultIds[actorId]; !exist { - if ref, exist := q.system.actors[actorId]; exist { - q.results = append(q.results, ref) - } else { - q.results = append(q.results, &ActorCore{ActorRef: NewActorRef(q.system, actorId)}) - } - q.resultIds[actorId] = struct{}{} - } - } - }) - return q - -} - -func (q *Query) MustActorId(actorIds ...vivid.ActorId) vivid.Query { - q.actions = append(q.actions, func() { - if q.resultIds == nil { - q.resultIds = make(map[vivid.ActorId]struct{}) - } - for _, actorId := range actorIds { - if _, exist := q.resultIds[actorId]; !exist { - if ref, exist := q.system.actors[actorId]; exist { - q.results = append(q.results, ref) - q.resultIds[actorId] = struct{}{} - } - } - } - }) - return q -} - -func (q *Query) ActorName(names ...vivid.ActorName) vivid.Query { - q.actions = append(q.actions, func() { - for _, actor := range q.system.actors { - for _, actorName := range names { - if actor.GetId().Name() == actorName { - q.results = append(q.results, actor) - } - } - } - }) - return q -} - -func (q *Query) ActorPath(actorPath vivid.ActorPath) vivid.Query { - findPath := path.Clean(path.Join(q.core.Id.Path(), actorPath)) - q.actions = append(q.actions, func() { - node := q.core - if path.IsAbs(findPath) { - node = q.system.user - } - for _, name := range filepath.SplitList(filepath.Clean(findPath)) { - if name == "*" { - for _, actor := range node.Children { - q.results = append(q.results, actor) - } - break - } - if len(name) == 0 { - continue - } - if actor, exist := node.Children[name]; exist { - node = actor - } else { - return - } - } - q.results = append(q.results, node) - }) - return q -} - -// Many 获取多个响应结果 -func (q *Query) Many() []vivid.ActorRef { - return collection.MappingFromSlice(q.internalMany(), func(value *ActorCore) vivid.ActorRef { - return value - }) -} - -// First 获取第一个响应结果 -func (q *Query) First() (vivid.ActorRef, error) { - return q.internalFirst() -} - -// One 获取唯一的响应结果 -func (q *Query) One() (vivid.ActorRef, error) { - return q.internalOne() -} - -// exec 执行查询 -func (q *Query) exec() { - if q.lock { - q.system.actorsRW.RLock() - defer q.system.actorsRW.RUnlock() - } - - for _, action := range q.actions { - action() - } -} - -// internalOne 获取唯一的响应结果 -func (q *Query) internalOne() (*ActorCore, error) { - q.exec() - if len(q.results) == 0 { - return nil, vivid.ErrActorNotFound - } - if len(q.results) > 1 { - return nil, vivid.ErrActorNotUnique - } - return q.results[0], nil -} - -// internalMany 获取多个响应结果 -func (q *Query) internalMany() []*ActorCore { - q.exec() - return q.results -} - -// internalFirst 获取第一个响应结果 -func (q *Query) internalFirst() (*ActorCore, error) { - q.exec() - if len(q.results) == 0 { - return nil, vivid.ErrActorNotFound - } - return q.results[0], nil -} - -// withLock 禁用锁 -func (q *Query) withLock(isLock bool) *Query { - q.lock = isLock - return q -} diff --git a/vivid/unsafevivid/user_guardian_actor.go b/vivid/unsafevivid/user_guardian_actor.go deleted file mode 100644 index 8facfdc4..00000000 --- a/vivid/unsafevivid/user_guardian_actor.go +++ /dev/null @@ -1,8 +0,0 @@ -package unsafevivid - -import "github.com/kercylan98/minotaur/vivid/vivids" - -// userGuardianActor 用户守护 Actor -type userGuardianActor struct { - vivids.BasicActor -} diff --git a/vivid2/user_guard.go b/vivid/user_guard.go similarity index 100% rename from vivid2/user_guard.go rename to vivid/user_guard.go diff --git a/vivid/vivid.go b/vivid/vivid.go index 2f759df8..04dffb8b 100644 --- a/vivid/vivid.go +++ b/vivid/vivid.go @@ -1,10 +1,74 @@ package vivid import ( - "github.com/kercylan98/minotaur/vivid/unsafevivid" - "github.com/kercylan98/minotaur/vivid/vivids" + "reflect" ) -func NewActorSystem(name string, opts ...*vivids.ActorSystemOptions) vivids.ActorSystem { - return unsafevivid.NewActorSystem(name, opts...) +// Context 上下文对象接口 +type Context interface { + GetActor() Actor +} + +func ActorOf[T Actor](actorOf actorOf, options ...*ActorOptions[T]) ActorRef { + var opts = parseActorOptions(options...) + var tof = reflect.TypeOf((*T)(nil)).Elem().Elem() + var ins = reflect.New(tof).Interface().(T) + var system = actorOf.getSystem() + + if opts.Parent == nil { + opts.Parent = actorOf.getContext() + } + + ctx, err := generateActor[T](actorOf.getSystem(), ins, opts) + if err != nil { + system.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeActorOf, DeadLetterEventActorOf{ + Error: err, + Parent: opts.Parent, + Name: opts.Name, + })) + } + if ctx == nil { + return &_DeadLetterActorRef{ + system: system, + } + } + return ctx +} + +func FreeActorOf[T any](actorOf actorOf, options ...*ActorOptions[*FreeActor[T]]) ActorRef { + var opts = parseActorOptions(options...) + var tof = reflect.TypeOf((*T)(nil)).Elem().Elem() + var ins = reflect.New(tof).Interface().(T) + var system = actorOf.getSystem() + + if opts.Parent == nil { + opts.Parent = actorOf.getContext() + } + + ctx, err := generateActor[*FreeActor[T]](actorOf.getSystem(), &FreeActor[T]{actor: ins}, opts) + if err != nil { + system.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeActorOf, DeadLetterEventActorOf{ + Error: err, + Parent: opts.Parent, + Name: opts.Name, + })) + } + if ctx == nil { + return &_DeadLetterActorRef{ + system: system, + } + } + return ctx +} + +// GetActor 快捷的将 Actor 上下文转换为对应的 Actor 对象。该函数兼容 FreeActorOf 创建的对象 +// - 需要注意的是,当被转换的对象类型不匹配时将会返回空指针 +func GetActor[T any](ctx Context) (t T) { + switch v := ctx.GetActor().(type) { + case *FreeActor[T]: + return v.GetActor() + default: + t, _ = v.(T) + return t + } } diff --git a/vivid/vivids/actor.go b/vivid/vivids/actor.go deleted file mode 100644 index f001d378..00000000 --- a/vivid/vivids/actor.go +++ /dev/null @@ -1,26 +0,0 @@ -package vivids - -// Actor 是 Actor 模型的接口,该接口用于定义一个 Actor -type Actor interface { - // OnPreStart 在 Actor 启动之前执行的逻辑,适用于对 Actor 状态的初始化 - OnPreStart(ctx ActorContext) (err error) - - // OnReceived 当 Actor 接收到消息时执行的逻辑,返回值的错误用于表示消息处理是否成功,若需要响应错误信息则应通过 MessageContext.Reply 函数进行回复 - OnReceived(ctx MessageContext) error - - // OnDestroy 当 Actor 被要求销毁时将会调用该函数,需要在该函数中释放 Actor 的资源 - // - 该函数会在重启前被调用,被用于重置 Actor 的状态 - OnDestroy(ctx ActorContext) (err error) - - // OnSaveSnapshot 当 Actor 被要求保存快照时将会调用该函数 - OnSaveSnapshot(ctx ActorContext) (snapshot []byte, err error) - - // OnRecoverSnapshot 当 Actor 被要求恢复快照时将会调用该函数 - OnRecoverSnapshot(ctx ActorContext, snapshot []byte) (err error) - - // OnChildTerminated 当 Actor 的子 Actor 被销毁时将会调用该函数 - OnChildTerminated(ctx ActorContext, child ActorTerminatedContext) - - // OnEvent 当 Actor 接收到事件时将会调用该函数 - OnEvent(ctx ActorContext, event Message) (err error) -} diff --git a/vivid/vivids/actor_context.go b/vivid/vivids/actor_context.go deleted file mode 100644 index 3c880940..00000000 --- a/vivid/vivids/actor_context.go +++ /dev/null @@ -1,36 +0,0 @@ -package vivids - -import "context" - -type ActorBehavior[T Message] func(ctx MessageContext, msg T) error - -type ActorContext interface { - context.Context - - // GetActorId 获取 Actor 的 ID - GetActorId() ActorId - - // GetActor 获取 Actor 的引用 - GetActor() Query - - // GetParentActor 获取父 Actor 的引用 - GetParentActor() ActorRef - - // Future 创建一个 Future 对象,用于异步获取 Actor 的返回值 - Future(handler func() Message) Future - - // ActorOf 创建一个 Actor,该 Actor 是当前 Actor 的子 Actor - ActorOf(actor Actor, opts ...*ActorOptions) (ActorRef, error) - - // NotifyTerminated 当 Actor 主动销毁时,务必调用该函数,以便在整个 Actor 系统中得到完整的释放 - NotifyTerminated(v ...Message) - - // RegisterBehavior 注册一个行为,该行为必须是一个 ActorBehavior 类型 - RegisterBehavior(message Message, behavior any) - - // PublishEvent 发布一个事件 - PublishEvent(event Message) - - // GetProps 获取 Actor 的属性 - GetProps() any -} diff --git a/vivid/vivids/actor_core.go b/vivid/vivids/actor_core.go deleted file mode 100644 index 8cf23f58..00000000 --- a/vivid/vivids/actor_core.go +++ /dev/null @@ -1,26 +0,0 @@ -package vivids - -// ActorCore 是 Actor 的核心接口定义,被用于高级功能的实现 -type ActorCore interface { - ActorRef - ActorContext - - // GetOptions 获取 Actor 的配置项 - GetOptions() *ActorOptions - - // SetContext 为 Actor 设置一个上下文 - SetContext(key, ctx any) - - // GetContext 获取 Actor 的上下文 - GetContext(key any) any - - // IsPause 判断 Actor 当前是否处于暂停处理消息的状态,返回一个只读的通道 - // - 当 Actor 未处于暂停状态时,返回 nil - IsPause() <-chan struct{} - - // BindMessageActorContext 绑定消息的 Actor 上下文为当前 Actor - BindMessageActorContext(ctx MessageContext) - - // OnReceived 用于处理接收到的消息 - OnReceived(ctx MessageContext) error -} diff --git a/vivid/vivids/actor_id.go b/vivid/vivids/actor_id.go deleted file mode 100644 index a2f790f3..00000000 --- a/vivid/vivids/actor_id.go +++ /dev/null @@ -1,232 +0,0 @@ -package vivids - -import ( - "encoding/binary" - "fmt" - "github.com/kercylan98/minotaur/toolkit/convert" - "path/filepath" - "regexp" - "strconv" - "strings" -) - -const ( - actorIdPrefix = "minotaur" - actorIdParseLocal = `^` + actorIdPrefix + `://([^/@]+@)?([^/:]+):(\d+)/([^/]+)/([^/]+)$` - actorIdParseTcp = `^` + actorIdPrefix + `\.tcp://([^/:]+):(\d+)/([^/]+)/([^/]+)$` - actorIdParseTcpCluster = `^` + actorIdPrefix + `\.tcp://([^/:]+):(\d+)/([^/]+)/([^/]+)/([^/]+)$` - actorIdMinLength = 12 -) - -// ActorId 是一个 Actor 的唯一标识符,该标识符是由紧凑的不可读字符串组成,其中包含了 Actor 完整的资源定位信息 -// - minotaur://my-system/user/my-localActorRef -// - minotaur.tcp://localhost:1234/user/my-localActorRef -// - minotaur.tcp://my-cluster@localhost:1234/user/my-localActorRef -type ActorId string - -type ActorName = string -type ActorPath = string - -func NewActorId(network, cluster, host string, port uint16, system, path ActorPath) ActorId { - networkLen := uint16(len(network)) - clusterLen := uint16(len(cluster)) - hostLen := uint16(len(host)) - systemLen := uint16(len(system)) - pathLen := uint16(len(path)) - - // 计算需要的字节数 - size := networkLen + clusterLen + hostLen + systemLen + pathLen + 12 // 添加端口号和长度信息 - - // 分配内存 - actorId := make([]byte, size) - offset := uint16(0) - - // 提前写入所有的长度信息,确保读取时可以快速定位 - binary.BigEndian.PutUint16(actorId[offset:], networkLen) - offset += 2 - binary.BigEndian.PutUint16(actorId[offset:], clusterLen) - offset += 2 - binary.BigEndian.PutUint16(actorId[offset:], hostLen) - offset += 2 - binary.BigEndian.PutUint16(actorId[offset:], systemLen) - offset += 2 - binary.BigEndian.PutUint16(actorId[offset:], pathLen) - offset += 2 - - // 写入网络信息 - copy(actorId[offset:], network) - offset += networkLen - - // 写入集群信息 - copy(actorId[offset:], cluster) - offset += clusterLen - - // 写入主机信息 - copy(actorId[offset:], host) - offset += hostLen - - // 写入系统信息 - copy(actorId[offset:], system) - offset += systemLen - - // 写入路径信息 - copy(actorId[offset:], path) - offset += pathLen - - // 写入端口信息 - binary.BigEndian.PutUint16(actorId[offset:], port) - - // 转换为字符串 - return ActorId(actorId) -} - -// ParseActorId 用于解析可读的 ActorId 字符串为 ActorId 对象 -// - minotaur://my-system/user/my-localActorRef -// - minotaur.tcp://localhost:1234/user/my-localActorRef -// - minotaur.tcp://my-cluster@localhost:1234/user/my-localActorRef -func ParseActorId(actorId string) (ActorId, error) { - var network, cluster, host, system, name string - var port int - var portStr string - - // 定义正则表达式来匹配不同格式的 ActorId - re1 := regexp.MustCompile(actorIdParseLocal) - re2 := regexp.MustCompile(actorIdParseTcp) - re3 := regexp.MustCompile(actorIdParseTcpCluster) - - if matches := re1.FindStringSubmatch(actorId); matches != nil { - cluster = matches[1] - host = matches[2] - portStr = matches[3] - system = matches[4] - name = matches[5] - network = "tcp" - } else if matches := re2.FindStringSubmatch(actorId); matches != nil { - host = matches[1] - portStr = matches[2] - system = matches[3] - name = matches[4] - network = "tcp" - } else if matches := re3.FindStringSubmatch(actorId); matches != nil { - system = matches[1] - name = matches[2] - network = "" - cluster = "" - host = "" - portStr = "0" - } else { - return "", fmt.Errorf("%w: %s", ErrActorIdInvalid, actorId) - } - - // 将端口号从字符串转换为整数 - port, err := strconv.Atoi(portStr) - if err != nil { - return "", fmt.Errorf("%w: %s, %w", ErrActorIdInvalid, actorId, err) - } - - return NewActorId(network, cluster, host, uint16(port), system, name), nil -} - -// Invalid 检查 ActorId 是否无效 -func (a ActorId) Invalid() bool { - if len(a) < actorIdMinLength { - return true - } - networkLen := binary.BigEndian.Uint16([]byte(a[:2])) - clusterLen := binary.BigEndian.Uint16([]byte(a[2:4])) - hostLen := binary.BigEndian.Uint16([]byte(a[4:6])) - systemLen := binary.BigEndian.Uint16([]byte(a[6:8])) - nameLen := binary.BigEndian.Uint16([]byte(a[8:10])) - totalLen := actorIdMinLength + networkLen + clusterLen + hostLen + systemLen + nameLen - if uint16(len(a)) < totalLen { - return true - } - - return networkLen == 0 || hostLen == 0 || systemLen == 0 || nameLen == 0 -} - -// Network 获取 ActorId 的网络信息 -func (a ActorId) Network() string { - length := binary.BigEndian.Uint16([]byte(a[:2])) - v := a[10 : 10+length] - return string(v) -} - -// Cluster 获取 ActorId 的集群信息 -func (a ActorId) Cluster() string { - networkLen := binary.BigEndian.Uint16([]byte(a[:2])) - clusterLen := binary.BigEndian.Uint16([]byte(a[2:4])) - v := a[10+networkLen : 10+networkLen+clusterLen] - return string(v) -} - -// Host 获取 ActorId 的主机信息 -func (a ActorId) Host() string { - networkLen := binary.BigEndian.Uint16([]byte(a[:2])) - clusterLen := binary.BigEndian.Uint16([]byte(a[2:4])) - hostLen := binary.BigEndian.Uint16([]byte(a[4:6])) - v := a[10+networkLen+clusterLen : 10+networkLen+clusterLen+hostLen] - return string(v) -} - -// Port 获取 ActorId 的端口信息 -func (a ActorId) Port() uint16 { - port := a[len(a)-2:] - return binary.BigEndian.Uint16([]byte(port)) -} - -// System 获取 ActorId 的系统信息 -func (a ActorId) System() string { - networkLen := binary.BigEndian.Uint16([]byte(a[:2])) - clusterLen := binary.BigEndian.Uint16([]byte(a[2:4])) - hostLen := binary.BigEndian.Uint16([]byte(a[4:6])) - systemLen := binary.BigEndian.Uint16([]byte(a[6:8])) - v := a[10+networkLen+clusterLen+hostLen : 10+networkLen+clusterLen+hostLen+systemLen] - return string(v) -} - -// Path 获取 ActorId 的路径信息 -func (a ActorId) Path() ActorPath { - networkLen := binary.BigEndian.Uint16([]byte(a[:2])) - clusterLen := binary.BigEndian.Uint16([]byte(a[2:4])) - hostLen := binary.BigEndian.Uint16([]byte(a[4:6])) - systemLen := binary.BigEndian.Uint16([]byte(a[6:8])) - nameLen := binary.BigEndian.Uint16([]byte(a[8:10])) - v := a[10+networkLen+clusterLen+hostLen+systemLen : 10+networkLen+clusterLen+hostLen+systemLen+nameLen] - return ActorPath(v) -} - -// Name 获取 ActorId 的名称信息 -func (a ActorId) Name() ActorName { - return filepath.Base(a.Path()) -} - -// String 获取 ActorId 的字符串表示 -func (a ActorId) String() string { - if a == "" { - return "" - } - var builder strings.Builder - builder.WriteString(actorIdPrefix) - if network := a.Network(); network != "" { - builder.WriteString(".") - builder.WriteString(network) - } - builder.WriteString("://") - if cluster := a.Cluster(); cluster != "" { - builder.WriteString(cluster) - builder.WriteString("@") - } - builder.WriteString(a.Host()) - if port := a.Port(); port != 0 { - builder.WriteString(":") - builder.WriteString(convert.Uint16ToString(port)) - } - if a.Host() != "" { - builder.WriteString("/") - } - builder.WriteString(a.System()) - builder.WriteString("/") - builder.WriteString(a.Path()) - return builder.String() -} diff --git a/vivid/vivids/actor_id_benchmark_test.go b/vivid/vivids/actor_id_benchmark_test.go deleted file mode 100644 index d4f020b7..00000000 --- a/vivid/vivids/actor_id_benchmark_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package vivids_test - -import ( - "github.com/kercylan98/minotaur/vivid/vivids" - "testing" -) - -func BenchmarkNewActorId(b *testing.B) { - for i := 0; i < b.N; i++ { - vivids.NewActorId("tcp", "my-cluster", "localhost", 1234, "my-system", "my-localActorRef") - } -} - -func BenchmarkActorIdInfo(b *testing.B) { - actorId := vivids.NewActorId("tcp", "my-cluster", "localhost", 1234, "my-system", "my-localActorRef") - - b.Run("Network", func(b *testing.B) { - for i := 0; i < b.N; i++ { - actorId.Network() - } - }) - - b.Run("Cluster", func(b *testing.B) { - for i := 0; i < b.N; i++ { - actorId.Cluster() - } - }) - - b.Run("Host", func(b *testing.B) { - for i := 0; i < b.N; i++ { - actorId.Host() - } - }) - - b.Run("Port", func(b *testing.B) { - for i := 0; i < b.N; i++ { - actorId.Port() - } - }) - - b.Run("System", func(b *testing.B) { - for i := 0; i < b.N; i++ { - actorId.System() - } - }) - - b.Run("Name", func(b *testing.B) { - for i := 0; i < b.N; i++ { - actorId.Name() - } - }) -} diff --git a/vivid/vivids/actor_id_test.go b/vivid/vivids/actor_id_test.go deleted file mode 100644 index 25b285cc..00000000 --- a/vivid/vivids/actor_id_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package vivids_test - -import ( - "github.com/kercylan98/minotaur/vivid/vivids" - "testing" -) - -func TestActorId_Network(t *testing.T) { - actorId := vivids.NewActorId("tcp", "my-cluster", "localhost", 1234, "my-system", "my-localActorRef") - - network := actorId.Network() - t.Log(network) - - if network != "tcp" { - t.Fail() - } -} - -func TestActorId_Cluster(t *testing.T) { - actorId := vivids.NewActorId("tcp", "my-cluster", "localhost", 1234, "my-system", "my-localActorRef") - - cluster := actorId.Cluster() - t.Log(cluster) - - if cluster != "my-cluster" { - t.Fail() - } -} - -func TestActorId_Host(t *testing.T) { - actorId := vivids.NewActorId("tcp", "my-cluster", "localhost", 1234, "my-system", "my-localActorRef") - - host := actorId.Host() - t.Log(host) - - if host != "localhost" { - t.Fail() - } -} - -func TestActorId_Port(t *testing.T) { - actorId := vivids.NewActorId("tcp", "my-cluster", "localhost", 1234, "my-system", "my-localActorRef") - - port := actorId.Port() - t.Log(port) - - if port != 1234 { - t.Fail() - } -} - -func TestActorId_System(t *testing.T) { - actorId := vivids.NewActorId("tcp", "my-cluster", "localhost", 1234, "my-system", "my-localActorRef") - - system := actorId.System() - t.Log(system) - - if system != "my-system" { - t.Fail() - } -} - -func TestActorId_Path(t *testing.T) { - actorId := vivids.NewActorId("tcp", "my-cluster", "localhost", 1234, "my-system", "my-localActorRef") - - path := actorId.Path() - t.Log(path) - - if path != "my-localActorRef" { - t.Fail() - } -} - -func TestActorId_Name(t *testing.T) { - actorId := vivids.NewActorId("tcp", "my-cluster", "localhost", 1234, "my-system", "my-localActorRef") - - name := actorId.Name() - t.Log(name) - - if name != "my-localActorRef" { - t.Fail() - } -} - -func TestActorId_String(t *testing.T) { - actorId := vivids.NewActorId("tcp", "my-cluster", "localhost", 1234, "my-system", "my-localActorRef") - - str := actorId.String() - t.Log(str) - - if str != "minotaur.tcp://my-cluster@localhost:1234/my-system/my-localActorRef" { - t.Fail() - - } -} - -func TestActorId_Parse(t *testing.T) { - actorId := "minotaur.tcp://my-cluster@localhost:1234/my-system/my-localActorRef" - - parsed, err := vivids.ParseActorId(actorId) - if err != nil { - panic(err) - } - t.Log(parsed.String()) - - if parsed.String() != actorId { - t.Fail() - } -} diff --git a/vivid/vivids/actor_options.go b/vivid/vivids/actor_options.go deleted file mode 100644 index faee448c..00000000 --- a/vivid/vivids/actor_options.go +++ /dev/null @@ -1,67 +0,0 @@ -package vivids - -// NewActorOptions 创建一个 ActorOptions -func NewActorOptions() *ActorOptions { - return &ActorOptions{} -} - -// ActorOptions 是 Actor 的配置项 -type ActorOptions struct { - Parent ActorContext // 父 Actor - Mailbox func() Mailbox // Actor 使用的邮箱 - Name string // Actor 名称 - DispatcherName string // Actor 使用的调度器名称,如果为空则使用默认调度器 - Props any // Actor 的属性 -} - -// Apply 应用配置项 -func (o *ActorOptions) Apply(opts ...*ActorOptions) *ActorOptions { - for _, opt := range opts { - if opt.Name != "" { - o.Name = opt.Name - } - if opt.Mailbox != nil { - o.Mailbox = opt.Mailbox - } - if opt.DispatcherName != "" { - o.DispatcherName = opt.DispatcherName - } - if opt.Parent != nil { - o.Parent = opt.Parent - } - if opt.Props != nil { - o.Props = opt.Props - } - } - return o -} - -// WithProps 设置 Actor 的属性 -func (o *ActorOptions) WithProps(props any) *ActorOptions { - o.Props = props - return o -} - -// WithName 设置 Actor 名称 -func (o *ActorOptions) WithName(name string) *ActorOptions { - o.Name = name - return o -} - -// WithMailbox 设置 Actor 使用的邮箱 -func (o *ActorOptions) WithMailbox(mailbox func() Mailbox) *ActorOptions { - o.Mailbox = mailbox - return o -} - -// WithDispatcherName 设置 Actor 使用的调度器名称 -func (o *ActorOptions) WithDispatcherName(name string) *ActorOptions { - o.DispatcherName = name - return o -} - -// WithParent 设置 Actor 的父 Actor -func (o *ActorOptions) WithParent(parent ActorContext) *ActorOptions { - o.Parent = parent - return o -} diff --git a/vivid/vivids/actor_ref.go b/vivid/vivids/actor_ref.go deleted file mode 100644 index 264a22fa..00000000 --- a/vivid/vivids/actor_ref.go +++ /dev/null @@ -1,16 +0,0 @@ -package vivids - -// ActorRef 是 Actor 的引用 -type ActorRef interface { - // GetId 用于获取 Actor 的 ActorId - GetId() ActorId - - // Tell 用于向 Actor 发送消息 - Tell(msg Message, opts ...MessageOption) error - - // Ask 用于向 Actor 发送消息并等待返回结果 - Ask(msg Message, opts ...MessageOption) (Message, error) - - // Subscribe 订阅事件 - Subscribe(ctx *ActorContext, event Event) -} diff --git a/vivid/vivids/actor_system.go b/vivid/vivids/actor_system.go deleted file mode 100644 index 8c838583..00000000 --- a/vivid/vivids/actor_system.go +++ /dev/null @@ -1,17 +0,0 @@ -package vivids - -type ActorSystem interface { - GetName() string - - Run() error - - Shutdown() error - - ActorOf(actor Actor, opts ...*ActorOptions) (ActorRef, error) - - GetActor() Query - - Tell(receiver ActorId, msg Message, opts ...MessageOption) error - - Ask(receiver ActorId, msg Message, opts ...MessageOption) (Message, error) -} diff --git a/vivid/vivids/actor_system_external.go b/vivid/vivids/actor_system_external.go deleted file mode 100644 index 140f86c4..00000000 --- a/vivid/vivids/actor_system_external.go +++ /dev/null @@ -1,8 +0,0 @@ -package vivids - -import "github.com/panjf2000/ants/v2" - -type ActorSystemExternal interface { - // GetGoroutinePool 用于获取 goroutine 池 - GetGoroutinePool() *ants.Pool -} diff --git a/vivid/vivids/actor_system_options.go b/vivid/vivids/actor_system_options.go deleted file mode 100644 index d932beba..00000000 --- a/vivid/vivids/actor_system_options.go +++ /dev/null @@ -1,134 +0,0 @@ -package vivids - -import ( - "github.com/panjf2000/ants/v2" - "runtime" - "time" -) - -// NewActorSystemOptions 用于创建一个 ActorSystemOptions -func NewActorSystemOptions() *ActorSystemOptions { - return (&ActorSystemOptions{ - Dispatchers: make(map[string]Dispatcher), - }). - WithNetwork("tcp"). - WithAskDefaultTimeout(time.Second * 2). - WithAntsPoolSize(ants.DefaultAntsPoolSize). - WithAntsOptions(ants.WithOptions(ants.Options{ - ExpiryDuration: time.Minute, - Nonblocking: true, - })). - WithRemoteProcessorNum(uint(runtime.NumCPU())) -} - -// ActorSystemOptions 是 ActorSystem 的配置选项 -type ActorSystemOptions struct { - ClusterName string // 集群名称 - Network string // 网络协议 - Host string // 主机地址 - Port uint16 // 端口 - Server Server // 远程调用服务端 - ClientFactory ClientFactory // 远程调用客户端 - Dispatchers map[string]Dispatcher // 消息分发器 - AskDefaultTimeout time.Duration // 默认的 Ask 超时时间 - AntsOptions []ants.Option // ants.Pool 的配置选项 - AntsPoolSize int // ants.Pool 的大小 - RemoteProcessorNum uint // 远程消息处理器数量 -} - -// Apply 用于应用配置选项 -func (o *ActorSystemOptions) Apply(options ...*ActorSystemOptions) *ActorSystemOptions { - for _, option := range options { - if option.ClusterName != "" { - o.ClusterName = option.ClusterName - } - if option.Network != "" { - o.Network = option.Network - } - if option.Host != "" { - o.Host = option.Host - } - if option.Port != 0 { - o.Port = option.Port - } - if option.Server != nil { - o.Server = option.Server - } - if option.ClientFactory != nil { - o.ClientFactory = option.ClientFactory - } - for r, d := range option.Dispatchers { - o.Dispatchers[r] = d - } - if option.AskDefaultTimeout != 0 { - o.AskDefaultTimeout = option.AskDefaultTimeout - } - if len(option.AntsOptions) > 0 { - o.AntsOptions = append(o.AntsOptions, option.AntsOptions...) - } - if option.AntsPoolSize != 0 { - o.AntsPoolSize = option.AntsPoolSize - } - if option.RemoteProcessorNum != 0 { - o.RemoteProcessorNum = option.RemoteProcessorNum - } - } - return o -} - -// WithRemoteProcessorNum 设置远程消息处理器数量 -func (o *ActorSystemOptions) WithRemoteProcessorNum(num uint) *ActorSystemOptions { - o.RemoteProcessorNum = num - return o -} - -// WithAntsPoolSize 设置 ants.Pool 的大小 -func (o *ActorSystemOptions) WithAntsPoolSize(size int) *ActorSystemOptions { - o.AntsPoolSize = size - return o -} - -// WithAntsOptions 设置 ants.Pool 的配置选项 -func (o *ActorSystemOptions) WithAntsOptions(options ...ants.Option) *ActorSystemOptions { - o.AntsOptions = append(o.AntsOptions, options...) - return o -} - -// WithNetwork 设置网络协议 -func (o *ActorSystemOptions) WithNetwork(network string) *ActorSystemOptions { - o.Network = network - return o -} - -// WithCluster 设置集群信息,该可选项用于 ActorSystem 的集群配置 -// - 该选项将会覆盖 WithAddress 的配置 -func (o *ActorSystemOptions) WithCluster(Server Server, clusterName, host string, port uint16) *ActorSystemOptions { - o.ClusterName = clusterName - return o.WithAddress(Server, host, port) -} - -// WithAddress 设置主机地址 -func (o *ActorSystemOptions) WithAddress(Server Server, host string, port uint16) *ActorSystemOptions { - o.Host = host - o.Port = port - o.Server = Server - return o -} - -// WithClientFactory 设置远程调用客户端工厂 -func (o *ActorSystemOptions) WithClientFactory(ClientFactory ClientFactory) *ActorSystemOptions { - o.ClientFactory = ClientFactory - return o -} - -// WithDispatcher 绑定消息分发器 -func (o *ActorSystemOptions) WithDispatcher(name string, dispatcher Dispatcher) *ActorSystemOptions { - o.Dispatchers[name] = dispatcher - return o -} - -// WithAskDefaultTimeout 设置默认的 Ask 超时时间 -func (o *ActorSystemOptions) WithAskDefaultTimeout(timeout time.Duration) *ActorSystemOptions { - o.AskDefaultTimeout = timeout - return o -} diff --git a/vivid/vivids/actor_terminated_context.go b/vivid/vivids/actor_terminated_context.go deleted file mode 100644 index 37624833..00000000 --- a/vivid/vivids/actor_terminated_context.go +++ /dev/null @@ -1,28 +0,0 @@ -package vivids - -// ActorTerminatedContext 是 Actor 销毁时的上下文 -type ActorTerminatedContext interface { - // GetActorId 获取 Actor 的 ID - GetActorId() ActorId - - // GetActor 获取 Actor 原始对象 - // - 常被用于类型断言进行不同 Actor 类型的处理 - GetActor() any - - // HasTerminatedMessage 判断是否有销毁消息 - HasTerminatedMessage() bool - - // GetTerminatedMessage 获取销毁消息 - GetTerminatedMessage() Message - - // Restart 以全新的状态重启 Actor,包括所有的子 Actor - // - 该函数将会一次执行 Actor.OnSaveSnapshot、 Actor.OnDestroy、 Actor.OnPreStart 三个函数来完成重启 - // - 当重启过程中发生错误时将会通过 Actor.OnRecoverSnapshot 函数来恢复 Actor 的状态 - Restart() error - - // Recover 保留当前的状态恢复 Actor - Recover() error - - // GetRestartNum 获取重启次数,该值将在重启成功后清零 - GetRestartNum() int -} diff --git a/vivid/vivids/basic_actor.go b/vivid/vivids/basic_actor.go deleted file mode 100644 index 7805fc20..00000000 --- a/vivid/vivids/basic_actor.go +++ /dev/null @@ -1,32 +0,0 @@ -package vivids - -// BasicActor 是 Actor 的基础实现,该实现仅提供了一个空的 Actor -type BasicActor struct{} - -func (b *BasicActor) OnPreStart(ctx ActorContext) error { - return nil -} - -func (b *BasicActor) OnReceived(ctx MessageContext) error { - return nil -} - -func (b *BasicActor) OnDestroy(ctx ActorContext) error { - return nil -} - -func (b *BasicActor) OnChildTerminated(ctx ActorContext, child ActorTerminatedContext) { - -} - -func (b *BasicActor) OnSaveSnapshot(ctx ActorContext) (snapshot []byte, err error) { - return -} - -func (b *BasicActor) OnRecoverSnapshot(ctx ActorContext, snapshot []byte) (err error) { - return -} - -func (b *BasicActor) OnEvent(ctx ActorContext, event Message) (err error) { - return -} diff --git a/vivid/vivids/client.go b/vivid/vivids/client.go deleted file mode 100644 index ccc8a8ff..00000000 --- a/vivid/vivids/client.go +++ /dev/null @@ -1,14 +0,0 @@ -package vivids - -// ClientFactory 是 ActorSystem 远程调用的客户端工厂 -type ClientFactory func(system ActorSystem, network, host string, port uint16) (Client, error) - -func (f ClientFactory) NewClient(system ActorSystem, network, host string, port uint16) (Client, error) { - return f(system, network, host, port) -} - -// Client 是 ActorSystem 远程调用的客户端 -type Client interface { - // Exec 执行远程调用 - Exec(data []byte) error -} diff --git a/vivid/vivids/dispatcher.go b/vivid/vivids/dispatcher.go deleted file mode 100644 index 56d818ce..00000000 --- a/vivid/vivids/dispatcher.go +++ /dev/null @@ -1,19 +0,0 @@ -package vivids - -// Dispatcher 消息调度器接口 -type Dispatcher interface { - // OnInit 用于初始化调度器 - OnInit(system ActorSystemExternal) - - // Attach 用于将一个 Actor 添加到调度器中 - Attach(actor ActorCore) error - - // Detach 用于将一个 Actor 从调度器中移除 - Detach(actor ActorCore) error - - // Send 用于向一个 Actor 发送消息 - Send(receiver ActorCore, msg MessageContext) error - - // Stop 用于停止调度器 - Stop() -} diff --git a/vivid/vivids/errors.go b/vivid/vivids/errors.go deleted file mode 100644 index a300c908..00000000 --- a/vivid/vivids/errors.go +++ /dev/null @@ -1,16 +0,0 @@ -package vivids - -import "errors" - -var ( - ErrActorIdInvalid = errors.New("actor id invalid") - ErrActorBehaviorInvalid = errors.New("actor behavior invalid") - ErrActorNotImplementActorRef = errors.New("actor not implement ActorRef") - ErrActorAlreadyExists = errors.New("actor already exists") - ErrActorNotFound = errors.New("actor not found") - ErrReplyTimeout = errors.New("actor reply timeout") - ErrActorNotUnique = errors.New("actor not unique") - ErrActorTerminated = errors.New("actor terminated or not exists") - ErrActorPreStart = errors.New("actor pre start error") - ErrDispatcherNotFound = errors.New("dispatcher not found") -) diff --git a/vivid/vivids/event.go b/vivid/vivids/event.go deleted file mode 100644 index e1e5d943..00000000 --- a/vivid/vivids/event.go +++ /dev/null @@ -1,3 +0,0 @@ -package vivids - -type Event = Message diff --git a/vivid/vivids/future.go b/vivid/vivids/future.go deleted file mode 100644 index b670075e..00000000 --- a/vivid/vivids/future.go +++ /dev/null @@ -1,11 +0,0 @@ -package vivids - -type Receiver interface { - Tell(v Message, opts ...MessageOption) error - Ask(v Message, opts ...MessageOption) (any, error) -} - -type Future interface { - // IsCompleted 判断 Future 是否已完成 - IsCompleted() bool -} diff --git a/vivid/vivids/mailbox.go b/vivid/vivids/mailbox.go deleted file mode 100644 index 12cfb890..00000000 --- a/vivid/vivids/mailbox.go +++ /dev/null @@ -1,7 +0,0 @@ -package vivids - -import "github.com/kercylan98/minotaur/toolkit/queues" - -type Mailbox interface { - queues.Queue[MessageContext] -} diff --git a/vivid/vivids/message.go b/vivid/vivids/message.go deleted file mode 100644 index d6b0dd30..00000000 --- a/vivid/vivids/message.go +++ /dev/null @@ -1,4 +0,0 @@ -package vivids - -// Message 在 Actor 之间传递的消息,消息可为任意可序列化类型 -type Message = any diff --git a/vivid/vivids/message_context.go b/vivid/vivids/message_context.go deleted file mode 100644 index 8b7b7e45..00000000 --- a/vivid/vivids/message_context.go +++ /dev/null @@ -1,20 +0,0 @@ -package vivids - -type MessageContext interface { - ActorContext - - // GetSeq 用于获取消息序号 - GetSeq() uint64 - - // Reply 用于回复消息 - Reply(msg Message) error - - // GetSenderId 用于获取消息发送者 - GetSenderId() ActorId - - // GetReceiverId 用于获取消息接收者 - GetReceiverId() ActorId - - // GetMessage 用于获取消息 - GetMessage() Message -} diff --git a/vivid/vivids/message_options.go b/vivid/vivids/message_options.go deleted file mode 100644 index 42758179..00000000 --- a/vivid/vivids/message_options.go +++ /dev/null @@ -1,39 +0,0 @@ -package vivids - -import "time" - -type MessageOption func(opts *MessageOptions) - -type MessageOptions struct { - ReplyTimeout time.Duration // 回复超时时间 - SenderId ActorId // 显式发送者 -} - -// WithMessageOptions 设置消息选项 -func WithMessageOptions(options *MessageOptions) MessageOption { - return func(opts *MessageOptions) { - opts.SenderId = options.SenderId - opts.ReplyTimeout = options.ReplyTimeout - } -} - -func (o *MessageOptions) Apply(options ...MessageOption) *MessageOptions { - for _, option := range options { - option(o) - } - return o -} - -// WithMessageSender 设置发送者 -func WithMessageSender(sender ActorContext) MessageOption { - return func(opts *MessageOptions) { - opts.SenderId = sender.GetActorId() - } -} - -// WithMessageReply 设置是否需要回复,当超时时间 <= 0 时,表示不需要回复 -func WithMessageReply(timeout time.Duration) MessageOption { - return func(opts *MessageOptions) { - opts.ReplyTimeout = timeout - } -} diff --git a/vivid/vivids/query.go b/vivid/vivids/query.go deleted file mode 100644 index 4f4351f6..00000000 --- a/vivid/vivids/query.go +++ /dev/null @@ -1,29 +0,0 @@ -package vivids - -type Query interface { - // ActorId 使用特定的 ActorId,该查询可能包含不存在的 ActorRef - ActorId(actorIds ...ActorId) Query - - // MustActorId 使用特定的 ActorId,该查询包含必须存在的 ActorRef - MustActorId(actorIds ...ActorId) Query - - // ActorName 通过 ActorName 进行查询匹配 - ActorName(names ...ActorName) Query - - // ActorPath 通过 ActorPath 进行查询匹配,支持格式如下: - // - "./user/actor1" - // - "/user/actor1" - // - "user/actor1" - // - "actor1" - // - "actor1/actor2/*" - ActorPath(actorPath ActorPath) Query - - // Many 获取多个响应结果 - Many() []ActorRef - - // First 获取第一个响应结果 - First() (ActorRef, error) - - // One 获取唯一的响应结果 - One() (ActorRef, error) -} diff --git a/vivid/vivids/server.go b/vivid/vivids/server.go deleted file mode 100644 index f453a94e..00000000 --- a/vivid/vivids/server.go +++ /dev/null @@ -1,8 +0,0 @@ -package vivids - -// Server 是 ActorSystem 用于接收远程调用的服务端 -// - 该服务端仅需提供一个可用于接收远程消息的通道即可 -type Server interface { - // C 用于获取一个用于接收远程消息的通道 - C() <-chan []byte -} diff --git a/vivid2/actor.go b/vivid2/actor.go deleted file mode 100644 index 9a2a35f5..00000000 --- a/vivid2/actor.go +++ /dev/null @@ -1,5 +0,0 @@ -package vivid - -type Actor interface { - OnReceive(ctx MessageContext) -} diff --git a/vivid2/actor_context.go b/vivid2/actor_context.go deleted file mode 100644 index 50c4810f..00000000 --- a/vivid2/actor_context.go +++ /dev/null @@ -1,27 +0,0 @@ -package vivid - -type ActorContext interface { - internalActorContext - // GetId 获取当前 ActorContext 的 ID - GetId() ActorId - - // GetSystem 获取当前 ActorContext 所属的 ActorSystem - GetSystem() *ActorSystem -} - -type _ActorContext struct { - *_internalActorContext - *_ActorCore -} - -func (c *_ActorContext) actorOf(actor Actor, opt any) ActorRef { - return nil -} - -func (c *_ActorContext) GetId() ActorId { - return c.id -} - -func (c *_ActorContext) GetSystem() *ActorSystem { - return c.system -} diff --git a/vivid2/actor_ref.go b/vivid2/actor_ref.go deleted file mode 100644 index 983d53b4..00000000 --- a/vivid2/actor_ref.go +++ /dev/null @@ -1,57 +0,0 @@ -package vivid - -type ActorRef interface { - Tell(msg Message, opts ...MessageOption) - Ask(msg Message, opts ...MessageOption) Message -} - -// _LocalActorRef 本地 Actor 引用 -type _LocalActorRef struct { - core *_ActorCore // Actor 核心 -} - -func (r *_LocalActorRef) Tell(msg Message, opts ...MessageOption) { - r.core.system.sendMessage(r.core._LocalActorRef, msg, opts...) -} - -func (r *_LocalActorRef) Ask(msg Message, opts ...MessageOption) Message { - return r.core.system.sendMessage(r.core._LocalActorRef, msg, append(opts, func(options *MessageOptions) { - options.reply = true - })...) -} - -// _RemoteActorRef 远程 Actor 引用 -type _RemoteActorRef struct { - system *ActorSystem // Actor 系统 - actorId ActorId // 远程 Actor ID -} - -func (r *_RemoteActorRef) Tell(msg Message, opts ...MessageOption) { - r.system.sendMessage(r, msg, opts...) -} - -func (r *_RemoteActorRef) Ask(msg Message, opts ...MessageOption) Message { - return r.system.sendMessage(r, msg, append(opts, func(options *MessageOptions) { - options.reply = true - })...) -} - -// _DeadLetterActorRef 死信 Actor 引用 -type _DeadLetterActorRef struct { - system *ActorSystem // Actor 系统 -} - -func (r *_DeadLetterActorRef) Tell(msg Message, opts ...MessageOption) { - r.system.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ - Error: ErrActorDeadOrNotExist, - Message: msg, - })) -} - -func (r *_DeadLetterActorRef) Ask(msg Message, opts ...MessageOption) Message { - r.system.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ - Error: ErrActorDeadOrNotExist, - Message: msg, - })) - return nil -} diff --git a/vivid2/actor_system.go b/vivid2/actor_system.go deleted file mode 100644 index fb9c9f56..00000000 --- a/vivid2/actor_system.go +++ /dev/null @@ -1,329 +0,0 @@ -package vivid - -import ( - "context" - "fmt" - "github.com/kercylan98/minotaur/toolkit/log" - "reflect" - "sync" - "sync/atomic" - "time" -) - -func NewActorSystem(name string) ActorSystem { - s := ActorSystem{ - dispatchers: make(map[DispatcherId]Dispatcher), - dispatcherRW: new(sync.RWMutex), - mailboxFactors: make(map[MailboxFactoryId]MailboxFactory), - mailboxFactorRW: new(sync.RWMutex), - actors: make(map[ActorId]*_ActorCore), - actorRW: new(sync.RWMutex), - deadLetters: new(_DeadLetterStream), - codec: new(_GobCodec), - askWaits: make(map[uint64]chan<- Message), - askWaitsLock: new(sync.RWMutex), - messageSeq: new(atomic.Uint64), - waitGroup: new(sync.WaitGroup), - name: name, - } - s.ctx, s.cancel = context.WithCancel(context.Background()) - s.BindDispatcher(new(_Dispatcher)) // default dispatcher - s.BindMailboxFactory(NewFIFOFactory(func(message MessageContext) { - // received message - core := message.GetReceiver().(*_ActorCore) - defer core.group.Done() - core.OnReceive(message) - })) - var err error - s.userGuard, err = generateActor(&s, new(UserGuardActor), parseActorOptions(NewActorOptions[*UserGuardActor]().WithName("user"))) - if err != nil { - panic(err) - } - return s -} - -type ActorSystem struct { - ctx context.Context - cancel context.CancelFunc - dispatchers map[DispatcherId]Dispatcher - dispatcherGuid DispatcherId - dispatcherRW *sync.RWMutex - mailboxFactors map[MailboxFactoryId]MailboxFactory // mailbox factory - mailboxFactorGuid MailboxFactoryId - mailboxFactorRW *sync.RWMutex - actors map[ActorId]*_ActorCore // actor id -> actor core - actorRW *sync.RWMutex - deadLetters *_DeadLetterStream // 死信队列 - userGuard *_ActorCore // 用户使用的顶级 Actor - codec Codec - server Server - client Client - messageSeq *atomic.Uint64 - askWaits map[uint64]chan<- Message - askWaitsLock *sync.RWMutex - waitGroup *sync.WaitGroup - - name string - network string - host string - port uint16 - cluster string -} - -func (s *ActorSystem) Shutdown() { - defer s.cancel() - s.unbindActor(s.userGuard) - s.waitGroup.Wait() -} - -func (s *ActorSystem) getSystem() *ActorSystem { - return s -} - -func (s *ActorSystem) GetDeadLetters() DeadLetterStream { - return s.deadLetters -} - -type actorOf interface { - getSystem() *ActorSystem -} - -func (s *ActorSystem) BindMailboxFactory(f MailboxFactory) MailboxFactoryId { - s.mailboxFactorRW.Lock() - defer s.mailboxFactorRW.Unlock() - - s.mailboxFactorGuid++ - s.mailboxFactors[s.mailboxFactorGuid] = f - - return s.mailboxFactorGuid -} - -func (s *ActorSystem) UnbindMailboxFactory(id MailboxFactoryId) { - if id == DefaultMailboxFactoryId { - return - } - s.mailboxFactorRW.Lock() - defer s.mailboxFactorRW.Unlock() - - delete(s.mailboxFactors, id) -} - -func (s *ActorSystem) getMailboxFactory(id MailboxFactoryId) MailboxFactory { - s.mailboxFactorRW.RLock() - defer s.mailboxFactorRW.RUnlock() - - return s.mailboxFactors[id] -} - -func (s *ActorSystem) getDispatcher(id DispatcherId) Dispatcher { - s.dispatcherRW.RLock() - defer s.dispatcherRW.RUnlock() - - return s.dispatchers[id] -} - -func (s *ActorSystem) BindDispatcher(d Dispatcher) DispatcherId { - s.dispatcherRW.Lock() - defer s.dispatcherRW.Unlock() - - s.dispatcherGuid++ - s.dispatchers[s.dispatcherGuid] = d - - return s.dispatcherGuid -} - -func (s *ActorSystem) UnbindDispatcher(id DispatcherId) { - if id == DefaultDispatcherId { - return - } - s.dispatcherRW.Lock() - defer s.dispatcherRW.Unlock() - - delete(s.dispatchers, id) -} - -func (s *ActorSystem) unbindActor(actor ActorContext) { - actor.getLockable().RLock() - var children = actor.getChildren() - for _, child := range children { - s.unbindActor(child) - } - actor.getLockable().RUnlock() - - core := actor.(*_ActorCore) - core.group.Wait() - core.dispatcher.Detach(core) - - s.actorRW.Lock() - delete(s.actors, core.GetId()) - s.actorRW.Unlock() - - s.waitGroup.Done() - log.Debug("actor unbind", log.String("actor", core.GetId().String())) -} - -func (s *ActorSystem) sendMessage(receiver ActorRef, message Message, options ...MessageOption) Message { - var opts = new(MessageOptions).apply(options) - var seq = s.messageSeq.Add(1) - var from, to ActorId - - switch ref := receiver.(type) { - case *_LocalActorRef: - if opts.replySeq > 0 { - s.askWaitsLock.RLock() - wait, exist := s.askWaits[opts.replySeq] - s.askWaitsLock.RUnlock() - if !exist { - return nil - } - wait <- message - return nil - } - - ctx := &_LocalMessageContext{ - ActorContext: ref.core, - message: message, - seq: seq, - network: s.network, - host: s.host, - port: s.port, - } - if opts.Sender != nil { - ctx.sender = opts.Sender - from = opts.Sender.(*_LocalActorRef).core.GetId() - } - to = ref.core.GetId() - - ref.core.group.Add(1) - ref.core.dispatcher.Send(ref.core, ctx) - case *_RemoteActorRef: - ctx := &_RemoteMessageContext{ - system: s, - ReceiverId: ref.actorId, - Message: message, - Seq: seq, - ReplySeq: opts.replySeq, - } - if opts.Sender != nil { - ctx.SenderId = opts.Sender.(*_LocalActorRef).core.GetId() - } - from = ctx.SenderId - to = ctx.ReceiverId - - data, err := s.codec.Encode(ctx) - if err != nil { - s.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ - Error: err, - From: from, - To: to, - Seq: seq, - })) - return nil - } - - // send data to remote actor - if err = s.client.Exec(data); err != nil { - s.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ - Error: err, - From: from, - To: to, - Seq: seq, - })) - } - default: - panic(fmt.Errorf("unsupported actor ref type: %s", reflect.TypeOf(receiver).String())) - } - - if opts.reply && opts.replySeq == 0 { - if opts.ReplyTimeout == 0 { - opts.ReplyTimeout = time.Second - } - - waitChan := make(chan Message) - ctx, cancel := context.WithTimeout(s.ctx, opts.ReplyTimeout) - - defer func(s *ActorSystem, seq uint64, cancel context.CancelFunc, wait chan Message) { - cancel() - close(wait) - s.askWaitsLock.Lock() - delete(s.askWaits, seq) - s.askWaitsLock.Unlock() - }(s, seq, cancel, waitChan) - - s.askWaitsLock.Lock() - s.askWaits[seq] = waitChan - s.askWaitsLock.Unlock() - - // wait for reply - select { - case <-ctx.Done(): - s.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ - Error: ErrMessageReplyTimeout, - From: from, - To: to, - Seq: seq, - })) - case reply := <-waitChan: - return reply - } - } - - return nil -} - -func (s *ActorSystem) onProcessServerMessage() { - for bytes := range s.server.C() { - var remoteCtx = new(_RemoteMessageContext) - if err := s.codec.Decode(bytes, remoteCtx); err != nil { - s.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ - Error: err, - })) - continue - } - - s.actorRW.RLock() - core, exist := s.actors[remoteCtx.ReceiverId] - s.actorRW.RUnlock() - if !exist { - s.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ - Error: fmt.Errorf("%w: %s", ErrActorDeadOrNotExist, remoteCtx.ReceiverId), - From: remoteCtx.SenderId, - To: remoteCtx.ReceiverId, - })) - continue - - } - - if remoteCtx.ReplySeq > 0 { - s.askWaitsLock.RLock() - wait, existWait := s.askWaits[remoteCtx.ReplySeq] - s.askWaitsLock.RUnlock() - if existWait { - wait <- remoteCtx.Message - continue - } - - s.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeMessage, DeadLetterEventMessage{ - Error: fmt.Errorf("%w: %d", ErrAskWaitNotExist, remoteCtx.ReplySeq), - From: remoteCtx.SenderId, - To: remoteCtx.ReceiverId, - })) - continue - } - - localCtx := &_LocalMessageContext{ - ActorContext: core, - sender: &_RemoteActorRef{ - system: s, - actorId: remoteCtx.SenderId, - }, - message: remoteCtx.Message, - network: remoteCtx.Network, - host: remoteCtx.Host, - port: remoteCtx.Port, - } - - core.group.Add(1) - core.dispatcher.Send(core, localCtx) - } -} diff --git a/vivid2/message_context.go b/vivid2/message_context.go deleted file mode 100644 index 199346bf..00000000 --- a/vivid2/message_context.go +++ /dev/null @@ -1,80 +0,0 @@ -package vivid - -type MessageContext interface { - ActorContext - - // GetReceiver 获取消息的接收者 - GetReceiver() ActorRef - - // GetMessage 获取消息内容 - GetMessage() Message - - // Reply 回复消息 - Reply(Message) -} - -// _LocalMessageContext 本地消息上下文 -type _LocalMessageContext struct { - ActorContext // 接收者的上下文 - sender ActorRef - message Message - seq uint64 - network string - host string - port uint16 -} - -func (c *_LocalMessageContext) GetReceiver() ActorRef { - return c.ActorContext.(*_ActorCore) -} - -func (c *_LocalMessageContext) GetMessage() Message { - return c.message -} - -func (c *_LocalMessageContext) Reply(message Message) { - if c.sender == nil { - system := c.ActorContext.GetSystem() - if c.network == system.network && c.host == system.host && c.port == system.port { - system.askWaitsLock.RLock() - wait, exist := system.askWaits[c.seq] - system.askWaitsLock.RUnlock() - if !exist { - return - } - wait <- message - return - } - } - c.ActorContext.(*_ActorCore).system.sendMessage(c.sender, message, func(options *MessageOptions) { - options.replySeq = c.seq - }) -} - -// _RemoteMessageContext 远程消息上下文 -type _RemoteMessageContext struct { - system *ActorSystem // Actor 系统 - ReceiverId ActorId // 接收者 ID - SenderId ActorId // 发送者 ID - Message Message // 消息内容 - Seq uint64 // 消息序号 - ReplySeq uint64 // 回复消息序号 - Network string // 网络地址 - Host string // 主机地址 - Port uint16 // 端口 -} - -func (c *_RemoteMessageContext) GetReceiver() ActorRef { - return &_RemoteActorRef{ - system: c.system, - actorId: c.ReceiverId, - } -} - -func (c *_RemoteMessageContext) GetMessage() Message { - return c.Message -} - -func (c *_RemoteMessageContext) Reply(message Message) { - panic("not implemented") -} diff --git a/vivid2/test/main.go b/vivid2/test/main.go deleted file mode 100644 index 1cc379e8..00000000 --- a/vivid2/test/main.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "fmt" - vivid "github.com/kercylan98/minotaur/vivid2" -) - -type TestActor struct { -} - -func (t *TestActor) OnReceive(ctx vivid.MessageContext) { - switch v := ctx.GetMessage().(type) { - case string: - fmt.Println(v) - case int: - ctx.Reply(v + 1) - } -} - -func main() { - system := vivid.NewActorSystem("test-system") - - actorRef := vivid.ActorOf[*TestActor](&system) - - actorRef.Tell("Hello, World!") - - fmt.Println(actorRef.Ask(1)) - - system.Shutdown() -} diff --git a/vivid2/vivid.go b/vivid2/vivid.go deleted file mode 100644 index b90aef42..00000000 --- a/vivid2/vivid.go +++ /dev/null @@ -1,100 +0,0 @@ -package vivid - -import ( - "fmt" - "github.com/google/uuid" - "github.com/kercylan98/minotaur/toolkit/charproc" - "path" - "reflect" - "sync" -) - -func ActorOf[T Actor](actorOf actorOf, options ...*ActorOptions[T]) ActorRef { - var opts = parseActorOptions(options...) - var tof = reflect.TypeOf((*T)(nil)).Elem() - var ins = reflect.New(tof).Elem().Interface().(T) - var system = actorOf.getSystem() - - if opts.Parent == nil { - opts.Parent = system.userGuard - } - - ctx, err := generateActor[T](actorOf.getSystem(), ins, opts) - if err != nil { - system.deadLetters.DeadLetter(NewDeadLetterEvent(DeadLetterEventTypeActorOf, DeadLetterEventActorOf{ - Error: err, - Parent: opts.Parent, - Name: opts.Name, - })) - } - if ctx == nil { - return &_DeadLetterActorRef{ - system: system, - } - } - return ctx -} - -func generateActor[T Actor](system *ActorSystem, actor T, options *ActorOptions[T]) (*_ActorCore, error) { - if options.Name == charproc.None { - options.Name = uuid.NewString() - } - - var actorPath = options.Name - if options.Parent != nil { - actorPath = path.Join(options.Parent.GetId().Path(), options.Name) - } else { - actorPath = path.Clean(options.Name) - } - - // 绝大多数情况均会成功,提前创建资源,减少锁粒度 - var actorId = NewActorId(system.network, system.cluster, system.host, system.port, system.name, actorPath) - var core = newActorCore(system, actorId, actor, options) - - // 检查是否重名 - var parentLock *sync.RWMutex - if options.Parent != nil { - parentLock = options.Parent.getLockable() - parentLock.Lock() - if options.Parent.hasChild(options.Name) { - parentLock.Unlock() - return nil, fmt.Errorf("%w: %s", ErrActorAlreadyExists, options.Name) - } - } - - // 绑定分发器 - core.dispatcher = system.getDispatcher(options.DispatcherId) - if core.dispatcher == nil { - if parentLock != nil { - parentLock.Unlock() - } - return nil, fmt.Errorf("%w: %d", ErrDispatcherNotFound, options.DispatcherId) - } - - // 绑定邮箱 - core.mailboxFactory = system.getMailboxFactory(options.MailboxFactoryId) - if core.mailboxFactory == nil { - if parentLock != nil { - parentLock.Unlock() - } - return nil, fmt.Errorf("%w: %d", ErrMailboxFactoryNotFound, options.MailboxFactoryId) - } - - // 绑定父 Actor 并注册到系统 - system.actorRW.Lock() - if options.Parent != nil { - options.Parent.bindChild(options.Name, core) - } - system.actors[actorId] = core - system.actorRW.Unlock() - if parentLock != nil { - parentLock.Unlock() - } - - // 启动 Actor - system.waitGroup.Add(1) - core.dispatcher.Attach(core) - core.Tell(OnPreStart{}) - - return core, nil -}