文档章节

Go 设计模式(Go patterns)

michaelpan
 michaelpan
发布于 2014/08/14 17:57
字数 1645
阅读 195
收藏 5

Generator(发生器)

在Google IO 2012大会中提到的Go pattern,记录如下,以便加深理解。 Go patterns 可以理解为Go的设计模式,这个往往是在实践中遇到的一些典型场景而总结出来的通用的方法论。 Generator可以理解为发生器

//golang partens
//Generator: function that returns a channel
func boring(msg string) <-chan string {
	c := make(chan string)
	go func() {
		for i := 0; ; i++ {
			c <- fmt.Sprintf("%s %d", msg, i)
			time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
		}
	}()
	return c
}

func main() {
	google := boring("google")
	apple := boring("apple")
	for i := 0; i < 5; i++ {
		fmt.Println(<-google)
		fmt.Println(<-apple)
	}
}

它返回一个只能接受的channel,内部起一个goruntine并发产生string(当然也可以是其他事物,这里只是用string举例) 事实上可以将boring看出一个generator服务,而且很容易生产多个实例,只要是不同的输入参数就可以了。 每个服务又是在不同的goruntine中独立运行的。 这里的管道(channel)很像服务器的handle,把主goruntine和服务的goruntine联系起来了。比起Android里面的handle 这里的channel更加直观和容易理解。 完整代码:

  1. https://github.com/panyingyun/gostudy/blob/master/generator.go

Multiplexing(多路复合)


func fanIn(input1, input2 <-chan string) <-chan string {
	c := make(chan string)
	go func() {
		for {
			c <- <-input1
		}
	}()

	go func() {
		for {
			c <- <-input2
		}
	}()
	return c
}

func main() {
	c := fanIn(boring("google"), boring("apple"))
	for i := 0; i < 10; i++ {
		fmt.Println(<-c)
	}

	fmt.Println("You're boing; I'm leaving.")
}

完整代码:

  1. https://github.com/panyingyun/gostudy/blob/master/multiplexing.go

##多路选择(select 实现的fanIn) fanIn的select语法的实现方式

func fanIn(input1, input2 <-chan string) <-chan string {
	c := make(chan string)
	go func() {
		for {
			select {
			case s := <-input1:
				c <- s
			case s := <-input2:
				c <- s
			}
		}
	}()
	return c
}

完整代码:

  1. https://github.com/panyingyun/gostudy/blob/master/select.go

##timeout(超时) 超时channel有数据则返回,超时往往都是这么弄的,特别是在网络请求的时候,超时是必要的选择。

func timeout(ch <-chan string) {
	timeout := time.After(5 * time.Second)
	for {
		select {
		case s := <-ch:
			fmt.Println(s)
		case <-timeout:
			fmt.Println("You talk too much!")
			return
		}
	}
}

完整代码:

  1. https://github.com/panyingyun/gostudy/blob/master/timeout.go

##退出(quit)

func boring(msg string, quit chan string) <-chan string {
	c := make(chan string)
	go func() {
		for i := 0; ; i++ {
			select {
			case c <- fmt.Sprintf("%s %d", msg, i):
				time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
			case <-quit:
				quit <- "See you!"
				return
			}
		}
	}()
	return c
}

func main() {
	quit := make(chan string)
	c := boring("goo", quit)
	for i := rand.Intn(10); i >= 0; i-- {
		fmt.Println(<-c)
	}
	quit <- "Bye!"
	fmt.Printf("goo says: %q\n", <-quit)

}

主goroutine通过往quit channel中写数据和boring进行通信,告诉它退出。 完整代码:

  1. https://github.com/panyingyun/gostudy/blob/master/quit.go

##Google Search的模拟实现 这里提供了Google Search模拟实现的4个版本,慢慢体会其中的道理(主要是利用前面的这些知识)

var (
	Web    = fakeSearch("web")
	Image  = fakeSearch("image")
	Video  = fakeSearch("video")
	Web1   = fakeSearch("web1")
	Image1 = fakeSearch("image1")
	Video1 = fakeSearch("video1")
	Web2   = fakeSearch("web2")
	Image2 = fakeSearch("image2")
	Video2 = fakeSearch("video2")
)

