彻底理解并解决服务器出现大量TIME_WAIT

news2024/11/28 8:25:21

首先我们说下状态 TIME_WAIT 出现的原因

TCP的新建连接,断开连接的流程和各个状态,如下图所示

 由上图可知:TIME_WAIT 是主动断开连接的一方会出现的,客户端,服务器都有可能出现

当客户端主动断开连接时,发出最后一个ACK后就会处于 TIME_WAIT状态

当服务器主动断开连接时,发出最后一个ACK后就会处于 TIME_WAIT状态

结论:TIME_WAIT 是必然会出现的状态,是正常现象,且会定时回收

TIME_WAIT 状态持续2MSL时间,MSL就是maximum segment lifetime(最大报文段的生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失(被丢弃)。RFC 793中规定MSL为2分钟,实际应用中,可能为30S,1分钟,2分钟。

我的系统是ubuntu,输入如下命令后可以看到,时间为60秒

 请注意两个状态,一个是TIME_WAIT,一个是CLOSE_WAIT,完全不同的两个状态

TIME_WAIT 出现在主动断开方,发出最后一个ACK后

CLOSE_WAIT 出现在被动断开方,收到主动断开方的FIN,发出自己的ACK后

问题:为什么要有TIME_WAIT状态?

1. 为了可靠地关闭TCP连接

举例:我们把主动断开连接的一方称为C端,被动断开连接的一方称为S端,由于网络不可靠,C端发送的最后一个ACK报文可能没成功发送到S端,那么S端就会重新发上一个报文即FIN,如果C端处于TIME_WAIT状态下,就可以重新发送报文ACK,然后重新计时2MSL时间才会进入CLOSED状态,S端收到ACK后就可以正常关闭TCP连接了。反之,如果这时C端处于 CLOSED 状态 ,就会响应 RST报文而不是ACK报文,那S端会认为这是一个错误,只能异常关闭TCP连接

2. 防止上一次连接中的包,迷路后重新出现,影响新连接

由于网络的不可靠,TCP分节可能因为路由器异常而“迷途”,在迷途期间,TCP发送端会因确认超时而重发这个分节,这个分节最终被发送到对方时,对方可能已经是一个新的连接了,由此造成混乱。举例:关闭一个TCP链接后,马上又创建了一个相同的IP地址和端口之间的TCP链接,后一个链接被称为前一个链接的化身(incarnation),那么此时有可能出现这种状况,前一个链接的迷途重复分节在前一个链接终止后出现了,从而被误解成从属于新的连接的数据。为了不出现这种混乱,TCP不容许处于TIME_WAIT状态的连接立即启动一个新连接,由于TIME_WAIT状态持续2MSL,就能够保证当成功创建一个TCP链接的时候,来自前一个连接的迷途重复分节已经在网络中消逝

注意close() 和 shutdown()的区别

close()其实只是将socket fd的引用计数减1,只有当该socket fd的引用计数减至0时,TCP传输层才会发起4次握手从而真正关闭连接。而shutdown则可以直接发起关闭连接所需的4次握手,而不用受到引用计数的限制

close()会终止TCP的双工链路。由于TCP连接的全双工特性,可能会存在这样的应用场景:local peer不会再向remote peer发送数据,而remote peer可能还有数据需要发送过来,在这种情况下,如果local peer想要通知remote peer自己不会再发送数据但还会继续收数据这个事实,用close()是不行的,而shutdown()可以完成这个任务

服务器短时间内大量的TIME_WAIT出现,才是问题

会引发以下问题

1. 由于处于TIME_WAIT状态,连接并未关闭,占据了大量的CPU,内存,文件描述符等,造成新的连接无法建立,客户端表现就是连接失败

2. 如果服务器上同时有nginx,且nginx由于反向代理,那么还会占用很多端口(S端处于TIME_WAIT,该连接的另一方即C端需独占一个端口,C端是由nginx代理建立的),要知道端口是有限的,最多65535,一旦端口占用完,无论服务器配置如何高,新连接都无法建立了,客户端表现仍然是连接失败

短时间内大量TIME_WAIT出现的根本原因:高并发且持续的短连接

1. 业务上使用了持续且大量的短连接,纯属设计缺陷,例如爬虫服务器就有可能出现这样的问题

2. http请求中connection的值被设置成close,因为服务器处理完http请求后会主动断开连接,然后这个连接就处于TIME_WAIT状态了。持续时间长且量级较大的话,问题就显现出来了。http洗衣1.0中,connection默认为close,但在http1.1中connection默认行为是keep-alive,就是因为这个原因

3. 服务器被攻击了,攻击方采用了大量的短连接

重点:解决办法

1. 代码层修改,把短连接改为长连接,但代价较大

2. 修改 ip_local_port_range,增大可用端口范围,比如1024 ~ 65535

3. 客户端程序中设置socket的 SO_LINGER 选项

4. 打开 tcp_tw_recycle 和tcp_timestamps 选项,有一定风险,且linux4.12之后被废弃

5. 打开 tcp_tw_reuse 和 tcp_timestamps 选项

6. 设置 tcp_max_tw_buckets 为一个较小的值

相关视频推荐

10道网络八股文,每道都很经典,让你在面试中逼格满满

全网最详细epoll讲解,6种epoll的设计,让你吊打面试官

c/c++ linux服务器开发/后台架构师免费学习地址

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

 下面我们开始对各个办法进行详细讲解

办法1:代码层修改,把短连接改为长连接

由于TIME_WAIT出现的根本原因是高并发且持续的短连接,所以如果能把短连接改成长连接,就能彻底解决问题。比如http请求中的connection设置为keep-alive。只是代码层的修改往往会比较大,不再啰嗦

办法2. 修改 ip_local_port_range,增大可用端口范围

我这里linux系统是ubuntu,输入如下命令可查看可用的端口范围

 默认差不多有3万个,我们可以修改这个值,但是注意最小不能小于1024,最大不能大于65535,也就是说改完之后最多有6万多个可用端口

只是一般线上遇到大量的TIME_WAIT,都是高并发且持续的短连接,单纯扩大端口范围并不能从根本上解决问题,只是能多撑一会儿

办法3. 客户端程序中设置socket的 SO_LINGER 选项

SO_LINGER 选项可以用来控制调用close函数关闭连接后的行为,linger的定义如下

struct linger {
     int l_onoff; /* 0 = off, nozero = on */
     int l_linger; /* linger time */

有三种情况

1. 设置 l_onoff 为0,l_linger的值会被忽略,也是内核缺省的情况,和不设置没区别。close调用会立即返回给调用者,TCP模块负责尝试发送残留的缓冲区数据,会经过通常四分组终止序列(FIN/ACK/FIN/ACK),不能解决任何问题

2. 设置 l_onoff 为1,l_linger为0,则连接立即终止,TCP将丢弃残留在发送缓冲区中的任何数据并发送一个RST报文给对方,而不是通常的四分组终止序列。对方收到RST报文后直接进入CLOSED状态,从根本上避免了TIME_WAIT状态

3. 设置 l_onoff 为1,l_linger > 0,有两种情况

a. 如果socket为阻塞的,则close将阻塞等待l_linger 秒的时间。如果在l_linger秒时间内TCP模块成功发送完残留在缓冲区的数据,则close返回0,表示成功。如果l_linger时间内TCP模块没有成功发送残留的缓冲区数据,则close返回-1,表示失败,并将errno设置为EWOULDBLOCK

b. 如果socket为非阻塞的,那么close立即返回,此时需要根据close返回值以及errno来判断TCP模块是否成功发送残留在缓冲区的数据

第3中情况,其实就是第1种和第2种的折中处理,且当socket为非阻塞的场景下是没有作用的

综上所述:第2种情况,也就是l_onoff为1,l_linger不为0,可以用于解决服务器大量TIME_WAIT的问题

只是Linux上测试的时候,并未发现发送了RST报文,而是正常进行了四步关闭流程,

初步推断是“只有在丢弃数据的时候才发送RST”,如果没有丢弃数据,则走正常的关闭流程

查看Linux源码,确实有这么一段注释和源码

/* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
*/
if (data_was_unread) {
    /* Unread data was tossed, zap the connection. */
    NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
    tcp_set_state(sk, TCP_CLOSE);
    tcp_send_active_reset(sk, sk->sk_allocation);
}

从原理上来说,这个选项有一定的危险性,可能导致丢数据,使用的时候要小心一些

从实测情况来看,打开这个选项后,服务器TIME_WAIT连接数为0,且不受网络组网(例如是否虚拟机等)的影响

办法4. 打开 tcp_tw_recycle 和tcp_timestamps 选项,有一定风险,且linux4.12之后被废弃

官方文档中解释如下:

tcp_tw_recycle 选项作用为:Enable fast recycling TIME-WAIT sockets. Default value is 0.

tcp_timestamps 选项作用为:Enable timestamps as defined in RFC1323. Default value is 1

这两个选项是linux内核提供的控制选项,和具体的应用程序没有关系,而且网上也能够查询到大量的相关资料,但信息都不够完整,最主要的几个问题如下;

1)快速回收到底有多快?

2)有的资料说只要打开tcp_tw_recycle即可,有的又说要tcp_timestamps同时打开,到底是哪个正确?

