Go語言異步API設(shè)計的扇入扇出模式詳解
前言
扇出/扇入模式是更高級 API 集成的主要內(nèi)容。這些應(yīng)用程序并不總是表現(xiàn)出相同的可用性或性能特征。
扇出是從電子工程中借用的一個術(shù)語,它描述了輸入的邏輯門連接到另一個輸出門的數(shù)量。輸出需要提供足夠的電流來驅(qū)動所有連接的輸入。在事務(wù)處理系統(tǒng)中,用來描述為了服務(wù)一個輸入請求而需要做的請求總數(shù)。
扇入是指為邏輯單元的輸入方程提供輸入信號的最大數(shù)量。扇入是定義單個邏輯門可以接受的最大數(shù)字輸入數(shù)量的術(shù)語。大多數(shù)晶體管-晶體管邏輯 (TTL) 門有一個或兩個輸入,盡管有些有兩個以上。典型的邏輯門具有 1 或 2 的扇入。

扇入/扇出服務(wù)
我們舉一個現(xiàn)實(shí)世界的例子,一個電子商務(wù)網(wǎng)站將自己與一個第三方支付網(wǎng)關(guān)整合在一起。 這里,網(wǎng)站使用支付網(wǎng)關(guān)的 API 來彈出支付屏幕并輸入安全證書。同時,網(wǎng)站可能會調(diào)用另一個稱為分析的 API 來記錄支付的嘗試。這種將一個請求分叉成多個請求的過程被稱為 fan-out 扇出。在現(xiàn)實(shí)世界中,一個客戶請求可能涉及許多扇出服務(wù)。
另一個例子是 MapReduce。Map 是一個扇入的操作,而 Reduce 是一個扇出的 操作。一個服務(wù)器可以將一個信息扇出到下一組服務(wù)(API),并忽略結(jié)果?;蛘呖梢缘鹊竭@些服務(wù)器的所有響應(yīng)都返回。如 如下圖所示,一個傳入的請求被服務(wù)器復(fù)用為轉(zhuǎn)換成兩個傳出的請求:

扇入 fan-in 是一種操作,即兩個或更多傳入的請求會聚成一個請求。這種情況下,API如何聚合來自多個后端服務(wù)的結(jié)果,并將結(jié)果即時返回給客戶。
例如,想想一個酒店價格聚合器或航班票務(wù)聚合器,它從不同的數(shù)據(jù)提供者那里獲取關(guān)于多個酒店或航班的請求信息并顯示出來。
下圖顯示了扇出操作是如何結(jié)合多個請求并準(zhǔn)備一個最終的響應(yīng),由客戶端消費(fèi)的。

客戶端也可以是一個服務(wù)器,為更多的客戶提供服務(wù)。如上圖所示,左側(cè)的服務(wù)器正在收集來自酒店 A、酒店 B 和 航空公司供應(yīng)商 A,并為不同的客戶準(zhǔn)備另一個響應(yīng)。
因此,扇入和扇出操作并不總是完全相互獨(dú)立的。大多數(shù)情況下,它將是一個混合場景,扇入和扇出操作都是相互配合的。
請記住,對下一組服務(wù)器的扇出操作可以是異步的。也是如此。對于扇入請求來說,這可能不是真的。扇入操作有時被稱為 API 調(diào)用。
Go 語言實(shí)現(xiàn)扇入/扇出模式
Fan-out:多個 goroutine 從同一個通道讀取數(shù)據(jù),直到該通道關(guān)閉。OUT 是一種張開的模式,所以又被稱為扇出,可以用來分發(fā)任務(wù)。
Fan-in:1 個 goroutine 從多個通道讀取數(shù)據(jù),直到這些通道關(guān)閉。IN 是一種收斂的模式,所以又被稱為扇入,用來收集處理的結(jié)果。

