12.3 مليون من WebSockets المتزامنة

شيء واحد عن WebSockets هو أنك تحتاج إلى الكثير من الموارد من جانب العميل لتوليد حمولة عالية بما يكفي للخادم حتى يستهلك جميع موارد وحدة المعالجة المركزية.


هناك العديد من التحديات التي يجب التغلب عليها لأن بروتوكول WebSockets يتطلب وحدة المعالجة المركزية من جانب العميل أكثر من جانب الخادم. في نفس الوقت ، تحتاج إلى الكثير من ذاكرة الوصول العشوائي لتخزين المعلومات حول الاتصالات المفتوحة إذا كان لديك ملايين منها.


لقد كنت محظوظًا بدرجة كافية للحصول على اثنين من الخوادم الجديدة لفترة محدودة من الوقت تحت تصرفي لاختبارات "الإرهاق" للأجهزة. لذلك قررت استخدام خادم التطبيقات Lua Application Server - LAppS الخاص بك للقيام بالمهمتين: اختبار الأجهزة وإجراء اختبارات تحميل عالية LAPSS.


دعنا نرى التفاصيل


التحديات


أولاً وقبل كل شيء ، تتطلب WebSockets من جانب العميل RNG سريعًا لإخفاء حركة المرور ما لم ترغب في مزيفه. أردت أن تكون الاختبارات واقعية ، لذا تخلصت من فكرة تزوير RNG عن طريق استبدالها بثابت. هذا يترك الخيار الوحيد - الكثير من قوة وحدة المعالجة المركزية. كما قلت ، لدي خادمين: أحدهما مزود بمعالج Intel Gold 6148 CPUs (40 مركزًا في المجموع) 256 جيجابايت من ذاكرة الوصول العشوائي (DDR4-2666) وواحد مزود بمعالج Intel Platinum 8180 من وحدات المعالجة المركزية (112 مركزًا في المجموع) 384GB RAM (DDR4-2666) . سأشير إليهم على أنهم الخادم A والخادم B فيما بعد.


التحدي الثاني - لا توجد مكتبات أو مجموعات اختبار لـ WebSockets بسرعة كافية. وهذا هو السبب الذي دفعني إلى تطوير وحدة عميل WebSockets لـ LAppS (cws).


إعداد الاختبارات


يحتوي كلا الخادمين على بطاقات 10 جيجابت ذات المنفذ المزدوج. يتم تجميع المنفذ 1 من كل بطاقة على واجهة الربط ويتم توصيل هذه المنافذ على أي من البطاقات مع بعضها البعض في وضع RR. يتم تجميع المنفذ 2 من كل بطاقة إلى واجهة الربط في وضع "النسخ الاحتياطي النشط" ، متصلة عبر محول Ethernet. كان كل خادم أجهزة يشغل RHEL 7.6. اضطررت إلى تثبيت gcc-8.3 من المصادر للحصول على مترجم حديث بدلاً من gcc-4.8.5 الافتراضي. ليس أفضل إعداد لاختبارات الأداء ، ولكن لا يمكن أن يكون المتسولون هم المختارون.


تحتوي وحدات المعالجة المركزية (CPU) على الخادم A (2xGold 6148) على تردد أعلى من الخادم B (4xPlatinum 8180). في الوقت نفسه ، يحتوي Server B على مزيد من النوى ، لذلك قررت تشغيل خادم صدى على الخادم A وعملاء الصدى على الخادم B.


أردت أن أرى كيف يتصرف LAppS تحت عبء كبير مع ملايين الاتصالات وما هو الحد الأقصى لمقدار طلبات الصدى في الثانية التي يمكن أن تقدمه. هذه في الواقع مجموعتان مختلفتان من الاختبارات لأنه مع زيادة حجم الاتصالات ، يقوم الخادم والعميل بعمل متزايد التعقيد.


قبل ذلك ، قمت بإجراء جميع الاختبارات على جهاز الكمبيوتر المنزلي الذي أستخدمه للتطوير. سيتم استخدام نتائج هذه الاختبارات للمقارنة. يحتوي جهاز الكمبيوتر المنزلي على وحدة المعالجة المركزية Intel Core i7-7700 سعة 3.6 جيجا هرتز (توربو 4.0 جيجاهيرتز) مع 4 مراكز و 32 جيجابايت من ذاكرة الوصول العشوائي DDR4-2400. يشغل هذا الكمبيوتر Gentoo مع 4.14.72 kernel.


مستويات تصحيح شبح والانهيار
  • كمبيوتر المنزل
    /sys/devices/system/cpu/vulnerabilities/l1tf:Mitigation: PTE Inversion; VMX: conditional cache flushes, SMT vulnerable /sys/devices/system/cpu/vulnerabilities/meltdown:Mitigation: PTI /sys/devices/system/cpu/vulnerabilities/spec_store_bypass:Mitigation: Speculative Store Bypass disabled via prctl and seccomp /sys/devices/system/cpu/vulnerabilities/spectre_v1:Mitigation: __user pointer sanitization /sys/devices/system/cpu/vulnerabilities/spectre_v2:Vulnerable: Minimal generic ASM retpoline, IBPB, IBRS_FW 
  • الخوادم A و B
     /sys/kernel/debug/x86/ibpb_enabled : 1 /sys/kernel/debug/x86/pti_enabled : 1 /sys/kernel/debug/x86/ssbd_enabled : 1 /sys/kernel/debug/x86/ibrs_enabled : 1 /sys/kernel/debug/x86/retp_enabled : 3 