3)为什么从虚拟机NAT出去发起客户端连接时选项无效,非虚拟机连接就有效?为了搞清楚上面的疑问,只能看代码,看出一些相关的代码供大家参考:

=====linux-2.6.37 net/ipv4/tcp_minisocks.c 269======

void tcp_time_wait(struct sock *sk, int state, int timeo)
{
    struct inet_timewait_sock *tw = NULL;
    const struct inet_connection_sock *icsk = inet_csk(sk);
    const struct tcp_sock *tp = tcp_sk(sk);
    int recycle_ok = 0;
 
 
    // 判断是否快速回收,这里可以看出tcp_tw_recycle和tcp_timestamps两个选项都打开的时候才进行快速回收,
    //且还有进一步的判断条件,后面会分析,这个进一步的判断条件和第三个问题有关
    if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp)
        recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);
 
 
    if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets)
        tw = inet_twsk_alloc(sk, state);
 
 
    if (tw != NULL) {
        struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
        //计算快速回收的时间,等于 RTO * 3.5,回答第一个问题的关键是RTO(RetransmissionTimeout)大概是多少
        const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);
        
        //。。。。。。此处省略很多代码。。。。。。
        
    if (recycle_ok) 
    {
        //设置快速回收的时间
        tw->tw_timeout = rto;
    } 
    else 
    {
        tw->tw_timeout = TCP_TIMEWAIT_LEN;
        if (state == TCP_TIME_WAIT)
            timeo = TCP_TIMEWAIT_LEN;
    }
        
    //。。。。。。此处省略很多代码。。。。。。
}

