Trong nhóm nghiên cứu của tôi, gần đây chúng tôi đã nâng cấp HĐH trên các máy của chúng tôi từ Red Hat 6.2 lên Debian 8.3 và nhận thấy rằng thời gian khứ hồi TCP thông qua các NIC Intel 1G tích hợp giữa các máy của chúng tôi đã tăng gấp đôi từ khoảng 110 đấm lên 220.
Lúc đầu, tôi nghĩ đó là vấn đề về cấu hình, vì vậy tôi đã sao chép tất cả các cấu hình sysctl (chẳng hạn như tcp_low_latency=1
) từ các máy Red Hat chưa được nâng cấp sang các máy Debian và điều đó không khắc phục được sự cố. Tiếp theo, tôi nghĩ rằng đây có thể là sự cố phân phối Linux và đã cài đặt Red Hat 7.2 trên các máy, nhưng thời gian khứ hồi vẫn còn khoảng 220 Lời.
Cuối cùng, tôi nhận ra rằng có lẽ vấn đề xảy ra với các phiên bản kernel của Linux vì Debian 8.3 và Red Hat 7.2 đều sử dụng kernel 3.x trong khi Red Hat 6.2 sử dụng kernel 2.6. Vì vậy, để kiểm tra điều này, tôi đã cài đặt Debian 6.0 với Linux kernel 2.6 và bingo! Thời gian đã nhanh chóng trở lại ở mức 110.
Những người khác cũng đã trải qua những độ trễ cao hơn này trong các phiên bản mới nhất của Linux và có cách giải quyết nào không?
Ví dụ làm việc tối thiểu
Dưới đây là một ứng dụng C ++ có thể được sử dụng để đánh giá độ trễ. Nó đo độ trễ bằng cách gửi tin nhắn, chờ phản hồi và sau đó gửi tin nhắn tiếp theo. Nó thực hiện điều này 100.000 lần với các tin nhắn 100 byte. Do đó, chúng tôi có thể chia thời gian thực hiện của khách hàng cho 100.000 để có được độ trễ chuyến đi khứ hồi. Để sử dụng lần đầu tiên biên dịch chương trình này:
g++ -o socketpingpong -O3 -std=c++0x Server.cpp
Tiếp theo chạy phiên bản ứng dụng phía máy chủ trên máy chủ (giả sử trên 192.168.0.101). Chúng tôi chỉ định IP để đảm bảo rằng chúng tôi đang lưu trữ trên một giao diện nổi tiếng.
socketpingpong 192.168.0.101
Và sau đó sử dụng tiện ích Unix time
để đo thời gian thực hiện của máy khách.
time socketpingpong 192.168.0.101 client
Chạy thử nghiệm này giữa hai máy chủ Debian 8.3 có phần cứng giống hệt nhau cho kết quả như sau.
real 0m22.743s
user 0m0.124s
sys 0m1.992s
Kết quả Debian 6.0 là
real 0m11.448s
user 0m0.716s
sys 0m0.312s
Mã số:
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <linux/futex.h>
#include <arpa/inet.h>
#include <algorithm>
using namespace std;
static const int PORT = 2444;
static const int COUNT = 100000;
// Message sizes are 100 bytes
static const int SEND_SIZE = 100;
static const int RESP_SIZE = 100;
void serverLoop(const char* srd_addr) {
printf("Creating server via regular sockets\r\n");
int sockfd, newsockfd;
socklen_t clilen;
char buffer[SEND_SIZE];
char bufferOut[RESP_SIZE];
struct sockaddr_in serv_addr, cli_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
perror("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(srd_addr);
serv_addr.sin_port = htons(PORT);
fflush(stdout);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
}
listen(sockfd, INT_MAX);
clilen = sizeof(cli_addr);
printf("Started listening on %s port %d\r\n", srd_addr, PORT);
fflush(stdout);
while (true) {
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
perror("ERROR on accept");
printf("New connection\r\n");
int status = 1;
while (status > 0) {
// Read
status = read(newsockfd, buffer, SEND_SIZE);
if (status < 0) {
perror("read");
break;
}
if (status == 0) {
printf("connection closed");
break;
}
// Respond
status = write(newsockfd, bufferOut, RESP_SIZE);
if (status < 0) {
perror("write");
break;
}
}
close(newsockfd);
}
close(sockfd);
}
int clientLoop(const char* srd_addr) {
// This example is copied from http://www.binarytides.com/server-client-example-c-sockets-linux/
int sock;
struct sockaddr_in server;
char message[SEND_SIZE] , server_reply[RESP_SIZE];
//Create socket
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock == -1)
{
printf("Could not create socket");
}
puts("Socket created");
server.sin_addr.s_addr = inet_addr(srd_addr);
server.sin_family = AF_INET;
server.sin_port = htons( PORT );
//Connect to remote server
if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0)
{
perror("connect failed. Error");
return 1;
}
printf("Connected to %s on port %d\n", srd_addr, PORT);
// Fill buffer
for (int i = 0; i < SEND_SIZE; ++i) {
message[i] = 'a' + (i % 26);
}
for (int i = 0; i < COUNT; ++i) {
if (send(sock, message, SEND_SIZE, 0) < 0) {
perror("send");
return 1;
}
if ( recv(sock, server_reply, RESP_SIZE, 0) < 0) {
perror("recv");
return 1;
}
}
close(sock);
printf("Sending %d messages of size %d bytes with response sizes of %d bytes\r\n",
COUNT, SEND_SIZE, RESP_SIZE);
return 0;
}
int main(int argc, char** argv) {
if (argc < 2) {
printf("\r\nUsage: socketpingpong <ipaddress> [client]\r\n");
exit(-1);
}
if (argc == 2)
serverLoop(argv[1]);
else
clientLoop(argv[1]);
return 0;
}