最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Golang設(shè)計(jì)一套可控的定時(shí)任務(wù)系統(tǒng)

 更新時(shí)間:2023年07月25日 14:38:24   作者:第八共同體  
這篇文章主要為大家學(xué)習(xí)介紹了如何基于Golang設(shè)計(jì)一套可控的定時(shí)任務(wù)系統(tǒng),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

現(xiàn)在的系統(tǒng)設(shè)計(jì)中,有許多規(guī)律性的功能特征需要用到定時(shí)任務(wù)來完成,比如每分鐘需要執(zhí)行一次清理數(shù)據(jù)的任務(wù),每個(gè)月的第一天,需要處理一項(xiàng)什么任務(wù)等等這種,還有一種規(guī)律性的任務(wù)不是以時(shí)間間隔為第一維度切割的,而是如果任務(wù)執(zhí)行完成,不管成功與否,都間隔一段時(shí)間執(zhí)行一次任務(wù)等等。

像上面說描述的任務(wù)的特征,都需要我們?nèi)ブ芷谛缘膱?zhí)行任務(wù)主體,如果沒辦法對(duì)定時(shí)任務(wù)進(jìn)行嚴(yán)格的控制管理,在生產(chǎn)環(huán)境下是非常危險(xiǎn)的。比如上周發(fā)生的一起生產(chǎn)事故,定時(shí)任務(wù)去拉取某服務(wù)器的數(shù)據(jù),因?yàn)槌绦虍惓#瑢?dǎo)致產(chǎn)生龐大的協(xié)程拉取對(duì)方數(shù)據(jù),致使對(duì)方數(shù)據(jù)庫崩潰。

所以在享受一件技術(shù)帶來好處的同時(shí),要嘗試在可控的范圍內(nèi)使用,才是我們調(diào)庫人員的基本操守,接下里,我們就一起探究一下,我是如何設(shè)計(jì)一套可控的定時(shí)任務(wù)系統(tǒng)的。

功能點(diǎn):

  • 任務(wù)的自動(dòng)注冊(cè)
  • 任務(wù)的信息管理,包括任務(wù)的cron表達(dá)式,任務(wù)的詳情信息備注
  • 手動(dòng)控制任務(wù)的啟動(dòng)和執(zhí)行
  • 任務(wù)的實(shí)例管理,可查看單次運(yùn)行實(shí)例的任務(wù)日志
  • 任務(wù)優(yōu)先級(jí)
  • 保證至少執(zhí)行一次
  • ...

整體結(jié)構(gòu)如下:

系統(tǒng)分為四個(gè)主要部分,從下到上,依次為:

  • 任務(wù)的執(zhí)行體,完成任務(wù)的主要功能,實(shí)現(xiàn)為單個(gè)rpc方法,方便進(jìn)行獨(dú)立部署,或者手動(dòng)執(zhí)行指定次數(shù)時(shí)進(jìn)行單獨(dú)調(diào)用的邏輯實(shí)現(xiàn)
  • Worker服務(wù)節(jié)點(diǎn),該節(jié)點(diǎn)單獨(dú)部署,對(duì)所有調(diào)度的任務(wù)進(jìn)行執(zhí)行
  • Schedule服務(wù)節(jié)點(diǎn),該節(jié)點(diǎn)也是單獨(dú)部署,對(duì)Cronweb管理的所有任務(wù)進(jìn)行同步
  • Cronweb節(jié)點(diǎn),任務(wù)的信息管理能力,可手動(dòng)停止任務(wù)的執(zhí)行,可調(diào)整任務(wù)的執(zhí)行cron表達(dá)式,可定制任務(wù)執(zhí)行的超時(shí)重啟次數(shù)等等信息

最終,在調(diào)研了大多數(shù)任務(wù)管理的庫后,選擇了asynq.選擇什么庫,亦或是自己造輪子,都是根據(jù)業(yè)務(wù)需求來定的,以能滿足功能需求為首要標(biāo)準(zhǔn)。

