tcp异步框架学习 tao框架 evio框架
什么是同步异步 先讲讲同步,同步可以理解为串行,100个人要进入一个空间,可空间入口只有一个,这时候,人们只能排队进入,假设一个人进入需要费时1秒,100个就是100秒,人越多,效率就越低;这时候要解决效率的问题就需要用到异步了。 异步可以理解为并行,为每一个人开辟一个入口,这时候有n个人就会有n个入口,进入的时间永远就只需一秒,这样效率就大大提高了。
异步加锁的必要性 异步解决了进入效率的问题,但人们进入房间是需要做事情的,如果这个事件只是记忆某样东西,不对这个东西进行改变,这时候就没必要加锁,假如空间里面有一堆糖果,100个人里面有50个人的任务只是进来数当前糖果的,这时候就没必要加锁了,而另外50个人的任务是拿糖果,这就有必要加锁了。拿糖果这个任务可以分为两个步骤,先确认是否有糖果,有的话才拿,如果AB两个人通过要执行拿糖果的,AB都确认了有糖果,可是B比A手速要快,先一步把糖果全部拿走了,A再去拿的时候就拿不到了,这时候A的逻辑就会出问题了,他就会一直在那里拿糖果(可永远没有拿到的可能),但是加了锁就不一样了,锁相当于一个闸门,上锁后,只有一个人可以执行任务,其他人只能等待上个人执行完解锁后才能继续去抢锁执行任务。
线程池的概念 异步解决了效率的问题,使空间不再拥堵,但空间再大也会有上限,可是想要进入空间做事情的人是没有上限的,假设这个空间人气很高,一个庞大的人流量都想要进入这个空间做事,如果说为每个人都开辟一个通道,这时候这个空间将面临千疮百孔,濒临崩溃的局面。也许空间加糖果的例子可能还是有点抽象了,这里以银行为例子,银行相当于空间,每一个窗口相当于一个通道,事件相当于不同的业务,在人多的情况下,窗口越多,办理业务的效率也就越高,而对于银行来说,窗口也是有上限的,而且每开一个窗口都需要消耗一定资源(给窗口业务员发工资、窗口设备维护等等)。
线程池的具体设计 上面有提到过无限开启窗口的确定,就是资源消耗的递增,假设银行只有在每个月的最后一天人流量会暴增,为了这一天开启了很多个窗口来提高效率,可是其他的时间里,平均人流量很小,一到两个窗口就能应付,这时候其他的窗口就会无比空闲,假设你是一个精打细算的银行老板,你会容忍其他窗口的无所事事么,要知道维护这些空闲窗口是需要消耗的。 1. 设定一个合理的窗口数,不要为每一个人都开一个窗口,人多的话还是要进行排队,银行就是这样的。。。 2. 合理的窗口数虽然减少了开销,但是作为一个一毛不拔的我,还是觉得浪费了,毕竟在人流量极少的情况下,还是会有无所事事的窗口,这个时候就需要动态的增删窗口了;人流量小的时候,无所事事的窗口关闭,人流量多的时候,增加窗口,极多的时候,那就排队吧。
回归语言 任何事物都是讲究逻辑性的,语言也是符合上面两个例子的逻辑。空间、银行这些都可以看做我们的服务端,而来办理业务的人就是各种携带事件的tcp请求,而窗口就是routine(类型java线程,但比线程的创建消耗要小很多),业务就是你通过routine要异步处理的携带事件。
好的异步协程池 ants框架就是一个非常好用的,对上述所说的动态删减routine,控制routine上限,异步处理事件都有非常好的实现
以下就是个人参考ants框架的举例逻辑实现
// 主函数,模拟1000个事件,交给线程池异步处理,p就是协程池,限制上限10,通过push函数将任何丢到通道中 func main() { for i := 0; i < 1000; i++ { // time.Sleep(2 * time.Millisecond) p.push(Msg{ from: uint64(i), data: "haha", }) } fmt.Println("参与的worker数量:", len(p.workers)) time.Sleep(1 * time.Second) } //这里的push函数,导致的实际效果是每次是放一个事件进来等待空闲routine进行处理,一对多 //而ants作者的逻辑效果是一群事件在那里等待争抢空闲routine,多对多 //从逻辑性来开ants作者的逻辑效率会更高 type Pool struct { max uint64 workers []*Worker //存放工作者 chl chan Msg lock sync.
Read More →
tcp长连接压测遇到的问题 问题1:在大量长连接下,会出现很多拨号失败。 、、、 dial tcp 192.168.31.232:6001: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 、、、 拨号失败的问题在于服务端响应不及时 问题分析:在大量长连接同时请求时,出现了cpu、资源调用阻塞,很多的连接在等待分配 解决办法:轮询创建客户端加上延时
问题2:部分连接成功的客户端,会在请求的时候得不到相应的响应 目前的模拟客户端设置的时1分钟的超时,在5000长连接的压测下,30s的超时设置,会出现n个超时,1分钟的超时设置,超时的客户端会减少 问题分析:估计也是资源占用造成的问题 解决办法:加大服务器内核
问题3:部分连接成功的客户端,请求后被告知连接被远程端强制关闭,但服务端却没有主动关闭连接的操作 客户端已经dial成功,但通过返回的conn进行write和read操作被告知连接强制关闭,但服务端没有任何close迹象 原因分析:dial是在第二次握手的时候返回conn,而服务端accept是在第三次握手的时候返回conn,也就是说握手只进行到第二次握手, 这是因为握手的时候有两个队列,一个syn队列一个backlog队列(accept队列),一次握手,cli告诉ser要建立连接,第二次握手,ser通知 cli等待确认连接,这时连接状态进入syn队列,第三次握手,cli通知ser确认连接,连接状态进入backlog队列。 以上就是握手的过程,syn和backlog队列有限制,如果在大量并发连接出现,连接数超过backlog的长度的连接,将会阻塞在队列外,一定时间没有进入到backlog队列,连接将会被丢弃,这就出现了客户端dail成功,而服务端却没有accept。 解决办法:修改服务器内核配置,syn队列上限: /proc/sys/net/core/somaxconn;accept队列上限:/proc/sys/net/ipv4/tcp_max_syn_backlog
服务端 服务端是用来接收连接的,因此需要一个监听的逻辑
// 函数ListenAndServe用来开启服务并且监听,而这个 // 函数的核心就是tcp连接,通过net.Listen(param1,param2)得到listener // 其中param1是连接类型,可以有tcp,tcp4,tcp6......,param2则是服务地址:ip:port // 返回的listener是一个interface{}类型 type Listener interface { // Accept waits for and returns the next connection to the listener. Accept() (Conn, error) // Close closes the listener. // Any blocked Accept operations will be unblocked and return errors. Close() error // Addr returns the listener's network address. Addr() Addr } 只要实现了Listener的三个函数的结构体,都能作为返回参数,以param1=tcp为例子,将会返回TCPListener // TCPListener is a TCP network listener. Clients should typically // use variables of type Listener instead of assuming TCP.
Read More →
tcp服务器 开启对服务器ip以及端口的监听
lis, err := net.Listen("tcp", ":6666") if err != nil { log.Fatalf("failed to listen:%v", err) } 用for循环从监听者处取发起拨号的连接
for { conn, err := lis.Accept() if err != nil { log.Fatalf("failed to accept:%v", err) continue } sendMessage(conn) time.Sleep(time.Second * 1) } 写两个函数用来收发消息
发消息:将需要发送的消息内容,通过相关的序列化操作,转化成字节流,将字节流通过write函数发送
func send(){ _,err := conn.Write([]byte(str)) if err != nil { log.Fatalf("failed to write:%v", err) } } 收消息:通过read函数将字节流读出来,然后通过相关的反序列化操作,得到想要的消息
func recv(){ b := make([]byte,1024) _,err := conn.
Read More →