Go語言併發之道學習十三 治癒異常的goroutine

治癒異常的goroutine

package main

import (
	"log"
	"time"
	"os"
)

/*
治癒異常的goroutine:
在長期運行的後臺程序中,經常會有一些長時間運行的goroutine。
這些goroutine 經常處於阻塞狀態,等待數據以某種方式到達,然後喚醒它們,進行一些處理,再返回一些數據。
如果沒有外部干預,一個goroutine 很容易進入一個不正常的狀態,並且無法恢復。
我們需要建立一個機制來監控你的goroutine 是否處於健康的狀態是很有用的,
當他們變得異常時,就可以儘快重啓。

我們將這種重啓goroutine的過程稱爲治癒。

爲了治癒goroutine ,我們需要使用心跳模式來檢查我們正在監控的goroutine 是否活躍。
心跳的類型取決於你想要監控的內容,但是如果你的goroutine 有可能會產生活鎖,確保心跳包含某些信息,表明該goroutine 在正常的工作而不僅僅是活着。
我們把監控goroutine 的健康這段邏輯稱爲管理員,它監視一個管理區的goroutine,
如果有goroutine 變得不健康,管理員將負責重新啓動這個管理區的goroutine 。

*/

func main() {
	log.Println("testhealing")
	testhealing()
	log.Println()
	log.Println("testhealing2")
	testhealink2()
}


func testhealing()  {
	var or func(channels ...<-chan interface{}) <-chan interface{}
	or = func(channels ...<-chan interface{}) <-chan interface{}{
		switch len(channels) {
		case 0:
			return nil
		case 1:
			return channels[0]
		}

		orDone := make(chan interface{})
		go func() {
			defer close(orDone)

			switch len(channels) {
			case 2:
				select {
				case <-channels[0]:
				case <-channels[1]:
				}
			default:
				select{
				case <-channels[0]:
				case <-channels[1]:
				case <-channels[2]:
				case <-or(append(channels[3:],orDone)...):
				}
			}
		}()
		return orDone
	}


	type startGoroutineFn func(
		done <-chan interface{},
		pulseInterval time.Duration,
		)(heartbeat <-chan interface{})
	
	newSteward := func(
		timeout time.Duration,
		startGoroutine startGoroutineFn,
		) startGoroutineFn {
			return func(
				done <-chan interface{},
				pulseInterval time.Duration,
				) (<-chan interface{}) {
				heartbeat := make(chan interface{})
				go func() {
					defer close(heartbeat)

					var wardDone chan interface{}
					var wardHeartbeat <-chan interface{}
					startWard := func() {
						wardDone = make(chan interface{})
						wardHeartbeat = startGoroutine(or(wardDone,done),timeout/2)
					}
					startWard()
					pulse := time.Tick(pulseInterval)

					monitorLoop:
						for{
							timeoutSignal := time.After(timeout)
							for {
								select {
								case <-pulse:
									select {
									case heartbeat<- struct {}{}:
									default:
									}
								case <-wardHeartbeat:
									continue monitorLoop
								case <-timeoutSignal:
									log.Println("steward:ward unhealthy;restarting")
									close(wardDone)
									startWard()
									continue monitorLoop
								case <-done:
									return
								}
							}
						}
				}()
				return heartbeat
			}
		
	}

	/*
	· pulseInterval time.Duration,
	   )(heartbeat <-chan interface{})
	在這裏,我們定義一個可以監控和重啓的goroutine 的信號,
	我們看到了熟悉的channel ,以及來自心跳模式的pulseInterval 和heartbeat

	·timeout time.Duration,
	  startGoroutine startGoroutineFn,
	  ) startGoroutineFn {
	在這,我們看到一個管理員監控goroutine 需要timeout 變量,
	還有一個函數startGoroutine 來啓動他監控的goroutine
	管理員本身會返回一個startGoroutineFn ,表示管理員本身也是可監控的

	·startWard := func() {
	在這裏,我們定義了一個閉包,他實現了一個統一的startWard 方法來啓動我們正在監視的goroutine

	·wardDone = make(chan interface{})
	這是我們創建的一個新的channel ,如果我們需要發出一個停止的信號,就會通過他傳入goroutine 中

	·wardHeartbeat = startGoroutine(or(wardDone,done),timeout/2)
	在這,我們啓動將要監控的goroutine ,如果管理員被停止了,或者管理員想要停止goroutine
	我們希望這些信息都能傳遞給管理區裏的goroutine ,所有我們把兩個done channel 用邏輯 or 包裝一下。
	我們設定心跳間隔時間是超時時間的一半

	·timeoutSignal := time.After(timeout)
	  for {
	這裏我們的內部循環,他確保管理員可以發出自己的心跳

	·case <-wardHeartbeat:
	在這,如果我們收到goroutine 的心跳,將繼續執行監控的循環

	·case <-timeoutSignal:
	這裏如果我們在暫停期間沒有收到管理區裏goroutine 的心跳,
	我們會要求管理區裏的goroutine 停下來,並啓動一個新的goroutine ,然後我們繼續監控

	*/

	log.SetOutput(os.Stdout)
	log.SetFlags(log.Ltime|log.LUTC)

	doWork := func(
		done <-chan interface{},
		_ time.Duration,
		) <-chan interface{}{
			log.Println("ward:hello, i,m irresponsible!")
			go func() {
				<-done
				log.Println("ward:i am halting.")
			}()
			return nil
	}
		doWorkWithSteward := newSteward(1*time.Second,doWork)

		done := make(chan interface{})
		time.AfterFunc(5*time.Second, func() {
			log.Println("main:halting steward and ward.")
			close(done)
		})

		for range doWorkWithSteward(done,2*time.Second){

		}
		log.Println("Done")

		/*
		·go func() {
		  <-done
		在這裏,我們看到這個goroutine 沒有做任何事情,只是等待被取消,他也沒有發出任何心跳

		·doWorkWithSteward := newSteward(4*time.Second,doWork)
		這裏創建了一個函數,爲goroutine doWork 創建一個管理員,我們設置doWork 的超時時間爲4秒

		·time.AfterFunc(9*time.Second, func() {
		在這我們設置9秒後停止管理員和goroutine ,這樣我們的測試就會結束。

		·for range doWorkWithSteward(done,4*time.Second){
		最後,我們啓動管理員並在其心跳範圍內防止我們的測試停止

		*/

}

