Go语言HTTPServer开发的六种实现学完了 net/http 和 fasthttp 两个HTTP协议接口的客户端实现,接下来就要开始Server的开发,不学不知道一学吓一跳,居然这两个库还支持Server的开发,太方便了 。
相比于Java的HTTPServer开发基本上都是使用Spring或者Springboot框架 , 总是要配置各种配置类,各种 handle 对象 。Golang的Server开发显得非常简单,就是因为特别简单,或者说没有形成特别统一的规范或者框架 , 我发现了很多实现方式,HTTP协议基于还是 net/http 和 fasthttp , 但是 handle 语法就多种多样了 。
先复习一下: Golang语言HTTP客户端实践 、 Golang fasthttp实践。
在Golang语言方面 , 实现某个功能的库可能会比较多 , 有机会还是要多跟同行交流 , 指不定就发现了更好用的库 。下面我分享我学到的六种Server开发的实现Demo 。
基于 net/http 实现,这是一种比较基础的,对于接口和 handle 映射关系处理并不优雅,不推荐使用 。
第二种也是基于 net/http , 这种编写语法可以很好地解决第一种的问题,handle和path有了类似配置的语法,可读性提高了很多 。
第三个基于 net/http 和 github.com/labstack/echo ,后者主要提供了 Echo 对象用来处理各类配置包括接口和handle映射,功能很丰富,可读性最佳 。
第四种依然基于 net/http 实现,引入了 github.com/gin-gonic/gin 的路由 , 看起来接口和 handle 映射关系比较明晰了 。
第五种基于 fasthttp 开发 , 使用都是 fasthttp 提供的API , 可读性尚可,handle配置倒是更像Java了 。
第六种依然基于 fasthttp , 用到了 github.com/buaazp/fasthttprouter,有点奇怪两个居然不在一个GitHub仓库里 。使用语法跟第三种方式有点类似,比较有条理,有利于阅读 。
Go 语言 channel 的阻塞问题Hello,大家好,又见面了!上一遍我们将 channel 相关基础以及使用场景 。这一篇,还需要再次进阶理解channel 阻塞问题 。以下创建一个chan类型为int,cap 为3 。
channel 内部其实是一个环形buf数据结构,是一种滑动窗口机制,当make完后,就分配在 Heap 上 。
上面,向 chan 发送一条“hello”数据:
如果 G1 发送数据超过指定cap时,会出现什么情况?
看下面实例:
以上会出现什么,chan 缓冲区允许大小为1,如果再往chan仍数据,满了就会被阻塞,那么是如何实现阻塞的呢?当 chan 满时 , 会进入 gopark,此时 G1 进入一个 waiting 状态,然后会创建一个 sudog 对象,其实就sendq队列 , 把 200放进去 。等 buf 不满的时候,再唤醒放入buf里面 。
通过如下源码,你会更加清晰:
上面,从 chan 获取数据:
Go 语言核心思想:“Do not communicate by sharing memory; instead, share memory by communicating.” 你可以看看这本书名叫:Effective Go
如果接收者,接收一个空对象,也会发生什么情况?
代码示例:
也会报错如下:
上面,从 chan 取出数据 , 可是没有数据了 。此时,它会把 接收者 G2 阻塞掉,也是和G1发送者一样,也会执行 gopark 将状态改为 waiting,不一样的点就是 。
正常情况下,接收者G2作为取出数据是去 buf 读取数据的,但现在,buf 为空了 , 此时,接收者G2会将sudog导出来,因为现在G2已经被阻塞了嘛 , 会把G2给G,然后将t := -ch中变量t是在栈上的地址,放进去elem , 也就是说,只存它的地址指针在sudog里面 。
最后,ch - 200当G1往 chan 添加200这个数据 , 正常情况是将数据添加到buf里面,然后唤醒 G2 是吧,而现在是将 G1 的添加200数据直接干到刚才G2阻塞的t这里变量里面 。
你会认为,这样真的可以吗?想一想 , G2 本来就是已经阻塞了,然后我们直接这么干肯定没有什么毛?。?而且效率提高了 , 不需要再次放入buf再取出,这个过程也是需要时间 。不然,不得往chan添加数据需要加锁、拷贝、解锁一序列操作,那肯定就慢了,我想Go语言是为了高效及内存使用率的考虑这样设计的 。(注意,一般都是在runtime里面完成,不然会出现象安全问题 。)
总结:
chan 类型的特点:chan 如果为空,receiver 接收数据的时候就会阻塞等待 , 直到 chan 被关闭或者有新的数据到来 。有这种个机制,就可以实现 wait/notify 的设计模式 。
相关面试题:
Go语言实践模式 - 函数选项模式(Functional Options Pattern)大家好,我是小白,有点黑的那个白 。
最近遇到一个问题 , 因为业务需求,需要对接第三方平台.
而三方平台提供的一些HTTP(S)接口都有统一的密钥生成规则要求.
为此我们封装了一个独立的包 xxx-go-sdk 以便维护和对接使用.
其中核心的部分是自定义HTTP Client,如下:
一些平台会要求appKey/appSecret等信息,所以Client结构体就变成了这样,这时参数还比较少, 而且是必填的参数 , 我们可以提供构造函数来明确指定 。
看起来很满足,但是当我们需要增加一个 Timeout 参数来控制超时呢?
或许go语言实现设计模式你会说这还不简单,像下面一样再加一个参数呗
那再加些其go语言实现设计模式他的参数呢go语言实现设计模式?那构造函数的参数是不是又长又串 , 而且每个参数不一定是必须的,有些参数我们又会考虑默认值的问题 。
为此,勤劳但尚未致富的 gophers 们使用了总结一种实践模式
首先提取所有需要的参数到一个独立的结构体 Options , 当然你也可以用 Configs 啥的.
然后为每个参数提供设置函数
这样我们就为每个参数设置了独立的设置函数 。返回值func(*Options)看着有点不友好,我们提取下定义为单个Option调整一下代码
当我们需要添加更多的参数时,只需要在 Options 添加新的参数并添加新参数的设置函数即可 。
比如现在要添加新的参数 Timeout
这样后续不管新增多少参数 , 只需要新增配置项并添加独立的设置函数即可轻松扩展,并且不会影响原有函数的参数顺序和个数位置等 。
至此,每个选项是区分开来了,那么怎么作用到我们的 Client 结构体上呢?
首先,配置选项都被提取到了 Options 结构体重,所以我们需要调整一下 Client 结构体的参数
其次,每一个选项函数返回 Option,那么任意多个就是 ...Option , 我们调整一下构造函数 NewClient 的参数形式,改为可变参数,不再局限于固定顺序的几个参数 。
然后循环遍历每个选项函数 , 来生成Client结构体的完整配置选项 。
那么怎么调用呢?对于调用方而已,直接在调用构造函数NewClient()的参数内添加自己需要的设置函数(WithXXX)即可
当需要设置超时参数,直接添加 WithTimeout即可,比如设置3秒的超时
配置选项的位置可以任意设置,不需要受常规的固定参数顺序约束 。
可以看到,这种实践模式主要作用于配置选项 , 利用函数支持的特性来实现的,为此得名 Functional Options Pattern,优美的中国话叫做「函数选项模式」 。
最后, 我们总结回顾一下在Go语言中函数选项模式的优缺点
Go语言设计与实现(上)【go语言实现设计模式 go语言设计模式pdf】基本设计思路:
类型转换、类型断言、动态派发 。iface , eface 。
反射对象具有的方法:
编译优化:
内部实现:
实现 Context 接口有以下几个类型(空实现就忽略了):
互斥锁的控制逻辑:
设计思路:
(以上为写被读阻塞,下面是读被写阻塞)
总结,读写锁的设计还是非常巧妙的:
设计思路:
WaitGroup 有三个暴露的函数:
部件:
设计思路:
结构:
Once 只暴露了一个方法:
实现:
三个关键点:
细节:
让多协程任务的开始执行时间可控(按顺序或归一) 。(Context 是控制结束时间)
设计思路: 通过一个锁和内置的 notifyList 队列实现,Wait() 会生成票据,并将等待协程信息加入链表中,等待控制协程中发送信号通知一个(Signal())或所有(Boardcast())等待者(内部实现是通过票据通知的)来控制协程解除阻塞 。
暴露四个函数:
实现细节:
部件:
包: golang.org/x/sync/errgroup
作用:开启func() error函数签名的协程 , 在同 Group 下协程并发执行过程并收集首次 err 错误 。通过 Context 的传入,还可以控制在首次 err 出现时就终止组内各协程 。
设计思路:
结构:
暴露的方法:
实现细节:
注意问题:
包: "golang.org/x/sync/semaphore"
作用:排队借资源(如钱,有借有还)的一种场景 。此包相当于对底层信号量的一种暴露 。
设计思路:有一定数量的资源 Weight , 每一个 waiter 携带一个 channel 和要借的数量 n 。通过队列排队执行借贷 。
结构:
暴露方法:
细节:
部件:
细节:
包: "golang.org/x/sync/singleflight"
作用:防击穿 。瞬时的相同请求只调用一次,response 被所有相同请求共享 。
设计思路:按请求的 key 分组(一个 *call 是一个组,用 map 映射存储组),每个组只进行一次访问 , 组内每个协程会获得对应结果的一个拷贝 。
结构:
逻辑:
细节:
部件:
如有错误,请批评指正 。
go语言实现设计模式的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于go语言设计模式pdf、go语言实现设计模式的信息别忘了在本站进行查找喔 。
推荐阅读
- jqueryhover移出,jquery移除readonly
- mysql服务器字符,mysql服务器名称是啥
- html5拖拽的图片删不掉,html拖拽页面特效
- php创建数据库的语法 php中数据库怎么设计
- js中的$和this,js中this的用法
- 手机怎么解除qq屏蔽,手机怎么解除屏蔽的人
- postgrefor循环,postman循环调用接口并传入动态参数
- go语言结构体怎么转 go 结构体转json
- flutter和js混合开发,flutter h5混合开发