java linux 调用32位so_从linux源码看socket(tcp)的timeout

从linux源码看socket(tcp)的timeout

前言

网络编程中超时时间是一个重要但又容易被忽略的问题,对其的设置需要仔细斟酌。在经历了数次物理机宕机之后,笔者详细的考察了在网络编程(tcp)中的各种超时设置,于是就有了本篇博文。本文大部分讨论的是socket设置为block的情况,即setNonblock(false),仅在最后提及了nonblock socket(本文基于linux 2.6.32-431内核)。

connectTimeout

在讨论connectTimeout之前,让我们先看下java和C语言对于socket connect调用的函数签名:

java:
// 函数调用中携带有超时时间
public void connect(SocketAddress endpoint, int timeout) ;
C语言:
// 函数调用中并不携带超时时间
int connect(int sockfd, const struct sockaddr * sockaddr, socklen_t socklent)

操作系统提供的connect系统调用并没有提供timeout的参数设置而java却有,我们先考察一下原生系统调用的超时策略。

connect系统调用

我们观察一下此系统调用的kernel源码,调用栈如下所示:

connect[用户态]
|->SYSCALL_DEFINE3(connect)[内核态]
|->sock->ops->connect

由于我们考察的是tcp的connect,其socket的内部结构如下图所示:1e365d54fb7dadf3d19ea14aa4260c6c.png
最终调用的是tcp_connect,代码如下所示:

int tcp_connect(struct sock *sk) {
......
// 发送SYN
err = tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
...
/* Timer for repeating the SYN until an answer. */
// 由于是刚建立连接,所以其rto是TCP_TIMEOUT_INIT
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
return 0;
}

又上面代码可知,在tcp_connect设置了重传定时器之后return回了tcp_v4_connect再return到inet_stream_connect。我们继续考察:

int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
......
// tcp_v4_connect=>tcp_connect
err = sk->sk_prot->connect(sk, uaddr, addr_len);
// 这边用的是sk->sk_sndtimeo
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
......
inet_wait_for_connect(sk, timeo));
......
out:
release_sock(sk);
return err;

sock_error:
err = sock_error(sk) ? : -ECONNABORTED;
sock->state = SS_UNCONNECTED;
if (sk->sk_prot->disconnect(sk, flags))
sock->state = SS_DISCONNECTING;
goto out
}

由上面代码可见,可以采用设置SO_SNDTIMEO来控制connect系统调用的超时,如下所示:

setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);

不设置SO_SNDTIMEO

如果不设置SO_SNDTIMEO,那么会由tcp重传定时器在重传超过设置的时候后超时,如下图所示:

e8398c64ac20f2e70bb0a096f0de2dcd.png
这个syn重传的次数由:

cat /proc/sys/net/ipv4/tcp_syn_retries 笔者机器上是5

来决定。那么我们就来看一下这个重传到底是多长时间:

tcp_connect中:
// 设置的初始超时时间为icsk_rto=TCP_TIMEOUT_INIT为1s
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
inet_csk(sk)->icsk_rto, TCP_RTO_MAX);

其重传定时器的回掉函数为tcp_retransmit_timer:

void tcp_retransmit_timer(struct sock *sk)
{
......
// 检测是否超时
if (tcp_write_timeout(sk))
goto out;
......
// icsk_rto = icsk_rto * 2,由于syn阶段,所以isck_rto不会由于网络传输而改变
// 重传的时候会以1,2,4,8指数递增
icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
// 重设timer
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
out:;
}

而计算tcp_write_timeout的逻辑则是在这篇blog中已经详细描述过,

https://my.oschina.net/alchemystar/blog/1936433

只不过在connect时刻,重传的计算以TCP_TIMEOUT_INIT为单位进行计算。而ESTABLISHED(read/write)时刻,重传以TCP_RTO_MIN进行计算。那么根据这段重传逻辑,我们就可以计算出不同tcp_syn_retries最终表现的超时时间。如下图所示:

4055478bed6e83f001da695b1d1d3379.png
那么整理下表格,对于系统调用,connect的超时时间为:

tcp_syn_retriestimeout
1min(so_sndtimeo,3s)
2min(so_sndtimeo,7s)
3min(so_sndtimeo,15s)
4min(so_sndtimeo,31s)
5min(so_sndtimeo,63s)