Asynq是一個(gè)用于排隊(duì)任務(wù)并與woker異步處理的庫。它由Redis支持,設(shè)計(jì)為可擴(kuò)展但易于啟動(dòng)。

大致概述Asynq的工作方式:

  • Client將任務(wù)放在隊(duì)列上
  • Server從隊(duì)列中刪除任務(wù),并為每個(gè)任務(wù)啟動(dòng)一個(gè)woker goroutine
  • 任務(wù)是由多名woker同時(shí)處理的

任務(wù)隊(duì)列被用作跨多個(gè)計(jì)算機(jī)分配工作的機(jī)制。系統(tǒng)可以由多個(gè)worker servers 和brokers組成,使位于高可用性和水平規(guī)模。

上面兩段話為asynq的簡(jiǎn)單介紹,剛看起來可能會(huì)有點(diǎn)抽象,那接下來,我們先來詳細(xì)的介紹一下這個(gè)庫,然后看看我們是如何對(duì)它進(jìn)行封裝和調(diào)整的。

看一個(gè)簡(jiǎn)單的實(shí)例

package main
import (
    "log"
    "github.com/hibiken/asynq"
    "your/app/package/tasks"
)
const redisAddr = "127.0.0.1:6379"
func main() {
    srv := asynq.NewServer(
        asynq.RedisClientOpt{Addr: redisAddr},
        asynq.Config{
            // Specify how many concurrent workers to use
            Concurrency: 10,
            // Optionally specify multiple queues with different priority.
            Queues: map[string]int{
                "critical": 6,
                "default":  3,
                "low":      1,
            },
            // See the godoc for other configuration options
        },
    )
    // mux maps a type to a handler
    mux := asynq.NewServeMux()
    mux.HandleFunc(tasks.TypeEmailDelivery, tasks.HandleEmailDeliveryTask)
    mux.Handle(tasks.TypeImageResize, tasks.NewImageProcessor())
    // ...register other handlers...
    if err := srv.Run(mux); err != nil {
        log.Fatalf("could not run server: %v", err)
    }
}

上面這塊代碼實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的server,可以看到,

  • asynq庫深度使用redis組件,所以你的系統(tǒng)如何沒有接入redis那就要重新考慮了,當(dāng)然現(xiàn)在的系統(tǒng)中redis的使用還是很普遍的。繼續(xù)看,實(shí)例化Server時(shí),可以指定有多少個(gè)并發(fā)任務(wù)來處理調(diào)度的task.此項(xiàng)可根據(jù)運(yùn)行調(diào)整進(jìn)行調(diào)整。
  • 另外的Queues是隊(duì)列的配置,此處提供了三個(gè)隊(duì)列,這里的隊(duì)列你可以根據(jù)自己的需要進(jìn)行調(diào)整,值為隊(duì)列的優(yōu)先級(jí)。
  • 此處需要注意的是,如果你指定了嚴(yán)格模式時(shí),低優(yōu)先級(jí)的任務(wù),只有等到高優(yōu)先級(jí)的任務(wù)執(zhí)行完成后,才能執(zhí)行。如果高優(yōu)先級(jí)的隊(duì)列中一直有任務(wù),那么低優(yōu)先級(jí)的任務(wù)可能會(huì)得不到執(zhí)行的機(jī)會(huì)。默認(rèn)的嚴(yán)格模式并沒有打開,如果你需要此模式,進(jìn)需要提供Config結(jié)構(gòu)體中StrictPriority屬性設(shè)置為true.
  • 然后就是類似于http server中的路由設(shè)置,你可在這里指定需要的不同任務(wù)
  • 最后啟動(dòng)了一個(gè)asynq的Server

任務(wù)的執(zhí)行邏輯