这里讲下RTO(Retransmission Time Out):重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传

RFC中有关于RTO计算的详细规定,一共有三个:RFC-793、RFC-2988、RFC-6298,Linux的实现是参考RFC-2988。

对于这些算法的规定和Linux的实现,有兴趣的同学可以自己深入研究,实际应用中我们只要记住Linux如下两个边界值:

=====linux-2.6.37 net/ipv4/tcp.c 126================

#define TCP_RTO_MAX ((unsigned)(120*HZ))

#define TCP_RTO_MIN ((unsigned)(HZ/5))

==========================================

这里的HZ是1s,因此可以得出RTO最大是120s,最小是200ms,对于局域网的机器来说,正常情况下RTO基本上就是200ms,因此3.5 RTO就是700ms

也就是说,快速回收是TIME_WAIT的状态持续700ms,而不是正常的2MSL

实测结果也验证了这个推论,不停的查看TIME_WAIT状态的连接,偶尔能看到1个

最后一个问题是为什么从虚拟机发起的连接即使设置了tcp_tw_recycle和tcp_timestamps,也不会快速回收,继续看代码:

tcp_time_wait函数中的代码行:recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);对应的实现如下:

=====linux-2.6.37 net/ipv4/tcp_ipv4.c 1772=====

int tcp_v4_remember_stamp(struct sock *sk)
{
    //。。。。。。此处省略很多代码。。。。。。
 
    //当获取对端信息时,进行快速回收,否则不进行快速回收
    if (peer) 
    {
        if ((s32)(peer->tcp_ts - tp->rx_opt.ts_recent) <= 0 ||
           ((u32)get_seconds() - peer->tcp_ts_stamp > TCP_PAWS_MSL &&
            peer->tcp_ts_stamp <= (u32)tp->rx_opt.ts_recent_stamp)) 
        {
            peer->tcp_ts_stamp = (u32)tp->rx_opt.ts_recent_stamp;
            peer->tcp_ts = tp->rx_opt.ts_recent;
        }
 
        if (release_it)
            inet_putpeer(peer);
        return 1;
    }
 
    return 0;
}

