
小型项目你甚至不用框架,用net/>Golang进行服务器开发, 最显耀的就是其并发架构, 能充分榨干每一个CPU 但是Golang和Erlang不一样, Golang使用了CSP的模型, 而Erlang采用的是Actor模型 两者区别仅仅只是消息队列归属范围区别而已 但带来的巨大的框架实现及使用差异让Golang和Erlang阵营里的童鞋们撕逼很久
其实可以这么理解 Erlang基于Actor模型的并发架构真正是一个框架, 让每一个人用同样的方法处理事情 而不用更多的担心横向扩展的问题 Golang的CSP并发架构没有很多框框条条 让开发者自由发挥,设计自己想要的结构 但碰到需要横向扩展时, 还是需要考验架构人的经验和实力
所以说, CSP和Actor其实着眼点不一样 所以还是不能同日而语 但项目还得做, 问题还得解决 不能为每一个项目重复设计, 编写重复的代码来应对各种横向扩展的问题 烦了, 火了, 所以就准备造一个用Golang实现Actor的轮子
调研了一段时间, 使用Golang做Actor模型的实现并不多 而且大多是实验性项目, 并没有真正像Skynet一样, 在项目中使用同时做开源的
说到Skynet, 这是一个极好的开源轻量级游戏服务器框架 使用lua的coroutine模拟goroutine, 同步+多线程逻辑, 用C底层帮你处理了复杂的Actor模型 留给上层只是发发消息, 管理下id, 很是惬意 再加上lua天生动态语言, 模拟Erlang的动态更新更不是啥大问题 因此在服务器界, Skynet变的有名了起来
既然要做轮子, 我果断选择不关门 讨论群都开了, 博客一直更新, github也有, 为啥不搞开源轮子呢 因此这次的服务器框架计划定位于开源 目的是为Golang贡献一款轻量级的游戏服务器框架, 由大家支持, 供大众使用这个net/>
目录:
以前我也认为TCP是相当底层的东西,我永远不需要去了解它。虽然差不多是这样,但是实际生活中,你依然可能遇见和TCP算法相关的bug,这时候懂一些TCP的知识就至关重要了。( 本文也可以引申为,系统调用, *** 作系统这些都很重要,这个道理适用于很多东西 )
这里推荐一篇小短文, 人人都应该懂点TCP
使用TCP协议通信的双方必须先建立TCP连接,并在内核中为该连接维持一些必要的数据结构,比如连接的状态、读写缓冲区、定时器等。当通信结束时,双方必须关闭连接以释放这些内核数据。TCP服务基于流,源源不断从一端流向另一端,发送端可以逐字节写入,接收端可以逐字节读出,无需分段。
需要注意的几点:
TCP状态(11种):
eg
以上为TCP三次握手的状态变迁
以下为TCP四次挥手的状态变迁
服务器通过 listen 系统调用进入 LISTEN 状态,被动等待客户端连接,也就是所谓的被动打开。一旦监听到SYN(同步报文段)请求,就将该连接放入内核的等待队列,并向客户端发送带SYN的ACK(确认报文段),此时该连接处于 SYN_RECVD 状态。如果服务器收到客户端返回的ACK,则转到 ESTABLISHED 状态。这个状态就是连接双方能进行全双工数据传输的状态。
而当客户端主动关闭连接时,服务器收到FIN报文,通过返回ACK使连接进入 CLOSE_WAIT 状态。此状态表示——等待服务器应用程序关闭连接。通常,服务器检测到客户端关闭连接之后,也会立即给客户端发送一个FIN来关闭连接,使连接转移到 LAST_ACK 状态,等待客户端对最后一个FIN结束报文段的最后一次确认,一旦确认完成,连接就彻底关闭了。
客户端通过 connect 系统调用主动与服务器建立连接。此系统调用会首先给服务器发一个SYN,使连接进入 SYN_SENT 状态。
connect 调用可能因为两种原因失败:1 目标端口不存在(未被任何进程监听)护着该端口被 TIME_WAIT 状态的连接占用( 详见后文 )。2 连接超时,在超时时间内未收到服务器的ACK。
如果 connect 调用失败,则连接返回初始的 CLOSED 状态,如果调用成功,则转到 ESTABLISHED 状态。
客户端执行主动关闭时,它会向服务器发送一个FIN,连接进入 TIME_WAIT_1 状态,如果收到服务器的ACK,进入 TIME_WAIT_2 状态。此时服务器处于 CLOSE_WAIT 状态,这一对状态是可能发生办关闭的状态(详见后文)。此时如果服务器发送FIN关闭连接,则客户端会发送ACK进行确认并进入 TIME_WAIT 状态。
流量控制是为了控制发送方发送速率,保证接收方来得及接收。
接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。
如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。 流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。
TCP 主要通过四种算法来进行拥塞控制: 慢开始、拥塞避免、快重传、快恢复。
在Linux下有多种实现,比如reno算法,vegas算法和cubic算法等。
发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。
为了便于讨论,做如下假设:
发送的最初执行慢开始,令 cwnd=1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8
注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。
如果出现了超时,则令 ssthresh = cwnd/2,然后重新执行慢开始。
在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。
在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。
在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。
慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。
发送端的每个TCP报文都必须得到接收方的应答,才算传输成功。
TCP为每个TCP报文段都维护一个重传定时器。
发送端在发出一个TCP报文段之后就启动定时器,如果在定时时间类未收到应答,它就将重发该报文段并重置定时器。
因为TCP报文段最终在网络层是以IP数据报的形式发送,而IP数据报到达接收端可能是乱序或者重复的。TCP协议会对收到的TCP报文进行重排、整理,确保顺序正确。
TCP报文段所携带的应用程序数据按照长度分为两种: 交互数据 和 成块数据
对于什么是粘包、拆包问题,我想先举两个简单的应用场景:
对于第一种情况,服务端的处理流程可以是这样的:当客户端与服务端的连接建立成功之后,服务端不断读取客户端发送过来的数据,当客户端与服务端连接断开之后,服务端知道已经读完了一条消息,然后进行解码和后续处理。对于第二种情况,如果按照上面相同的处理逻辑来处理,那就有问题了,我们来看看 第二种情况 下客户端发送的两条消息递交到服务端有可能出现的情况:
第一种情况:
服务端一共读到两个数据包,第一个包包含客户端发出的第一条消息的完整信息,第二个包包含客户端发出的第二条消息,那这种情况比较好处理,服务器只需要简单的从网络缓冲区去读就好了,第一次读到第一条消息的完整信息,消费完再从网络缓冲区将第二条完整消息读出来消费。
第二种情况:
服务端一共就读到一个数据包,这个数据包包含客户端发出的两条消息的完整信息,这个时候基于之前逻辑实现的服务端就蒙了,因为服务端不知道第一条消息从哪儿结束和第二条消息从哪儿开始,这种情况其实是发生了TCP粘包。
第三种情况:
服务端一共收到了两个数据包,第一个数据包只包含了第一条消息的一部分,第一条消息的后半部分和第二条消息都在第二个数据包中,或者是第一个数据包包含了第一条消息的完整信息和第二条消息的一部分信息,第二个数据包包含了第二条消息的剩下部分,这种情况其实是发送了TCP拆,因为发生了一条消息被拆分在两个包里面发送了,同样上面的服务器逻辑对于这种情况是不好处理的。
我们知道tcp是以流动的方式传输数据,传输的最小单位为一个报文段(segment)。tcp Header中有个Options标识位,常见的标识为mss(Maximum Segment Size)指的是,连接层每次传输的数据有个最大限制MTU(Maximum Transmission Unit),一般是1500比特,超过这个量要分成多个报文段,mss则是这个最大限制减去TCP的header,光是要传输的数据的大小,一般为1460比特。换算成字节,也就是180多字节。
tcp为提高性能,发送端会将需要发送的数据发送到缓冲区,等待缓冲区满了之后,再将缓冲中的数据发送到接收方。同理,接收方也有缓冲区这样的机制,来接收数据。
发生TCP粘包、拆包主要是由于下面一些原因:
既然知道了tcp是无界的数据流,且协议本身无法避免粘包,拆包的发生,那我们只能在应用层数据协议上,加以控制。通常在制定传输数据时,可以使用如下方法:
写了一个简单的 golang 版的tcp服务器实例,仅供参考:
例子
参考和推荐阅读书目:
注释:
eg
解决办法:①关闭杀毒软件 (首先尝试)。②设置 gopoxy代理。
具体的设置如下:设置环境变量: 变量名GOPROXY,变量值:关闭 vscode 之后,重新打开程序,然后运行。如果还是很慢,连结果都不出的话,重新检查程序,看看程序的循环是不是有问题,或者是程序本身就没有输出。近期正在探索前端、后端、系统端各类常用组件与工具,对其一些常见的组件进行再次整理一下,形成标准化组件专题,后续该专题将包含各类语言中的一些常用组件。欢迎大家进行持续关注。
本节我们分享的是基于Golang实现的高性能和d性的流处理器 benthos ,它能够以各种代理模式连接各种 源 和 接收器,并对有效负载执行 水合、浓缩、转换和过滤 。
它带有 强大的映射语言 ,易于部署和监控,并且可以作为静态二进制文件、docker 映像或 无服务器函数 放入您的管道,使其成为云原生。
Benthos 是完全声明性的,流管道在单个配置文件中定义,允许您指定连接器和处理阶段列表:
Apache Pulsar, AWS (DynamoDB, Kinesis, S3, SQS, SNS), Azure (Blob storage, Queue storage, Table storage), Cassandra, Elasticsearch, File, GCP (Pub/Sub, Cloud storage), HDFS, >
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)