用C++打造高并发服务器:从基础到实战,轻松掌握高性能编程

文摘   2024-12-16 09:01   浙江  

写高并发服务器一直是C++程序员最想征服的高地之一。不过别被“高并发”这个词吓到,掌握几个核心概念,搞懂基本原理,就能轻松驾驭。我写了不少服务器程序,今天就和大家分享下经验心得。


1

服务器那些事儿

说到服务器开发, 网络编程 是绕不开的话题。在Linux系统下,咱们主要用到 socket编程 。socket就像两个程序之间的电话线,建立连接后就能互相传数据了。


int sock = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in server_addr;

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons(8080);

server_addr.sin_addr.s_addr = INADDR_ANY;

bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));

温馨提示:记得检查socket相关函数的返回值,处理可能的错误情况。这个习惯得养成,不然程序可能莫名其妙就崩溃了。


2

I/O模型大比拼

说到高并发, I/O模型 是个大重点。传统的 阻塞式I/O 就像排队买奶茶,前面的人不完事,后面的就只能干等。


epoll 就厉害了,它像个超级店员,谁的奶茶做好了就通知谁,不用傻等。看代码:


int epfd = epoll_create(1);

struct epoll_event ev, events[MAX_EVENTS];

ev.events = EPOLLIN;

ev.data.fd = listen_sock;

epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sock, &ev);

while(true) {

int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);

for(int i = 0; i < nfds; i++) {

if(events[i].data.fd == listen_sock) {

// 处理新连接

} else {

// 处理数据

}

}

}

3

线程池搞起来

光有epoll还不够, 多线程 才是提升性能的关键。不过开太多线程也不好,整个 线程池 最合适。


class ThreadPool {

private:

vector<thread> workers;

queue<function<void()>> tasks;

mutex queue_mutex;

condition_variable condition;

bool stop;

public:

ThreadPool(size_t threads) : stop(false) {

for(size_t i = 0; i < threads; i++)

workers.emplace_back([this] {

while(true) {

function<void()> task;

{

unique_lock<mutex> lock(queue_mutex);

condition.wait(lock, [this] {

return stop || !tasks.empty();

});

if(stop && tasks.empty()) return;

task = move(tasks.front());

tasks.pop();

}

task();

}

});

}

};

温馨提示:用 智能指针 管理资源,别直接用裸指针。内存泄漏这种低级错误咱可不能犯。


4

性能优化小技巧

写高并发服务器, 内存管理 特别重要。用 对象池 预分配内存,避免频繁的内存申请和释放:


template<typename T>

class ObjectPool {

queue<T*> pool;

mutex mtx;

public:

T* acquire() {

lock_guard<mutex> lock(mtx);

if (pool.empty()) {

return new T();

}

T* obj = pool.front();

pool.pop();

return obj;

}

void release(T* obj) {

lock_guard<mutex> lock(mtx);

pool.push(obj);

}

};

还有个小妙招,用 无锁队列 减少线程竞争:


template<typename T>

class LockFreeQueue {

struct Node {

T data;

atomic<Node*> next;

};

atomic<Node*> head;

atomic<Node*> tail;

};

5

调试和监控

服务器程序一跑起来就得有 日志系统 ,不然出了问题真找不着北。我习惯用 异步日志 ,性能好很多:


class AsyncLogger {

queue<string> log_queue;

thread logger_thread;

void background_logging() {

while(true) {

string log;

{

lock_guard<mutex> lock(mtx);

if(!log_queue.empty()) {

log = move(log_queue.front());

log_queue.pop();

}

}

if(!log.empty()) {

write_to_file(log);

}

this_thread::sleep_for(chrono::milliseconds(100));

}

}

};

写服务器最怕的就是 内存泄漏 和 死锁 。用 valgrind 检查内存问题,用 gdb 调试死锁,这俩工具必须熟练掌握。


代码写完了,还得做 压力测试 。ab、wrk这些工具都挺好用,能快速发现性能瓶颈。


高并发服务器说难也难,说简单也简单。掌握这些核心知识点,动手写几个demo,慢慢就上手了。错误和bug是难免的,关键是要及时发现和解决。



椰子树的秘密
优质内容创作者
 最新文章