上面这段代码应该就是测试的时候虚拟机环境不会释放的原因,当使用虚拟机NAT出去的时候,服务器无法获取隐藏在NAT后的机器信息。

生产环境也出现了设置了选项,但TIME_WAIT连接数达到4W多的现象,可能和虚拟机有关,也可能和组网有关。

总结一下:

1)快速回收到底有多快?

答:局域网环境下,700ms就回收

2)有的资料说只要打开tcp_tw_recycle即可,有的又说要tcp_timestamps同时打开,到底是哪个正确?

答:需要同时打开,但默认情况下tcp_timestamps就是打开的,所以会有人说只要打开tcp_tw_recycle即可

3)为什么从虚拟机发起客户端连接时选项无效,非虚拟机连接就有效?

答:和网络组网有关系,无法获取对端信息时就不进行快速回收。

注意1

NAT环境下,打开 tcp_tw_recycle选项可能会引发其他问题,tcp_tw_recycle是依赖tcp_timestamps参数的。例如办公室的外网地址只有一个,所有人访问后台都会通过路由器做SNAT将内网地址映射为公网IP,由于服务端和客户端都启用了tcp_timestamps,因此TCP头部中增加时间戳信息,而在服务器看来,同一客户端的时间戳必然是线性增长的,但是,由于我的客户端网络环境是NAT,因此每台主机的时间戳都是有差异的,在启用tcp_tw_recycle后,一旦有客户端断开连接,服务器可能就会丢弃那些时间戳较小的客户端的SYN包,这也就导致了网站访问极不稳定。

简单来说就是,Linux会丢弃所有来自远端的timestramp时间戳小于上次记录的时间戳(由同一个远端发送的)的任何数据包。也就是说要使用该选项,则必须保证数据包的时间戳是单调递增的。同时从4.10内核开始,官方修改了时间戳的生成机制,所以导致 tcp_tw_recycle 和新时间戳机制工作在一起不那么友好,同时 tcp_tw_recycle 帮助也不那么的大。

此处的时间戳并不是我们通常意义上面的绝对时间,而是一个相对时间。很多情况下,我们是没法保证时间戳单调递增的,比如业务服务器之前部署了NAT,LVS等情况。相信很多小伙伴上班的公司大概率实用实用各种公有云,而各种公有云的 LVS 网关都是 FullNAT 。所以可能导致在高并发的情况下,莫名其妙的 TCP 建联不是那么顺畅或者丢连接

主机A SIP:P1 (时间戳T0) —> Server

主机A断开后

主机B SIP:P1 (时间戳T1) T1 < T0 —> Server 丢弃

注意2

在linux内核版本从4.12之后,tcp_tw_recycle已经被废弃了

我的linux机器是ubuntu,查看内核版本如下

 linux 内核版本已经4.15了,如果开发tcp_tw_recycle,执行命令刷新的话,就会有如下报错

 提示得很明确,这个文件不存在

sysctl: cannot stat /proc/sys/net/ipv4/tcp_tw_recycle: No such file or directory

综上

可以看出这种方法不是很保险,在实际应用中可能受到虚拟机、网络组网、防火墙之类的影响从而导致不能进行快速回收。