func testhealink2(){
	var or func(channels ...<-chan interface{}) <-chan interface{}
	or = func(channels ...<-chan interface{}) <-chan interface{}{
		switch len(channels) {
		case 0:
			return nil
		case 1:
			return channels[0]
		}

		orDone := make(chan interface{})
		go func() {
			defer close(orDone)

			switch len(channels) {
			case 2:
				select {
				case <-channels[0]:
				case <-channels[1]:
				}
			default:
				select{
				case <-channels[0]:
				case <-channels[1]:
				case <-channels[2]:
				case <-or(append(channels[3:],orDone)...):
				}
			}
		}()
		return orDone
	}

	orDone := func(
		done <-chan interface{},
		valueStream <-chan interface{},
	)<-chan interface{}{
		doneStream := make(chan interface{})
		go func() {
			defer close(doneStream)
			for{
				select {
				case <-done:
					return
				case val,ok := <-valueStream:
					if ok == false{
						return
					}
					doneStream<-val
				}
			}
		}()
		return doneStream
	}
	//橋接channel
	bridge := func(
		done <-chan interface{},
		chanStream <-chan <-chan interface{},
	)<-chan interface{}{
		valueStream := make(chan interface{})
		go func() {
			defer close(valueStream)
			for{
				var stream <-chan interface{}
				select{
				case maybeStream,ok := <-chanStream:
					if ok == false{
						return
					}
					stream = maybeStream
				case <-done:
					return
				}
				for val := range orDone(done,stream){
					select{
					case <-done:
					case valueStream <-val:
					}
				}
			}
		}()
		return valueStream
	}
	take := func(
		done <-chan interface{},
		intStream <-chan interface{},
		num int,
		) <-chan interface {}{
			takeStream := make(chan interface{})
			go func() {
				defer close(takeStream)
				for i :=0;i<num;i++{
					select {
					case <-done:
						return
					case takeStream<-<-intStream:
					}
				}
			}()
			return takeStream
	}


	type startGoroutineFn func(
		done <-chan interface{},
		pulseInterval time.Duration,
	)(heartbeat <-chan interface{})

	newSteward := func(
		timeout time.Duration,
		startGoroutine startGoroutineFn,
	) startGoroutineFn {
		return func(
			done <-chan interface{},
			pulseInterval time.Duration,
		) (<-chan interface{}) {
			heartbeat := make(chan interface{})
			go func() {
				defer close(heartbeat)

				var wardDone chan interface{}
				var wardHeartbeat <-chan interface{}
				startWard := func() {
					wardDone = make(chan interface{})
					wardHeartbeat = startGoroutine(or(wardDone,done),timeout/2)
				}
				startWard()
				pulse := time.Tick(pulseInterval)

			monitorLoop:
				for{
					timeoutSignal := time.After(timeout)
					for {
						select {
						case <-pulse:
							select {
							case heartbeat<- struct {}{}:
							default:
							}
						case <-wardHeartbeat:
							continue monitorLoop
						case <-timeoutSignal:
							log.Println("steward:ward unhealthy;restarting")
							close(wardDone)
							startWard()
							continue monitorLoop
						case <-done:
							return
						}
					}
				}
			}()
			return heartbeat
		}

	}

	doWorkFn := func(
		done <-chan interface{},
		intList ...int,
		) (startGoroutineFn,<-chan interface{}){
			intChanStream := make(chan (<-chan interface{}))
			intStream := bridge(done,intChanStream)

			doWork := func(
				done <-chan interface{},
				pulseInterval time.Duration,
				) <-chan interface {}{
					intStream := make(chan interface{})
					heartbeat := make(chan interface{})
					go func() {
						defer close(intStream)
						select {
						case intChanStream <- intStream:
						case <-done:
							return
						}
						pulse := time.Tick(pulseInterval)

						for{
						valueLoop:
							for _,intVal := range intList{
								if intVal < 0{
									log.Printf("negative value: %v\n",intVal)
									return
								}
								for{
									select {
									case <- pulse:
										select {
										case heartbeat<- struct {}{}:
										default:
										}
									case intStream<-intVal:
										continue valueLoop
									case <-done:
										return
									}
								}
							}
						}
					}()
					return heartbeat
			}
		return doWork,intStream
	}

	/*
	·) (startGoroutineFn,<-chan interface{}){
	在這裏,我們填入一些我們管理區所需的參數,並返回我們管理區用來通信的channel

	·intChanStream := make(chan (<-chan interface{}))
	這裏我們創建了作爲橋接模式一部分的channel

	·) <-chan interface {}{
	我們創建一個將被管理員啓動和監控的閉包

	·intStream := make(chan interface{})
	這是我們實例化channel 的地方,我們將利用這些channel 與管理區中的goroutine 通信

	·case intChanStream <- intStream:
	這裏我們把我們即將用來通信的channel 通知給bridge

	·if intVal < 0{
	  log.Printf("negative value: %v\n",intVal)
	當我們處理到負數時,在這裏打印出一個錯誤信息,然後從goroutine 中返回
	*/

	log.SetFlags(log.Ltime|log.LUTC)
	log.SetOutput(os.Stdout)

	done := make(chan interface{})
	defer close(done)

	doWork,intStream := doWorkFn(done,1,2,-1,3,4,5)
	doWorkWithSteward := newSteward(1*time.Millisecond,doWork)
	doWorkWithSteward(done,1*time.Hour)

	for intVal := range take(done,intStream,6){
		log.Printf("Received:%v\n",intVal)
	}

	/*
	·doWork,intStream := doWorkFn(done,1,2,-1,3,4,5)
	這裏我們創建管理區的函數,允許他結束我們的可變整數切片,並返回一個用來返回的流

	·doWorkWithSteward := newSteward(1*time.Millisecond,doWork)
	我們創建一個管理員,用來監聽doWork 閉包,
	因爲我們希望能儘快知道失敗的信息,所以我們將監聽時間間隔設置爲1 毫秒

	·doWorkWithSteward(done,1*time.Hour)
	我們通知管理員啓動管理區並開始監控

	·for intVal := range take(done,intStream,6){
	最後,使用我們開發的一段管道代碼,並從intStream 中取出前6 個值
	*/

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章