欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang?HTTP服務(wù)超時(shí)控制實(shí)現(xiàn)原理分析

 更新時(shí)間:2023年05月10日 10:09:42   作者:未來誰可知  
這篇文章主要介紹了Golang?HTTP服務(wù)超時(shí)控制實(shí)現(xiàn)原理,HTTP服務(wù)的超時(shí)控制是保障服務(wù)高可用性的重要措施之一,由于HTTP服務(wù)可能會(huì)遇到網(wǎng)絡(luò)延遲,資源瓶頸等問題,因此需要對(duì)請(qǐng)求進(jìn)行超時(shí)控制,以避免服務(wù)雪崩等問題,需要的朋友可以參考下

前情提要

因?yàn)?a href="http://www.dbjr.com.cn/article/283878.htm" target="_blank">上一篇提過,每次來一個(gè)請(qǐng)求,然后就會(huì)起一個(gè)goroutinue那么導(dǎo)致的可能就是一個(gè)樹形結(jié)構(gòu)的請(qǐng)求圖,底下節(jié)點(diǎn)在執(zhí)行中如果發(fā)生了超時(shí),那么就有協(xié)程會(huì)堆積,所以超時(shí)控制是有必要的,一般的實(shí)現(xiàn)都由一個(gè)頂層設(shè)計(jì)一個(gè)Context進(jìn)行自頂向下傳遞,這樣可以從一個(gè)地方去避免多處執(zhí)行異常,對(duì)于Context的過多細(xì)節(jié)我不在這里一一闡述,有需要的我將單獨(dú)出一篇關(guān)于Context的介紹,下面我們就來看一下源碼是如何設(shè)計(jì)的:

Context

// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
  // 當(dāng)Context被取消或者到了deadline,返回一個(gè)被關(guān)閉的channel
  Done() <-chan struct{}
}
// 函數(shù)句柄
type CancelFunc func()

設(shè)計(jì)初衷最關(guān)注的兩個(gè)點(diǎn)就是一個(gè)是如何主動(dòng)結(jié)束下游,另一個(gè)是如何通知上游結(jié)束下游時(shí)

前者利用CancelFunc 后者利用Done,后者需要不斷監(jiān)聽所以利用channel的返回值做監(jiān)聽

//創(chuàng)建退出Context
func WithCancel(parent Context)(ctx Context,cancel CancelFunc){}
//創(chuàng)建有超時(shí)時(shí)間的Context
func WithTimeout(parent Context,timeout time.Duration)(Context,CancelFunc){}
//創(chuàng)建有截止時(shí)間的Context
func WithDeadline(parent Context,d time.Time)(Context,CancelFunc){}

WithCancel/WithTimeout/WithDeadline都是通過定時(shí)器來自動(dòng)觸發(fā)終結(jié)通知的,也就是說為父節(jié)點(diǎn)生成一個(gè)Done的子節(jié)點(diǎn),并且返回子節(jié)點(diǎn)的CancelFunc函數(shù)句柄.

封裝自定義的Context

context.go

可以定義一個(gè)自己的Context,里面先擁有最基本的request和response兩個(gè)參數(shù),最后是因?yàn)樗伎嫉讲l(fā)寫resposne的writer所以需要加入鎖成員變量以及防止重復(fù)寫的超時(shí)標(biāo)志位

package framework
import (
	"context"
	"encoding/json"
	"net/http"
	"sync"
)
type Context struct {
	Request        *http.Request
	ResponseWriter http.ResponseWriter
	hasTimeOut     bool // 是否超時(shí)標(biāo)記位
	writerMux      *sync.Mutex
}
func NewContext()*Context{
	return &Context{}
}
func (ctx *Context) BaseContext() context.Context {
	return ctx.Request.Context()
}
func (ctx *Context) Done() <-chan struct{} {
	return ctx.BaseContext().Done()
}
func (ctx *Context)SetHasTimeOut(){
	ctx.hasTimeOut=true
}
func (ctx *Context)HasTimeOut()bool{
	return ctx.hasTimeOut
}
// 自行封裝一個(gè)Json的方法
func (ctx *Context) Json(status int, obj interface{}) (err error) {
	if ctx.HasTimeOut(){
		return nil
	}
	bytes, err := json.Marshal(obj)
	ctx.ResponseWriter.WriteHeader(status)
	_, err = ctx.ResponseWriter.Write(bytes)
	return
}
// 對(duì)外暴露鎖
func (ctx *Context) WriterMux() *sync.Mutex {
	return ctx.writerMux
}
// 統(tǒng)一處理器Controller方法
type ControllerHandler func(c *Context) error

