0%

保持 SSH 连接不中断的完整方案:客户端与服务端配置

使用 SSH 连接服务器时,若长时间无操作,连接常因超时被自动断开,需重新登录,影响工作效率。本文将从客户端(Linux/Mac/Windows)和服务端两个维度,详细介绍如何通过配置保持 SSH 连接长期稳定。

客户端配置(主动发送保活信号)

客户端配置的核心是定期向服务器发送 “保活消息”,避免连接因超时被断开。

Linux 或 Mac 客户端(终端直接连接)

(1)全局配置(对所有服务器生效)
1
2
# 编辑 SSH 客户端配置文件
vi ~/.ssh/config

添加以下内容:

1
2
3
4
5
6
# 对所有服务器生效(* 表示通配符)
Host *
# 每 120 秒向服务器发送一次保活消息(检查连接是否存活)
ServerAliveInterval 120
# 最多允许 30 次未收到服务器响应(超过则断开连接)
ServerAliveCountMax 30
(2)局部配置(仅对特定服务器生效)

若只需对某台服务器保持连接,可指定服务器 IP 或域名:

阅读全文 »

Linux 内存映射(mmap):将文件直接映射到内存的高效 IO 技术

内存映射(Memory Mapping)是 Linux 内核提供的一种高效文件访问机制,通过将磁盘文件的部分或全部内容映射到进程的地址空间,使进程可以像访问内存一样读写文件,无需通过传统的 read()/write() 系统调用。这种技术在高性能 IO 场景(如数据库、大文件处理)中被广泛应用。

内存映射的基本原理

核心思想

内存映射通过 mmap() 系统调用建立进程地址空间的一块区域磁盘文件的关联:

  • 内核会在进程的虚拟地址空间中分配一段连续的地址范围(称为 “映射区”);
  • 这段地址不直接对应物理内存,而是与目标文件的磁盘区块建立映射关系;
  • 当进程访问映射区时,内核会通过页表将访问转换为对文件的读写操作(按需加载数据到物理内存,即 “页缓存” 机制)。

与传统 IO 的区别

传统文件访问(read()/write())需要经过 “用户态缓冲区 ↔ 内核页缓存 ↔ 磁盘” 的两次数据拷贝,而内存映射简化了流程:

操作方式 数据路径 优势场景
传统 IO 用户缓冲区 ↔ 内核页缓存 ↔ 磁盘 小文件、随机读写、需精确控制 IO
内存映射(mmap) 进程地址空间(映射区) ↔ 内核页缓存 ↔ 磁盘 大文件、顺序读写、频繁访问

核心优势:减少用户态与内核态之间的数据拷贝,提升大文件访问效率。

mmap () 系统调用详解

函数原型

1
2
3
#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数说明:
  • addr:指定映射区的起始地址(通常设为 NULL,由内核自动分配);

  • length:映射的文件长度(字节数,必须 ≥ 0);

  • prot:映射区的保护权限(内存访问权限):

  • PROT_READ:可读;

    • PROT_WRITE:可写;
  • PROT_EXEC:可执行;

    • PROT_NONE:无权限。
  • flags:映射类型和行为(关键参数):

    • MAP_SHARED:映射区的修改会同步到文件(进程间共享修改);
    • MAP_PRIVATE:映射区的修改是私有的(Copy-on-Write,不影响原文件);
    • MAP_ANONYMOUS:匿名映射(无关联文件,用于进程间共享内存)。
  • fd:待映射的文件描述符(需先通过 open() 打开);

  • offset:文件映射的起始偏移量(必须是页大小的整数倍,通常为 0)。

返回值:

  • 成功:返回映射区的起始地址(void *);
  • 失败:返回 MAP_FAILED(void *)-1),并设置 errno

配套函数:munmap ()

用于解除内存映射,释放映射区:

1
int munmap(void *addr, size_t length);
  • addrmmap() 返回的映射区起始地址;
  • length:映射区的长度(需与 mmap() 一致);
  • 返回值:0 表示成功,-1 表示失败。

内存映射的关键特性

两种映射模式(flags 参数)

(1)MAP_SHARED(共享映射)
  • 进程对映射区的修改会同步到磁盘文件,且其他映射该文件的进程可见;
  • 适用于需要持久化修改或多进程协作的场景(如共享日志文件)。