سأقدم ملاحظة إضافية بنتائج اختبارات الخوادم A و B سواء تم تمكين هذه التصحيحات أم لا.


على جهاز الكمبيوتر الشخصي الخاص بي لا يتم تغيير مستوى التصحيح.


تثبيت LAppS 0.8.1


تثبيت المتطلبات الأساسية و LAppS

يعتمد LAppS على luajit-2.0.5 أو أعلى ، libcrypto ++ 8.2 ومكتبات wolfSSL-3.15.7 ويجب تثبيتها من مصادر في RHEL 7.6 ومن المحتمل في أي توزيع linux آخر.


بادئة التثبيت هي / usr / local. هنا هو جزء Dockerfile الذاتي إلى حد كبير لتثبيت wolfSSL


 ADD https://github.com/wolfSSL/wolfssl/archive/v3.15.7-stable.tar.gz ${WORKSPACE} RUN tar xzvf v3.15.7-stable.tar.gz WORKDIR ${WORKSPACE}/wolfssl-3.15.7-stable RUN ./autogen.sh RUN ./configure CFLAGS="-pipe -O2 -march=native -mtune=native -fomit-frame-pointer -fstack-check -fstack-protector-strong -mfpmath=sse -msse2avx -mavx2 -ftree-vectorize -funroll-loops -DTFM_TIMING_RESISTANT -DECC_TIMING_RESISTANT -DWC_RSA_BLINDING" --prefix=/usr/local --enable-tls13 --enable-openssh --enable-aesni --enable-intelasm --enable-keygen --enable-certgen --enable-certreq --enable-curve25519 --enable-ed25519 --enable-intelasm --enable-harden RUN make -j40 all RUN make install 

وهنا هو الجزء لتثبيت libcrypto ++


 ADD https://github.com/weidai11/cryptopp/archive/CRYPTOPP_8_2_0.tar.gz ${WORKSPACE} RUN rm -rf ${WORKSPACE}/cryptopp-CRYPTOPP_8_2_0 RUN tar xzvf ${WORKSPACE}/CRYPTOPP_8_2_0.tar.gz WORKDIR ${WORKSPACE}/cryptopp-CRYPTOPP_8_2_0 RUN make CFLAGS="-pipe -O2 -march=native -mtune=native -fPIC -fomit-frame-pointer -fstack-check -fstack-protector-strong -mfpmath=sse -msse2avx -mavx2 -ftree-vectorize -funroll-loops" CXXFLAGS="-pipe -O2 -march=native -mtune=native -fPIC -fomit-frame-pointer -fstack-check -fstack-protector-strong -mfpmath=sse -msse2avx -mavx2 -ftree-vectorize -funroll-loops" -j40 libcryptopp.a libcryptopp.so RUN make install 

و luajit


 ADD http://luajit.org/download/LuaJIT-2.0.5.tar.gz ${WORKSPACE} WORKDIR ${WORKSPACE} RUN tar xzvf LuaJIT-2.0.5.tar.gz WORKDIR ${WORKSPACE}/LuaJIT-2.0.5 RUN env CFLAGS="-pipe -Wall -pthread -O2 -fPIC -march=native -mtune=native -mfpmath=sse -msse2avx -mavx2 -ftree-vectorize -funroll-loops -fstack-check -fstack-protector-strong -fno-omit-frame-pointer" make -j40 all RUN make install 

إحدى التبعيات الاختيارية لـ LAppS ، والتي قد يتم تجاهلها ، هي المكتبة الجديدة من Microsoft mimalloc . تحقق هذه المكتبة تحسينًا كبيرًا في الأداء (حوالي 1٪) ولكنها تتطلب cmake-3.4 أو أعلى. بالنظر إلى مقدار الوقت المحدود للاختبارات ، فقد قررت التضحية بتحسين الأداء المذكور.


على جهاز الكمبيوتر المنزلي الخاص بي ، لن أقوم بتعطيل mimalloc أثناء الاختبارات.


يتيح الخروج LAPSS من المستودع:


 WORKDIR ${WORKSPACE} RUN rm -rf ITCLib ITCFramework lar utils LAppS RUN git clone https://github.com/ITpC/ITCLib.git RUN git clone https://github.com/ITpC/utils.git RUN git clone https://github.com/ITpC/ITCFramework.git RUN git clone https://github.com/ITpC/LAppS.git RUN git clone https://github.com/ITpC/lar.git WORKDIR ${WORKSPACE}/LAppS 

نحتاج الآن إلى إزالة "-lmimalloc" من جميع Makefiles في الدليل الفرعي nbproject ، حتى نتمكن من إنشاء LAppS (على افتراض أن دليلنا الحالي هو $ {WORKSPACE} / LAppS)


 # find ./nbproject -type f -name "*.mk" -exec sed -i.bak -e 's/-lmimalloc//g' {} \; # find ./nbproject -type f -name "*.bak" -exec rm {} \; 