package tasks
import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "time"
    "github.com/hibiken/asynq"
)
// A list of task types.
const (
    TypeEmailDelivery   = "email:deliver"
    TypeImageResize     = "image:resize"
)
type EmailDeliveryPayload struct {
    UserID     int
    TemplateID string
}
type ImageResizePayload struct {
    SourceURL string
}
//----------------------------------------------
// Write a function NewXXXTask to create a task.
// A task consists of a type and a payload.
//----------------------------------------------
func NewEmailDeliveryTask(userID int, tmplID string) (*asynq.Task, error) {
    payload, err := json.Marshal(EmailDeliveryPayload{UserID: userID, TemplateID: tmplID})
    if err != nil {
        return nil, err
    }
    return asynq.NewTask(TypeEmailDelivery, payload), nil
}
func NewImageResizeTask(src string) (*asynq.Task, error) {
    payload, err := json.Marshal(ImageResizePayload{SourceURL: src})
    if err != nil {
        return nil, err
    }
    // task options can be passed to NewTask, which can be overridden at enqueue time.
    return asynq.NewTask(TypeImageResize, payload, asynq.MaxRetry(5), asynq.Timeout(20 * time.Minute)), nil
}
//---------------------------------------------------------------
// Write a function HandleXXXTask to handle the input task.
// Note that it satisfies the asynq.HandlerFunc interface.
//
// Handler doesn't need to be a function. You can define a type
// that satisfies asynq.Handler interface. See examples below.
//---------------------------------------------------------------
func HandleEmailDeliveryTask(ctx context.Context, t *asynq.Task) error {
    var p EmailDeliveryPayload
    if err := json.Unmarshal(t.Payload(), &p); err != nil {
        return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
    }
    log.Printf("Sending Email to User: user_id=%d, template_id=%s", p.UserID, p.TemplateID)
    // Email delivery code ...
    return nil
}
// ImageProcessor implements asynq.Handler interface.
type ImageProcessor struct {
    // ... fields for struct
}
func (processor *ImageProcessor) ProcessTask(ctx context.Context, t *asynq.Task) error {
    var p ImageResizePayload
    if err := json.Unmarshal(t.Payload(), &p); err != nil {
        return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
    }
    log.Printf("Resizing image: src=%s", p.SourceURL)
    // Image resizing code ...
    return nil
}
func NewImageProcessor() *ImageProcessor {
	return &ImageProcessor{}
}

上面的代碼定義了不同的任務(wù)類型,以及任務(wù)在調(diào)度過程中的Payload信息。這個(gè)payload是一個(gè)可以利用的點(diǎn),在你需要實(shí)現(xiàn)更加高級(jí)的控制能力的時(shí)候,這里我們先簡(jiǎn)單的看看如何使用的即可。

NewEmailDeliveryTask

NewImageResizeTask

其實(shí)就是定義了兩個(gè)不同的任務(wù)類型,在實(shí)現(xiàn)時(shí),你可以指定不同的屬性,比如最大的重試次數(shù),以及單個(gè)任務(wù)的實(shí)例的超時(shí)時(shí)間等等。

接下來的兩段代碼,我摘出來著重說一下:

//---------------------------------------------------------------
// Write a function HandleXXXTask to handle the input task.
// Note that it satisfies the asynq.HandlerFunc interface.
//
// Handler doesn't need to be a function. You can define a type
// that satisfies asynq.Handler interface. See examples below.
//---------------------------------------------------------------
func HandleEmailDeliveryTask(ctx context.Context, t *asynq.Task) error {
    var p EmailDeliveryPayload
    if err := json.Unmarshal(t.Payload(), &p); err != nil {
        return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
    }
    log.Printf("Sending Email to User: user_id=%d, template_id=%s", p.UserID, p.TemplateID)
    // Email delivery code ...
    return nil
}
// ImageProcessor implements asynq.Handler interface.
type ImageProcessor struct {
    // ... fields for struct
}
func (processor *ImageProcessor) ProcessTask(ctx context.Context, t *asynq.Task) error {
    var p ImageResizePayload
    if err := json.Unmarshal(t.Payload(), &p); err != nil {
        return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
    }
    log.Printf("Resizing image: src=%s", p.SourceURL)
    // Image resizing code ...
    return nil
}
func NewImageProcessor() *ImageProcessor {
	return &ImageProcessor{}
}

