Go 自带的 http/server.go 的连接解析 与 如何结合 master-worker 并发模式,提高单机并发能力
作者:林冠宏 / 指尖下的幽灵
GitHub : https://github.com/af913337456/
腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities
关于
server.go
源码的解析可以去搜下,已经有很多且还不错的文章。
正文:
从我们启动http.ListenAndServe(port,router)
开始,server.go
内部最终在一个for
循环中的 accept
方法中不停地等待客户端
的连接到来。
每接收到一个accept
就启动一个 gorutine
去处理当前ip
的连接。也就是源码里的go c.serve(ctx)
。这一个步骤在 c.serve(ctx)
它并不是简单的形式:
请求-->处理请求-->返回结果-->断开这个连接-->结束当前的 gorutine
根据我的调试结果
与源码分析
显示,正确的形式是下面这样的:
为每一个连接的用户启动了一个长连接,
serve
方法内部有个超时的设置是c.rwc.SetReadDeadline(time.Time{})
,这样子的情况,如果内部不出错,当前的连接断开的条件是客户端
自己断开,或nat
超时。这个连接建立后,以
ip
为单位,当前的客户端
,此时它的所有http请求
,例如get
,post
,它们都会在这个启动的gorutine
内进行分发
与被处理
。也就是说,同一个
ip
,多个不同的
请求,这里不会触发另一个accept
,不会再去启动一个go c.serve(ctx)
上述我们得出结论:
如果有
100万
个accept
,就证明有100万
个连接,100万
个ip
与当前server
连接。即是我们说的百万连接
百万连接
不是百万请求
每一个连接,它可以进行多个
http请求
,它的请求都在当前启动这个连接的gorutine
里面进行。c.serve(...)
源码中的for 死循环
就是负责读取每个请求再分发
for {
w, err := c.readRequest(ctx) // 读取一个 http 请求
//...
ServeHTTP(...)
}
- 我们的
100万
连接里面,有可能并发更多的请求,例如几百万请求,一个客户端
快速调用多个请求api
图解总结
结合 master-worker 并发模式
根据我们上面的分析,每一个新连接到来,go 就会启动一个 gorutine
,在源码里面也没有看到有一个量级的限制,也就是达到多少连接就不再接收。我们也知道,服务器是有处理瓶颈的。
所以,在这里插播一个优化点
,就是在server.go
内部做一个连接数目的限制。
master-worker
模式本身是启动多个worker
线程,去并发读取
有界队列里面的任务,并执行。
我自身已经实现了一个go版本
的master-worker
,做过下面的尝试:
- 在
go c.serve(ctx)
处做修改,如下。
if srv.masterWorkerModel {
// lgh --- way to execute
PoolMaster.AddJob(
masterworker.Job{
Tag:" http server ",
Handler: func() {
c.serve(ctx)
fmt.Println("finish job") // 这一句在当前 ip 断开连接后才会输出
},
})
}else{
go c.serve(ctx)
}
func (m Master) AddJob(job Job) {
fmt.Println("add a job ")
m.JobQueue <- job // jobQueue 是具备缓冲的
}
// worker
func (w Worker) startWork(master *Master) {
go func() {
for {
select {
case job := <-master.JobQueue:
job.doJob(master)
}
}
}()
}
// job
func (j Job) doJob(master *Master) {
go func() {
fmt.Println(j.Tag+" --- doing job...")
j.Handler()
}()
}
不难理解它的模式。
现在我们使用生产者--消费者模式
进行假设,连接的产生
是生产者
,<-master.JobQueue
是消费者
,因为每一次消费就是启动一个处理的gorutine
。
因为我们在accept
一个请求到<-master.JobQueue
,管道输出一个的这个过程中,可以说是没有耗时操作的,这个job
,它很快就被输出了管道。也就是说,消费很快
,那么实际的生产环境
中,我们的worker
工作协程
启动5~10
个就有余了。
考虑如果出现了消费跟不上
的情况,那么多出来的job
将会被缓冲到channel
里面。这种情况可能出现的情景是:
短时间十万+级别连接的建立,就会导致
worker
读取不过来。不过,即使发生了,也是很快就取完的。因为间中的耗时几乎可以忽略不计!
也就说,短时间
大量连接的建立,它的瓶颈在队列的缓冲数
。但是即使瓶颈发生了,它又能很快被分发处理掉。所以说:
我的这个第一点的尝试的意义事实上没有多大的。只不过是换了一种方式去分发
go c.serve(ctx)
。
- 这个是第二种结合方式,把
master-worker
放置到ServeHTTP
的分发阶段。例如下面代码,是常见的http handler
写法,我们就可以嵌套进去。
func (x XHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//...
if x.MasterWorker {
poolMaster.AddJob(master_worker.Job{
Tag:"normal",
XContext:xc,
Handler: func(context model.XContext) {
x.HandleFunc(w,r)
},
})
return
}
x.HandleFunc(w,r)
//...
}
这样的话,我们就能控制所有连接的并发请求最大数。超出的将会进行排队,等待被执行,而不会因为短时间 http 请求数目不受控暴增
而导致服务器
挂掉。
此外上述第二种
还存在一个:读,过早关闭问题
,这个留给读者尝试解决。
Go 自带的 http/server.go 的连接解析 与 如何结合 master-worker 并发模式,提高单机并发能力的更多相关文章
- 使用Visual Studio下自带的SQL Server Express
软件环境:Windows7(x64) + Visual Studio 2010 + SQL Server Express 2008 1.配置数据库 装VS2010不小心把自带的SQL Server 2 ...
- centos6.4-x86-64系统更新系统自带Apache Http Server
系统自带Apache Http Server 版本比较老,有漏洞.现在对Apache Http Server进行升级.总体思路:先删除老的,再安装新的.详细步骤如下: 1 删除老版本 1.1 删除老A ...
- SQL SERVER 无法正常连接的那些事
1.确保sqlserver服务正常运行. >一般可以从两个地方控制服务,一是系统自带的服务管理器,最快捷的方式是运行“services.msc”,二是使用sqlserver自带的“SQL Ser ...
- 「从零单排canal 05」 server模块源码解析
基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...
- win7中 SQL server 2005无法连接到服务器,错误码:18456
win7中 SQL server 2005无法连接到服务器,错误码:18456.. 数据库刚装完.我用Windows登陆 结果登陆不上去.. 选中SQL Server Management Stud ...
- 在Windows Server 2012 R2的Hyper-V中设置虚拟机启用增强会话模式
在Windows Server 2012 R2的Hyper-V中,可以为虚拟机提供一种全新的连接方式,就是“增强会话模式”,它将让您更加方便的对虚拟机进行操作,比如分辨率的调整.设备的加载,最为方便的 ...
- 处于同一个域中的两台Sql server 实例无法连接
处于同一个域中的两台Sql server 实例无法连接,报的错误信息如下: A network-related or instance-specific error occurred while es ...
- 本地数据库(SQL Server)远程连接服务器端服务器
本地数据库(SQL Server 2012) 连接外网服务器的数据库,外网的服务器端需要做如下配置: 1. 首先是要打开 数据的配置管理工具 2. 配置相关的客户端协议,开启TCP/IP 3. 数据库 ...
- ora-01445:无法从不带保留关键字的表的连接视图中选择ROWID或采样
系统要创建一个物化试图,用到很多张表,执行的时候报错: ora-01445:无法从不带保留关键字的表的连接视图中选择ROWID或采样 网上搜了下,有多种原因和解决方法,最终我选择先尝试一下修改 ...
随机推荐
- 栈的顺序存储 - 设计与实现 - API实现
Stack基本概念 栈是一种 特殊的线性表 栈仅能在线性表的一端进行操作 栈顶(Top):允许操作的一端 栈底(Bottom):不允许操作的一端 Stack的常用操作 创建栈 销毁栈 清空栈 进栈 出 ...
- Vim/Vi常用操作(第二版)
周围同事不是用vim就是Emacs,你要是用一ultraedit,你都不好意思跟人家打招呼;什么插件呀,语法高亮呀,拼写检查呀,能给它开的都给它开着,就是一个字儿:酷. 你说这么牛B一东西,怎么着学会 ...
- Android开发技巧——自定义控件之增加状态
Android开发技巧--自定义控件之增加状态 题外话 这篇本该是上周四或上周五写的,无奈太久没写博客,前几段把我的兴头都用完了,就一拖再拖,直到今天.不想把这篇拖到下个月,所以还是先硬着头皮写了. ...
- application之OnLowMemory()和 OnTrimMemory(level)讲解
1. OnLowMemory OnLowMemory是Android提供的API,在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLo ...
- 高通 android平台LCD驱动分析
目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...
- LeetCode之旅(15)-Odd Even Linked List
题目描述: Given a singly linked list, group all odd nodes together followed by the even nodes. Please no ...
- obj-c编程09:块的语法
在obj-c中,有一种和C截然不同的东西--块.块可以在外边定义,也可以在函数或方法内部定义,可以被赋值给一个变量,然后用该变量调用.默认情况下块对外部变量的访问只能读不能写,除非用__block显示 ...
- 排序算法入门之冒泡排序及其优化(java实现)
冒泡排序思想(从小到大): 比较相邻两个元素,如果第一个元素比第二个元素大,就交换他们的位置.第一趟,从第一个元素开始,第一个元素和第二个元素比较,如果第一个元素比第二个元素大,则交换位置:接下来比较 ...
- 干货!从Tomcat执行流程了解jsp是如何被解析的,错误提示是哪里生成的。
一.关于Tomcat组成 先上张图: Tomcat组成: 1. Server:代表整个 servlet 容器,如Tomcat,JBoss之类的. 2. Service:它由一个或者多 ...
- 【js-xlsx和file-saver插件】前端html的table导出数据到excel的表格合并显示boder
最近在做项目,需要从页面的表格中导出excel,一般导出excel有两种方法:一.习惯上是建模版从后台服务程序中导出:二.根据页面table中导出:综合考虑其中利弊选择二.根据页面table中导出ex ...