main.go

業(yè)務(wù)方法使用一下自己封裝的Context,里面考慮到了超時(shí)控制以及并發(fā)讀寫,以及處理panic

package main
import (
	"context"
	"fmt"
	"testdemo1/coredemo/framework"
	"time"
)
func FooController(ctx *framework.Context) error {
	durationCtx, cancel := context.WithTimeout(ctx.BaseContext(), time.Second)
	defer cancel()
	finish := make(chan struct{}, 1)
	panicChan := make(chan interface{}, 1)
	go func() {
		defer func() {
			if p := recover(); p != nil {
				panicChan <- p
			}
		}()
		time.Sleep(time.Second * 10)
		finish <- struct{}{}
	}()
	select {
	case p := <-panicChan: // panic
	fmt.Println("panic:",p)
	    ctx.WriterMux().Lock()  // 防止多個(gè)協(xié)程之前writer的消息亂序
		defer ctx.WriterMux().Unlock()
		ctx.Json(500, "panic")
	case <-finish: // 正常退出
		ctx.Json(200, "ok")
		fmt.Println("finish")
	case <-durationCtx.Done(): // 超時(shí)事件
		ctx.WriterMux().Lock()
		defer ctx.WriterMux().Unlock()
		ctx.Json(500, "timed out")
		ctx.SetHasTimeOut()  // 防止多次協(xié)程重復(fù)寫入超時(shí)日志
	}
	return nil
}

Core.go

serverHandler的類,進(jìn)行處理請(qǐng)求的邏輯,可以先注冊(cè)對(duì)應(yīng)的映射器和方法

package framework
import (
	"net/http"
)
type Core struct {
	RouterMap map[string]ControllerHandler
}
func (c Core) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	http.DefaultServeMux.ServeHTTP(writer, request)
}
func NewCore() *Core {
	return &Core{
		RouterMap:make(map[string]ControllerHandler,0),
	}
}
// 注冊(cè)Get方法
func (c *Core) Get(pattern string, handler ControllerHandler) {
	c.RouterMap["get"+"-"+pattern]=handler
}
// 注冊(cè)Post方法
func (c *Core) Post(pattern string, handler ControllerHandler) {
	c.RouterMap["post"+"-"+pattern]=handler
}

router.go

router統(tǒng)一管理注冊(cè)進(jìn)我們對(duì)應(yīng)的http方法到我們的請(qǐng)求邏輯類里去

package main
import "testdemo1/coredemo/framework"
func registerRouter(core *framework.Core){
	// 設(shè)置控制器
	core.Get("foo",FooController)
}

main.go

最后是主程序的執(zhí)行http服務(wù)監(jiān)聽和調(diào)用初始化router的注冊(cè)!傳入我們自定義的Context

package main
import (
	"log"
	"net/http"
	"testdemo1/coredemo/framework"
)
func main() {
	server:=&http.Server{Addr: ":8080",Handler: framework.NewCore()}
	// 注冊(cè)router
	registerRouter(framework.NewCore())
	err := server.ListenAndServe()
    if err!=nil{
    	log.Fatal(err)
	}
}

本文到此結(jié)束!可以自行實(shí)現(xiàn)一遍,體驗(yàn)一下,實(shí)際和gin的源碼封裝就是類似的~

我們下一篇再見