(2)MAP_PRIVATE(私有映射)
  • 进程对映射区的修改不会同步到原文件,也不会被其他进程看到;
  • 内核采用 “写时复制(Copy-on-Write)” 机制:仅当进程修改映射区时,才复制物理页,避免影响其他进程;
  • 适用于临时读写文件(如读取配置文件并临时修改)。

匿名映射(MAP_ANONYMOUS

  • 无需关联文件(fd 设为 -1,offset 忽略),映射区的内容在进程退出后消失;

  • 常用于进程间共享内存(结合 fork() 或线程),或需要大块内存临时存储数据的场景;

  • 示例:

    1
    2
    // 分配 4KB 匿名共享内存(可被子进程继承)
    void *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

页缓存与延迟写入

内存映射依赖内核的页缓存(Page Cache) 机制:

  • 首次访问映射区时,内核会将文件内容从磁盘加载到物理内存(页缓存);

  • 进程修改映射区时,内核先更新页缓存,再通过 “延迟写回” 机制异步将脏页(修改过的页)写入磁盘(可通过 msync() 强制同步);

  • msync()函数:手动同步映射区与文件,确保修改持久化:

    1
    2
    int msync(void *addr, size_t length, int flags);
    // flags:MS_SYNC(同步等待写完成)、MS_ASYNC(异步写)、MS_INVALIDATE(丢弃缓存,重新从文件加载)

使用示例:通过 mmap 读写文件

读取文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

int main() {
int fd = open("test.txt", O_RDONLY); // 打开文件
struct stat st;
fstat(fd, &st); // 获取文件大小

// 映射文件(只读,共享模式)
char *addr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap failed");
return 1;
}

// 像访问内存一样读取文件内容
printf("File content: %s\n", addr);

// 解除映射并关闭文件
munmap(addr, st.st_size);
close(fd);
return 0;
}

写入文件内容(共享映射)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>

int main() {
int fd = open("test.txt", O_RDWR | O_CREAT, 0644); // 读写方式打开
ftruncate(fd, 1024); // 确保文件大小足够(如创建新文件)

// 映射文件(可读写,共享模式)
char *addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap failed");
return 1;
}

// 写入数据(直接修改内存)
strcpy(addr, "Hello, mmap!");

// 强制同步到磁盘
msync(addr, 1024, MS_SYNC);

munmap(addr, 1024);
close(fd);
return 0;
}

注意事项与限制

  1. 文件大小与映射长度
    • length 不能超过文件大小(除非文件可扩展,且以 O_RDWR 打开);
    • 映射超过文件大小的部分写入时,可能导致文件扩展(取决于文件系统支持)。
  2. 对齐要求
    • offset 必须是系统页大小(通常 4KB)的整数倍,否则 mmap() 失败(EINVAL)。
  3. 文件描述符关闭
    • 映射建立后,可关闭 fd(映射仍有效),但建议保持打开直至映射解除(便于错误排查)。
  4. 信号安全
    • mmap()munmap() 不是异步信号安全的,避免在信号处理函数中调用。
  5. 性能陷阱
    • 小文件使用 mmap() 可能因页缓存 overhead 导致性能不如传统 IO;
    • 频繁修改小区域时,MAP_PRIVATE 的写时复制可能带来额外开销。

应用场景

内存映射适用于以下场景:

  • 大文件处理:如数据库表文件、日志文件,避免频繁 read()/write() 拷贝;
  • 进程间共享内存:通过 MAP_SHARED 或匿名映射实现高效通信(比管道、消息队列快);
  • 动态加载代码:如操作系统加载可执行文件到内存执行(映射二进制文件并设置 PROT_EXEC);
  • 零拷贝 IO:结合网络编程(如 sendfile()),实现文件数据从页缓存直接发送到网络,避免用户态拷贝

Netty 缓冲区 ByteBuf 详解:设计与使用指南

Netty 的 ByteBuf 是对 JDK 原生 ByteBuffer 的增强,解决了其固定容量、读写指针切换繁琐等问题,是 Netty 网络数据传输的核心容器。本文将深入解析 ByteBuf 的设计理念、核心特性及使用模式,帮助理解其如何提升网络编程效率。

