_______. _______.___ ___ .______ ____ ____ _______ .______
/ | / | / / | _ / / | ____|| _
| (----` | (----` V / | |_) | / / | |__ | |_) |
> < | / / | __| | /
.----) | .----) | / . | | ----. / | |____ | | ----.
|_______/ |_______/ /__/ __ | _| `._____| __/ |_______|| _| `._____|
O SSXRVER é uma biblioteca de rede de alto desempenho e alta concorrência em execução na plataforma Linux. Está escrito em C ++ 17 e suporta protocolos TCP e UDP.
Por favor, tente combinar o mesmo ambiente de desenvolvimento que eu. Se você não precisar de um módulo de banco de dados, modifique cmakelists.txt de acordo.
Instalação do CMake
# debian/ubuntu
sudo apt-get install cmakeBoost Library Instalação
wget http://sourceforge.net/projects/boost/files/boost/1.72.0/boost_1_72_0.tar.bz2
tar -xvf boost_1_72_0.tar.bz2
cd ./boost_1_72_0
./bootstrap.sh --prefix=/usr/local
sudo ./b2 install --with=allRun ./build.sh No diretório SSXRVER, você pode modificar o Build.sh para optar por gerar a versão de depuração ou a versão de lançamento (a versão de versão padrão)
./build.shO compilado gerará com sucesso o diretório de compilação/ o arquivo executável está no diretório de versão correspondente. Por exemplo, quando você seleciona a versão de liberação, o arquivo executável está em/build/release/ssxrver.
Imite o formato de conf/ssxrver.json.example para criar seu arquivo de configuração (observe que o arquivo de configuração não pode ser comentado, não comentado, não comentado). Vou explicar as opções de cada arquivo de configuração abaixo. Na verdade, eu defino os valores padrão para muitos parâmetros. Se não estiver configurado, isso não o afetará.
{
" port " : 4507, # 端口号,不填的话默认4507
" address " : " 127.0.0.1 " , # 绑定的地址
" worker_processes " : 4, # IO 线程数量,不填默认为 4 个
" worker_connections " : -1, # 一个 IO 线程最多支持多少连接, -1 表示最多能创建多少就创建多少,不做限制
" task_processes " : 0, # 任务线程,不填的话默认为 0
" cpu_affinity " : " off " , # cpu 亲和度 ,默认关闭
" http " : { # http 模块
" max_body_size " : 67108864, # 单个 http 包最大支持大小
" root_path " : " /home/randylambert/sunshouxun/ssxrver/html/ " # 文件访问根路径
},
" log " : { # log 模块
" level " : " INFO " , # 输出等级,可填三种等级, DEBUG,INFO,WARN 不填默认为 INFO 等级
" ansync_started " : " off " , # 是否打开异步日志线程,不填默认关闭
" flush_second " : 3, # 异步线程每隔多久持久化一次
" roll_size " : 67108864, # 日志文件滚动大小
" path " : " /home/randylambert/sunshouxun/ssxrver/logs/ " , # 日志文件存放路径
" base_name " : " ssxrver " # 日志文件基础名
},
" mysql " : { # 数据库模块
" mysql_started " : " off " , # 是否打开数据库模块,默认关闭
" address " : " 127.0.0.1 " , # 以下是对应数据库连接信息
" user " : " root " ,
" password " : " 123456 " ,
" database_name " : " ttms " ,
" port " : 0,
" unix_socket " : null,
" client_flag " : 0
},
" blocks_ip " : [ " 122.0.0.2 " , " 198.1.2.33 " ] # 可屏蔽部分恶意 IP
}Execute o arquivo executável.
./ssxrver -f /配置文件的路径
# 例如
./build/Release/ssxrver -f ./conf/ssxrver.json| Ambiente de teste | Valor |
|---|---|
| Versão de penteado do sistema operacional | Edição Comunitária Deepin V20.1 (1030) |
| Versão do kernel | 5.4.70-AMD64-DESKTOP (64 bits) |
| Versão do compilador | GCC 8.3 |
| Versão da biblioteca Boost | 1.72 |
| processador | Intel (R) Core (TM) I7-8750H CPU @2.20GHz |
| Tamanho do cache L1 | 32k |
| Tamanho do cache L2 | 256k |
| Tamanho do cache L3 | 9216k |
| Velocidade do disco rígido | 1,8 DISCURSO MECÂNICO TIB 5400 rpm |
| Velocidade de leitura e gravação em disco rígido | 370 MB em 3,03 segundos = 122,27 Mb/s |
| Memória | 7.6 GB |
| Partição de troca | 4,7 GB |
| Contagem de núcleo lógico | 12 núcleos |
Para controlar as variáveis, reinicie o computador antes de testar para garantir que o ambiente de teste não tenha outros aplicativos com alta carga de CPU e alta carga de IO.
A ferramenta de teste é webbench1.5. Remova os primeiros dados de aquecimento. O comando de teste é o seguinte (100 clientes foram acessados continuamente por 15 segundos).
./webbench -c 100 -t 15 http://127.0.0.1:8081/Os objetos de teste são Apache/2.4.38, nginx/1.14.2, ssxrver.
Nota: Seja usando o Webbench ou AB, os dados medidos por essa ferramenta de medição de pressão só podem ser usados como uma referência simples. A medição de pressão é um teste que requer total e multi-ângulo, em vez de simplesmente executar um comando. Mesmo durante a medição da pressão, os dados não são transmitidos pela rede, mas apenas circula no kernel.
| Biblioteca de rede | Velocidade (páginas/min) | Solicita taxa de sucesso |
|---|---|---|
| SSXRVER retorna a resposta gerada na memória | 7107414 | 100% |
| SSXRVER Retorna arquivos estáticos | 5114376 | 100% |
| Apache/2.4.28 | 2884072 | 100% |
| nginx/1.14.2 | 4728748 | 100% |
Os resultados do teste do SSXRVER são muito bons, mas estranhamente, eu pensei que os dados seriam mais altos, porque quando eu estava desenvolvendo nos primeiros dias, não fiz muitas otimizações naquele momento. Quando devolvi a resposta gerada diretamente na memória, ela foi medida no máximo 8000000 páginas/min (os resultados do teste de 8000000 páginas/min não foram tirados em capturas de tela, deixando um 7550778). Naquela época, o Nginx/1.14.2 tinha no máximo 5000000 páginas/min. No entanto, não importa se era ssxrver ou nginx/1.14.2, não consegui encontrar um valor tão alto. Não sei qual foi o motivo, o que levou a uma lacuna tão grande no resultado final (é porque meu computador está envelhecendo?  ̄ □  ̄||)
Atualmente, pessoalmente modificarei o módulo de buffer e o módulo de log do SSXRVER, se eu tiver tempo.
Primeiro de tudo, a maneira mais fácil de modificar o módulo de buffer é alterá-lo para um tampão cíclico, reduzindo assim efetivamente o número de vezes que o buffer move os dados para a frente ou abandonando diretamente essa implementação do buffer e reimplementando um buffer de alto desempenho.
Em segundo lugar, o módulo de log atual é escrito na forma de fluxo de C ++. Embora seja definitivamente mais alto em desempenho do que usar C ++ diretamente com o iostam, a sobrecarga do log no formulário << simbólico ainda causará controle inconveniente de formato e problemas de desempenho causados por cadeias de chamadas de função. Ambos os problemas podem ser resolvidos implementando o log na forma de printf.
Por razões de tempo, o SSXRVER não implementa o módulo de gerenciamento de memória, por isso é quase impossível escrever um módulo geral de gerenciamento de memória de alto desempenho (é melhor ir diretamente para Jemalloc ou TCMalloc). No entanto, analisando o cenário da biblioteca de rede, ainda é uma pequena chance de escrever um módulo de gerenciamento de memória com maior desempenho nesse cenário. Se eu tiver tempo, vou dar uma olhada na implementação no NGINX e aprender.
Quando eu estava consultando as informações, cheguei à conclusão de que, no C ++ 17, você pode usar o std :: string_view para substituir a const String &, o que melhorará alguma eficiência. Portanto, tentei substituir todos os lugares onde a String const & no meu projeto com STD :: String_View. No entanto, quando finalmente usei perf -top para visualizar a carga alterada, descobri inesperadamente que algumas funções realmente aumentaram depois de usar o std :: string_view para substituí -lo. Fiquei muito confuso por que essa situação ocorreu. Por razões de tempo, não investigarei a causa específica desse problema por enquanto. Tenho a oportunidade de verificar a implementação subjacente para verificar o motivo específico.
Ao implementar o módulo HTTP Parsing, usei uma máquina de estado manuscrita que corresponda diretamente a strings na primeira versão. Então eu o substituí pela máquina de estado implementada por Ragel. No entanto, durante testes recentes, descobri que a carga da função de análise HTTP é muito exagerada, atingindo 10%. Será que o uso de ragel causou degradação do desempenho? (Se a análise do cabeçalho causará uma carga tão alta do sistema, parece que o HTTP/2.0 ainda melhorará significativamente o desempenho), infelizmente, quando eu escrevi a máquina de estado antes, não testei a carga da função de análise correspondente. Agora, não consigo obter a comparação de dados entre os dois de uma só vez e tenho a oportunidade de escrever um teste de referência.
O SSXRVER suporta transmissão UDP simples, mas eu pessoalmente acho que uma estrutura UDP sem controle de congestionamento, controle de tráfego e funções de retransmissão por perda de pacotes pode ser basicamente que não possa ser usada normalmente. No futuro, tenho tempo para aprender protocolos QIC e KCP. Vou complementar o conhecimento relacionado à UDP. Acredito que o protocolo UDP mais eficiente e flexível será cada vez mais amplamente utilizado no futuro!
Na verdade, eu realmente acho que a melhor estrutura de rede atualmente deve ser que a multiplexação por endereço de portas multiplexação e vários threads (multiprocess) ligue o mesmo endereço e porta, e o kernel executa automaticamente o balanceamento de carga. Ao mesmo tempo, o sistema Blocks chama através da Coroutine Framework + Hook. Depois de usar essa estrutura, ele pode garantir alto desempenho sem usar o thread principal para distribuir conexões e não há necessidade de cair no inferno de retorno de chamada assíncrono.
Além disso, se você puder usar o mecanismo de IO assíncrono io_uring adicionado após o Linux Kernel 5.1, acredito que o desempenho do servidor será maior. No entanto, não sei muito sobre io_uring atualmente e não tenho a capacidade de projetar uma biblioteca de rede de IO assíncrona com base em io_uring.