這其實(shí)就是說明了如何將一個(gè)task對(duì)象轉(zhuǎn)換為可以在Server中進(jìn)行路由注冊(cè)的工具方法。你可以把它當(dāng)做是http HandlerFunc一樣對(duì)待,當(dāng)然了,你也可以使用第二種方式,自己定義一個(gè)結(jié)構(gòu)體,然后實(shí)現(xiàn)ProcessTask方法.

Client如何讓任務(wù)進(jìn)行調(diào)度的,三種不同場(chǎng)景下的使用方式

package main
import (
    "log"
    "time"
    "github.com/hibiken/asynq"
    "your/app/package/tasks"
)
const redisAddr = "127.0.0.1:6379"
func main() {
    client := asynq.NewClient(asynq.RedisClientOpt{Addr: redisAddr})
    defer client.Close()
    // ------------------------------------------------------
    // Example 1: Enqueue task to be processed immediately.
    //            Use (*Client).Enqueue method.
    // ------------------------------------------------------
    task, err := tasks.NewEmailDeliveryTask(42, "some:template:id")
    if err != nil {
        log.Fatalf("could not create task: %v", err)
    }
    info, err := client.Enqueue(task)
    if err != nil {
        log.Fatalf("could not enqueue task: %v", err)
    }
    log.Printf("enqueued task: id=%s queue=%s", info.ID, info.Queue)
    // ------------------------------------------------------------
    // Example 2: Schedule task to be processed in the future.
    //            Use ProcessIn or ProcessAt option.
    // ------------------------------------------------------------
    info, err = client.Enqueue(task, asynq.ProcessIn(24*time.Hour))
    if err != nil {
        log.Fatalf("could not schedule task: %v", err)
    }
    log.Printf("enqueued task: id=%s queue=%s", info.ID, info.Queue)
    // ----------------------------------------------------------------------------
    // Example 3: Set other options to tune task processing behavior.
    //            Options include MaxRetry, Queue, Timeout, Deadline, Unique etc.
    // ----------------------------------------------------------------------------
    task, err = tasks.NewImageResizeTask("https://example.com/myassets/image.jpg")
    if err != nil {
        log.Fatalf("could not create task: %v", err)
    }
    info, err = client.Enqueue(task, asynq.MaxRetry(10), asynq.Timeout(3 * time.Minute))
    if err != nil {
        log.Fatalf("could not enqueue task: %v", err)
    }
    log.Printf("enqueued task: id=%s queue=%s", info.ID, info.Queue)
}

上面這部分代碼實(shí)現(xiàn)了三種場(chǎng)景下的使用方法,你可以立即調(diào)用,也可以在未來的某個(gè)時(shí)間點(diǎn)調(diào)用,還可以更加詳盡的控制任務(wù)執(zhí)行。

上面的代碼僅僅是關(guān)于asynq的簡(jiǎn)單的一個(gè)介紹。在生產(chǎn)環(huán)境下,如何使用呢,一般情況下,我們會(huì)提供一個(gè)provider.provider來提供配置的源,源可以是文件,也可以是Mysql還可以是其他存儲(chǔ)源,最重要的是需要實(shí)現(xiàn)對(duì)應(yīng)的方法。下面一個(gè)文件源為例來說明如何實(shí)現(xiàn)一個(gè)源