type Result string
type Search func(query string) Result

func fakeSearch(kind string) Search {
	return func(query string) Result {
		time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
		return Result(fmt.Sprintf("%s result for %q\n", kind, query))
	}
}

func Google10(query string) (results []Result) {
	results = append(results, Web(query))
	results = append(results, Image(query))
	results = append(results, Video(query))
	return
}

func Google20(query string) (results []Result) {
	c := make(chan Result)
	go func() { c <- Web(query) }()
	go func() { c <- Image(query) }()
	go func() { c <- Video(query) }()

	for i := 0; i < 3; i++ {
		result := <-c
		results = append(results, result)
	}
	return
}

func Google21(query string) (results []Result) {
	c := make(chan Result)
	go func() { c <- Web(query) }()
	go func() { c <- Image(query) }()
	go func() { c <- Video(query) }()

	timeout := time.After(80 * time.Millisecond)
	for i := 0; i < 3; i++ {
		select {
		case result := <-c:
			results = append(results, result)
		case <-timeout:
			fmt.Println("timed out")
			return
		}
	}
	return
}

func Google30(query string) (results []Result) {
	c := make(chan Result)
	go func() { c <- First(query, Web1, Web2) }()
	go func() { c <- First(query, Image1, Image2) }()
	go func() { c <- First(query, Video1, Video2) }()
	timeout := time.After(80 * time.Millisecond)
	for i := 0; i < 3; i++ {
		select {
		case result := <-c:
			results = append(results, result)
		case <-timeout:
			fmt.Println("timed out")
			return
		}
	}
	return
}

func main() {
	rand.Seed(time.Now().UnixNano())
	start := time.Now()
	results := Google30("golang")
	elapsed := time.Since(start)
	fmt.Println(results)
	fmt.Println(elapsed)
}

func First(query string, replicas ...Search) Result {
	c := make(chan Result)
	searchReplica := func(i int) { c <- replicas[i](query) }
	for i := range replicas {
		go searchReplica(i)
	}
	return <-c
}

// test first function
// func main() {

// 	rand.Seed(time.Now().UnixNano())
// 	start := time.Now()
// 	result := First("golang",
// 		fakeSearch("replica 1"),
// 		fakeSearch("replica 2"))
// 	elapsed := time.Since(start)
// 	fmt.Println(result)
// 	fmt.Println(elapsed)
// }

部分代码注解: 本人对“函数”和“方法”叫法不做什么区别,所以看到“函数”等名字可以和“方法”等同。 (1) type Result string 和type Search func(query string) Result 分别自定义一个Result类型(实际上等同一个字符串) 自定义一个函数类型 Search 定义Search的好处显而易见,代码会简化不少,而且意思表达也清晰

(2) fakeSearch是一个闭包,用于模拟搜索引擎的搜索状态 其中使用time.Sleep来模拟等待服务器返回的时间

(3) Web、Image、Video、Web1等9个var变量可以认为是9台搜索服务器, 可以认为是9台分布式服务器

(4) Google1.0搜索版本 func Google10(query string) (results []Result) 比较浅显,是将3台服务器Web、Image、Video 返回的结果进行整合返回 对这个函数中results变量的写法和return的写法是非常的Golang style, 对返回值定义了变量名,运行时会自动创建0值变量,返回的时候也就可以省略掉 这个返回参数。 从Google1.0看,它实际上是三个搜索查询是串行的,在主goroutine中运行

(5) Google2.0 搜索版本func Google20(query string) (results []Result) 将三个搜索放到不同的goroutine中,并且用channel进行连接整合,从实际运行 时间看,并发后总的搜索时间要少很多。

(6) Google2.1 搜索版本 func Google21(query string) (results []Result) 加上了 超时处理,这样可以防止搜索时间太久,在规定的80ms内必须响应用户查询 请求,返回搜索结果。 从结果看,Google2.1比Google1.0版本用好,但是搜索结果没有能最优,所以我们有了Google3.0版本