ByteBuf 与 ByteBuffer 的核心差异

JDK 原生 ByteBuffer 在高并发网络编程中存在明显局限,而 ByteBuf 通过针对性设计解决了这些问题:

特性 JDK ByteBuffer Netty ByteBuf
容量灵活性 固定容量,创建后不可修改 动态扩容,支持自动调整容量(≤ maxCapacity)
读写指针 单指针 position,需手动 flip() 切换读写模式 双指针 readerIndex(读)和 writerIndex(写),无需切换
内存管理 仅堆内存(HeapByteBuffer 支持堆内存、直接内存(堆外)及复合缓冲区
操作便捷性 方法繁琐(如 get()/put() 需手动控制指针) 提供丰富的读写方法(如 readInt()/writeInt()),自动更新指针

动态扩容机制

ByteBuf 允许在写入数据时自动扩容(默认最大容量为 Integer.MAX_VALUE),扩容策略为:

  • 当容量 < 4MB 时,每次翻倍扩容(如 64B → 128B → 256B…)。
  • 当容量 ≥ 4MB 时,每次增加 4MB。
阅读全文 »

分布式锁详解:实现方案与对比分析

在分布式系统中,多个节点竞争同一资源(如库存扣减、订单创建)时,需通过分布式锁保证操作的原子性,避免数据不一致。常见实现方式包括 Redis、ZooKeeper 和数据库,每种方案各有优劣,需根据场景选择。以下详细解析各方案的原理、代码实现及适用场景。

Redis 分布式锁:高性能的选择

Redis 凭借其高性能和原子操作特性,成为分布式锁的主流实现方案。核心思路是通过 set 命令的原子性实现加锁,结合过期时间和唯一标识避免死锁。

基础实现(setnx + expire 的问题与优化)

  • 原始方案:使用 setnx 加锁(仅当 key 不存在时成功),expire 设置过期时间(避免死锁)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 加锁:若 key 不存在则设置,返回 1 表示成功
    Long setnx = jedis.setnx("lock:order", "1");
    if (setnx == 1) {
    // 设置过期时间(30 秒)
    jedis.expire("lock:order", 30);
    // 执行业务逻辑
    // ...
    // 释放锁
    jedis.del("lock:order");
    }

    问题setnxexpire 是非原子操作,若加锁后未设置过期时间就宕机,会导致锁永久有效。

  • 优化方案:使用 Redis 2.6.12+ 支持的 set 命令扩展参数,实现 “加锁 + 过期时间” 原子操作:

阅读全文 »

TCP 连接参数优化详解:聚焦 TIME_WAIT 与端口管理

下面详细解析每个参数的作用、配置逻辑及注意事项。

核心参数解析与配置建议

参数 作用 风险与注意事项 推荐配置
net.ipv4.tcp_max_tw_buckets 限制系统中 TIME_WAIT 状态的最大连接数,超过则强制清除 数值过低可能导致正常连接被误删;过高会占用大量端口资源 10000-50000(根据并发量调整,建议不低于 10000)
net.ipv4.ip_local_port_range 定义客户端发起连接时可用的端口范围 范围过小可能导致高并发下 “端口耗尽”;需避免与知名端口(1-1023)冲突 1024 65000(扩大可用端口池,覆盖非特权端口)
net.ipv4.tcp_tw_reuse 允许将 TIME_WAIT 状态的端口复用为新连接 仅对客户端主动发起的连接有效;需配合 tcp_timestamps 开启 1(开启,显著减少 TIME_WAIT 占用)
net.ipv4.tcp_tw_recycle 加速 TIME_WAIT 状态的端口回收(已逐步被内核移除) 在 NAT 网络(如云服务器、局域网)中会导致连接失败(时间戳校验冲突) 不推荐开启(设为 0,新内核已废弃该参数)
net.ipv4.tcp_syncookies 当 SYN 队列溢出时,用 Cookie 验证连接合法性,防御 SYN 洪水攻击 开启会轻微增加 CPU 消耗;极端高并发下可能影响连接建立效率 1(默认开启,平衡安全性与可用性)

参数协同逻辑

这些参数需配合使用才能发挥最大效果,核心逻辑如下:

阅读全文 »