github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/prepare/18channeluse/fanout/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"time"
     7  )
     8  
     9  /*
    10  扇出模式(FanOut)是将一个输入channel扇出为多个channel。
    11  
    12  扇出行为至少可以分为两种:
    13  
    14  1. 从输入channel中读取一个数据,发送给每个输入channel,这种模式称之为Tee模式; 个人理解:有点类似于redis中的订阅发布
    15  2. 从输入channel中读取一个数据,在输出channel中选择一个channel发送
    16  */
    17  
    18  //1.
    19  // 一个输入 ,多个输出
    20  func fanOut1(ch <-chan interface{}, out []chan interface{}, async bool) {
    21  	go func() {
    22  		defer func() {
    23  			for i := 0; i < len(out); i++ {
    24  				close(out[i])
    25  			}
    26  		}()
    27  		for v := range ch {
    28  			v := v // 重新声明,避免闭包应用问题
    29  			for i := 0; i < len(out); i++ {
    30  				i := i
    31  				if async {
    32  					go func() {
    33  						out[i] <- v
    34  					}()
    35  				} else {
    36  					out[i] <- v
    37  				}
    38  			}
    39  		}
    40  	}()
    41  }
    42  
    43  // 递归方式
    44  func fanOut1Reflect(ch <-chan interface{}, out []chan interface{}) {
    45  	go func() {
    46  		defer func() {
    47  			for i := 0; i < len(out); i++ {
    48  				close(out[i])
    49  			}
    50  		}()
    51  		cases := make([]reflect.SelectCase, len(out))
    52  		for i := range cases {
    53  			cases[i].Dir = reflect.SelectSend // 发送选择
    54  		}
    55  
    56  		for v := range ch {
    57  			v := v
    58  			for i := range cases {
    59  				cases[i].Chan = reflect.ValueOf(out[i])
    60  				cases[i].Send = reflect.ValueOf(v)
    61  			}
    62  			// 选择
    63  			for _ = range cases {
    64  				i, _, _ := reflect.Select(cases)
    65  				cases[i].Chan = reflect.ValueOf(nil)
    66  			}
    67  		}
    68  
    69  	}()
    70  }
    71  
    72  // 2.
    73  // 一个输入, 选其中一个输出 分布模式
    74  func fanOut2(ch <-chan interface{}, out []chan interface{}) {
    75  	go func() {
    76  		defer func() {
    77  			for i := 0; i < len(out); i++ {
    78  				close(out[i])
    79  			}
    80  		}()
    81  
    82  		// roundrobin
    83  		var i = 0
    84  		var n = len(out)
    85  		for v := range ch {
    86  			v := v
    87  			out[i] <- v
    88  			i = (i + 1) % n
    89  		}
    90  	}()
    91  }
    92  
    93  func fanOut2Reflect(ch <-chan interface{}, out []chan interface{}) {
    94  	go func() {
    95  		defer func() {
    96  			for i := 0; i < len(out); i++ {
    97  				close(out[i])
    98  			}
    99  		}()
   100  
   101  		cases := make([]reflect.SelectCase, len(out))
   102  		for i := range cases {
   103  			cases[i].Dir = reflect.SelectSend
   104  			cases[i].Chan = reflect.ValueOf(out[i])
   105  
   106  		}
   107  
   108  		for v := range ch {
   109  			v := v
   110  			for i := range cases {
   111  				cases[i].Send = reflect.ValueOf(v)
   112  			}
   113  			_, _, _ = reflect.Select(cases)
   114  		}
   115  	}()
   116  }
   117  
   118  func asStream(done <-chan struct{}) <-chan interface{} {
   119  	s := make(chan interface{})
   120  	values := []int{1, 2, 3, 4, 5}
   121  	go func() {
   122  		defer close(s)
   123  
   124  		for _, v := range values {
   125  			select {
   126  			case <-done:
   127  				return
   128  			case s <- v:
   129  			}
   130  		}
   131  
   132  	}()
   133  	return s
   134  }
   135  
   136  func runFanout1() {
   137  	source := asStream(nil)
   138  	channels := make([]chan interface{}, 5)
   139  
   140  	fmt.Println("fanOut1")
   141  	for i := 0; i < 5; i++ {
   142  		channels[i] = make(chan interface{})
   143  	}
   144  	fanOut1(source, channels, false)
   145  	for i := 0; i < 5; i++ {
   146  		for j := 0; j < 5; j++ {
   147  			fmt.Printf("channel#%d: %v\n", j, <-channels[j])
   148  		}
   149  	}
   150  
   151  	fmt.Println("\nfanOut1 By Reflect")
   152  	source = asStream(nil)
   153  	for i := 0; i < 5; i++ {
   154  		channels[i] = make(chan interface{})
   155  	}
   156  	fanOut1Reflect(source, channels)
   157  	for i := 0; i < 5; i++ {
   158  		for j := 0; j < 5; j++ {
   159  			fmt.Printf("channel#%d: %v\n", j, <-channels[j])
   160  		}
   161  	}
   162  }
   163  func runFanout2() {
   164  	done := make(chan struct{})
   165  	source := asStream(done)
   166  	channels := make([]chan interface{}, 5)
   167  
   168  	fmt.Println("fanOut")
   169  	for i := 0; i < 5; i++ {
   170  		channels[i] = make(chan interface{})
   171  	}
   172  	fanOut2(source, channels)
   173  	for i := 0; i < 5; i++ {
   174  		i := i
   175  		go func() {
   176  			for j := 0; j < 5; j++ {
   177  				v, ok := <-channels[i]
   178  				if ok {
   179  					fmt.Printf("channel#%d: %v\n", i, v)
   180  				}
   181  
   182  			}
   183  		}()
   184  	}
   185  	time.Sleep(time.Second)
   186  	close(done)
   187  
   188  	fmt.Println("fanOut By Reflect")
   189  	done = make(chan struct{})
   190  	source = asStream(done)
   191  	for i := 0; i < 5; i++ {
   192  		channels[i] = make(chan interface{})
   193  	}
   194  	fanOut2Reflect(source, channels)
   195  	for i := 0; i < 5; i++ {
   196  		i := i
   197  		go func() {
   198  			for j := 0; j < 5; j++ {
   199  				v, ok := <-channels[i]
   200  				if ok {
   201  					fmt.Printf("channel#%d: %v\n", i, v)
   202  				}
   203  			}
   204  		}()
   205  	}
   206  	time.Sleep(time.Second)
   207  	close(done)
   208  }
   209  
   210  func main() {
   211  
   212  	//runFanout1()
   213  	runFanout2()
   214  }