到此這篇關(guān)于Golang HTTP服務(wù)超時(shí)控制實(shí)現(xiàn)原理分析的文章就介紹到這了,更多相關(guān)Golang HTTP服務(wù)超時(shí)控制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 分析Go語言中CSP并發(fā)模型與Goroutine的基本使用

    分析Go語言中CSP并發(fā)模型與Goroutine的基本使用

    我們都知道并發(fā)是提升資源利用率最基礎(chǔ)的手段,尤其是當(dāng)今大數(shù)據(jù)時(shí)代,流量對(duì)于一家互聯(lián)網(wǎng)企業(yè)的重要性不言而喻。串流顯然是不行的,尤其是對(duì)于web后端這種流量的直接載體。并發(fā)是一定的,問題在于怎么執(zhí)行并發(fā)。常見的并發(fā)方式有三種,分別是多進(jìn)程、多線程和協(xié)程
    2021-06-06
  • go語言中slice,map,channl底層原理

    go語言中slice,map,channl底層原理

    這篇文章主要介紹了go語言中slice,map,channl底層原理,slice,map,channl是我們Go語言中最最常用的幾個(gè)數(shù)據(jù)結(jié)構(gòu),對(duì)于其更多相關(guān)內(nèi)容需要的小伙伴可以參考下面文章詳細(xì)內(nèi)容
    2022-06-06
  • Go中的gRPC入門教程詳解

    Go中的gRPC入門教程詳解

    本文詳細(xì)講解了Go中的gRPC入門教程,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-03-03
  • Go語言編譯程序從后臺(tái)運(yùn)行,不出現(xiàn)dos窗口的操作

    Go語言編譯程序從后臺(tái)運(yùn)行,不出現(xiàn)dos窗口的操作

    這篇文章主要介紹了Go語言編譯程序從后臺(tái)運(yùn)行,不出現(xiàn)dos窗口的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go singleflight使用以及原理

    Go singleflight使用以及原理

    singleflight官方解釋其為:singleflight提供了一個(gè)重復(fù)的函數(shù)調(diào)用抑制機(jī)制。通俗的解釋其作用是,若有多個(gè)協(xié)程運(yùn)行某函數(shù)時(shí),只讓一個(gè)協(xié)程去處理,然后批量返回。非常適合來做并發(fā)控制。常見用于緩存穿透的情況
    2023-01-01
  • golang交叉編譯詳細(xì)

    golang交叉編譯詳細(xì)

    Golang 支持交叉編譯,在一個(gè)平臺(tái)上生成另一個(gè)平臺(tái)的可執(zhí)行程序,只需要按照我下面的這個(gè)環(huán)境變量表設(shè)置對(duì)應(yīng)的環(huán)境變量就可以了,下面文章將對(duì)該內(nèi)容做詳細(xì)介紹,感興趣的小伙伴可以參考一下
    2021-10-10
  • Golang如何將上傳的文件壓縮成zip(小案例)

    Golang如何將上傳的文件壓縮成zip(小案例)

    這篇文章主要介紹了Golang如何將上傳的文件壓縮成zip(小案例),這是一個(gè)簡單的golang壓縮文件小案例,可做很多的拓展,這里使用的庫是archive/zip,在gopkg里面搜zip就行,需要的朋友可以參考下
    2024-01-01
  • 淺析Go語言中包的介紹與初始化

    淺析Go語言中包的介紹與初始化

    這篇文章主要為大家詳細(xì)介紹了Go語言中包的介紹與初始化,從而搞清Go程序的執(zhí)行次序,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解下
    2023-10-10
  • Go語言計(jì)算指定年月天數(shù)的方法

    Go語言計(jì)算指定年月天數(shù)的方法

    這篇文章主要介紹了Go語言計(jì)算指定年月天數(shù)的方法,實(shí)例分析了Go語言操作時(shí)間的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • Go逃逸分析示例詳解

    Go逃逸分析示例詳解

    這篇文章主要為大家介紹了Go逃逸分析示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08

最新評(píng)論