上述超时时间和笔者的实测一致。

推荐一本书

这本书教你怎么用wireshark来分析网络问题,非常有意思,而且很薄~

kernel代码版本细微变化

值得注意的是,linux本身官方发布的2.6.32源码对于tcp_syn_retries2的解释和RFC并不一致(至少笔者阅读的代码如此,这个细微的变化困扰了笔者好久,笔者下载了和机器对应的内核版本后才发现代码改了)。而redhat发布的2.6.32-431已经修复了这个问题(不清楚具体哪个小版本修改的),并将初始RTO设置为1s(官方2.6.32为3s)。这也是,不同内核小版本上的实验会有不同的connect timeout表现的原因(有的抓包到的重传SYN时间间隔为3,6,12……)。以下为代码对比:

========================>linux 内核版本2.6.32-431<========================
#define TCP_TIMEOUT_INIT ((unsigned)(1*HZ)) /* RFC2988bis initial RTO value */

static inline bool retransmits_timed_out(struct sock *sk,
unsigned int boundary,
unsigned int timeout,
bool syn_set)
{
......
unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN;
......
timeout = ((2 << boundary) - 1) * rto_base;
......

}
========================>linux 内核版本2.6.32.63<========================
#define TCP_TIMEOUT_INIT ((unsigned)(3*HZ)) /* RFC 1122 initial RTO value */

static inline bool retransmits_timed_out(struct sock *sk,
unsigned int boundary
{
......
timeout = ((2 << boundary) - 1) * TCP_RTO_MIN;
......
}

另外,tcp_syn_retries重传次数可以在单个socket中通过setsockopt设置。

JAVA connect API

现在我们考察下java的connect api,其connect最终调用下面的代码:

Java_java_net_PlainSocketImpl_socketConnect(...){

if (timeout <= 0) {
......
connect_rv = NET_Connect(fd, (struct sockaddr *)&him, len);
.....
}else{
// 如果timeout > 0 ,则设置为nonblock模式
SET_NONBLOCKING(fd);
/* no need to use NET_Connect as non-blocking */
connect_rv = connect(fd, (struct sockaddr *)&him, len);
/*
* 这边用系统调用select来模拟阻塞调用超时
*/
while (1) {
......
struct timeval t;
t.tv_sec = timeout / 1000;
t.tv_usec = (timeout % 1000) * 1000;
connect_rv = NET_Select(fd+1, 0, &wr, &ex, &t);
......
}
......
// 重新设置为阻塞模式
SET_BLOCKING(fd);
......
}
}

其和connect系统调用的不同点是,在timeout为0的时候,走默认的系统调用不设置超时时间的逻辑。在timeout>0时,将socket设置为非阻塞,然后用select系统调用去模拟超时,而没有走linux本身的超时逻辑,如下图所示:3ca34bf15bd7a1af6fc8e6275c372ca7.png
由于没有java并没有设置so_sndtimeo的选项,所以在timeout为0的时候,直接就通过重传次数来控制超时时间。而在调用connect时设置了timeout(不为0)的时候,超时时间如下表格所示:

tcp_syn_retriestimeout
1min(timeout,3s)
2min(timeout,7s)
3min(timeout,15s)
4min(timeout,31s)
5min(timeout,63s)

socketTimeout

write系统调用的超时时间

socket的write系统调用最后调用的是tcp_sendmsg,源码如下所示:

int tcp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
size_t size){
......
timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
......
while (--iovlen >= 0) {
......
// 此种情况是buffer不够了
if (copy <= 0) {
new_segment:
......
if (!sk_stream_memory_free(sk))
goto wait_for_sndbuf;

skb = sk_stream_alloc_skb(sk, select_size(sk),sk->sk_allocation);
if (!skb)
goto wait_for_memory;
}
......
}
......
// 这边等待write buffer有空间
wait_for_sndbuf:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:
if (copied)
tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
// 这边等待timeo长的时间
if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
goto do_error;
......
out:
// 如果拷贝了数据,则返回
if (copied)
tcp_push(sk, flags, mss_now, tp->nonagle);
TCP_CHECK_TIMER(sk);
release_sock(sk);
return copied;
out_err:
// error的处理
err = sk_stream_error(sk, flags, err);
TCP_CHECK_TIMER(sk);
release_sock(sk);
return err;
}