办法5. 打开tcp_tw_reuse和tcp_timestamps选项

官方文档中解释如下:

tcp_tw_recycle选项:Allow to reuse TIME-WAIT sockets for new connections when it is

safe from protocol viewpoint. Default value is 0

这里的关键在于“协议什么情况下认为是安全的”,由于环境限制,没有办法进行验证,通过看源码简单分析了一下

=====linux-2.6.37 net/ipv4/tcp_ipv4.c 114=====

int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
{
    const struct tcp_timewait_sock *tcptw = tcp_twsk(sktw);
    struct tcp_sock *tp = tcp_sk(sk);
 
 
    /* With PAWS, it is safe from the viewpoint
      of data integrity. Even without PAWS it is safe provided sequence
      spaces do not overlap i.e. at data rates <= 80Mbit/sec.
      Actually, the idea is close to VJ's one, only timestamp cache is
      held not per host, but per port pair and TW bucket is used as state
      holder.
      If TW bucket has been already destroyed we fall back to VJ's scheme
      and use initial timestamp retrieved from peer table.
    */
    
    //从代码来看,tcp_tw_reuse选项和tcp_timestamps选项也必须同时打开;否则tcp_tw_reuse就不起作用
    //另外,所谓的“协议安全”,从代码来看应该是收到最后一个包后超过1s
    if (tcptw->tw_ts_recent_stamp &&
       (twp == NULL || (sysctl_tcp_tw_reuse &&
        get_seconds() - tcptw->tw_ts_recent_stamp > 1))) 
    {
        tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
        if (tp->write_seq == 0)
            tp->write_seq = 1;
        tp->rx_opt.ts_recent  = tcptw->tw_ts_recent;
        tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
        sock_hold(sktw);
        return 1;
    }
 
    return 0;
}

总结一下:

1. tcp_tw_reuse选项和tcp_timestamps选项也必须同时打开;

2. 重用TIME_WAIT的条件是收到最后一个包后超过1s。

官方手册有一段警告:

It should not be changed without advice/request of technical experts.

对于大部分局域网或者公司内网应用来说,满足条件都是没有问题的,因此官方手册里面的警告其实也没那么可怕

办法6:设置tcp_max_tw_buckets为一个较小的值,要比可用端口范围小,比如可用端口范围为6万,这个值可以设置为5.5万

tcp_max_tw_buckets - INTEGER

官方文档解释如下

Maximal number of timewait sockets held by system simultaneously. If this number is exceeded time-wait socket is immediately destroyed and warning is printed.

翻译一下:内核持有的状态为TIME_WAIT的最大连接数。如果超过这个数字,新的TIME_WAIT的连接会被立即销毁,并打印警告

官方文档没有说明默认值,通过几个系统的简单验证,初步确定默认值是180000

源码如下

void tcp_time_wait(struct sock *sk, int state, int timeo)
{
    struct inet_timewait_sock *tw = NULL;
    const struct inet_connection_sock *icsk = inet_csk(sk);
    const struct tcp_sock *tp = tcp_sk(sk);
    int recycle_ok = 0;
 
    if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp)
        recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);
 
    // 这里判断TIME_WAIT状态的连接数是否超过上限
    if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets)
        tw = inet_twsk_alloc(sk, state);
 
    if (tw != NULL) 
    {
        //分配成功,进行TIME_WAIT状态处理,此处略去很多代码
    }   
    else 
    {
        //分配失败,不进行处理,只记录日志: TCP: time wait bucket table overflow
        /* Sorry, if we're out of memory, just CLOSE this
        * socket up.  We've got bigger problems than
        * non-graceful socket closings.
        */
        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPTIMEWAITOVERFLOW);
    }
 
    tcp_update_metrics(sk);
    tcp_done(sk);
}

官方手册中有一段警告:

This limit exists only to prevent simple DoS attacks, you _must_ not lower the limit artificially,

but rather increase it (probably, after increasing installed memory), if network conditions require more than default value.

基本意思是这个用于防止Dos攻击,我们不应该人工减少,如果网络条件需要的话,反而应该增加。