(7) Google3.0办法 func Google30(query string) (results []Result) 对于同类服务提供了多路复用,web服务器增加为两台,那么我们给两台服务器发相同 的搜索请求,谁返回结果快,我们就用谁的结果,这样比Google2.1的搜索结果要更加快, 结果也更加优秀,因为多路都超时的概率毕竟下降很多。 这里的多路复用在函数func First(query string, replicas ...Search) Result 中完成的,其中replicas 即是提供多个相同服务的服务器,输入包括搜索词和可变参数replicas,它从多服务器中响应那个最快返回结果 丢弃其他的服务器结果。

完整代码:

  1. https://github.com/panyingyun/gostudy/blob/master/googlesearch.go ##参考文献:
  2. https://talks.golang.org/2012/concurrency.slide#25

© 著作权归作者所有

共有 人打赏支持
michaelpan
粉丝 4
博文 33
码字总数 9970
作品 0
杭州
高级程序员
私信 提问
加载中

评论(2)

十一文
十一文
不错顶
i
i仅此而已
受益了! 感谢楼主,最近也在学习golang。
学了那么多年设计模式依然不会用!那可真蠢!

什么是设计模式? 设计模式(Design Pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决...

GitChat技术杂谈
2018/10/26
0
0
六个前端开发工程师必备的Web设计模式/模块资源

Yahoo的设计模式库 Yahoo的设计模式库包含了很多可以帮助开发设计人员解决遇到的问题的资源,包括开发中常常需要处理的导航,互动效果及其布局网格等大家常用的组件和模块 响应式设计模式库 ...

gbin1
2014/07/30
13
0
设计模式的作用

设计模式描述的是软件设计,因此它是独立于编程语言的,但是最终实现仍然要使用编程语言来表达。设计模式不像算法技巧,可以照搬照用,它是建立在对“面向对象”纯熟、深入理解的基础上的经验...

Dwyane_Coding
2018/01/22
0
0
阿里P7大牛细说架构——设计模式专栏

设计模式介绍 对于有经验的开发人员,学习设计模式有助于我们找到在软件开发过程中所面临的问题的最佳解决方案。一直以来软件都是为了用来解决现实生活中遇到的复杂问题而存在,设计模式(D...

别打我会飞
2018/11/25
0
0
设计模式(Swift) - 3.观察者模式、建造者模式

上一篇 设计模式(Swift) - 2.单例模式、备忘录模式和策略模式中讲了三种常见的设计模式. 单例模式: 限制了类的实例化,一个类只能实例化一个对象,所有对单例对象的引用都是指向了同一个对象....

Dariel
2018/07/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Java单例模式学习记录

在项目开发中经常能遇见的设计模式就是单例模式了,而实现的方式最常见的有两种:饿汉和饱汉(懒汉)。由于日常接触较多而研究的不够深入,导致面试的时候被询问到后有点没底,这里记录一下学习...

JerryLin123
昨天
2
0
VSCODE 无法调试

VSCODE 无法调试 可以运行 可能的原因: GCC 的参数忘了加 -g

shzwork
昨天
3
0
理解去中心化 稳定币 DAI

随着摩根大通推出JPM Coin 稳定币,可以预见稳定币将成为区块链落地的一大助推器。 坦白来讲,对于一个程序员的我来讲(不懂一点专业经济和金融),理解DAI的机制,真的有一点复杂。耐心看完...

Tiny熊
昨天
4
0
5.线程实现

用于线程实现的Python模块 Python线程有时称为轻量级进程,因为线程比进程占用的内存少得多。 线程允许一次执行多个任务。 在Python中,以下两个模块在一个程序中实现线程 - _thread 模块 th...

Eappo_Geng
昨天
4
0
ServiceLoader

创建一个接口文件在resources资源目录下创建META-INF/services文件夹在services文件夹中创建文件,以接口全名命名创建接口实现类 内容me.zzp.ar.d.PostgreSQLDialectme.zzp.ar.d.Hype...

Cobbage
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部