// FileBasedConfigProvider implements asynq.PeriodicTaskConfigProvider interface.
type FileBasedConfigProvider struct {
? ? ? ? filename string
}
type PeriodicTaskConfigContainer struct {
? ? ? ? Configs []*Config `yaml:"configs"`
}
type Config struct {
? ? ? ? Cronspec string `yaml:"cronspec"`
? ? ? ? TaskType string `yaml:"task_type"`
}  
// Parses the yaml file and return a list of PeriodicTaskConfigs.
func (p *FileBasedConfigProvider) GetConfigs() ([]*asynq.PeriodicTaskConfig, error) {
? ? ? ? data, err := os.ReadFile(p.filename)
? ? ? ? if err != nil {
? ? ? ? ? ? ? ? return nil, err
? ? ? ? }
? ? ? ? var c PeriodicTaskConfigContainer
? ? ? ? if err := yaml.Unmarshal(data, &c); err != nil {
? ? ? ? ? ? ? ? return nil, err
? ? ? ? }
? ? ? ? var configs []*asynq.PeriodicTaskConfig
? ? ? ? for _, cfg := range c.Configs {
? ? ? ? ? ? ? ? configs = append(configs, &asynq.PeriodicTaskConfig{Cronspec: cfg.Cronspec, Task: asynq.NewTask(cfg.TaskType, nil)})
? ? ? ? }
? ? ? ? return configs, nil
}

如何使用

        provider := &FileBasedConfigProvider{filename: "./periodic_task_config.yml"}
? ? ? ? mgr, err := asynq.NewPeriodicTaskManager(
? ? ? ? ? ? ? ? asynq.PeriodicTaskManagerOpts{
? ? ? ? ? ? ? ? ? ? ? ? RedisConnOpt: asynq.RedisClientOpt{
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Addr: ? ? "127.0.0.1:6379",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Password: "123456",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DB: ? ? ? 1,
? ? ? ? ? ? ? ? ? ? ? ? },
? ? ? ? ? ? ? ? ? ? ? ? PeriodicTaskConfigProvider: provider, ? ? ? ? // this provider object is the interface to your config source
? ? ? ? ? ? ? ? ? ? ? ? SyncInterval: ? ? ? ? ? ? ? 10 * time.Second, // this field specifies how often sync should happen
? ? ? ? ? ? ? ? })
? ? ? ? if err != nil {
? ? ? ? ? ? ? ? log.Fatal(err)
? ? ? ? }
? ? ? ? if err := mgr.Run(); err != nil {
? ? ? ? ? ? ? ? log.Fatal(err)
? ? ? ? }

在任務(wù)調(diào)度的邏輯中,我們一般會(huì)實(shí)現(xiàn)一個(gè)調(diào)度器來進(jìn)行任務(wù)調(diào)度,而不是單個(gè)任務(wù)進(jìn)行獨(dú)立的調(diào)度,例如:

? ? ? ? loc, err := time.LoadLocation("Asia/Shanghai")
? ? ? ? if err != nil {
            panic(err)
        }
? ? ? ? scheduler := asynq.NewScheduler(
? ? ? ? ? ? ? asynq.RedisClientOpt{
? ? ? ? ? ? ? ? ? ? ? Addr: ? ? "127.0.0.1:6379",
? ? ? ? ? ? ? ? ? ? ? Password: "123456",
? ? ? ? ? ? ? ? ? ? ? DB: ? ? ? 1,
? ? ? ? ? ? ? },
? ? ? ? ? ? ? &asynq.SchedulerOpts{
? ? ? ? ? ? ? ? ? ? ? Location: loc,
? ? ? ? ? ? ? },
? ? ? ?  )
? ? ? ?  task := asynq.NewTask("example_task", nil)
? ? ? ?  // You can use cron spec string to specify the schedule.
? ? ? ?  entryID, err := scheduler.Register("*/1 * * * *", task)
? ? ? ?  if err != nil {
? ? ? ?  ? ? log.Fatal(err)
? ? ? ?  }
? ? ? ?  fmt.Println(entryID)
? ? ? ?  if err := scheduler.Run(); err != nil {
? ? ? ? ? ? ? log.Fatal(err)
? ? ? ?  }

