<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] ## 7、動態保活Worker工作池設計 ### 一、我們如何知道一個Goroutine已經死亡? 實際上,Go語言并沒有給我們暴露如何知道一個Goroutine是否存在的接口,如果要證明一個Go是否存在,可以在子Goroutine的業務中,定期寫向一個keep live的Channel,然后主Goroutine來發現當前子Go的狀態。Go語言在對于Go和Go之間沒有像進程和線程一樣有強烈的父子、兄弟等關系,每個Go實際上對于調度器都是一個獨立的,平等的執行流程。 >PS: 如果你是監控子線程、子進程的死亡狀態,就沒有這么簡單了,這里也要感謝go的調度器給我們提供的方便,我們既然用Go,就要基于Go的調度器來實現該模式。 那么,我們如何做到一個Goroutine已經死亡了呢? #### 子Goroutine 可以通過給一個被監控的Goroutine添加一個`defer` ,然后`recover()` 捕獲到當前Goroutine的異常狀態,最后給主Goroutine發送一個死亡信號,通過`Channel`。 #### 主Goroutine 在`主Goroutine`上,從這個`Channel`讀取內容,當讀到內容時,就重啟這個`子Goroutine`,當然`主Goroutine`需要記錄`子Goroutine`的`ID`,這樣也就可以針對性的啟動了。 ### 二、代碼實現 我們這里以一個工作池的場景來對上述方式進行實現。 `WorkerManager`作為`主Goroutine`, `worker`作為子`Goroutine` > WorkerManager ```go type WorkerManager struct { //用來監控Worker是否已經死亡的緩沖Channel workerChan chan *worker // 一共要監控的worker數量 nWorkers int } //創建一個WorkerManager對象 func NewWorkerManager(nworkers int) *WorkerManager { return &WorkerManager{ nWorkers:nworkers, workerChan: make(chan *worker, nworkers), } } //啟動worker池,并為每個Worker分配一個ID,讓每個Worker進行工作 func (wm *WorkerManager)StartWorkerPool() { //開啟一定數量的Worker for i := 0; i < wm.nWorkers; i++ { i := i wk := &worker{id: i} go wk.work(wm.workerChan) } //啟動保活監控 wm.KeepLiveWorkers() } //保活監控workers func (wm *WorkerManager) KeepLiveWorkers() { //如果有worker已經死亡 workChan會得到具體死亡的worker然后 打出異常,然后重啟 for wk := range wm.workerChan { // log the error fmt.Printf("Worker %d stopped with err: [%v] \n", wk.id, wk.err) // reset err wk.err = nil // 當前這個wk已經死亡了,需要重新啟動他的業務 go wk.work(wm.workerChan) } } ``` >worker ```go type worker struct { id int err error } func (wk *worker) work(workerChan chan<- *worker) (err error) { // 任何Goroutine只要異常退出或者正常退出 都會調用defer 函數,所以在defer中想WorkerManager的WorkChan發送通知 defer func() { //捕獲異常信息,防止panic直接退出 if r := recover(); r != nil { if err, ok := r.(error); ok { wk.err = err } else { wk.err = fmt.Errorf("Panic happened with [%v]", r) } } else { wk.err = err } //通知 主 Goroutine,當前子Goroutine已經死亡 workerChan <- wk }() // do something fmt.Println("Start Worker...ID = ", wk.id) // 每個worker睡眠一定時間之后,panic退出或者 Goexit()退出 for i := 0; i < 5; i++ { time.Sleep(time.Second*1) } panic("worker panic..") //runtime.Goexit() return err } ``` ### 三、測試 >main ```go func main() { wm := NewWorkerManager(10) wm.StartWorkerPool() } ``` 結果: ```bash $ go run workmanager.go Start Worker...ID = 2 Start Worker...ID = 1 Start Worker...ID = 3 Start Worker...ID = 4 Start Worker...ID = 7 Start Worker...ID = 6 Start Worker...ID = 8 Start Worker...ID = 9 Start Worker...ID = 5 Start Worker...ID = 0 Worker 9 stopped with err: [Panic happened with [worker panic..]] Worker 1 stopped with err: [Panic happened with [worker panic..]] Worker 0 stopped with err: [Panic happened with [worker panic..]] Start Worker...ID = 9 Start Worker...ID = 1 Worker 2 stopped with err: [Panic happened with [worker panic..]] Worker 5 stopped with err: [Panic happened with [worker panic..]] Worker 4 stopped with err: [Panic happened with [worker panic..]] Start Worker...ID = 0 Start Worker...ID = 2 Start Worker...ID = 4 Start Worker...ID = 5 Worker 7 stopped with err: [Panic happened with [worker panic..]] Worker 8 stopped with err: [Panic happened with [worker panic..]] Worker 6 stopped with err: [Panic happened with [worker panic..]] Worker 3 stopped with err: [Panic happened with [worker panic..]] Start Worker...ID = 3 Start Worker...ID = 6 Start Worker...ID = 8 Start Worker...ID = 7 ... ... ``` 我們會發現,無論子Goroutine是因為 panic()異常退出,還是Goexit()退出,都會被主Goroutine監聽到并且重啟。主要我們就能夠起到保活的功能了. 當然如果線程死亡?進程死亡?我們如何保證? 大家不用擔心,我們用Go開發實際上是基于Go的調度器來開發的,進程、線程級別的死亡,會導致調度器死亡,那么我們的全部基礎框架都將會塌陷。那么就要看線程、進程如何保活啦,不在我們Go開發的范疇之內了。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看