GoDasturchi
Fan-Out / Fan-In Pattern

Fan-Out / Fan-In Pattern

Fan-Out / Fan-In naqshi

Worker Pool'da bitta kanal (jobs) — ko'p worker orasida taqsimlanadi. "Fan-Out / Fan-In" naqshi shunga o'xshaydi, lekin bir oz kengroq: bir nechta bosqichni bir-biriga ulaydi, har bir bosqichda ishni "tarqatib" (fan-out), so'ng natijalarni "bitta joyga yig'ib" (fan-in) oladi.

Buni daryoning tarmoqlanishi va qayta qo'shilishiga o'xshating: bitta katta daryo bir nechta kichik irmoqqa bo'linadi (fan-out — ishni parallel bajarish uchun tarqatish), so'ng bu irmoqlar pastroqda yana bitta katta daryoga qo'shiladi (fan-in — natijalarni birlashtirish).

example.go
package main

import (
	"fmt"
	"sync"
)

func generator(nums ...int) <-chan int {
	out := make(chan int)
	go func() {
		defer close(out)
		for _, n := range nums {
			out <- n
		}
	}()
	return out
}

// fan-out: bitta kanaldan ikkita ishlov beruvchi yaratamiz
func square(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		defer close(out)
		for n := range in {
			out <- n * n
		}
	}()
	return out
}

// fan-in: bir nechta kanalni bitta kanalga birlashtiramiz
func merge(channels ...<-chan int) <-chan int {
	out := make(chan int)
	var wg sync.WaitGroup
	wg.Add(len(channels))
	for _, c := range channels {
		go func(c <-chan int) {
			defer wg.Done()
			for n := range c {
				out <- n
			}
		}(c)
	}
	go func() {
		wg.Wait()
		close(out)
	}()
	return out
}

func main() {
	in := generator(1, 2, 3, 4)
	c1 := square(in)
	c2 := square(in)

	sum := 0
	for n := range merge(c1, c2) {
		sum += n
	}
	fmt.Println(sum)
}

generator — bitta kanal orqali 1, 2, 3, 4 sonlarini "chiqarib beradi". Keyin biz shu bitta in kanalidan ikkita square ishlovchisini (c1, c2) yaratamiz — ikkalasi ham bir xil indan o'qiydi, shuning uchun sonlar ular orasida avtomatik taqsimlanadi (fan-out). Nihoyat, merge ikkala natija kanalini (c1, c2) bitta kanalga birlashtiradi (fan-in) — har bir kirish kanali uchun alohida goroutine ochib, hammasini bitta outga yozadi.

merge ichidagi sync.WaitGroup muhim: u "barcha kirish kanallari tugagach, outni yopish kerak" degan signalni to'g'ri boshqaradi. Agar buni unutsak, outni o'qiyotgan range sikli abadiy kutib qolardi ("Channel Directions" darsida ko'rgan yopilmagan kanal muammosi).

>_ Exercise

Uchinchi ishlov beruvchini (fan-out'ni kengaytirib) qo'shing.

  • generator(1, 2, 3, 4, 5, 6) chaqiring
  • shu bitta kanaldan UCHTA square ishlovchisini yarating (c1, c2, c3)
  • merge(c1, c2, c3) orqali birlashtirib, yig'indisini chop eting

Stuck? Reveal a hint to help you.

Hints (0/3)

Key Takeaway

Key Takeaway:

Fan-Out — bitta manbadan bir nechta ishlovchini oziqlantirish, Fan-In — bir nechta natijani bitta oqimga birlashtirish; ikkalasi birga ishlatilib, ishni parallel bajarish quvurini (pipeline) tashkil qiladi.

NEXT UP

Circuit Breaker Pattern

OUTPUT

$ go run main.go
Kodingizni ishga tushiring

Fan-Out / Fan-In Pattern

Fan-Out / Fan-In naqshi

Worker Pool'da bitta kanal (jobs) — ko'p worker orasida taqsimlanadi. "Fan-Out / Fan-In" naqshi shunga o'xshaydi, lekin bir oz kengroq: bir nechta bosqichni bir-biriga ulaydi, har bir bosqichda ishni "tarqatib" (fan-out), so'ng natijalarni "bitta joyga yig'ib" (fan-in) oladi.

Buni daryoning tarmoqlanishi va qayta qo'shilishiga o'xshating: bitta katta daryo bir nechta kichik irmoqqa bo'linadi (fan-out — ishni parallel bajarish uchun tarqatish), so'ng bu irmoqlar pastroqda yana bitta katta daryoga qo'shiladi (fan-in — natijalarni birlashtirish).

example.go
package main

import (
	"fmt"
	"sync"
)

func generator(nums ...int) <-chan int {
	out := make(chan int)
	go func() {
		defer close(out)
		for _, n := range nums {
			out <- n
		}
	}()
	return out
}

// fan-out: bitta kanaldan ikkita ishlov beruvchi yaratamiz
func square(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		defer close(out)
		for n := range in {
			out <- n * n
		}
	}()
	return out
}

// fan-in: bir nechta kanalni bitta kanalga birlashtiramiz
func merge(channels ...<-chan int) <-chan int {
	out := make(chan int)
	var wg sync.WaitGroup
	wg.Add(len(channels))
	for _, c := range channels {
		go func(c <-chan int) {
			defer wg.Done()
			for n := range c {
				out <- n
			}
		}(c)
	}
	go func() {
		wg.Wait()
		close(out)
	}()
	return out
}

func main() {
	in := generator(1, 2, 3, 4)
	c1 := square(in)
	c2 := square(in)

	sum := 0
	for n := range merge(c1, c2) {
		sum += n
	}
	fmt.Println(sum)
}

generator — bitta kanal orqali 1, 2, 3, 4 sonlarini "chiqarib beradi". Keyin biz shu bitta in kanalidan ikkita square ishlovchisini (c1, c2) yaratamiz — ikkalasi ham bir xil indan o'qiydi, shuning uchun sonlar ular orasida avtomatik taqsimlanadi (fan-out). Nihoyat, merge ikkala natija kanalini (c1, c2) bitta kanalga birlashtiradi (fan-in) — har bir kirish kanali uchun alohida goroutine ochib, hammasini bitta outga yozadi.

merge ichidagi sync.WaitGroup muhim: u "barcha kirish kanallari tugagach, outni yopish kerak" degan signalni to'g'ri boshqaradi. Agar buni unutsak, outni o'qiyotgan range sikli abadiy kutib qolardi ("Channel Directions" darsida ko'rgan yopilmagan kanal muammosi).

>_ Exercise

Uchinchi ishlov beruvchini (fan-out'ni kengaytirib) qo'shing.

  • generator(1, 2, 3, 4, 5, 6) chaqiring
  • shu bitta kanaldan UCHTA square ishlovchisini yarating (c1, c2, c3)
  • merge(c1, c2, c3) orqali birlashtirib, yig'indisini chop eting

Stuck? Reveal a hint to help you.

Hints (0/3)

Key Takeaway

Key Takeaway:

Fan-Out — bitta manbadan bir nechta ishlovchini oziqlantirish, Fan-In — bir nechta natijani bitta oqimga birlashtirish; ikkalasi birga ishlatilib, ishni parallel bajarish quvurini (pipeline) tashkil qiladi.

NEXT UP

Circuit Breaker Pattern