socket与操作系统和网络

前言

最近在做一个分布式微服务的压测时,发现当请求量达到一定量服务特别繁忙后,就会有极少量同步请求会有socket time out的情况。其它大量的请求都会在几秒内响应客户端。

后来采用了多个方案去解决,这里分别结合socket网络编程、操作系统和计算机网络三个方面的知识记录一下其中的原因。如下图是我理解的三者的整体关系图。

一. 计算机网络

计算机网络七层结构OSI(Open Systems Interconnection)模型:

  • 应用层(Application Layer):应用层是OSI模型的最上层,它为用户提供网络服务的应用程序接口。可以将我们的应用程序代码归结于该层。
  • 表示层(Presentation Layer):表示层负责对数据进行编码和解码,以确保发送方和接收方之间的数据格式一致。它还处理数据压缩、加密和解密等问题。
  • 会话层(Session Layer):会话层负责建立、管理和终止会话。它负责同步通信,并确保数据在参与会话的各方之间正确且有序地交换。
    如:我们使用JMeter压测时,我们启动的JMeter工具是一个进程,同时我们可以在其中定义多个线程并发的去请求,但同一个线程在处理完上一个请求后,会根据我们定义的请求量接着发起下一个请求。所以我们发出的所有请求其实都是使用的同一个IP地址,同一个端口号(因为在同一进程中),那么服务端是如何区别每个请求的呢?显然如果仅仅是通过socket的四元组(源IP/源端口/目标IP/目标端口)是没有办法精确确定并响应每一个请求的。这个就是会话层的主要作用之一了,他需要为每次请求标记会话id。用于确定每一次的请求。
  • 传输层(Transport Layer):传输层负责提供端到端的通信服务,它确保数据在源端和目的端之间可靠、有序和正确地传输。这一层还处理流量控制和错误纠正等问题。
    注:传输层主要负责的是端到端的通信,也就是源端口到目的端口(可简单理解用户程序到服务程序)的通信,也就是我们通常说的socketsocket是建立在传输层协议之上的,如TCP/UDP/SMTP等。
  • 网络层(Network Layer):网络层负责将数据包从源地址IP发送到目的地址IP。它通过路由和转发机制来实现这一目标,并处理数据包的寻址和转发。
    注:数据包在转发过程中源IP地址和目的IP地址不变,但源MAC地址和目的MAC地址逐个链路改变。
  • 数据链路层(Data Link Layer):数据链路层负责将比特流组合成帧,并提供发送和接收数据的功能。它还负责处理错误检测和流量控制等问题。

     注:数据链路层在整个网络传输中其实是至关重要的,试想一下如第一张大图,数据在网络传输的过程中的一个环节,路由器X -->路由器Y,如果 路由器Y 发现 路由器X 传给他的数据包有损坏,路由器Y 会发起请求上游重发,那路由器Y不肯能请求,用户端电脑A重发,因为这样前面经过的路由器B,C,D...等所做的工就白费了,所以网络传输过程中需要的是想铁链一样的层层负责,当前的路由器Y发现数据包有损坏,它只需要找它的上一个路由器X重发就行了,且路由器X中的数据一定是完整的,因为X在接收到数据包之后首先就需要确保数据是完整的才会发给Y,如此层层负责,就能减少对整个网络重复发送,同时也提高了网络的可靠性。因此链路层的最为重要的一个指标也就是MAC地址,数据在传输中也会不断地拆包组装帧,组装成不同的数据帧并发送(此处可以去了解一下ARP协议)。

  • 物理层(Physical Layer):物理层负责传输比特流,它从数据链路层接收数据帧,并将帧的结构发送。此外,物理层还涉及传输媒介的特性,例如网卡,调制器,光纤、电压、介质类型和阻抗等。

二. 计算机操作系统

学习和理解操作系统的过程中,我想到了电影《满城尽带黄金甲》里面的一个经典名场面,皇帝对王子说 “朕给你的,才是你的。朕不给你,你不能抢”。诚然,操作系统在一台计算机里面的地位也如同资源主宰者一样,用户程序是臣子。

如用户程序想要启动: 首先需要操作系统将用户程序的代码等加载到内存中,并给用户程序建立

  1. PCB进程控制块用于记录进程内部的控制信息。
  2. 代码段用于存放程序代码。
  3. 数据段用于存放程序运行过程中的产生的数据。

程序加载之后,程序需要获得处理机也就是CPU的使用权之后才能运行,处理机是宝贵的资源,单核心的电脑只有一个处理机。这个时候就需要操作系统分时间片分别分给不同的进程来实现不同进程的并发运行。当然其中的知识点有很多,进程的调度也有各种优先权算法等。总之都是有操作系统分配处理资源时间片给用户程序,用户程序才能运行。
你以为这就完了,不,操作系统在各用户程序运行的过程中随时都在发挥着作用,当用户程序想要接送外部发送的请求时,首先需要的也是操作系统去监听,加载使用网卡,内存等应用资源,然后在传给不同的进程,这就是网络IO。如果没有一个统一的管理者(操作系统)。用户程序A不是可以随便监听用户程序B的请求内容。相互信号冲突怎么办?如果你想要加载磁盘或者硬盘SSD中的文件也需要操作系统统一去管理和调度(权限管理等)。用户程序不能直接调度所有的硬件,必须都经过操作系统。
当然从第一张大图中我们也可以看到用户线程处理任务需要有内核线程,这样会有一个任务在执行过程中需要在内核和用户态直接切换的过程。设计这方面的细节可以详细的去学习操作系统。

计算机网络中的链路层,网络层,传输层,会话层,表示层都有操作系统的身影和功能贡献。

操作系统如何实现多进程或多线程并发运行呢?其核心就是操作系统的中断机制。

三.socket网络编程

当我们的用户服务启动时,Tomcat首先需要去请求系统调度用,到操作系统哪里去申请端口号,让操作系统帮程序建立网络监听,Tomcat向操作系统申请内核态的线程池用于接收请求并转发到用户程序。
当有外部请求来时,操作系统会根据请求信息创建socket,避免同一个用户下次请求重复建立。管理不同请求的会话。
这次的socket time out,就是因为tomcat申请的内核级线程拒绝策略。
当内核线程池中的核心线程处理的任务饱和,同时任务排序队列也排满时,线程会根据拒绝策略丢弃少许任务,这些被丢弃的任务没有响应客户端,导致客户端等待服务端响应超时而socket time out。
这些被丢弃的任务由于NIO的原因,虽然任务已经发送到用户程序中去运行了。但是单个内核线程在NIO模式下它可以并发处理多个任务,当这个任务已经发送到服务端的用户级线程中去运行了,但是由于服务繁忙运行的慢,导致内核线程发现这个任务运行时长有点长,内核队列又满了,而丢弃了这个已经发送到了用户线程里去的任务。当用户线程运行完响应内核级线程时,内核线程发现他哪里已经没有对应挂起的任务了,而选择忽略了这个响应,那么客户端自然也永远得不到服务端的响应了。

处理问题的方案是要么优化用户程序,提高用户程序处理效率。当然你也可以去查看自己程序的tomcat的线程池是否合理,并合理扩张。

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值