والآن يمكننا بناء LAppS. يوفر LAppS عدة تكوينات بناء ، والتي قد تستبعد أو لا تستبعد بعض الميزات على جانب الخادم:


  • مع دعم طبقة المقابس الآمنة ومع جمع إحصاءات الدعم
  • مع طبقة المقابس الآمنة (SSL) وبدون تجميع الإحصاءات (على الرغم من أن الحد الأدنى من الإحصائيات سوف يستمر لأنه يستخدم لضبط LAppS الديناميكي في وقت التشغيل)
  • بدون SSL ودون تجمع إحصائي.

قبل الخطوة التالية ، تأكد من أنك مالك دليل / opt / lapps (أو قم بإجراء عمليات التثبيت مع sudo)


دعنا نجعل نوعين من الثنائيات مع دعم طبقة المقابس الآمنة وجمع الإحصائيات وبدون (على افتراض أننا داخل دليل $ {WORKSPACE} / LAppS):


 # make clean # make CONF=Release.AVX2 install # make CONF=Release.AVX2.NO_STATS.NO_TLS install 

الثنائيات الناتجة هي:


  • dist / Release.AVX2 / GNU-Linux / lapps.avx2
  • dist / Release.AVX2.NO_STATS.NO_TLS / GNU-Linux / lapps.avx2.nostats.notls

سيتم تثبيتها في / opt / lapps / bin.