該庫還提供了一個(gè)工具用于監(jiān)控任務(wù)的運(yùn)行情況。Asynqmon是一個(gè)基于web的工具,用于監(jiān)控和管理Asynq隊(duì)列和任務(wù)。下面是Web UI的一些截圖

以上就是asynq的全部介紹,下面,看看我對(duì)任務(wù)系統(tǒng)的改動(dòng)

首先,增加任務(wù)的注冊(cè)和發(fā)現(xiàn)

  • 客戶端在啟動(dòng)時(shí),注冊(cè)節(jié)點(diǎn)信息到etcd,并且注冊(cè)該rpc服務(wù)器提供的所有遠(yuǎn)程調(diào)用方法,供web頁面建立cron任務(wù)時(shí),進(jìn)行選擇執(zhí)行相應(yīng)的定時(shí)方法,如果采用任務(wù)對(duì)列不需要提供調(diào)用方法,框架自己提供
  • 調(diào)度器定期拉取etcd注冊(cè)信息,維護(hù)web端和客戶端的rpc連接池,方便web端調(diào)度器進(jìn)行rpc連接和rpc調(diào)用

訂閱任務(wù)取消的信號(hào),對(duì)任務(wù)取消的信號(hào)進(jìn)行處理,在頁面增加任務(wù)啟停的功能

pubsub := AsyncClient.Subscribe(context.Background(), "asynq:cancel")
	cancelCh := pubsub.Channel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	now := time.Now()
	ch1 := make(chan int)
	stop := make(chan bool)
	go func(ctx context.Context, xxx string) {
		err := xxxx
		if err != nil {
			ch1 <- 2
			logx.Error(fmt.Sprintf("本次定時(shí)任務(wù)執(zhí)行失敗,失敗原因: %s", err))
			return
		}
		 for i := 0; i < 100; i++ {
		 	select {
		 	default:
		 		time.Sleep(1 * time.Second)
		 		logx.Info("我是任務(wù)主流程,我在運(yùn)行中...")
		 	case <-stop:
		 		logx.Info("收到退出信號(hào),任務(wù)結(jié)束...")
		 		return
		 	}
		 }
		usedTime := time.Since(now)
		msg := fmt.Sprintf("本次定時(shí)任務(wù)成功結(jié)束執(zhí)行,用時(shí): %f秒", usedTime.Seconds())
		// Signal the goroutine has completed
		ch1 <- 1
	}(ctx, in.Args)
	for {
		select {
		case cd := <-ch1:
			if cd == 1 {
				return &CommonReply{Message: "本次定時(shí)任務(wù)成功結(jié)束執(zhí)行"}, nil
			} else {
				return &CommonReply{Message: "本次定時(shí)任務(wù)執(zhí)行失敗"}, nil
			}
		case msg := <-cancelCh:
			if msg.Payload == in.TaskId {
				// stop <- true
				respMsg := fmt.Sprintf("任務(wù)[%s]已被取消.Exist.", msg.Payload)
				logx.Info(respMsg)
				return &CommonReply{Message: respMsg}, nil
			}
		}
	}

針對(duì)自身系統(tǒng)的功能需求,可以對(duì)asynq庫已有的功能進(jìn)行更多的豐富和改進(jìn),同時(shí),你也可以借鑒其他優(yōu)秀的庫,對(duì)現(xiàn)有的庫進(jìn)行豐富和改造。