从上面的内核代码看出,如果socket的write buffer依旧有空间的时候,会立马返回,并不会有timeout。但是write buffer不够的时候,会等待SO_SNDTIMEO的时间(nonblock时候为0)。但是如果SO_SNDTIMEO没有设置的时候,默认初始化为MAX_SCHEDULE_TIMEOUT,可以认为其超时时间为无限。那么其超时时间会有另一个条件来决定,我们看下sk_stream_wait_memory的源码:

int sk_stream_wait_memory(struct sock *sk, long *timeo_p){
// 等待socket shutdown或者socket出现err
sk_wait_event(sk, &current_timeo, sk->sk_err ||
(sk->sk_shutdown & SEND_SHUTDOWN) ||
(sk_stream_memory_free(sk) &&
!vm_wait));
}

在write等待的时候,如果出现socket被shutdown或者socket出现错误的时候,则会跳出wait进而返回错误。在不考虑对端shutdown的情况下,出现sk_err的时间其实就是其write的timeout时间,那么我们看下什么时候出现sk->sk_err。

SO_SNDTIMEO不设置,write buffer满之后ack一直不返回的情况(例如,物理机宕机)

物理机宕机后,tcp发送msg的时候,ack不会返回,则会在重传定时器tcp_retransmit_timer到期后timeout,其重传到期时间通过tcp_retries2以及TCP_RTO_MIN计算出来。其源码可见笔者的blog:

https://my.oschina.net/alchemystar/blog/1936433

tcp_retries2的设置位置为:

cat /proc/sys/net/ipv4/tcp_retries2 笔者机器上是5,默认是15

SO_SNDTIMEO不设置,write buffer满之后对端不消费,导致buffer一直满的情况

和上面ack超时有些许不一样的是,一个逻辑是用TCP_RTO_MIN通过tcp_retries2计算出来的时间。另一个是真的通过重传超过tcp_retries2次数来time_out,两者的区别和rto的动态计算有关。但是可以大致认为是一致的。

上述逻辑如下图所示:

f1e492f875be9185361c341caa4f4cd0.png

write_timeout表格