但其实对于我们的局域网或者公司内网应用来说,这个风险并不大

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/88276.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言回调函数,提升C技巧必备

一、函数指针 在讲回调函数之前&#xff0c;我们需要了解函数指针。 我们都知道&#xff0c;C语言的灵魂是指针&#xff0c;我们经常使用整型指针&#xff0c;字符串指针&#xff0c;结构体指针等 int *p1; char *p2; STRUCT *p3; //STRUCT为我们定义的结构体 但是好像我们…

今天又get到一个小技能,collect_set函数

collect_set函数今天又get到一个小技能&#xff0c;掌握了hive一个关于列转行专用函数 collect_set函数。在这里做个学习笔记。collect_set是Hive内置的一个聚合函数, 结果返回一个消除了重复元素的对象集合, 其返回值类型是 array 。和collect_set相似的还有另一个函数collect…

Android12指纹调用流程介绍(一)

Android12指纹框架流程介绍(一) 前言&#xff1a;根据网上的相关资料&#xff0c;Android 12开始支持屏下指纹方案&#xff0c;根据自己手中的测试机&#xff0c; ​ 下载了android 12的源码&#xff0c;准备学习下新的指纹框架 注&#xff1a;目前笔者使用的版本是android_…

Qt扫盲-QSlider理论总结

QSlider理论总结1. 简述2. 常用功能3. 外观4. 最常用信号5. 键盘功能1. 简述 QSlider是用于控制边界值的经典小部件。它允许用户沿水平或垂直凹槽移动QSlider 的滑块&#xff0c;并将 滑块 的位置转换为合法范围内的整数值。但是 QSlider 仅提供整数范围。如果QSlider处理非常…

RSTP快速生成树简介、RSTP与STP的区别、RSTP BPDU字段信息、RSTP角色与端口状态介绍、RSTP工作过程之P/A机制详细分解)

2.11.0 以太网 RSTP快速生成树&#xff08;简介、RSTP与STP、RSTP BPDU、RSTP端口状态、RSTP工作过程之P/A机制&#xff09; RSTP快速生成树简介RSTP、STP的区别RSTP BPDU格式RSTP与STP的兼容性RSTP如何兼容STP&#xff1f;RSTP端口状态RSTP的5种端口角色RSTP的3种端口状态RSTP…

Windows OS中关闭445 Port

445端口是什么&#xff1f; 445端口是一个毁誉参半的端口&#xff0c;它和139端口一起是IPC$入侵的主要通道。有了它我们可以在局域网中轻松访问各种共享文件夹或共享打印机&#xff0c;但也正是因为有了它&#xff0c;黑客们才有了可乘之机&#xff0c;他们能通过该端口偷偷共…

Aba | 全自动biomarker分析神包!~(原作者用这个包发了三篇Nature啦~)

1写在前面 今天介绍一个Github上的神包吧, 主要是用于Biomarker的临床分析, 原作者用这个包已经发了3篇Nature了, 一起看看吧:&#x1f447; 在一些针对Biomarker的临床研究中, 我们常常需对Biomarker进行模型拟合, 预测效果评估等等.&#x1f970; 这个包可以完美解决这些问题…

[附源码]Python计算机毕业设计SSM基于web的火车订票管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Ansible概述及命令行模块

目录 一、Ansible概述 1 Ansible是什么 2 Ansible的四个组件&#xff1a; 3 Ansible的特性 二、Ansible 环境安装部署 三、ansible常用的命令行模块 1 command模块 2 shell模块 3 cron模块 4 user模块 5 group模块 6 copy模块 7 file模块 8 hostname模块 9 ping模块 10 …

Web端H5播放FLV、HLS、MP4 (二)

使用video.js播放HLS、FLV、MP4。 MP4、FLV、HLS、RTMP等协协和播放器之间的支持情况&#xff0c;参看这里。 一、主要JS插件&#xff1a; jquery(v3.6.0) video.js (v7.21.0&#xff0c;其它版本没试) https://cdn.bootcdn.net/ajax/libs/video.js/7.21.0/video.min.js f…