以上就是基于Golang設(shè)計(jì)一套可控的定時(shí)任務(wù)系統(tǒng)的詳細(xì)內(nèi)容,更多關(guān)于Golang定時(shí)任務(wù)系統(tǒng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Gin的中間件執(zhí)行流程與用法詳解

    Gin的中間件執(zhí)行流程與用法詳解

    我們?cè)谑褂肎in框架進(jìn)行Web開發(fā)的時(shí)候,基本上都會(huì)遇到登錄攔截的場(chǎng)景,在Gin當(dāng)中,?中間件和業(yè)務(wù)處理函數(shù)都是一樣的類型,都是一種函數(shù),本文給大家介紹了Gin的中間件執(zhí)行流程與用法,需要的朋友可以參考下
    2024-04-04
  • 淺談golang并發(fā)操作變量安全的問題

    淺談golang并發(fā)操作變量安全的問題

    這篇文章主要介紹了淺談golang并發(fā)操作變量安全的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang驗(yàn)證器之validator是使用詳解

    Golang驗(yàn)證器之validator是使用詳解

    Validator是一個(gè)?Golang?的第三方庫,用于對(duì)數(shù)據(jù)進(jìn)行校驗(yàn),常用于?API?的開發(fā)中,對(duì)客戶端發(fā)出的請(qǐng)求數(shù)據(jù)進(jìn)行嚴(yán)格校驗(yàn),防止惡意請(qǐng)求。本文通過示例詳細(xì)講解了Validator的使用,需要的可以參考一下
    2022-08-08
  • Golang中的自定義類型之間的轉(zhuǎn)換的實(shí)現(xiàn)(type conversion)

    Golang中的自定義類型之間的轉(zhuǎn)換的實(shí)現(xiàn)(type conversion)

    這篇文章主要介紹了Golang中的自定義類型之間的轉(zhuǎn)換的實(shí)現(xiàn)(type conversion),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • golang elasticsearch Client的使用詳解

    golang elasticsearch Client的使用詳解

    這篇文章主要介紹了golang elasticsearch Client的使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Go語言通過反射實(shí)現(xiàn)獲取各種類型變量的值

    Go語言通過反射實(shí)現(xiàn)獲取各種類型變量的值

    反射是程序在運(yùn)行期間獲取變量的類型和值、或者執(zhí)行變量的方法的能力,這篇文章主要為大家講講Go語言通過反射獲取各種類型變量值的方法,需要的可以參考下
    2023-07-07
  • golang中for循環(huán)遍歷channel時(shí)需要注意的問題詳解

    golang中for循環(huán)遍歷channel時(shí)需要注意的問題詳解

    這篇文章主要給大家介紹了關(guān)于golang中for循環(huán)遍歷channel時(shí)需要注意的問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04
  • 獲取Golang環(huán)境變量的三種方式小結(jié)

    獲取Golang環(huán)境變量的三種方式小結(jié)

    本文介紹了Golang中獲取環(huán)境變量的三種方式,包含使用Viper包、GoDotEnv包和os包,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-11-11
  • Golang實(shí)現(xiàn)單元測(cè)試中的接口層

    Golang實(shí)現(xiàn)單元測(cè)試中的接口層

    接口層主要負(fù)責(zé)的就是請(qǐng)求的處理,最常見的就是?HTTP?請(qǐng)求的處理。這篇文章主要為大家介紹了Golang如何實(shí)現(xiàn)單元測(cè)試中的接口層,需要的可以參考一下
    2023-03-03
  • 深入理解golang的基本類型排序與slice排序

    深入理解golang的基本類型排序與slice排序

    大家都知道排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。下面就來詳細(xì)介紹golang的基本類型排序與slice排序,有需要的朋友們可以參考借鑒。
    2016-09-09

最新評(píng)論

富源县| 沙洋县| 凯里市| 宣武区| 桃源县| 西吉县| 将乐县| 铅山县| 白河县| 南和县| 文安县| 正宁县| 泽普县| 灵寿县| 台湾省| 远安县| 东源县| 建德市| 舞阳县| 东兴市| 长治县| 遂宁市| 铜山县| 阿城市| 古田县| 波密县| 邵东县| 治县。| 乌兰浩特市| 定兴县| 本溪| 沂源县| 石柱| 全椒县| 历史| 崇义县| 吉木萨尔县| 凉城县| 柏乡县| 蚌埠市| 罗山县|