在Golang中實現Actor模型的原始碼 - Gaurav
Actor模型是一種這樣的程式設計結構,它對大量獨立作業進行建模,以任何順序進行處理,無需鎖同步。如Java中Play!框架。
在本文中,我將描述如何在 golang 中實現一個原始的 Actor 模型。我們將利用 golang 提供的工具進行併發處理——goroutine、通道和等待組。
Actor 有一個任務佇列和一個監聽任務佇列並執行任務的 goroutine。
這裡 A 是一個阻塞任務佇列並繼續執行佇列中的任務的 goroutine。Actor 的介面如下所示:
type Actor interface { AddTask(task Task) Start() Stop() } |
task在actor中執行。它是具有Execute 方法的給定介面的實現。任何可以通過呼叫 Execute 來執行的東西。
task是我們需要做的工作的業務實現。
actor系統介面:
type ActorSystem interface { Run() SubmitTask(task Task) Shutdown(shutdownWG *sync.WaitGroup) } |
task使用SubmitTask方法提交給ActorSystem。一個任務分配器將每個任務task分配給一個行為體。每個行動者也有一個小佇列,在其中緩衝任務task並逐一執行。
ActorSystem
type ActorSystem struct { name string assigner entities.Actor wg *sync.WaitGroup tracker *tracker.Tracker } func (system *ActorSystem) Run() { log.Debug("actor system %s started \n", system.name) // start the assigner in seprate go routine go system.assigner.Start() } func (system *ActorSystem) SubmitTask(task entities.Task) error { // adding submitted task to assigner return system.assigner.AddTask(task) } func (system *ActorSystem) Shutdown(wg *sync.WaitGroup) { defer wg.Done() system.assigner.Stop() system.wg.Wait() system.tracker.Shutdown() log.Debug("actor system: %s shutdown completed ", system.name) } |
當ActorSystem啟動時,它啟動一個taskAssigner actor。通過在這個actor上呼叫 AddTask 方法,將每個傳入Task系統的資料新增到 taskAssigner actor。
task使用SubmitTask方法提交給ActorSystem。我們通過呼叫AddTask方法將每個收到的任務放到taskAssigner中。
在Shutdown 時,它關閉任務通道,阻止任何新進入的任務,等待所有收到的任務被分配給行為者。然後,它對每個actor呼叫 "停止",並等待它們完成。
Task Assigner
我們把每個傳入的任務放在一個通道tasks中,taskAssigner和Task在一個Actor的內部佇列中。
type AssignerActor struct { name string closeSig chan bool tasks chan entities.Task assignerIndex int tracker *tracker.Tracker scalar *autoScalar *TaskActorPool *Config } func (assigner *AssignerActor) AddTask(task entities.Task) error{ if len(assigner.tasks) >= assignerQueueSize { assigner.tracker.GetTrackerChan() <- tracker.CreateCounterTrack(tracker.Task, tracker.Rejected) return errors.New("task queue is full") } // task getting added to assigner actor channel assigner.tasks <- task assigner.tracker.GetTrackerChan() <- tracker.CreateCounterTrack(tracker.Task, tracker.Submitted) return nil } |
taskAssigner內部處理任務通道,並通過對其呼叫AddTask,將任務路由到池中的一個任務執行者。
func (assigner *AssignerActor) Start() { poolStarted := make(chan bool) assigner.scalar = GetAutoScaler(assigner, poolStarted) <- poolStarted // will loop forever till tasks channel is closed for task := range assigner.tasks { for { assigner.poolLock.Lock() assigner.assignerIndex = assigner.assignerIndex % len(assigner.pool) actor := assigner.pool[assigner.assignerIndex] assigner.assignerIndex += 1 assigner.poolLock.Unlock() // assigning task to a task actor from pool err := actor.AddTask(task) if err == nil { break } } } assigner.closeSig <- true } |
autoScalar持續關注任務中的專案數量,並增加或減少任務actor池的大小。
// auto scalar is part of task assigner actor // It scales task actor pool based on queue len size type autoScalar struct { *AssignerActor lastActorId int closingSig chan bool closedSig chan bool } func(scalar *autoScalar) run(poolStarted chan bool) { log.Debug("running auto scalar with min actor") // provision starting actors scalar.provisionActors(scalar.Config.MinActor) // waiting for scalar to start task actors poolStarted <- true completed := false // loops till it gets a closing signal from task assigner for !completed { select { case <- scalar.closingSig: completed = true case <-time.After(100 * time.Millisecond): if scalar.QueueSize() > scalar.UpscaleQueueSize && len(scalar.pool) < scalar.MaxActor { scalar.provisionActors(1) } else if scalar.QueueSize() < scalar.DownscaleQueueSize && len(scalar.pool) > scalar.MinActor { scalar.deprovisionActors(1) } } } // when it comes out, it closes all task actors in pool scalar.deprovisionActors(len(scalar.pool)) log.Debug("scalar exited") scalar.closedSig <- true } |
Task Actor
它也是一個Actor,它的工作是執行任務,這些任務被新增到它的通道任務中,類似於分配者assigner的Actor。
type TaskActor struct { id int closeSig chan bool wg *sync.WaitGroup tasks chan entities.Task tracker *tracker.Tracker } // add task only if channel has space func (a *TaskActor) AddTask(task entities.Task) error { if len(a.tasks) >= taskQueueSize { return errors.New("filled queue") } // task added to channel a.tasks <- task return nil } func (a *TaskActor) Start() { defer a.wg.Done() a.wg.Add(1) log.Debug("starting actor :%d", a.id) // forever loop on tasks channel till channel is closed for task := range a.tasks{ task.Execute() a.tracker.GetTrackerChan() <- tracker.CreateCounterTrack(tracker.Task, tracker.Completed) } log.Debug("stopped actor :%d", a.id) a.closeSig <- true } func (a *TaskActor) Stop() { // closing task channel close (a.tasks) <- a.closeSig } |
原始碼:Github
相關文章
- Lru在Rust中的實現, 原始碼解析Rust原始碼
- PHP下用Swoole實現Actor併發模型PHP模型
- Golang的GMP排程模型與原始碼解析Golang模型原始碼
- Golang網路模型netpoll原始碼解析Golang模型原始碼
- Percolator模型及其在TiKV中的實現模型
- Lfu快取在Rust中的實現及原始碼解析快取Rust原始碼
- Lru-k在Rust中的實現及原始碼解析Rust原始碼
- golang 中,對稱加密的程式碼實現Golang加密
- 帶有ttl的Lru在Rust中的實現及原始碼解析Rust原始碼
- Lite Actor:方舟Actor併發模型的輕量級優化模型優化
- 解讀vue-server-renderer原始碼並在react中的實現VueServer原始碼React
- redis個人原始碼分析1----hyperloglog(golang實現)Redis原始碼Golang
- 原始碼解析.Net中DependencyInjection的實現原始碼
- 原始碼解析.Net中Middleware的實現原始碼
- 在 Golang 中實現一個簡單的Http中介軟體GolangHTTP
- 原始碼解析.Net中IConfiguration配置的實現原始碼
- 在相親原始碼的多人音視訊聊天中插入現場直播的實現方式原始碼
- 原始碼剖析 golang 中 sync.Mutex原始碼GolangMutex
- golang 中 sync.Mutex 的實現GolangMutex
- 短視訊原始碼,在Android 中opengl es實現燈光效果原始碼Android
- 在關卡藍圖中獲得actor的資訊
- JDK中的BitMap實現之BitSet原始碼分析JDK原始碼
- Netty 原始碼中對 Redis 協議的實現Netty原始碼Redis協議
- 看原始碼1. ArrayList中的Iterator實現原始碼
- 在婚戀app原始碼開發中,如何實現滑動驗證碼元件?APP原始碼元件
- golang 中,非對稱加密的實現Golang加密
- 三分鐘掌控Actor模型和CSP模型模型
- musl中strlen原始碼實現和分析原始碼
- [原始碼解析] 模型並行分散式訓練 Megatron (3) ---模型並行實現原始碼模型並行分散式
- 在相親原始碼開發中,如何實現圓角及特殊圓角的使用?原始碼
- 原始碼|ThreadLocal的實現原理原始碼thread
- 成品直播原始碼,實現在平臺內部的搜尋原始碼
- 相親交友原始碼中,音訊AAC解碼的實現程式碼原始碼音訊
- 關於Golang中的依賴注入實現Golang依賴注入
- Spring 原始碼(4)在Spring配置檔案中自定義標籤如何實現?Spring原始碼
- Golang WaitGroup原始碼分析GolangAI原始碼
- Golang 中字典的 Comma Ok 是如何實現的Golang
- 巧斷梯度:單個loss實現GAN模型(附開原始碼)梯度模型原始碼