При разработке современного программного обеспечения производительность часто является одним из ключевых факторов, особенно при передаче больших файлов. Эффективные протоколы и инструменты могут значительно повысить скорость и надежность передачи. В этой статье подробно описано, как использовать gRPC и Protobuf для передачи больших файлов, а также сравнивается производительность с традиционной передачей TCP.
Раньше для передачи больших файлов часто использовался традиционный протокол TCP/IP. Хотя он и был простым, он часто работал медленно при передаче крупномасштабных данных, особенно в средах с плохими условиями сети. В недавнем проекте возникла необходимость передавать большие файлы данных. Тестирование с использованием традиционных методов показало, что задержка передачи не соответствует требованиям, поэтому я поискал информацию в Интернете и нашел лучшее решение.
По сравнению с TCP, gRPC представляет собой современное высокопроизводительное решение. gRPC — это высокопроизводительная платформа удаленного вызова процедур (RPC), разработанная Google. Она использует HTTP/2 в качестве протокола транспортного уровня и поддерживает несколько языков разработки, таких как C++, Java, Python и Go. Protobuf (Protocol Buffers) — это облегченный формат обмена данными, который позволяет эффективно сериализовать структурированные данные.
Чтобы использовать gRPC для разработки проектов, сначала необходимо установить gRPC и зависимые от него библиотеки в среде разработки. Ниже приведены шаги по установке gRPC, которые применимы к различным операционным системам, включая Windows, Linux и macOS.
Установку gRPC можно выполнить различными способами, включая использование менеджера пакетов или компиляцию из исходного кода. Ниже описано, как установить версию gRPC для C++ в Ubuntu (в комплекте с буферами протокола).
Примечание. Если версии gRPC и буферов протокола не совпадают, возникнут проблемы, и он не будет работать должным образом.
sudo apt install -y cmake
export MY_INSTALL_DIR=$HOME/.local
mkdir -p $MY_INSTALL_DIR
export PATH="$MY_INSTALL_DIR/bin:$PATH" # Чтобы эта команда вступила в силу навсегда, вы можете записать эту команду в файл ~/.bashrc.
sudo apt install -y build-essential autoconf libtool pkg-config
git clone --recurse-submodules -b v1.62.0 --depth 1 --shallow-submodules https://github.com/grpc/grpc
cd grpc
mkdir -p cmake/build
pushd cmake/build
cmake -DgRPC_INSTALL=ON \
-DgRPC_BUILD_TESTS=OFF \
-DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \
../..
make -j 4
make install
popd
common.cmake
является вспомогательным средством CMake Файлы модулей обычно используются для хранения файлов, совместно используемых проектами. CMake Конфигурация для упрощения и централизации управления CMakeLists.txt
код в файле. Такой подход помогает улучшить ремонтопригодность и читабельность проекта.
существовать gRPC В проекте,в примере кодаcommon.cmake
Включает следующее:
find_package()
или find_library()
Команда для поиска и настройки зависимых библиотек, необходимых для проекта, таких как gRPC、protobuf、SSL ждать.CMakeLists.txt
В файле повторяется тот же блок кода.# Copyright 2018 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# cmake build file for C++ route_guide example.
# Assumes protobuf and gRPC have been installed using cmake.
# See cmake_externalproject/CMakeLists.txt for all-in-one cmake build
# that automatically builds all the dependencies before building route_guide.
cmake_minimum_required(VERSION 3.8)
if(MSVC)
add_definitions(-D_WIN32_WINNT=0x600)
endif()
find_package(Threads REQUIRED)
if(GRPC_AS_SUBMODULE)
# One way to build a projects that uses gRPC is to just include the
# entire gRPC project tree via "add_subdirectory".
# This approach is very simple to use, but the are some potential
# disadvantages:
# * it includes gRPC's CMakeLists.txt directly into your build script
# without and that can make gRPC's internal setting interfere with your
# own build.
# * depending on what's installed on your system, the contents of submodules
# in gRPC's third_party/* might need to be available (and there might be
# additional prerequisites required to build them). Consider using
# the gRPC_*_PROVIDER options to fine-tune the expected behavior.
#
# A more robust approach to add dependency on gRPC is using
# cmake's ExternalProject_Add (see cmake_externalproject/CMakeLists.txt).
# Include the gRPC's cmake build (normally grpc source code would live
# in a git submodule called "third_party/grpc", but this example lives in
# the same repository as gRPC sources, so we just look a few directories up)
add_subdirectory(../../.. ${CMAKE_CURRENT_BINARY_DIR}/grpc EXCLUDE_FROM_ALL)
message(STATUS "Using gRPC via add_subdirectory.")
# After using add_subdirectory, we can now use the grpc targets directly from
# this build.
set(_PROTOBUF_LIBPROTOBUF libprotobuf)
set(_REFLECTION grpc++_reflection)
set(_ORCA_SERVICE grpcpp_orca_service)
if(CMAKE_CROSSCOMPILING)
find_program(_PROTOBUF_PROTOC protoc)
else()
set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>)
endif()
set(_GRPC_GRPCPP grpc++)
if(CMAKE_CROSSCOMPILING)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
else()
set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:grpc_cpp_plugin>)
endif()
elseif(GRPC_FETCHCONTENT)
# Another way is to use CMake's FetchContent module to clone gRPC at
# configure time. This makes gRPC's source code available to your project,
# similar to a git submodule.
message(STATUS "Using gRPC via add_subdirectory (FetchContent).")
include(FetchContent)
FetchContent_Declare(
grpc
GIT_REPOSITORY https://github.com/grpc/grpc.git
# when using gRPC, you will actually set this to an existing tag, such as
# v1.25.0, v1.26.0 etc..
# For the purpose of testing, we override the tag used to the commit
# that's currently under test.
GIT_TAG vGRPC_TAG_VERSION_OF_YOUR_CHOICE)
FetchContent_MakeAvailable(grpc)
# Since FetchContent uses add_subdirectory under the hood, we can use
# the grpc targets directly from this build.
set(_PROTOBUF_LIBPROTOBUF libprotobuf)
set(_REFLECTION grpc++_reflection)
set(_PROTOBUF_PROTOC $<TARGET_FILE:protoc>)
set(_GRPC_GRPCPP grpc++)
if(CMAKE_CROSSCOMPILING)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
else()
set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:grpc_cpp_plugin>)
endif()
else()
# This branch assumes that gRPC and all its dependencies are already installed
# on this system, so they can be located by find_package().
# Find Protobuf installation
# Looks for protobuf-config.cmake file installed by Protobuf's cmake installation.
option(protobuf_MODULE_COMPATIBLE TRUE)
find_package(Protobuf CONFIG REQUIRED)
message(STATUS "Using protobuf ${Protobuf_VERSION}")
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
set(_REFLECTION gRPC::grpc++_reflection)
if(CMAKE_CROSSCOMPILING)
find_program(_PROTOBUF_PROTOC protoc)
else()
set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>)
endif()
# Find gRPC installation
# Looks for gRPCConfig.cmake file installed by gRPC's cmake installation.
find_package(gRPC CONFIG REQUIRED)
message(STATUS "Using gRPC ${gRPC_VERSION}")
set(_GRPC_GRPCPP gRPC::grpc++)
if(CMAKE_CROSSCOMPILING)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
else()
set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_cpp_plugin>)
endif()
endif()
Этот файл конфигурации включает команды для генерации кода C++ из файлов прототипов.,и команды для компиляции этих сгенерированных файлов исходного кода в библиотеки и исполняемые файлы. Использование CMake,Мы можем гарантировать, что существующий проект может быть воспроизводимо построен в различных средах.
cmake_minimum_required(VERSION 3.15)
project(grpcDemo)
set(CMAKE_CXX_STANDARD 17)
include(common.cmake)
# Proto file
get_filename_component(transfer_proto "transferfile.proto" ABSOLUTE)
get_filename_component(transfer_proto_path "${transfer_proto}" PATH)
# Generated sources
set(transfer_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/transferfile.pb.cc")
set(transfer_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/transferfile.pb.h")
set(transfer_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/transferfile.grpc.pb.cc")
set(transfer_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/transferfile.grpc.pb.h")
add_custom_command(
OUTPUT "${transfer_proto_srcs}" "${transfer_proto_hdrs}" "${transfer_grpc_srcs}" "${transfer_grpc_hdrs}"
COMMAND ${_PROTOBUF_PROTOC}
ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
--cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
-I "${transfer_proto_path}"
--plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
"${transfer_proto}"
DEPENDS "${transfer_proto}")
# Include generated *.pb.h files
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
# transfer_grpc_proto
add_library(transfer_grpc_proto
${transfer_grpc_srcs}
${transfer_grpc_hdrs}
${transfer_proto_srcs}
${transfer_proto_hdrs})
target_link_libraries(transfer_grpc_proto
${_REFLECTION}
${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF})
# Targets greeter_[async_](client|server)
foreach(_target
transfer_client transfer_server)
add_executable(${_target} "${_target}.cpp")
target_link_libraries(${_target}
transfer_grpc_proto
${_REFLECTION}
${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF})
endforeach()
существоватьgRPCсередина,Определение служб и сообщений выполняется через файлы .proto. Например,Определить службу передачи файлов,Можетсуществоватьtransferfile.proto
середина如下определение:
syntax = "proto3";
package filetransfer;
service FileTransferService {
rpc Upload(stream FileChunk) returns (UploadStatus) {}
}
message FileChunk {
bytes content = 1;
}
message UploadStatus {
bool success = 1;
string message = 2;
}
Здесь определяетсяFileTransferService
Служить,содержитUpload
метод,Этот метод принимаетFileChunk
тип потока,и возвращаетUploadStatus
состояние。
Клиент и сервер реализуются через интерфейсы, созданные платформой gRPC, которые основаны на файлах .proto, определенных ранее.
Основная обязанность клиента — открыть файл, прочитать данные и затем отправить их на сервер в виде потока. Код реализации следующий:
#include <iostream>
#include <string>
#include <fstream>
#include <chrono>
#include <grpcpp/grpcpp.h>
#include <grpcpp/channel.h>
#include <grpcpp/client_context.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/security/credentials.h>
#include "transferfile.grpc.pb.h"
using grpc::Channel;
using grpc::ClientContext;
using grpc::ClientWriter;
using grpc::Status;
using transferfile::FileChunk;
using transferfile::FileUploadStatus;
using transferfile::TransferFile;
#define CHUNK_SIZE 3 * 1024 * 1024 // 3MB
class TransferClient
{
public:
TransferClient(std::shared_ptr<Channel> channel) : stub_(TransferFile::NewStub(channel)) {}
void uploadFile(const std::string &filename);
private:
std::unique_ptr<TransferFile::Stub> stub_;
};
void TransferClient::uploadFile(const std::string &filename)
{
FileChunk chunk;
char *buffer = new char[CHUNK_SIZE];
FileUploadStatus status;
ClientContext context;
std::ifstream infile;
unsigned long len = 0;
auto start = std::chrono::steady_clock::now();
infile.open(filename, std::ios::binary | std::ios::in);
if (!infile.is_open())
{
std::cerr << "Error: File not found" << std::endl;
return;
}
std::unique_ptr<ClientWriter<FileChunk>> writer(stub_->UploadFile(&context, &status)); // Create a writer to send chunks of file
while (!infile.eof())
{
infile.read(buffer, CHUNK_SIZE);
chunk.set_buffer(buffer, infile.gcount());
if (!writer->Write(chunk))
{
std::cerr << "Error: Failed to write chunk" << std::endl;
break;
}
len += infile.gcount();
}
infile.close();
delete[] buffer;
writer->WritesDone();
Status status1 = writer->Finish();
if (status1.ok() && len != status.length())
{
auto end = std::chrono::steady_clock::now();
std::cout << "File uploaded successfully" << std::endl;
std::cout << "Time taken: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
std::cout << "File size: " << len << " bytes" << std::endl;
auto speed = (len / std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()) * 1000 / 1024 / 1024;
std::cout << "Speed: " << speed << " MB/s" << std::endl;
}
else
{
std::cerr << "Error: " << status1.error_message() << "len: " << len << " status.length(): " << status.length()
<< std::endl;
}
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " <filename> [server_ip]" << std::endl;
return 1;
}
std::string filename(argv[1]);
std::string server_ip = "localhost";
if (argc == 3)
{
server_ip = argv[2];
}
TransferClient client(grpc::CreateChannel(server_ip + ":50051", grpc::InsecureChannelCredentials()));
client.uploadFile(filename);
return 0;
}
Код клиента показывает, как создать клиент gRPC, как открыть файл, как разрезать файл на фрагменты и как отправить эти фрагменты по сети на сервер.
Реализация на стороне сервера отвечает за получение блоков данных от клиента и запись их в файлы на сервере. Код сервера следующий:
#include <iostream>
#include <string>
#include <fstream>
#include <grpcpp/grpcpp.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>
#include <grpcpp/security/server_credentials.h>
#include "transferfile.grpc.pb.h"
#include <sys/resource.h>
#include <unistd.h>
#include <chrono>
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerReader;
using grpc::Status;
using transferfile::FileChunk;
using transferfile::FileUploadStatus;
using transferfile::TransferFile;
using namespace std::chrono;
#define CHUNK_SIZE 3 * 1024 * 1024 // 3MB
class TransferServiceImpl final : public TransferFile::Service
{
public:
Status UploadFile(ServerContext *context, ServerReader<FileChunk> *reader, FileUploadStatus *status) override;
protected:
void printUsage(const struct rusage &start, const struct rusage &end)
{
std::cout << "CPU Usage: User time: " << (end.ru_utime.tv_sec - start.ru_utime.tv_sec) + (end.ru_utime.tv_usec - start.ru_utime.tv_usec) / 1000000.0
<< "s, System time: " << (end.ru_stime.tv_sec - start.ru_stime.tv_sec) + (end.ru_stime.tv_usec - start.ru_stime.tv_usec) / 1000000.0 << "s" << std::endl;
std::cout << "Max resident set size: " << (end.ru_maxrss - start.ru_maxrss) << " KB" << std::endl;
}
};
Status TransferServiceImpl::UploadFile(ServerContext *context, ServerReader<FileChunk> *reader, FileUploadStatus *status)
{
FileChunk chunk;
std::ofstream outfile;
const char *data;
struct rusage usage_start, usage_end;
getrusage(RUSAGE_SELF, &usage_start);
auto start = high_resolution_clock::now();
outfile.open("output.bin", std::ios::binary | std::ios::out | std::ios::trunc);
if (!outfile.is_open())
{
std::cerr << "Error: Failed to open file" << std::endl;
return Status::CANCELLED;
}
while (reader->Read(&chunk))
{
data = chunk.buffer().c_str();
outfile.write(data, chunk.buffer().length());
}
long pos = outfile.tellp();
status->set_length(pos);
outfile.close();
auto end = high_resolution_clock::now();
getrusage(RUSAGE_SELF, &usage_end);
auto duration = duration_cast<milliseconds>(end - start);
printUsage(usage_start, usage_end);
std::cout << "Total transmission time: " << duration.count() << " ms" << std::endl;
std::cout << "Total data transmitted: " << pos << " bytes" << std::endl;
std::cout << "Transmission rate: " << (pos * 1000.0 / duration.count()) / 1024 / 1024 << " MB/s" << std::endl;
return Status::OK;
}
void RunServer()
{
std::string server_address("0.0.0.0:50051");
TransferServiceImpl service;
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
server->Wait();
}
int main(int argc, char **argv)
{
RunServer();
return 0;
}
Код сервера показывает, как создать сервер gRPC, как получать блоки данных, отправленные клиентом, и как записывать эти блоки данных в файл на диске.
Функции такие же, как у клиента gRPC.
#include <iostream>
#include <fstream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>
#include <cstring>
int main(int argc, char *argv[])
{
const char *server_ip = "127.0.0.1";
int server_port = 8888;
std::string file_path = argv[1];
// Create socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
std::cerr << "Socket creation failed." << std::endl;
return 1;
}
// Define the server address
struct sockaddr_in server_address;
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(server_port);
inet_pton(AF_INET, server_ip, &server_address.sin_addr);
// Connect to the server
if (connect(sock, (struct sockaddr *)&server_address, sizeof(server_address)) < 0)
{
std::cerr << "Connection to server failed." << std::endl;
close(sock);
return 1;
}
std::ifstream file(file_path, std::ios::binary);
if (!file.is_open())
{
std::cerr << "Failed to open file: " << file_path << std::endl;
close(sock);
return 1;
}
// Send file contents
char *buffer = new char[1024];
while (file.read(buffer, sizeof(buffer)) || file.gcount())
{
if (send(sock, buffer, file.gcount(), 0) < 0)
{
std::cerr << "Failed to send data." << std::endl;
break;
}
}
delete[] buffer;
std::cout << "File sent successfully." << std::endl;
file.close();
close(sock);
return 0;
}
Функции такие же, как у сервера gRPC.
#include <iostream>
#include <fstream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <string>
#include <sys/resource.h>
#include <unistd.h>
#include <chrono>
#define CHUNK_SIZE 3 * 1024 * 1024 // 3MB
using namespace std::chrono;
void printUsage(const struct rusage &start, const struct rusage &end)
{
std::cout << "CPU Usage: User time: " << (end.ru_utime.tv_sec - start.ru_utime.tv_sec) + (end.ru_utime.tv_usec - start.ru_utime.tv_usec) / 1000000.0
<< "s, System time: " << (end.ru_stime.tv_sec - start.ru_stime.tv_sec) + (end.ru_stime.tv_usec - start.ru_stime.tv_usec) / 1000000.0 << "s" << std::endl;
std::cout << "Max resident set size: " << (end.ru_maxrss - start.ru_maxrss) << " KB" << std::endl;
}
int main()
{
int server_port = 8888;
const char *output_file = "output_received_file";
// Create socket
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock < 0)
{
std::cerr << "Socket creation failed." << std::endl;
return 1;
}
// Bind socket to IP / port
struct sockaddr_in server_address;
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(server_port);
server_address.sin_addr.s_addr = INADDR_ANY;
if (bind(server_sock, (struct sockaddr *)&server_address, sizeof(server_address)) < 0)
{
std::cerr << "Bind failed." << std::endl;
close(server_sock);
return 1;
}
// Listen
if (listen(server_sock, 10) < 0)
{
std::cerr << "Listen failed." << std::endl;
close(server_sock);
return 1;
}
std::cout << "Server is listening on port " << server_port << std::endl;
// Accept connection
struct sockaddr_in client_address;
socklen_t client_len = sizeof(client_address);
int client_sock = accept(server_sock, (struct sockaddr *)&client_address, &client_len);
if (client_sock < 0)
{
std::cerr << "Accept failed." << std::endl;
close(server_sock);
return 1;
}
struct rusage usage_start, usage_end;
getrusage(RUSAGE_SELF, &usage_start);
auto start = high_resolution_clock::now();
std::ofstream file(output_file, std::ios::binary);
if (!file.is_open())
{
std::cerr << "Failed to open file for writing." << std::endl;
close(client_sock);
close(server_sock);
return 1;
}
// Receive data
char *buffer = new char[CHUNK_SIZE];
int bytes_received;
while ((bytes_received = recv(client_sock, buffer, sizeof(buffer), 0)) > 0)
{
file.write(buffer, bytes_received);
}
if (bytes_received < 0)
{
std::cerr << "Error in recv()." << std::endl;
}
else
{
std::cout << "File received successfully." << std::endl;
long pos = file.tellp();
auto end = high_resolution_clock::now();
getrusage(RUSAGE_SELF, &usage_end);
auto duration = duration_cast<milliseconds>(end - start);
printUsage(usage_start, usage_end);
std::cout << "Total transmission time: " << duration.count() << " ms" << std::endl;
std::cout << "Total data transmitted: " << pos << " bytes" << std::endl;
std::cout << "Transmission rate: " << (pos * 1000.0 / duration.count()) / 1024 / 1024 << " MB/s" << std::endl;
}
delete[] buffer;
file.close();
close(client_sock);
close(server_sock);
return 0;
}
Чтобы проверить эффективность gRPC и Protobuf, я провел тест производительности, чтобы сравнить разницу в производительности между использованием gRPC и традиционным TCP для прямой передачи больших файлов.
Методы испытаний включают в себя:
fallocate -l 2G 2GBfile.txt
)User time | System time | Max resident set size | Transmission time | Transmission rate | |
---|---|---|---|---|---|
gRPC | 28.5267 s | 17.2359 s | 18240 KB | 18.265 s | 112.127 MB/s |
TCP | 43.2163 s | 143.779 s | 0 KB | 187.408 s | 10.928 MB/s |
gRPC | 29.3631 s | 17.477 s | 17724 KB | 18.312 s | 111.839 MB/s |
TCP | 42.2783 s | 140.658 s | 0 KB | 183.123 s | 11.1837 MB/s |
gRPC | 28.8431 s | 17.7514 s | 16852 KB | 18.691 s | 109.571 MB/s |
TCP | 43.4233 s | 144.232 s | 0 KB | 189.021 s | 10.834 MB/s |
gRPC | 37.5632 s | 19.1164 s | 15424 KB | 18.589 s | 110.173 MB/s |
TCP | 40.2059 s | 140.3 s | 0 KB | 180.772 s | 11.3292 MB/s |
gRPC | 36.712 s | 18.1812 s | 14016 KB | 18.274 s | 112.072 MB/s |
TCP | 37.9126 s | 144.276 s | 0 KB | 182.392 s | 11.2286 MB/s |
gRPCсуществоватьCPUиспользоватьзначительно ниже традиционногоTCP метод сокета. Это происходит главным образом потому, что gRPC использует внутри себя более современный протокол HTTP/2, который поддерживает эффективные механизмы передачи данных, такие как мультиплексирование и передача данных на сервер, без необходимости устанавливать отдельное соединение для каждой задачи передачи файлов, например TCP. Кроме того, реализации gRPC могут включать более оптимизированные пути обработки данных, которые уменьшают накладные расходы на переключение контекста и системные вызовы.
Максимальный размер резидентного набора (максимальный размер резидентного набора) представляет собой максимальное пространство, занимаемое процессом, существующее в памяти. Максимальный размер резидентного набора для режима TCP всегда равен 0 КБ.,gRPC выделяет больше,Это связано с требованиями к памяти самой среды gRPC.,и возможные механизмы буферизации памяти,Это помогает повысить скорость и эффективность обработки данных.
gRPCсуществовать Скорость передача значительно превышает TCP socket。Эта огромная разница в основном обусловленаgRPCиспользоватьHTTP/2Преимущества,Например, сжатие заголовка, передача двоичных кадров и мультиплексирование соединений. Двоичная структура кадра HTTP/2 делает передачу более эффективной.,И сократить накладные расходы, вызванные синтаксическим анализом текста. также,Повторное использование соединения позволяет обмениваться сообщениями параллельно по одному соединению.,Таким образом, значительно повышается эффективность передачи данных.,Уменьшает задержку и потребление ресурсов, вызванное установкой и закрытием нескольких TCP-соединений.
Результаты тестирования показывают, что использование gRPCиProtobuf лучше традиционного TCP-метода при передаче больших файлов существует во многих аспектах:
использоватьgRPCиProtobuf для передачи больших файлов,Не только улучшает скорость передачи,Это также обеспечивает более высокую надежность и меньшее потребление ресурсов. Это делает gRPC идеальным для крупномасштабной обработки данных и распределенных систем. будущее,По мере дальнейшего развития и оптимизации технологии,Ожидается, что gRPCсуществовать покажет свое превосходство в большем количестве сценариев.
Я надеюсь, что эта статья будет полезна разработчикам, стремящимся улучшить производительность передачи больших файлов и вдохновить на новые технические инновации и приложения.