面试题答案
一键面试策略思路
- 简单轮询法:维护一个子进程的数组,每当有新请求到来,按顺序依次将请求分配给数组中的子进程。这种方法实现简单,但可能存在某些子进程处理请求速度较慢,导致新请求等待时间过长的问题。
- 加权轮询法:考虑到不同子进程处理能力可能不同,为每个子进程分配一个权重值。例如,性能好的子进程权重高,性能差的子进程权重低。每次分配请求时,根据权重值来决定分配给哪个子进程。这样能更合理地利用不同子进程的处理能力。
- 最少连接数法:记录每个子进程当前处理的连接数,将新请求分配给当前连接数最少的子进程。这种方法能动态反映子进程的负载情况,使负载更加均衡。
关键代码片段(以简单轮询法为例)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAX_PROCESSES 10
#define PORT 8080
int main() {
int sockfd, connfd;
struct sockaddr_in servaddr, cliaddr;
pid_t child_pids[MAX_PROCESSES];
int child_count = 0;
int next_child = 0;
// 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// 初始化服务器地址结构
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(PORT);
// 绑定套接字到地址
if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {
perror("Bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
// 监听连接
if (listen(sockfd, 5) != 0) {
perror("Listen failed");
close(sockfd);
exit(EXIT_FAILURE);
}
// 创建子进程
for (int i = 0; i < MAX_PROCESSES; i++) {
pid_t pid = fork();
if (pid < 0) {
perror("Fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程逻辑
close(sockfd); // 子进程关闭监听套接字
while (1) {
// 子进程等待连接
int local_connfd = accept(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
if (local_connfd < 0) {
perror("Accept failed");
continue;
}
// 处理请求
// 这里可添加具体的请求处理代码
close(local_connfd);
}
} else {
child_pids[child_count++] = pid;
}
}
// 父进程负载均衡逻辑
while (1) {
socklen_t len = sizeof(cliaddr);
connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);
if (connfd < 0) {
perror("Accept failed");
continue;
}
// 将连接分配给下一个子进程
int child_index = next_child % child_count;
send_fd(child_pids[child_index], connfd); // 假设send_fd函数用于传递文件描述符
next_child++;
}
// 清理
for (int i = 0; i < child_count; i++) {
kill(child_pids[i], SIGTERM);
}
close(sockfd);
return 0;
}
// 辅助函数:传递文件描述符
void send_fd(pid_t pid, int fd) {
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("Pipe creation failed");
return;
}
if (sendmsg(pid, pipefd[1], fd) == -1) {
perror("Sendmsg failed");
}
close(pipefd[0]);
close(pipefd[1]);
}
上述代码实现了一个简单的基于轮询的负载均衡。父进程监听新连接,并按顺序将连接分配给各个子进程。子进程负责处理具体的请求。实际应用中,send_fd
函数需要根据具体的操作系统和需求进行完善,并且请求处理部分应根据实际业务逻辑填充。