يرجى ملاحظة أن وحدة عميل WebSockets لـ Lua مصممة دائمًا بدعم SSL. يعتمد تمكينه أو عدمه على URI الذي تستخدمه للاتصال (ws: // أو wss: //) في وقت التشغيل.


اختبار 1. أعلى أداء على أجهزة الكمبيوتر المنزلية. التكوين لخط الأساس.


لقد أثبتت بالفعل أنني أحصل على أفضل أداء عندما أقوم بتكوين أربعة مثيلات خدمة قياسية مع 100 اتصال لكل منها. في الوقت نفسه ، أحتاج إلى ثلاث حالات فقط لخدمة IOWorkers وأربعة صدى لتحقيق أفضل أداء. تذكر؟ لدي فقط 4 النوى هنا.


الغرض من هذا الاختبار هو فقط وضع خط الأساس لمزيد من المقارنة. لا شيء مثير هنا حقا.


رفع الصوت عاليا هي الخطوات اللازمة لتكوين LAppS للاختبارات.


شهادات موقعة ذاتيا


النصي certgen.sh
 #!/bin/bash openssl genrsa -out example.org.key 2048 openssl req -new -key example.org.key -out example.org.csr openssl genrsa -out ca.key 2048 openssl req -new -x509 -key ca.key -out ca.crt openssl x509 -req -in example.org.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out example.org.crt cat example.org.crt ca.crt > example.org.bundle.crt 

سيؤدي تشغيل هذا البرنامج النصي من داخل الدليل / opt / lapps / conf / ssl إلى إنشاء جميع الملفات المطلوبة. فيما يلي إخراج البرنامج النصي وما كتبته:


certgen.sh الإخراج
 # certgen.sh Generating RSA private key, 2048 bit long modulus .................................................................................................................................................................+++++ .....................................+++++ e is 65537 (0x010001) You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:KZ State or Province Name (full name) [Some-State]:none Locality Name (eg, city) []:Almaty Organization Name (eg, company) [Internet Widgits Pty Ltd]:NOORG.DO.NOT.FORGET.TO.REMOVE.FROM.BROWSER Organizational Unit Name (eg, section) []: Common Name (eg server FQDN or YOUR name) []: Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: Generating RSA private key, 2048 bit long modulus ...+++++ ............................................................................+++++ e is 65537 (0x010001) You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:KZ State or Province Name (full name) [Some-State]:none Locality Name (eg, city) []:Almaty Organization Name (eg, company) [Internet Widgits Pty Ltd]:none Organizational Unit Name (eg, section) []: Common Name (eg server FQDN or YOUR name) []:*.example.org Email Address []: Signature ok subject=C = KZ, ST = none, L = Almaty, O = NOORG.DO.NOT.FORGET.TO.REMOVE.FROM.BROWSER Getting CA Private Key 

التكوين LAPSS


فيما يلي ملف تكوين WebSockets الذي تم تعيينه لـ TLS 1.3 ، ويتضمن الشهادات التي تم إنشاؤها أعلاه وتم تهيئته باستخدام 3 IOWorkers (انظر الوصف التفصيلي للمتغيرات في ويكي LAppS).


/opt/lapps/etc/conf/ws.json
 { "listeners" : 2, "connection_weight": 1.0, "ip" : "0.0.0.0", "port" : 5083, "lapps_config_auto_save" : true , "workers" : { "workers": 3, "max_connections" : 40000, "auto_fragment" : false, "max_poll_events" : 256, "max_poll_wait_ms" : 10, "max_inbounds_skip" : 50, "input_buffer_size" : 2048 }, "acl" : { "policy" : "allow", "exclude" : [] }, "tls":true, "tls_server_version" : 4, "tls_client_version" : 4, "tls_certificates":{ "ca":"/opt/lapps/conf/ssl/ca.crt", "cert": "/opt/lapps/conf/ssl/example.org.bundle.crt", "key": "/opt/lapps/conf/ssl/example.org.key" } } 

تكوين الخدمات: خادم الارتداد وعميل الارتداد (معيار) ، ولكل منها أربع حالات.


/opt/lapps/etc/conf/lapps.json
 { "directories": { "app_conf_dir": "etc", "applications": "apps", "deploy": "deploy", "tmp": "tmp", "workdir": "workdir" }, "services": { "echo": { "auto_start": true, "instances": 4, "internal": false, "max_inbound_message_size": 16777216, "protocol": "raw", "request_target": "/echo", "acl" : { "policy" : "allow", "exclude" : [] } }, "benchmark": { "auto_start": true, "instances" : 4, "internal": true, "preload": [ "nap", "cws", "time" ], "depends" : [ "echo" ] } } } 

خدمة الصدى تافهة للغاية:


صدى رمز مصدر الخدمة
 echo = {} echo.__index = echo; echo.onStart=function() print("echo::onStart"); end echo.onDisconnect=function() end echo.onShutdown=function() print("echo::onShutdown()"); end echo.onMessage=function(handler,opcode, message) local result, errmsg=ws:send(handler,opcode,message); if(not result) then print("echo::OnMessage(): "..errmsg); end return result; end return echo 

تنشئ الخدمة المعيارية ما يصل إلى benchmark.max_connections إلى benchmark.target ثم تعمل فقط حتى توقف LAppS. لا يوجد توقف في إنشاء الروابط أو طلب صدى القصف. تشبه واجهة برمجة تطبيقات وحدة cws واجهة برمجة تطبيقات الويب لـ WebSockets. بمجرد إنشاء جميع أجهزة القياس القياسية ، يقوم المؤشر بطباعة كمية المقابس المتصلة. بمجرد إنشاء الاتصال ، يرسل المعيار benchmark.message إلى الخادم. بعد أن يرد الخادم ، يتم استدعاء طريقة onmessage المجهولة لكائن cws ، والتي ترسل الرسالة نفسها مرة أخرى إلى الخادم.


رمز مصدر الخدمة المعياري
 benchmark={} benchmark.__index=benchmark benchmark.init=function() end benchmark.messages_counter=0; benchmark.start_time=time.now(); benchmark.max_connections=100; benchmark.target="wss://127.0.0.1:5083/echo"; benchmark.message="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; benchmark.meter=function() benchmark.messages_counter=benchmark.messages_counter+1; local slice=time.now() - benchmark.start_time; if( slice >= 1000) then print(benchmark.messages_counter.." messages received per "..slice.."ms") benchmark.messages_counter=0; benchmark.start_time=time.now(); end end benchmark.run=function() local n=nap:new(); local counter=1; n:sleep(1); local array={}; local start=time.now(); while(#array < benchmark.max_connections) and (not must_stop()) do local sock, err_msg=cws:new(benchmark.target, { onopen=function(handler) local result, errstr=cws:send(handler,benchmark.message,2); if(not result) then print("Error on websocket send at handler "..handler..": "..errstr); end end, onmessage=function(handler,message,opcode) benchmark.meter(); cws:send(handler,message,opcode); end, onerror=function(handler, message) print("Client WebSocket connection is failed for socketfd "..handler..". Error: "..message); end, onclose=function(handler) print("Connection is closed for socketfd "..handler); end }); if(sock ~= nil) then table.insert(array,sock); else print(err_msg); err_msg=nil; collectgarbage("collect"); end -- poll events once per 10 outgoing connections -- this will improve the connection establishment speed if counter == 10 then cws:eventLoop(); counter=1 else counter = counter + 1; end end print("Sockets connected: "..#array); benchmark.start_time=time.now(); while not must_stop() do cws:eventLoop(); end for i=1,#array do array[i]:close(); cws:eventLoop(); end end return benchmark; 

تركيب الخدمات


الآن نحتاج إلى وضع البرامج النصية للخدمة في /opt/lapps/apps//.lua:

  • /opt/lapps/apps/benchmark/benchmark.lua
  • /opt/lapps/apps/echo/echo.lua

نحن على استعداد لتشغيل معيارنا الآن. مجرد تشغيل: rm -f lapps.log; /opt/lapps/bin/lapps.avx2 > log rm -f lapps.log; /opt/lapps/bin/lapps.avx2 > log من داخل دليل LAppS وانتظر لمدة 5 دقائق ، ثم اضغط على Ctrl-C مرة واحدة ، لإيقاف LAppS (لن تتوقف على الفور ، ستغلق الاتصالات أولاً) ، أو مرتين (سيؤدي ذلك إلى مقاطعة تسلسل إيقاف التشغيل).


حسنًا ، لدينا ملف نصي يحتوي على شيء من هذا القبيل داخل:


الناتج القياسي

صدى :: onStart
صدى :: onStart
صدى :: onStart
صدى :: onStart
تشغيل
تلقى 1 رسائل لكل 3196ms
تلقى 1 رسائل لكل 3299ms
تلقى 1 رسائل لكل 3299ms
تلقى 1 رسائل لكل 3305ms
مآخذ توصيل: 100
مآخذ توصيل: 100
مآخذ توصيل: 100
مآخذ توصيل: 100
تلقى 134597 رسالة لكل 1000ms
تلقى 139774 رسالة لكل 1000ms
تلقى 138521 رسالة لكل 1000ms
تلقى 139404 رسالة لكل 1000 مللي ثانية
تلقى 140162 رسالة لكل 1000ms
تلقى 139337 رسالة لكل 1000ms
140088 رسالة وردت لكل 1000ms
تلقى 139946 رسالة لكل 1000ms
تلقى 141204 رسالة لكل 1000ms
تلقى 137988 رسالة لكل 1000ms
تلقى 141805 رسالة لكل 1000ms
تلقى 134733 رسالة لكل 1000ms
...


دعونا تنظيف ملف السجل هذا مثل هذا:


 echo -e ':%s/ms/^V^M/g\n:%g/^$/d\nGdd1G4/Sockets\n4\ndggZZ' | vi $i 

'^ V ^ M' هو تمثيل مرئي للنقاط الرئيسية التالية: Ctrl-V Ctrl-V Ctrl-V Ctrl-M ، لذلك سيكون من غير المجدي أن تقوم فقط بنسخ لصق خط bash هذا. شرح قصير:


  • يتعين علينا استبدال رموز "ms" بنهاية السطر ، لأننا لسنا في حاجة إليها ، فسوف تفسد الحسابات في وقت لاحق ، وقد تطبع 4 معايير تعمل على التوازي نتائجها في سطر واحد.
  • نحن بحاجة إلى إزالة جميع الخطوط الفارغة بعد ذلك
  • نزيل السطر الأخير أيضًا ، لأننا توقف الخادم
  • في ملف السجل ، لن يكون هناك سوى خطوط أمامية تتكون من مآخذ توصيل متصلة: 100 (لأننا نقوم بتشغيل أربع خدمات مرجعية فقط). لذا ، فإننا نتخطى 4 أسطر بعد آخرها ، ومن إزالة كل شيء إلى أعلى الملف.
  • حفظ الملف.

يتم حفظ الملف وقد عدت إلى الوراء الآن ، وملف السجل جاهز للخطوة التالية:


 # awk -v avg=0 '{rps=($1/$5);avg+=rps;}END{print ((avg*1000)/NR)*4}' log 

هذا awk أحادي الطبقة يحسب مقدار ردود الصدى لكل مللي ثانية ، ويجمع النتيجة في متوسط متغير. بعد أن تتم معالجة جميع الأسطر الموجودة في ملف السجل ، يتضاعف متوسط ​​العدد إلى 1000 للحصول على المبلغ الإجمالي لاستجابات الصدى في الثانية ، مقسومًا على عدد الخطوط ومضاعفة عدد الخدمات المرجعية. هذا يعطينا متوسط ​​عدد استجابات الصدى في الثانية لتشغيل الاختبار.


على جهاز الكمبيوتر الخاص بي ، هذا الرقم (ERps) هو: 563854


دعونا نفعل الشيء نفسه دون دعم طبقة المقابس الآمنة ونرى الفرق:


  • تغيير قيمة tls في ws.json إلى false
  • تغيير benchmark.target في benchmark.lua من wss: // إلى ws: //
  • تشغيل rm -f lapps.log; /opt/lapps/bin/lapps.avx2.nostats.notls > log rm -f lapps.log; /opt/lapps/bin/lapps.avx2.nostats.notls > log من داخل دليل LAppS ، وكرر الخطوات أعلاه.

لدي: 721236 ردود في الثانية الواحدة


الفرق في الأداء مع SSL وبدون SSL هو حوالي 22 ٪. دعونا نضع هذه الأرقام في الاعتبار للرجوع إليها مستقبلاً.


مع نفس الإعداد على Server A ، لدي: 421905 ERpS مع SSL و 443145 ERpS بدون SSL. تم تعطيل تصحيحات Specter و Meltdown.
على الخادم B ، لدي 270996 ERpS مع SSL و 318522 ERpS بدون SSL مع تمكين التصحيحات. 385726 و 372126 بدون ومع SSL. تم تعطيل تصحيحات Specter و Meltdown أيضًا.


النتائج أسوأ مع نفس الإعداد لأن وحدات المعالجة المركزية على هذه الخوادم لها تردد أقل.


يرجى الانتباه إلى أن العملاء يعتمدون بشدة على توفر البيانات في / dev / urandom. قد يستغرق الأمر بعض الوقت حتى يبدأ العملاء بالفعل في التشغيل ، إذا قمت بتشغيله بالفعل مرة واحدة. لذا ، انتظر حتى يبدأوا العمل ، ثم يصومون بسرعة فيما يفعلون. فقط قم بمراقبة ما إذا كانت مثيلات LAppS تقوم بأي عمل على الإطلاق. إذا تم استنفاد / dev / urandom فإن LAppS لن تأكل وحدات المعالجة المركزية الخاصة بك حتى تتوفر بعض البيانات.


التحضير للاختبارات الكبيرة


بادئ ذي بدء ، نحن بحاجة إلى إجراء بعض التغييرات في معلمات kernel ولا تنسَ ulimit لـ nr من الملفات المفتوحة. اعتدت تقريبا نفس الإعداد كما هو الحال في هذه المقالة .


قم بإنشاء ملف بالمحتوى التالي


 : sysctl -w fs.file-max=14000000 sysctl -w fs.nr_open=14000000 ulimit -n 14000000 sysctl -w net.ipv4.tcp_mem="100000000 100000000 100000000" sysctl -w net.core.somaxconn=20000 sysctl -w net.ipv4.tcp_max_syn_backlog=20000 sysctl -w net.ipv4.ip_local_port_range="1025 65535" 

ثم استخدم المصدر ./filename على كلا الخادمين لتغيير معلمات kernel و ulimit.


هدفي هذه المرة هو إنشاء عدة ملايين من العملاء على خادم واحد وربطهم بالخادم الثاني.


سيكون الخادم A بمثابة جانب خادم خدمة WebSockets echo.
سيكون الخادم B بمثابة جانب عميل خدمة صدى WebSockets.


يوجد قيد في LAppS تفرضه LuaJIT. يمكنك استخدام ذاكرة وصول عشوائي (RAM) سعة 2 جيجابايت فقط لكل جهاز LuaJIT VM. هذا هو الحد الأقصى لذاكرة الوصول العشوائي التي يمكنك استخدامها ضمن مثيل LAppS واحد لجميع الخدمات ، حيث أن الخدمات هي مؤشرات الترابط المرتبطة بمثيل libluajit واحد. إذا تجاوزت أي من خدماتك التي تعمل تحت مثيل LAppS الفردي هذا الحد ، فستكون جميع الخدمات خارج الذاكرة.


اكتشفت أنه لكل مثيل LAppS لا يمكنك إنشاء أكثر من 2 464000 عميل WebSockets بحجم رسالة يبلغ 64 بايت. قد يغير حجم الرسالة هذا الحد قليلاً ، لأن LAppS تقوم بتمرير الرسالة إلى خدمة cws عن طريق تخصيص مساحة لهذه الرسالة داخل LuaJIT.


هذا يعني أنه يجب علي بدء عدة مثيلات LAppS بنفس التكوين على Server B لإنشاء أكثر من 2.4 مليون WebSockets. لا يستخدم Server A (خادم الارتداد) أكبر قدر ممكن من الذاكرة على جانب LuaJIT ، وبالتالي فإن مثيل LAPSS سوف يتولى 12.3 مليون من WebSockets دون أي مشكلة.


لنعد إعدادين مختلفين للخوادم A و B.


خادم و ws.json
 { "listeners" : 224, "connection_weight": 1.0, "ip" : "0.0.0.0", "port" : 5084, "lapps_config_auto_save" : true , "workers" : { "workers": 40, "max_connections" : 2000000, "auto_fragment" : false, "max_poll_events" : 256, "max_poll_wait_ms" : 10, "max_inbounds_skip" : 50, "input_buffer_size" : 2048 }, "acl" : { "policy" : "allow", "exclude" : [] }, "tls": true, "tls_server_version" : 4, "tls_client_version" : 4, "tls_certificates":{ "ca":"/opt/lapps/conf/ssl/ca.crt", "cert": "/opt/lapps/conf/ssl/example.org.bundle.crt", "key": "/opt/lapps/conf/ssl/example.org.key" } } 

خادم و lapps.json
 { "directories": { "app_conf_dir": "etc", "applications": "apps", "deploy": "deploy", "tmp": "tmp", "workdir": "workdir" }, "services": { "echo": { "auto_start": true, "instances": 40, "internal": false, "max_inbound_message_size": 16777216, "protocol": "raw", "request_target": "/echo", "acl" : { "policy" : "allow", "exclude" : [] } } } } 

خادم B ws.json
 { "listeners" : 0, "connection_weight": 1.0, "ip" : "0.0.0.0", "port" : 5083, "lapps_config_auto_save" : true , "workers" : { "workers": 0, "max_connections" : 0, "auto_fragment" : false, "max_poll_events" : 2048, "max_poll_wait_ms" : 10, "max_inbounds_skip" : 50, "input_buffer_size" : 2048 }, "acl" : { "policy" : "deny", "exclude" : [] }, "tls": true, "tls_server_version" : 4, "tls_client_version" : 4, "tls_certificates":{ "ca":"/opt/lapps/conf/ssl/ca.crt", "cert": "/opt/lapps/conf/ssl/example.org.bundle.crt", "key": "/opt/lapps/conf/ssl/example.org.key" } } 

يحتوي الخادم A على واجهتين:


  • bond0 - xx203.37
  • bond1 - xx23.10

واحد هو أسرع والآخر أبطأ ولكن لا يهم حقا. سيكون الخادم تحت الحمل الثقيل على أي حال.


يتيح إعداد قالب من /opt/lapps/benchmark/benchmark.lua


benchmark.lua
 benchmark={} benchmark.__index=benchmark benchmark.init=function() end benchmark.messages_counter=0; benchmark.start_time=time.now(); benchmark.max_connections=10000; benchmark.target_port=0; benchmark.target_prefix="wss://IPADDR:"; benchmark.target_postfix="/echo"; benchmark.message="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; benchmark.meter=function() benchmark.messages_counter=benchmark.messages_counter+1; local slice=time.now() - benchmark.start_time; if( slice >= 1000) then print(benchmark.messages_counter.." messages received per "..slice.."ms") benchmark.messages_counter=0; benchmark.start_time=time.now(); end end benchmark.run=function() local n=nap:new(); local counter=1; n:sleep(1); local array={}; local start=time.now(); while(#array < benchmark.max_connections) and (not must_stop()) do benchmark.target_port=math.random(5084,5307); local sock, err_msg=cws:new(benchmark.target_prefix..benchmark.target_port..benchmark.target_postfix, { onopen=function(handler) local result, errstr=cws:send(handler,benchmark.message,2); if(not result) then print("Error on websocket send at handler "..handler..": "..errstr); end end, onmessage=function(handler,message,opcode) benchmark.meter(); cws:send(handler,message,opcode); end, onerror=function(handler, message) print("Client WebSocket connection is failed for socketfd "..handler..". Error: "..message); end, onclose=function(handler) print("Connection is closed for socketfd "..handler); end }); if(sock ~= nil) then table.insert(array,sock); else print(err_msg); err_msg=nil; collectgarbage("collect"); -- force garbage collection on connection failure. end -- poll events once per 10 outgoing connections -- this will improve the connection establishment speed if counter == 10 then cws:eventLoop(); counter=1 else counter = counter + 1; end end print("Sockets connected: "..#array); benchmark.start_time=time.now(); while not must_stop() do cws:eventLoop(); end for i=1,#array do array[i]:close(); cws:eventLoop(); end end return benchmark; 

دعنا نخزن عناوين IP Server في ملفات IP1 و IP2 على التوالي ومن ثم:


 for i in 1 2 do mkdir -p /opt/lapps/apps/benchmark${i} sed -e "s/IPADDR/$(cat IP1)/g" /opt/lapps/apps/benchmark/benchmark.lua > /opt/lapps/apps/benchmark${i}/benchmark${i}.lua done 

الآن نقوم بتعديل /opt/lapps/etc/conf/lapps.json على Server B لاستخدام هاتين الخدمتين المعياريتين:


خادم B lapps.json
 { "directories": { "app_conf_dir": "etc", "applications": "apps", "deploy": "deploy", "tmp": "tmp", "workdir": "workdir" }, "services": { "benchmark1": { "auto_start": true, "instances" : 112, "internal": true, "preload": [ "nap", "cws", "time" ] }, "benchmark2": { "auto_start": true, "instances" : 112, "internal": true, "preload": [ "nap", "cws", "time" ] } } } 

هل نحن مستعدون؟ لا نحن لسنا كذلك. نظرًا لأننا عازمون على إنشاء 2 240 ألف مقبس صادر لعنوانين فقط ، ونحن بحاجة إلى المزيد من المنافذ على جانب الخادم. من المستحيل إنشاء اتصالات أكثر من 64 كيلو بايت لنفس ip: port pair (في الواقع أقل قليلاً من 64 كيلو بايت).


على الخادم A في الملف LAppS / include / wsServer.h توجد دالة startListeners () خالية. في هذه الوظيفة ، سنستبدل السطر 126


 LAppSConfig::getInstance()->getWSConfig()["port"], 

مع هذا:


 static_cast<int>(LAppSConfig::getInstance()->getWSConfig()["port"])+i, 

إعادة بناء LAPSS:


 make CONF=Release.AVX2 install 

تشغيل الاختبارات لـ 2 240 000 عميل WebSockets.


ابدأ تشغيل LAppS على Server B ، ثم ابدأ تشغيل LAppS على Server B وأعد توجيه الإخراج إلى ملف مثل هذا:


 /opt/lapps/bin/lapps.avx2 > log 

تحرير ، ملاحظة:


 if you want to run several LAppS instances, then create separate directory for each instnace (like run1,run2, etc) and run each instance from within these directories. This is required for lapps.log file and of course for resulting standard output, for not to be overlapped/overwritten by concurrent LAppS instances. 

قد يستغرق هذا بعض الوقت حتى يتم تأسيس جميع الاتصالات. دعونا رصد التقدم المحرز.


لا تستخدم netstat لمشاهدة الاتصالات الثابتة ، فمن غير المجدي في حين أنه يعمل إلى أجل غير مسمى بعد مثل الاتصالات 150k التي أنشئت في بعض الثواني. انظر إلى lapps.log على Server A ، في الدليل الذي كنت فيه عندما بدأت تشغيل LAppS. يمكنك استخدام الخطوط الفردية التالية لترى كيف يتم تأسيس الاتصالات وكيف يتم توزيعها بين IOWorkers:


 date;awk '/ will be added to connection pool of worker/{a[$22]++}END{for(i in a){ print i, a[i]; sum+=a[i]} print sum}' lapps.log | sort -n;date 

فيما يلي فكرة عن مدى سرعة إنشاء هذه الاتصالات:


 375874 Sun Jul 21 20:33:07 +06 2019 650874 Sun Jul 21 20:34:42 +06 2019 2894 connections per second 1001874 Sun Jul 21 20:36:45 +06 2019 2974 connections per second 1182874 Sun Jul 21 20:37:50 +06 2019 2784 connections per second 1843874 Sun Jul 21 20:41:44 +06 2019 2824 connections per second 2207874 Sun Jul 21 20:45:43 +06 2019 3058 connections per second 

على الخادم B ، قد نتحقق من عدد المعايير التي تم الانتهاء من تأسيس اتصالاتها:


 # grep Sockets log | wc -l 224 

بعد أن ترى أن الرقم هو 224 ، دع الخوادم تعمل لفترة من الوقت ، راقب استخدام وحدة المعالجة المركزية والذاكرة باستخدام الجزء العلوي. قد ترى شيئًا كهذا (الخادم B في اليسار والخادم A في الجانب الأيمن):


صورة


أو مثل هذا (الخادم B في اليسار والخادم A في الجانب الأيمن):



من الواضح أن الخادم B ، حيث تعمل خدمات العميل القياسية ، تحت عبء ثقيل. خادم A تحت عبء ثقيل جدا ولكن ليس دائما. في بعض الأحيان يكون قشعريرة بينما Server B يكافح مع مقدار المهام للعمل. تشفير حركة المرور على TLS مكثف للغاية وحدة المعالجة المركزية.


لنوقف الخوادم (اضغط على Ctrl-C عدة مرات) وقم بتعديل السجل كما كان من قبل ولكن فيما يتعلق بالكمية المتغيرة من الخدمات المرجعية (224):


 echo -e ':%s/ms/^V^M/g\n:%g/^$/d\nGdd1G224/Sockets\n224\ndggZZ' | vi $i awk -v avg=0 '{rps=($1/$5);avg+=rps;}END{print ((avg*1000)/NR)*224}' log 

قد ترغب في حذف العديد من الأسطر الأخيرة أيضًا ، لحساب عمليات الطباعة من إيقاف الخدمات المرجعية. لديك بالفعل فكرة عن كيفية القيام بذلك. لذلك سأقوم بنشر النتائج لسيناريوهات اختبار مختلفة ومتابعة المشكلات التي واجهتها.


نتائج جميع الاختبارات الأخرى (4.9 مليون وما بعدها)



اختبار # 4 تحميل


اختبار # 5 تحميل


اختبار # 5 متوازنة لتقاسم وحدة المعالجة المركزية على قدم المساواة


اختبار # 6


اختبار # 8


اختبار # 12


اختبار رقم 13


مشاكل.


وضعت في الجدول أعلاه باللون الأحمر.


ثبت أن تشغيل أكثر من 224 نسخة مرجعية على الخادم B هو الفكرة السيئة ، لأن الخادم الموجود على جانب العميل كان غير قادر على توزيع وقت وحدة المعالجة المركزية بالتساوي بين العمليات / سلاسل العمليات. استحوذت المثيلات 224 القياسية الأولى التي أنشأت جميع اتصالاتها على معظم موارد وحدة المعالجة المركزية وبقية المثيلات المرجعية متأخرة. حتى استخدام renice -20 لا يساعد كثيرًا (448 حالة مرجعية ، حالتان LAppS):


448 الحالات المرجعية


خادم B (الجانب الأيسر) تحت عبء ثقيل للغاية ولا يزال لدى الخادم A موارد وحدة المعالجة المركزية المجانية.


لذلك ضاعفت في benchmark.max_connections بدلاً من بدء المزيد من مثيلات LAppS المنفصلة.


ومع ذلك ، لتشغيل 12.3 مليون من WebSockets ، بدأت تشغيل الإصدار الخامس من LAppS (الاختباران 5 و 6) دون إيقاف تشغيل أربعة منها قيد التشغيل بالفعل. ولعب دور CFQ عن طريق تعليق يدويا وإعادة تشغيل العمليات ذات الأولوية مع قتل -STOP / -CONT أو / وكره أولوياتهم. يمكنك استخدام البرنامج النصي القالب التالي لهذا:


 while [ 1 ]; do kill -STOP <4 fast-processes pids> sleep 10 kill -CONT <4 fast-processes pids> sleep 5 done 

مرحبًا بكم في 2019 RHEL 7.6! بصراحة ، لقد استخدمت أمر renice لأول مرة منذ عام 2009. ما هو الأسوأ ، - لقد نجحت في استخدامه هذه المرة تقريبًا.


كان لدي مشكلة مع محرك مبعثر لجمع بطاقات NIC. لذلك قمت بتعطيله لبعض الاختبارات ، وليس في الواقع وضع هذا الحدث في الجدول.


كان لدي اضطرابات جزئية في الارتباطات تحت الحمل الثقيل وعلاقة برنامج التشغيل NIC ، لذلك اضطررت إلى تجاهل نتائج الاختبار ذات الصلة.


نهاية القصة


بصراحة ، ذهبت الاختبارات أكثر سلاسة مما توقعت.


أنا مقتنع أنني لم أتمكن من تحميل LAppS على Server A إلى كامل إمكاناته (بدون SSL) ، لأنني لم أحصل على ما يكفي من موارد وحدة المعالجة المركزية من جانب العميل. على الرغم من تمكين TLS 1.3 ، إلا أن LAppS على الخادم A كانت تستخدم جميع موارد وحدة المعالجة المركزية المتوفرة تقريبًا.


ما زلت مقتنعا بأن LAppS هو خادم WebSockets مفتوح المصدر الأكثر قابلية للتوسعة وأسرع وأن وحدة عميل WebSockets cws هي الوحيدة من نوعها ، التي توفر إطار عمل لاختبار الحمل العالي.


يرجى التحقق من النتائج على الأجهزة الخاصة بك.


ملاحظة: لا تستخدم أبدًا nginx أو apache كموازن التحميل أو كبروكسي بديل لـ WebSockets ، أو ستنتهي في النهاية بخفض الأداء حسب الحجم. فهي ليست بناء ل WebSockets.

Source: https://habr.com/ru/post/ar460847/


All Articles