go语言接口复用 go语言接口实现

Go: HTTP何时才会复用TCP 连接 几乎每个ITgo语言接口复用的同学都知道 HTTP底层是 TCPgo语言接口复用,也知道多个HTTP请求可以复用一个TCP连接,但究其细节又不甚了解 。
那么HTTP 请求何时才能复用一个TCP连接呢?我们先做个例子
输出的结果如下
也就是说底层并没有复用 。也可以通过命令行查看连接,的确是建立了两条连接 。
仔细阅读 go的http包,可以发现
只有当前一次的请求的数据被读完并且关闭请求后,才能复用
我们改造一下这个例子
输出的结果如下
通过Linux系统查看也只有一个连接 。
golang复用http.request.body业务当中有需要分发http.request.body的场景 。比如微信回调消息只能指定一个地址,所以期望可以复制一份消息发给其他服务 。由服务B和接收微信回调的服务A一起处理微信回调信息 。
最开始考虑的是直接转发http.request 。使用 ReverseProxy 直接将http.request由服务A转发给服务B 。但是微信涉及到验证等问题,完全调整好非常麻烦 。所以转换思路,打算将http.request.body的内容直接post给服务B 。
可是http.request是readcloser 。我们将http.request readAll的时候讲无法再次读取http.request里面的信息 。
其中c表示的是http的上下文
1.我们先将body从http.request里面读取出来,保存到一个变量里面 。
2.然后再将变量里面的数据使用ioutil.NopCloser方法写回到http.request里面 。
NopCloser returns a ReadCloser with a no-op Close method wrapping the provided Reader r.
NopCloser用一个无操作的Close方法包装Reader r返回一个ReadCloser接口 。
这样我们就可以再次使用c.request来进行处理了 。
golang复用http-request-body
golang http client keep-alive最大连接数Client前两个请求对同一个host发起,复用了55564接口的链接,第三次请求对另外一个HOST发起 , 由于MaxIdleConns=1,会关闭前一个链接然后发起一个新的链接,第四次同样也会关闭第三次的链接,重新发起 。说明MaxIdleConns限制了最大keep-alive的连接数,超出的连接会被关闭掉 。
Client对不同的两个Host发起的请求,都复用了连接
golang sync.pool对象复用 并发原理 缓存池 在go http每一次go serve(l)都会构建Request数据结构 。在大量数据请求或高并发的场景中 , 频繁创建销毁对象,会导致GC压力 。解决办法之一就是使用对象复用技术 。在http协议层之下,使用对象复用技术创建Request数据结构 。在http协议层之上,可以使用对象复用技术创建(w,*r,ctx)数据结构 。这样即可以回快TCP层读包之后的解析速度,也可也加快请求处理的速度 。
先上一个测试:
结论是这样的:
貌似使用池化,性能弱爆了???这似乎与net/http使用sync.pool池化Request来优化性能的选择相违背 。这同时也说明了一个问题,好的东西,如果滥用反而造成了性能成倍的下降 。在看过pool原理之后,结合实例,将给出正确的使用方法,并给出预期的效果 。
sync.Pool是一个 协程安全 的 临时对象池。数据结构如下:
local 成员的真实类型是一个 poolLocal 数组 , localSize 是数组长度 。这涉及到Pool实现,pool为每个P分配了一个对象 , P数量设置为runtime.GOMAXPROCS(0) 。在并发读写时,goroutine绑定的P有对象 , 先用自己的,没有去偷其它P的 。go语言将数据分散在了各个真正运行的P中,降低了锁竞争,提高了并发能力 。
不要习惯性地误认为New是一个关键字 , 这里的New是Pool的一个字段,也是一个闭包名称 。其API:
如果不指定New字段,对象池为空时会返回nil,而不是一个新构建的对象 。Get()到的对象是随机的 。
原生sync.Pool的问题是,Pool中的对象会被GC清理掉 , 这使得sync.Pool只适合做简单地对象池,不适合作连接池 。
pool创建时不能指定大?。?没有数量限制 。pool中对象会被GC清掉,只存在于两次GC之间 。实现是pool的init方法注册了一个poolCleanup()函数,这个方法在GC之前执行,清空pool中的所有缓存对象 。
为使多协程使用同一个POOL 。最基本的想法就是每个协程,加锁去操作共享的POOL,这显然是低效的 。而进一步改进 , 类似于ConcurrentHashMap(JDK7)的分Segment,提高其并发性可以一定程度性缓解 。
注意到pool中的对象是无差异性的 , 加锁或者分段加锁都不是较好的做法 。go的做法是为每一个绑定协程的P都分配一个子池 。每个子池又分为私有池和共享列表 。共享列表是分别存放在各个P之上的共享区域,而不是各个P共享的一块内存 。协程拿自己P里的子池对象不需要加锁 , 拿共享列表中的就需要加锁了 。
Get对象过程:
Put过程:
如何解决Get最坏情况遍历所有P才获取得对象呢:
方法1止前sync.pool并没有这样的设置 。方法2由于goroutine被分配到哪个P由调度器调度不可控,无法确保其平衡 。
由于不可控的GC导致生命周期过短 , 且池大小不可控,因而不适合作连接池 。仅适用于增加对象重用机率 , 减少GC负担 。2
执行结果:
单线程情况下,遍历其它无元素的P , 长时间加锁性能低下 。启用协程改善 。
结果:
测试场景在goroutines远大于GOMAXPROCS情况下,与非池化性能差异巨大 。
测试结果
可以看到同样使用*sync.pool , 较大池大小的命中率较高,性能远高于空池 。
结论:pool在一定的使用条件下提高并发性能,条件1是协程数远大于GOMAXPROCS,条件2是池中对象远大于GOMAXPROCS 。归结成一个原因就是使对象在各个P中均匀分布 。
池pool和缓存cache的区别 。池的意思是,池内对象是可以互换的,不关心具体值,甚至不需要区分是新建的还是从池中拿出的 。缓存指的是KV映射 , 缓存里的值互不相同,清除机制更为复杂 。缓存清除算法如LRU、LIRS缓存算法 。
池空间回收的几种方式 。一些是GC前回收,一些是基于时钟或弱引用回收 。最终确定在GC时回收Pool内对象 , 即不回避GC 。用java的GC解释弱引用 。GC的四种引用:强引用、弱引用、软引用、虚引用 。虚引用即没有引用,弱引用GC但有空间则保留,软引用GC即清除 。ThreadLocal的值为弱引用的例子 。
regexp 包为了保证并发时使用同一个正则,而维护了一组状态机 。
fmt包做字串拼接,从sync.pool拿[]byte对象 。避免频繁构建再GC效率高很多 。
【go语言接口复用 go语言接口实现】关于go语言接口复用和go语言接口实现的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息 , 记得收藏关注本站 。

    推荐阅读