作者:林冠宏 / 指尖下的幽灵

掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8

博客:http://www.cnblogs.com/linguanh/

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

根据我的调试结果源码分析显示,正确的形式是下面这样的:

  1. 为每一个连接的用户启动了一个长连接,serve 方法内部有个超时的设置是c.rwc.SetReadDeadline(time.Time{}),这样子的情况,如果内部不出错,当前的连接断开的条件是客户端自己断开,或nat超时。

  2. 这个连接建立后,以ip为单位,当前的客户端,此时它的所有http请求,例如getpost,它们都会在这个启动的gorutine 内进行分发被处理

  3. 也就是说,同一个ip,多个不同的请求,这里不会触发另一个 accept,不会再去启动一个go c.serve(ctx)

上述我们得出结论:

  1. 如果有 100万accept,就证明有100万个连接,100万ip与当前server连接。即是我们说的百万连接

  2. 百万连接 不是百万请求

  3. 每一个连接,它可以进行多个http请求,它的请求都在当前启动这个连接的gorutine里面进行。

  4. c.serve(...) 源码中的for 死循环就是负责读取每个请求再分发

for {
w, err := c.readRequest(ctx) // 读取一个 http 请求
//...
ServeHTTP(...)
}
  1. 我们的100万 连接里面,有可能并发更多的请求,例如几百万请求,一个客户端快速调用多个请求api

图解总结

结合 master-worker 并发模式

根据我们上面的分析,每一个新连接到来,go 就会启动一个 gorutine,在源码里面也没有看到有一个量级的限制,也就是达到多少连接就不再接收。我们也知道,服务器是有处理瓶颈的。

所以,在这里插播一个优化点,就是在server.go 内部做一个连接数目的限制。

master-worker 模式本身是启动多个worker 线程,去并发读取有界队列里面的任务,并执行。

我自身已经实现了一个go版本master-worker,做过下面的尝试:

  1. 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)

  1. 这个是第二种结合方式,把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 并发模式,提高单机并发能力的更多相关文章

  1. 使用Visual Studio下自带的SQL Server Express

    软件环境:Windows7(x64) + Visual Studio 2010 + SQL Server Express 2008 1.配置数据库 装VS2010不小心把自带的SQL Server 2 ...

  2. centos6.4-x86-64系统更新系统自带Apache Http Server

    系统自带Apache Http Server 版本比较老,有漏洞.现在对Apache Http Server进行升级.总体思路:先删除老的,再安装新的.详细步骤如下: 1 删除老版本 1.1 删除老A ...

  3. SQL SERVER 无法正常连接的那些事

    1.确保sqlserver服务正常运行. >一般可以从两个地方控制服务,一是系统自带的服务管理器,最快捷的方式是运行“services.msc”,二是使用sqlserver自带的“SQL Ser ...

  4. 「从零单排canal 05」 server模块源码解析

    基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...

  5. win7中 SQL server 2005无法连接到服务器,错误码:18456

    win7中 SQL server 2005无法连接到服务器,错误码:18456.. 数据库刚装完.我用Windows登陆  结果登陆不上去.. 选中SQL Server Management Stud ...

  6. 在Windows Server 2012 R2的Hyper-V中设置虚拟机启用增强会话模式

    在Windows Server 2012 R2的Hyper-V中,可以为虚拟机提供一种全新的连接方式,就是“增强会话模式”,它将让您更加方便的对虚拟机进行操作,比如分辨率的调整.设备的加载,最为方便的 ...

  7. 处于同一个域中的两台Sql server 实例无法连接

    处于同一个域中的两台Sql server 实例无法连接,报的错误信息如下: A network-related or instance-specific error occurred while es ...

  8. 本地数据库(SQL Server)远程连接服务器端服务器

    本地数据库(SQL Server 2012) 连接外网服务器的数据库,外网的服务器端需要做如下配置: 1. 首先是要打开 数据的配置管理工具 2. 配置相关的客户端协议,开启TCP/IP 3. 数据库 ...

  9. ora-01445:无法从不带保留关键字的表的连接视图中选择ROWID或采样

    系统要创建一个物化试图,用到很多张表,执行的时候报错:   ora-01445:无法从不带保留关键字的表的连接视图中选择ROWID或采样   网上搜了下,有多种原因和解决方法,最终我选择先尝试一下修改 ...

随机推荐

  1. 【面试笔试算法】Program 5 : 推箱子 (网易游戏笔试题)

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 推箱子是一款经典游戏.如图所示,灰色格子代表不能通过区域,蓝色方格是箱子,黑色圆形代表玩家,含有圆点的格子代表目标点. 规 ...

  2. 【Qt编程】QWT在QtCreator中的安装与使用

    由于导师项目的需要,需要画图,二维+三维.三维图我用的是Qt+opengl,二维图我决定使用qwt工具库来加快我的项目进展,毕竟还有期末考试.关于Qt+opengl的使用有时间的话以后再介绍.     ...

  3. Developing RIA Web Applications with Oracle ADF

      Developing RIA Web Applications with Oracle ADF Purpose This tutorial shows you how to build a ric ...

  4. C语言之数值计算--级数算法

    在编程语言的学习中,我们学习过不少的算法,比如累加,累乘,数值交换,排序等等.在一些软件比赛和面试题中,有一类算法不容忽视,属于高频题目,我之前去企业面试的时候就遇到这样的一类题目,题目不算难,掌握方 ...

  5. 30多种iOS常用动画

    转自:http://blog.csdn.net/zhibudefeng/article/details/8691567 // //  CoreAnimationEffect.h //  CoreAni ...

  6. 【34】包含min函数的stack

    题目: 实现一个包含min函数的栈,min和push,pop都是o(1)时间 思路: 采用一个辅助的栈,来存储不同阶段的最小值 - 代码: push(int value){ //data是数据栈,mi ...

  7. ELF 文件 动态连接 - 延迟绑定(PLT)

    PLT 全称:Procedure Linkage Table ,直译:过程连接表 由于在动态连接中,程序的模块之间包含了大量的函数引用,所以在程序开始执行前,动态链接会耗费较多的时间用于模块之间函数引 ...

  8. java的finalize方法使用

    1. finalize的作用 finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法. finalize()与C++中的析构函数 ...

  9. Android studio统计项目总行数

    有时候在项目开发中会用到统计项目的总行数功能,今天就教大家如何利用studio进行总行数的统计: 1.打开要统计总行数的项目,按住Ctrl+Shift+A(或者点击导航栏的Help->Find ...

  10. 通向从容之道——Getting things done读书笔记

    一.主要的两个目的:         1. 抓住所有一切需要处理的事情:         2. 训练自己在接受一切"输入信息"的前期作出决定.   二.目前的问题:         ...