package main
import (
"context"
"log"
"sync"
"time"
)
// Task 包含任務(wù)編號及任務(wù)所需時長
type Task struct {
Number int
Cost time.Duration
}
// task channel 生成器
func taskChannelGerenator(ctx context.Context, taskList []Task) <-chan Task {
taskCh := make(chan Task)
go func() {
defer close(taskCh)
for _, task := range taskList {
select {
case <-ctx.Done():
return
case taskCh <- task:
}
}
}()
return taskCh
}
// doTask 處理并返回已處理的任務(wù)編號作為通道的函數(shù)
func doTask(ctx context.Context, taskCh <-chan Task) <-chan int {
doneTaskCh := make(chan int)
go func() {
defer close(doneTaskCh)
for task := range taskCh {
select {
case <-ctx.Done():
return
default:
log.Printf("do task number: %d\n", task.Number)
// task 任務(wù)處理
// 根據(jù)任務(wù)耗時休眠
time.Sleep(task.Cost)
doneTaskCh <- task.Number // 已處理任務(wù)的編號放入通道
}
}
}()
return doneTaskCh
}
// `fan-in` 意味著將多個數(shù)據(jù)流復(fù)用或合并成一個流。
// merge 函數(shù)接收參數(shù)傳遞的多個通道 “taskChs”,并返回單個通道 “<-chan int”
func merge(ctx context.Context, taskChs []<-chan int) <-chan int {
var wg sync.WaitGroup
mergedTaskCh := make(chan int)
mergeTask := func(taskCh <-chan int) {
defer wg.Done()
for t := range taskCh {
select {
case <-ctx.Done():
return
case mergedTaskCh <- t:
}
}
}
wg.Add(len(taskChs))
for _, taskCh := range taskChs {
go mergeTask(taskCh)
}
// 等待所有任務(wù)處理完畢
go func() {
wg.Wait()
close(mergedTaskCh)
}()
return mergedTaskCh
}
func main() {
start := time.Now()
// 使用 context 來防止 goroutine 泄漏,即使在處理過程中被中斷
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// taskList 定義每個任務(wù)及其成本
taskList := []Task{
Task{1, 1 * time.Second},
Task{2, 7 * time.Second},
Task{3, 2 * time.Second},
Task{4, 3 * time.Second},
Task{5, 5 * time.Second},
Task{6, 3 * time.Second},
}
// taskChannelGerenator 是一個函數(shù),它接收一個 taskList 并將其轉(zhuǎn)換為 Task 類型的通道
// 執(zhí)行結(jié)果(int slice channel)存儲在 worker 中
// 由于 doTask 的結(jié)果是一個通道,被分給了多個 worker,這就對應(yīng)了 fan-out 處理
taskCh := taskChannelGerenator(ctx, taskList)
numWorkers := 4
workers := make([]<-chan int, numWorkers)
for i := 0; i < numWorkers; i++ {
workers[i] = doTask(ctx, taskCh) // doTask 處理并返回已處理的任務(wù)編號作為通道的函數(shù)
}
count := 0
for d := range merge(ctx, workers) { // merge 從中讀取已處理的任務(wù)編號
count++
log.Printf("done task number: %d\n", d)
}
log.Printf("Finished. Done %d tasks. Total time: %fs", count, time.Since(start).Seconds())
}
參考鏈接:
Understanding the Fan-Out/Fan-In API Integration Pattern
以上就是Go語言異步API設(shè)計的扇入扇出模式詳解的詳細(xì)內(nèi)容,更多關(guān)于Go異步API扇入扇出模式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用Golang快速構(gòu)建出命令行應(yīng)用程序
在日常開發(fā)中,大家對命令行工具(CLI)想必特別熟悉了,如果說你不知道命令工具,那你可能是個假開發(fā)。每天都會使用大量的命令行工具,例如最常用的Git、Go、Docker等,這篇文章主要介紹了使用Golang快速構(gòu)建出命令行應(yīng)用程序,需要的朋友可以參考下2023-02-02
Go語言defer與return執(zhí)行的先后順序詳解
這篇文章主要為大家介紹了Go語言defer與return執(zhí)行的先后順序詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Golang實(shí)現(xiàn)微信公眾號后臺接入的示例代碼
這篇文章主要介紹了Golang實(shí)現(xiàn)微信公眾號后臺接入的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
詳解Go語言如何實(shí)現(xiàn)一個最簡化的協(xié)程池
這篇文章主要為大家詳細(xì)介紹了Go語言如何實(shí)現(xiàn)一個最簡化的協(xié)程池,文中的示例代碼講解詳細(xì),具有一定的參考價值,有需要的小伙伴可以了解一下2023-10-10