基于FPGA的RGB转HSV图像算法设计

一、什么是HSV色彩空间&#xff1f; RGB色彩空间是基于三基色而言&#xff0c;即红色、绿色、蓝色。而HSV色彩空间则是基于色调、饱和度和亮度而言的。 色调&#xff08;H&#xff09;是指光的颜色&#xff0c;例如&#xff0c;彩虹中的赤&#xff0c;橙&#xff0c;黄&#…

如何自学黑客?自学黑客技术需要学多久?

问题一&#xff1a;黑客如何学起&#xff1f; 答&#xff1a;必须从学习者的角度来看&#xff0c;如果你是一个已经学过编程&#xff0c;通晓几门语言的人那么这个答案就会和一个从没有接触过的计算机&#xff0c;甚至连什么叫高级语言还不知道的人有所区别的对待。 这就像是登…

LeetCode題目笔记——面试题 01.01. 判定字符是否唯一

文章目录题目描述题目难度——简单方法一&#xff1a;使用集合代码/Python方法二——用一个数组代码/Python方法三——位运算代码/Python总结题目描述 实现一个算法&#xff0c;确定一个字符串 s 的所有字符是否全都不同。 示例 1&#xff1a; 输入: s “leetcode” 输出: …

nvm 实战过程

文章目录前情提要应用场景实战解析最后前情提要 nvm是什么&#xff1a; nvm 全名 node.js version management&#xff0c;顾名思义是一个nodejs的版本管理工具。通过它可以安装和切换不同版本的nodejs。 应用场景 在我们前端工程师的日常工作中&#xff0c;不免要与不同基于n…

解决JavaWeb报错:此处不允许注解

目录 前言必读 一、遇到问题 二、解决办法 前言必读 读者手册&#xff08;必读&#xff09;_云边的快乐猫的博客-CSDN博客 一、遇到问题 1.在使用时候会发现报错为此处不允许使用注解&#xff0c;其实这是粗心和不熟练使用导致的 二、解决办法 2.把WebServlet("/ddd&…

【基于多尺度的融合:IVIF】

Infrared and visible image fusion via gradientlet filter &#xff08;基于梯度波滤波器的红外与可见光图像融合&#xff09; 本文从亮度和梯度分离的角度出发&#xff0c;提出了一种基于模糊梯度阈值函数和全局优化的图像滤波器梯度小波滤波器。它可以在保持图像的整体亮…

android aidl使用demo

android使用aidl原理 参考链接: https://www.twle.cn/l/yufei/android/android-basic-service-aidl.html 通过这部分代码, 加深对Android AIDL的理解 aidl server端 ILanguage.aidl 步骤: 在main目录下创建aidl文件夹, 并创建对应的包 com.example.aidl, 然后创建ILanguage.ai…

分享5款好用但一直不火的宝藏软件

很多软件用起来很好用&#xff0c;但是由于这样那样的原因&#xff0c;一直没什么知名度&#xff0c;但是不代表它们不好用&#xff0c;我的任务就是把这些宝藏分享给大家。 1.系统字体美化——MacType MacType是一款非常著名的系统字体美化软件&#xff0c;相比 GDI 更加容易…

【JVM故障问题排查心得】「内存诊断系列」Docker容器经常被kill掉,k8s中该节点的pod也被驱赶,怎么分析?

背景介绍 最近的docker容器经常被kill掉&#xff0c;k8s中该节点的pod也被驱赶。 我有一个在主机中运行的Docker容器&#xff08;也有在同一主机中运行的其他容器&#xff09;。该Docker容器中的应用程序将会计算数据和流式处理&#xff0c;这可能会消耗大量内存。 该容器会不…

操作系统之线程和进程

文章目录一. 什么是操作系统二. 进程和线程1. 进程2. 线程3. 进程的管理3.1 PBC中的一些属性3.2 并发和并行3.3 进程的调度3.4 内存管理3.5 进程间的通信3.6 并发编程4. 进程与线程的区别一. 什么是操作系统 操作系统本质上是一个软件, 发挥的是管理作用, 可以管理软件和硬件,…