tcp_retries2buffer未满buffer满
5立即返回min(SO_SNDTIMEO,(25.6s-51.2s)根据动态rto定
15立即返回min(SO_SNDTIMEO,(924.6s-1044.6s)根据动态rto定

java的SocketOutputStream的sockWrite0超时时间

java的sockWrite0没有设置超时时间的地方,同时也没有设置过SO_SNDTIMEOUT,其直接调用了系统调用,所以其超时时间和write系统调用保持一致。

readTimeout

ReadTimeout可能是最容易导致问题的地方。我们先看下系统调用的源码:

read系统调用

socket的read系统调用最终调用的是tcp_recvmsg, 其源码如下:

int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int nonblock, int flags, int *addr_len)
{
......
// 这边timeo=SO_RCVTIMEO
timeo = sock_rcvtimeo(sk, nonblock);
......
do{
......
// 下面这一堆判断表明,如果出现错误,或者已经被CLOSE/SHUTDOWN则跳出循环
if(copied) {
if (sk->sk_err ||
sk->sk_state == TCP_CLOSE ||
(sk->sk_shutdown & RCV_SHUTDOWN) ||
!timeo ||
signal_pending(current))
break;
} else {
if (sock_flag(sk, SOCK_DONE))
break;

if (sk->sk_err) {
copied = sock_error(sk);
break;
}
// 如果socket shudown跳出
if (sk->sk_shutdown & RCV_SHUTDOWN)
break;
// 如果socket close跳出
if (sk->sk_state == TCP_CLOSE) {
if (!sock_flag(sk, SOCK_DONE)) {
/* This occurs when user tries to read
* from never connected socket.
*/
copied = -ENOTCONN;
break;
}
break;
}
.......
}
.......

if (copied >= target) {
/* Do not sleep, just process backlog. */
release_sock(sk);
lock_sock(sk);
} else /* 如果没有读到target自己数(和水位有关,可以暂认为是1),则等待SO_RCVTIMEO的时间 */
sk_wait_data(sk, &timeo);
} while (len > 0);
......
}

上面的逻辑如下图所示:5c2df5e64cd4f5934babec82817a5788.png
重传以及探测定时器timeout事件的触发时机如下图所示:181b467eb8d5a05f07e02ff0876249bb.png
如果内核层面ack正常返回而且对端窗口不为0,仅仅应用层不返回任何数据,那么就会无限等待,直到对端有数据或者socket close/shutdown为止,如下图所示:aba4be23186fefa6cd6b2d63ac8d1f96.png
很多应用就是基于这个无限超时来设计的,例如activemq的消费者逻辑。

java的SocketInputStream的sockRead0超时时间

java的超时时间由SO_TIMOUT决定,而linux的socket并没有这个选项。其sockRead0和上面的java connect一样,在SO_TIMEOUT>0的时候依旧是由nonblock socket模拟,在此就不再赘述了。

ReadTimeout超时表格

C系统调用:

tcp_retries2对端无响应对端内核响应正常
5min(SO_RCVTIMEO,(25.6s-51.2s)根据动态rto定SO_RCVTIMEO==0?无限,SO_RCVTIMEO)
15min(SO_RCVTIMEO,(924.6s-1044.6s)根据动态rto定SO_RCVTIMEO==0?无限,SO_RCVTIMEO)

Java系统调用

tcp_retries2对端无响应对端内核响应正常
5min(SO_TIMEOUT,(25.6s-51.2s)根据动态rto定SO_TIMEOUT==0?无限,SO_RCVTIMEO
15min(SO_TIMEOUT,(924.6s-1044.6s)根据动态rto定SO_TIMEOUT==0?无限,SO_RCVTIMEO

对端物理机宕机之后的timeout

对端物理机宕机后还依旧有数据发送

对端物理机宕机时对端内核也gg了(不会发出任何包通知宕机),那么本端发送任何数据给对端都不会有响应。其超时时间就由上面讨论的
min(设置的socket超时[例如SO_TIMEOUT],内核内部的定时器超时来决定)。

对端物理机宕机后没有数据发送,但在read等待

这时候如果设置了超时时间timeout,则在timeout后返回。但是,如果仅仅是在read等待,由于底层没有数据交互,那么其无法知道对端是否宕机,所以会一直等待。但是,内核会在一个socket两个小时都没有数据交互情况下(可设置)启动keepalive定时器来探测对端的socket。如下图所示:6fa730360265d10eb61af0127114f25b.png
大概是2小时11分钟之后会超时返回。keepalive的设置由内核参数指定:

cat /proc/sys/net/ipv4/tcp_keepalive_time 7200 即两个小时后开始探测
cat /proc/sys/net/ipv4/tcp_keepalive_intvl 75 即每次探测间隔为75s
cat /proc/sys/net/ipv4/tcp_keepalve_probes 9 即一共探测9次

可以在setsockops中对单独的socket指定是否启用keepalive定时器(java也可以)。

对端物理机宕机后没有数据发送,也没有read等待

和上面同理,也是在keepalive定时器超时之后,将连接close。所以我们可以看到一个不活跃的socket在对端物理机突然宕机之后,依旧是ESTABLISHED状态,过很长一段时间之后才会关闭。

进程宕后的超时

如果仅仅是对端进程宕机的话(进程所在内核会close其所拥有的所有socket),由于fin包的发送,本端内核可以立刻知道当前socket的状态。如果socket是阻塞的,那么将会在当前或者下一次write/read系统调用的时候返回给应用层相应的错误。如果是nonblock,那么会在select/epoll中触发出对应的事件通知应用层去处理。
如果fin包没发送到对端,那么在下一次write/read的时候内核会发送reset包作为回应。

nonblock

设置为nonblock=true后,由于read/write都是立刻返回,且通过select/epoll等处理重传超时/probe超时/keep alive超时/socket close等事件,所以根据应用层代码决定其超时特性。定时器超时事件发生的时间如上面几小节所述,和是否nonblock无关。nonblock的编程模式可以让应用层对这些事件做出响应。

总结

网络编程中超时时间是个重要但又容易被忽略的问题,这个问题只有在遇到物理机宕机等平时遇不到的现象时候才会凸显。笔者在经历数次物理机宕机之后才好好的研究了一番,希望本篇文章可以对读者在以后遇到类似超时问题时有所帮助。

推荐一本书,这本书教你怎么用wireshark来分析网络问题,非常有意思,而且很薄~

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

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

相关文章

spark DAGScheduler、TaskSchedule、Executor执行task源码分析

摘要 spark的调度一直是我想搞清楚的东西&#xff0c;以及有向无环图的生成过程、task的调度、rdd的延迟执行是怎么发生的和如何完成的&#xff0c;还要就是RDD的compute都是在executor的哪个阶段调用和执行我们定义的函数的。这些都非常的基础和困难。花一段时间终于弄白了其中…

代码实现tan graph model for classification_自定义 Estimator 实现(以BERT为例)

本文将主要介绍tensorflow 的Estimator 这个高级API&#xff0c;它的主要作用就是提出一个高级范式&#xff08;paradigm&#xff09;&#xff0c;将模型的训练&#xff0c;验证&#xff0c;预测&#xff0c;以及保存规范起来&#xff0c;免去了tensorflow的Session.run 的操作…

英雄联盟怎么解除小窗口_英雄联盟手游怎么加好友_英雄联盟手游怎么加好友一起玩_资讯...

英雄联盟手游是腾讯联合英雄联盟开发商拳头开发的英雄联盟手游。不仅能够高度还原端游的经典操作和竞技体验&#xff0c;也具有非常多创新的元素&#xff0c;对于英雄联盟的全球生态布局具有重要意义。英雄联盟手游游戏中有非常多的英雄可以供玩家选择&#xff0c;并且拥有排位…

Sonar与jenkins集成

2019独角兽企业重金招聘Python工程师标准>>> 参考文档&#xff1a;http://blog.csdn.net/kefengwang/article/details/54377055 一.下载&#xff1a;wget https://fossies.org/linux/misc/sonarqube-7.0.zip 二.配置sonar.properties ## sudo vim /opt/sonarqube-6.…

eplan连接定义点不显示_EPLAN电气图实例--控制柜(控制面板)

EPLAN电气图实例--控制柜(控制面板)上期回顾(上期主要画了硬件的布局图)&#xff1a;这期主要画一个控制面板控制柜布局1.0 上期主要做了一个长方形的结构板&#xff0c;里面插入了一个结构盒&#xff0c;然后放置一个HMI的宏(这里是KTP1000&#xff0c;在官网随便找下就行了)&…

markdown 语法_markdown特殊语法之上下标

markdown特殊语法之上下标​markdown的基本语法很简单&#xff0c;百度一下就可以了&#xff0c;有空的话我再转载一些过来。我想的是平常其实需要用到的一些输入技巧&#xff0c;特殊用法或者扩展语法&#xff0c;还有一些难点倒是要记录学习一下。在写作的时候&#xff0c;大…

oracle安装向导卡住了_JDK 8 的安装与配置

一、安装环节1. 打开网页https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html2.找到对象的版本 尽量从官网下载(官网可能会比较慢&#xff0c;也可以通过浏览器输入jdk版本号进行下载)官网下载需要注册一个账号3.双击下载的 exe,如 jdk-8u131-windows…

web 前端 如何分享到instagram_如何找到靠谱的Web培训机构?web前端培训机构哪个好?...

现如今Web前端开发应用越来越广泛&#xff0c;Web前端工程师人才需求逐年递增&#xff0c;薪资待遇也是水涨船高&#xff0c;也因此吸引了越来越多的人想要迈入Web前端行业&#xff0c;参加Web前端培训是很多人选择学习前端开发技能的途径&#xff0c;那么Web前端培训机构哪个好…

Spring Data JPA 实例查询

转自&#xff1a;https://www.cnblogs.com/rulian/p/6533109.html 一、相关接口方法 在继承JpaRepository接口后&#xff0c;自动拥有了按“实例”进行查询的诸多方法。这些方法主要在两个接口中定义&#xff0c;一是QueryByExampleExecutor&#xff0c;一个是JpaRepository&am…

windows找不到文件gpedit.msc_此电脑右键管理提示windows找不到文件的解决方法

也许当你右键点击此电脑管理时&#xff0c;也许会出现Windows找不到文件的提示&#xff0c;下面提供一些解决方法&#xff1a;1、首先按下“Windows”R组合键打开运行&#xff0c;在弹出的“运行“对话框中输入”compmgmt.msc“&#xff0c;点击”确定“&#xff1b;2、如果此时…

java序列化流_java 序列化流与反序列化流

一 对象序列化流ObjectOutputStreamObjectOutputStream 将Java对象的基本数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。注意&#xff1a;只能将支持 java.io.Serializable 接口的对象写入流中用于从流…

win10文件显示后缀名_win10系统,如何去除“此电脑” 里的6个多余文件夹

Windows系统从XP到win7,再到进化到如今的win10&#xff0c;在系统性能和使用便捷性方面&#xff0c;确实改进了很多&#xff0c;但是&#xff0c;金无赤足&#xff0c;没有完美的东西&#xff0c;总是有一些小细节让我们感觉不舒服&#xff0c;如如鲠在喉。比如我们下面看到的这…

数字化工厂的五大系统_如何搭建以MES系统为核心的数字化工厂?

MES强调车间级的过程集成、控制和监控&#xff0c;以及合理地配置和组织所有资源。满足车间信息化需要&#xff0c;提高车间对随机事件的快速响应和处理能力&#xff0c;有力地促进企业信息化进程向车间层拓展。通过构建以“精益生产、智能制造”为特点的车间管理系统&#xff…

zookeeper数据结构及Znode类型

结构 1、层次化的目录结构&#xff0c;命名符合常规文件系统规范 2、每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识。 3、节点Znode可以包含数据和子节点&#xff08;但是EPHEMERAL类型的节点不能有子节点&#xff09;。 4、客户端应用可以在节点上设置监视器…

设计企业网站大纲_企业网站设计布局

网站制作一年350元&#xff0c;五站合一&#xff0c;快速建站 &#xff0c;www.sxjcwzjs.com,只需进入网站右上角注册快速建站即可(需要电脑登录注册)&#xff0c;需要联系我吧&#xff01;电话&#xff1a;13752214574&#xff0c;微信号&#xff1a;m1078582894企业网站布局很…

vue导入静态js_如何在vue js中加载静态图像

我有一个组件,它接受一个prop字符串,即一个url。它可以是远程图片,也可以是资产文件夹中的本地静态资产。目录结构:-assets/- logo.png-app.vue-components/-ImageTest.vue图像测试.vue{{imagelink}}export default {name: "ImageTest",props:{imagelink: String,},c…

搭建linux测试环境有什么用_谈谈现在搭建网站用什么程序好,选择对的程序是很重要的开头...

目前可以选择的网站程序还是蛮多的&#xff0c;开源的系统也很多&#xff0c;也有很多精品。更多时候&#xff0c;选择网站程序要结合网站的定位。说说dedecms&#xff0c;就是我们都知道的织梦程序&#xff0c;功能很齐全&#xff0c;作为一个简单的资讯门户网站&#xff0c;算…

Zookeeper应用:服务端上下线

需求 客户端感知服务器的上下线。 示意图 步骤 服务端&#xff1a; 1、所有机子向Zookeeper注册&#xff0c;注册znode为临时的。 2、有机子下线&#xff0c;连接断开后被Zookeeper自动删除&#xff0c;触发监听事件。 3、有机子上线&#xff0c;触发监听事件。 客户端&am…

哈希表查找失败的平均查找长度_你还应该知道的哈希冲突解决策略

本文首发于 vivo互联网技术 微信公众号 链接&#xff1a;https://mp.weixin.qq.com/s/5vxYoeARG1nC7Z0xTYXELA作者&#xff1a;Xuegui Chen哈希是一种通过对数据进行压缩, 从而提高效率的一种解决方法&#xff0c;但由于哈希函数有限&#xff0c;数据增大等缘故&#xff0c;哈希…

python 正则替换_5分钟速览Python正则表达式常用函数!五分钟就掌握它!

导读&#xff1a;正则表达式是处理字符串类型的"核武器"&#xff0c;不仅速度快&#xff0c;而且功能强大。本文不过多展开正则表达式相关语法&#xff0c;仅简要介绍python中正则表达式常用函数及其使用方法&#xff0c;以作快速查询浏览。01 Re概览Re模块是python的…