เพิ่มจำนวนสูงสุดของการเชื่อมต่อ TCP / IP ใน Linux


214

ฉันกำลังเขียนโปรแกรมเซิร์ฟเวอร์และดูเหมือนว่าจำนวนการเชื่อมต่อของฉันถูก จำกัด เนื่องจากแบนด์วิดท์ของฉันไม่อิ่มตัวแม้เมื่อฉันตั้งค่าจำนวนการเชื่อมต่อเป็น "ไม่ จำกัด "

ฉันจะเพิ่มหรือกำจัดจำนวนการเชื่อมต่อสูงสุดที่กล่อง Ubuntu Linux ของฉันสามารถเปิดได้ในแต่ละครั้งได้อย่างไร ระบบปฏิบัติการนี้ จำกัด หรือไม่หรือเป็นเราเตอร์หรือ ISP หรือไม่ หรือมันเป็นอย่างอื่น?


2
@ ซอฟต์แวร์ Monkey: ฉันตอบคำถามต่อไปนี้เพราะฉันหวังว่านี่อาจเป็นประโยชน์กับคนที่กำลังเขียนเซิร์ฟเวอร์ในอนาคต
Derobert

1
@derobert: ฉันเห็น +1 แล้ว ที่จริงแล้วฉันมีความคิดเดียวกันหลังจากความคิดเห็นก่อนหน้าของฉัน แต่คิดว่าฉันจะให้ความคิดเห็นยืน
Lawrence Dol

คำตอบ:


396

จำนวนการเชื่อมต่อสูงสุดได้รับผลกระทบจากข้อ จำกัด บางประการทั้งฝั่งไคลเอ็นต์และเซิร์ฟเวอร์แม้ว่าจะต่างกันเล็กน้อย

บนฝั่งไคลเอ็นต์: เพิ่มช่วงพอร์ต ephermal และลดtcp_fin_timeout

เพื่อหาค่าเริ่มต้น:

sysctl net.ipv4.ip_local_port_range
sysctl net.ipv4.tcp_fin_timeout

ช่วงพอร์ต ephermal กำหนดจำนวนซ็อกเก็ตขาออกสูงสุดที่โฮสต์สามารถสร้างได้จากที่อยู่ IP เฉพาะ การfin_timeoutกำหนดเวลาต่ำสุดซ็อกเก็ตเหล่านี้จะยังคงอยู่ในTIME_WAITสถานะ (ไม่สามารถใช้งานได้หลังจากใช้ครั้งเดียว) ค่าเริ่มต้นของระบบปกติคือ:

  • net.ipv4.ip_local_port_range = 32768 61000
  • net.ipv4.tcp_fin_timeout = 60

โดยทั่วไปหมายความว่าระบบของคุณไม่สามารถรับประกันได้มากกว่า(61000 - 32768) / 60 = 470ซ็อกเก็ตต่อวินาทีอย่างสม่ำเสมอ port_rangeถ้าคุณไม่ได้มีความสุขกับการที่คุณสามารถเริ่มต้นด้วยการเพิ่ม การตั้งค่าช่วง15000 61000เป็นเรื่องธรรมดาในทุกวันนี้ fin_timeoutนอกจากนี้คุณยังสามารถเพิ่มความพร้อมโดยการลด สมมติว่าคุณทำทั้งสองอย่างคุณควรเห็นการเชื่อมต่อขาออกมากกว่า 1,500 ครั้งต่อวินาทีพร้อมมากขึ้น

วิธีเปลี่ยนค่า :

sysctl net.ipv4.ip_local_port_range="15000 61000"
sysctl net.ipv4.tcp_fin_timeout=30

ไม่ควรตีความข้างต้นเป็นปัจจัยที่มีผลต่อความสามารถของระบบในการทำการเชื่อมต่อขาออกต่อวินาที แต่ปัจจัยเหล่านี้ส่งผลกระทบต่อความสามารถของระบบในการจัดการการเชื่อมต่อพร้อมกันในลักษณะที่ยั่งยืนสำหรับ "กิจกรรม" เป็นระยะเวลานาน

ค่าเริ่มต้น Sysctl ในกล่อง Linux ทั่วไปสำหรับtcp_tw_recycle& tcp_tw_reuseจะเป็น

net.ipv4.tcp_tw_recycle=0
net.ipv4.tcp_tw_reuse=0

สิ่งเหล่านี้ไม่อนุญาตให้มีการเชื่อมต่อจากซ็อกเก็ต "ใช้แล้ว" (อยู่ในสถานะรอ) และบังคับให้ซ็อกเก็ตใช้งานจนครบtime_waitวงจร ฉันแนะนำการตั้งค่า:

sysctl net.ipv4.tcp_tw_recycle=1
sysctl net.ipv4.tcp_tw_reuse=1 

วิธีนี้ช่วยให้สามารถหมุนซ็อกเก็ตได้อย่างรวดเร็วtime_waitและนำกลับมาใช้อีกครั้ง แต่ก่อนที่คุณจะทำการเปลี่ยนแปลงตรวจสอบให้แน่ใจว่าสิ่งนี้ไม่ขัดแย้งกับโปรโตคอลที่คุณจะใช้สำหรับแอปพลิเคชันที่ต้องการซ็อกเก็ตเหล่านี้ อย่าลืมอ่านโพสต์"การรับมือกับ TCP TIME-WAIT" จาก Vincent Bernatเพื่อทำความเข้าใจกับความหมาย net.ipv4.tcp_tw_recycle ตัวเลือกที่เป็นปัญหามากสำหรับเซิร์ฟเวอร์สาธารณะมันจะไม่จัดการการเชื่อมต่อจากคอมพิวเตอร์สองเครื่องที่แตกต่างกันที่อยู่เบื้องหลังอุปกรณ์ NAT เดียวกันซึ่งเป็นปัญหาที่ยากที่จะตรวจสอบและรอคอยที่จะกัดคุณ โปรดทราบว่าnet.ipv4.tcp_tw_recycleถูกลบออกจาก Linux 4.12

ทางด้านเซิร์ฟเวอร์:net.core.somaxconnค่ามีบทบาทสำคัญ มัน จำกัด จำนวนสูงสุดของการร้องขอเข้าคิวเพื่อฟังซ็อกเก็ต หากคุณมั่นใจในความสามารถของแอปพลิเคชันเซิร์ฟเวอร์ของคุณให้ชนจากค่าเริ่มต้น 128 เป็นอย่าง 128 ถึง 1024 ตอนนี้คุณสามารถใช้ประโยชน์จากการเพิ่มขึ้นนี้ได้โดยการแก้ไขตัวแปรไฟล์เสียงค้างในการฟังการเรียกใช้แอปพลิเคชันของคุณ

sysctl net.core.somaxconn=1024

txqueuelenพารามิเตอร์ของการ์ดอีเธอร์เน็ตของคุณยังมีบทบาทในการเล่น ค่าเริ่มต้นคือ 1,000 ดังนั้นชนพวกเขามากถึง 5,000 หรือมากกว่านั้นหากระบบของคุณสามารถจัดการกับมันได้

ifconfig eth0 txqueuelen 5000
echo "/sbin/ifconfig eth0 txqueuelen 5000" >> /etc/rc.local

ในทำนองเดียวกันชนขึ้นค่าสำหรับและnet.core.netdev_max_backlog net.ipv4.tcp_max_syn_backlogค่าเริ่มต้นของพวกเขาคือ 1,000 และ 1024 ตามลำดับ

sysctl net.core.netdev_max_backlog=2000
sysctl net.ipv4.tcp_max_syn_backlog=2048

ตอนนี้อย่าลืมเริ่มต้นทั้งแอพพลิเคชั่นไคลเอนต์และเซิร์ฟเวอร์โดยเพิ่ม FD ulimts ในเชลล์

นอกจากเทคนิคหนึ่งที่นิยมใช้โดยโปรแกรมเมอร์คือการลดจำนวนการเรียกใช้การเขียน tcp การตั้งค่าของฉันเองคือการใช้บัฟเฟอร์ที่ฉันผลักดันข้อมูลที่ฉันต้องการส่งไปยังลูกค้าแล้วที่จุดที่เหมาะสมฉันจะเขียนข้อมูลบัฟเฟอร์ลงในซ็อกเก็ตที่เกิดขึ้นจริง เทคนิคนี้ช่วยให้ฉันใช้แพ็กเก็ตข้อมูลขนาดใหญ่ลดการกระจายตัวลดการใช้งาน CPU ของฉันทั้งในพื้นที่ของผู้ใช้และระดับเคอร์เนล


4
คำตอบที่ยอดเยี่ยม! ปัญหาของฉันแตกต่างกันเล็กน้อยคือฉันพยายามย้ายข้อมูลเซสชันจากที่จัดเก็บข้อมูลระดับแอปพลิเคชันไปยัง Redis ผ่าน PHP ด้วยเหตุผลบางอย่างฉันไม่สามารถเพิ่มเซสชันได้มากกว่า 28230 เซสชันโดยไม่ต้องเพิ่มสลีปจำนวนมากในคราวเดียวโดยไม่พบข้อผิดพลาดทั้งใน php หรือบนบันทึก redis เราหัวของเราในเรื่องนี้ตลอดทั้งวันจนฉันคิดว่าอาจจะมีปัญหาไม่ได้เป็น php / redis แต่ในชั้น tcp / ip เชื่อมต่อทั้งสองและมาถึงคำตอบนี้ จัดการเพื่อแก้ไขปัญหาในเวลาไม่นานหลังจากนั้น :) ขอบคุณมาก!
s1d

27
อย่าลืมว่าเรากำลังพูดถึงพอร์ต IP + อยู่เสมอ คุณสามารถเปิดซ็อกเก็ต "ไม่ จำกัด " ให้กับพอร์ต XY จาก IP ที่แตกต่างกันได้ ขีด จำกัด ของ 470 ใช้กับซ็อกเก็ตที่เปิดพร้อมกันกับ IP เดียวกันเท่านั้น IP อื่นสามารถมีการเชื่อมต่อ 470 ของตัวเองไปยังพอร์ตเดียวกัน
Marki555

6
@ Marki555: ความคิดเห็นของคุณถูกต้องมาก แอปพลิเคชันที่พัฒนาขึ้นสำหรับการสร้างและรักษาจำนวนการเชื่อมต่อขาออกจำนวนมากต้องมี "การรับรู้" ของ IP ที่มีอยู่สำหรับการสร้างการเชื่อมต่อขาออกและจากนั้นจะต้องผูกกับที่อยู่ IP เหล่านี้อย่างเหมาะสมโดยใช้ "กระดานคะแนน"
mdk

8
คำตอบนี้มีข้อผิดพลาด อย่างแรกคือ net.ipv4.tcp_fin_timeout เป็นสถานะ FIN_WAIT_2 เท่านั้น ( cs.uwaterloo.ca/~brecht/servers/ip-sysctl.txt ) ประการที่สองตามที่ @Eric กล่าวว่า "ซ็อกเก็ต 470 ตัวตามเวลาที่กำหนด" ไม่ถูกต้อง
Sharvanath

3
@mdk: ฉันไม่ชัดเจนกับส่วนการคำนวณ(61000 - 32768) / 60 = 470 sockets per secondนี้ คุณช่วยอธิบายเรื่องนี้ได้ไหม?
Tom Taylor

64

มีตัวแปรสองตัวที่จะกำหนดจำนวนการเชื่อมต่อสูงสุด เป็นไปได้มากว่าคุณมีจำนวนไฟล์ที่หมดก่อน ตรวจสอบ ulimit -n หลังจากนั้นมีการตั้งค่าใน / proc แต่ค่าเริ่มต้นเป็นหมื่น

ที่สำคัญดูเหมือนว่าคุณกำลังทำอะไรผิด การเชื่อมต่อ TCP เดียวควรจะสามารถใช้แบนด์วิดท์ทั้งหมดระหว่างสองฝ่ายได้ ถ้าไม่ใช่:

  • ตรวจสอบว่าการตั้งค่าหน้าต่าง TCP ของคุณใหญ่พอหรือไม่ ค่าเริ่มต้นของ Linux เหมาะสำหรับทุกสิ่งยกเว้นลิงก์ inet ที่รวดเร็ว (หลายร้อย mbps) หรือลิงก์ดาวเทียมที่รวดเร็ว แบนด์วิดท์ของคุณล่าช้าคืออะไร
  • ตรวจสอบการสูญเสียแพ็กเก็ตโดยใช้ ping กับแพ็กเก็ตขนาดใหญ่ ( ping -s 1472... )
  • ตรวจสอบการ จำกัด อัตรา บน Linux สิ่งนี้ถูกกำหนดค่าด้วยtc
  • ยืนยันว่ามีแบนด์วิดท์ที่คุณคิดว่ามีอยู่จริงโดยใช้เช่น iperf
  • ยืนยันว่าโปรโตคอลของคุณมีสติ จำความล่าช้า
  • หากเป็น gigabit + LAN คุณสามารถใช้จัมโบ้แพ็คเก็ตได้หรือไม่? คุณเป็น

อาจเป็นไปได้ว่าฉันเข้าใจผิด บางทีคุณอาจกำลังทำบางอย่างเช่น Bittorrent ที่คุณต้องการการเชื่อมต่อมากมาย ถ้าเป็นเช่นนั้นคุณต้องทราบว่าคุณใช้การเชื่อมต่อจำนวนเท่าใด (ลองnetstatหรือlsof) หากตัวเลขนั้นเป็นกอบเป็นกำคุณอาจ:

  • มีแบนด์วิดท์จำนวนมากเช่น 100mbps + ulimit -nในกรณีนี้คุณอาจต้องจริงขึ้น ถึงกระนั้นการเชื่อมต่อประมาณ 1,000 ครั้ง (ค่าเริ่มต้นในระบบของฉัน) ค่อนข้างน้อย
  • มีปัญหาเครือข่ายที่ทำให้การเชื่อมต่อของคุณช้าลง (เช่นการสูญหายของแพ็กเก็ต)
  • มีสิ่งอื่นที่ทำให้คุณช้าลงเช่นแบนด์วิดท์ IO โดยเฉพาะอย่างยิ่งหากคุณกำลังมองหา คุณตรวจสอบแล้วiostat -xหรือยัง

นอกจากนี้หากคุณใช้เราเตอร์ NAT ระดับผู้บริโภค (Linksys, Netgear, DLink ฯลฯ ) ระวังว่าคุณอาจเกินขีดความสามารถของมันด้วยการเชื่อมต่อหลายพันครั้ง

ฉันหวังว่านี่จะช่วยได้บ้าง คุณกำลังถามคำถามเกี่ยวกับเครือข่ายจริงๆ


16

เพื่อปรับปรุงคำตอบที่ได้รับจาก Derobert

คุณสามารถกำหนดได้ว่าขีด จำกัด การเชื่อมต่อระบบปฏิบัติการของคุณคืออะไรโดยการใส่ nf_conntrack_max

ตัวอย่างเช่น: cat / proc / sys / net / netfilter / nf_conntrack_max

คุณสามารถใช้สคริปต์ต่อไปนี้เพื่อนับจำนวนการเชื่อมต่อ tcp ไปยังช่วงที่กำหนดของพอร์ต tcp โดยค่าเริ่มต้น 1-65535

นี่จะเป็นการยืนยันว่าคุณใช้ขีด จำกัด การเชื่อมต่อระบบปฏิบัติการของคุณสูงสุดหรือไม่

นี่คือสคริปต์

#!/bin/bash
OS=$(uname)

case "$OS" in
    'SunOS')
            AWK=/usr/bin/nawk
            ;;
    'Linux')
            AWK=/bin/awk
            ;;
    'AIX')
            AWK=/usr/bin/awk
            ;;
esac

netstat -an | $AWK -v start=1 -v end=65535 ' $NF ~ /TIME_WAIT|ESTABLISHED/ && $4 !~ /127\.0\.0\.1/ {
    if ($1 ~ /\./)
            {sip=$1}
    else {sip=$4}

    if ( sip ~ /:/ )
            {d=2}
    else {d=5}

    split( sip, a, /:|\./ )

    if ( a[d] >= start && a[d] <= end ) {
            ++connections;
            }
    }
    END {print connections}'

3
which awkเป็นเพื่อนของคุณในการกำหนดเส้นทางไปยัง awk, SunOS มีการเชื่อมโยงกับมันเช่นกัน :)
Panagiotis Moustafellos

2
@PanagiotisM whichอาศัยโปรแกรมในPATHกรณีที่คุณสามารถใช้awkแทนการให้เส้นทางแบบเต็ม (ที่กล่าวว่าฉันไม่แน่ใจว่าทางออกในสคริปต์นั้นใกล้เคียงกับความสมบูรณ์แบบหรือไม่ แต่นี่ไม่ใช่สิ่งที่สคริปต์จะพูดถึง)
Michael Krelin - แฮกเกอร์

5
ฉันชอบวิธีที่สคริปต์นี้ใช้เพื่อกำหนดawkตำแหน่ง แต่จะถือว่าเชลล์นั้นอยู่เสมอ/bin/bash (เคล็ดลับโปร: AIX5 / 6 จะไม่มี bash เป็นค่าเริ่มต้น)
kubanczyk

การawkตรวจจับมีประโยชน์หรือไม่ โดยส่วนตัวฉันก็จะถือว่าถูกต้องPATHแต่ทางเลือกที่เหมาะสมอาจเป็น/usr/bin/env awkและ/usr/bin/env bashตามลำดับ สำหรับสิ่งที่คุ้มค่ามันได้ตำแหน่งที่ไม่ถูกต้องในระบบ Linux ของฉัน /usr/bin/awkไม่ได้อยู่ในนั้น/bin/awk
Wolph

1
เมื่อฉันเรียกใช้สคริปต์นี้ฉันได้รับ 798 ดังนั้นมันหมายความว่าอย่างไร

10

ในระดับแอปพลิเคชันนี่คือสิ่งที่นักพัฒนาสามารถทำได้:

จากฝั่งเซิร์ฟเวอร์:

  1. ตรวจสอบว่าโหลดบาลานเซอร์ (ถ้าคุณมี) ทำงานได้ถูกต้องหรือไม่

  2. เปลี่ยนการหมดเวลา TCP ช้าลงเป็น 503 การตอบสนองอย่างรวดเร็วหากคุณโหลดบาลานเซอร์ทำงานอย่างถูกต้องมันควรเลือกทรัพยากรการทำงานที่จะให้บริการและดีกว่าการแขวนที่นั่นด้วยการนวดผิดพลาดที่ไม่คาดคิด

เช่น: ถ้าคุณใช้เซิร์ฟเวอร์โหนดคุณสามารถใช้ toobusy ได้ตั้งแต่ npm การดำเนินการบางอย่างเช่น:

var toobusy = require('toobusy');
app.use(function(req, res, next) {
  if (toobusy()) res.send(503, "I'm busy right now, sorry.");
  else next();
});

ทำไมต้อง 503 ต่อไปนี้เป็นข้อมูลเชิงลึกที่ดีเกี่ยวกับการโอเวอร์โหลด: http://ferd.ca/queues-don-t-fix-overload.html

เราสามารถทำงานด้านลูกค้าได้ด้วย:

  1. ลองจัดกลุ่มสายเป็นชุด ๆ ลดปริมาณการรับส่งข้อมูลรวมจำนวนคำขอ b / w ไคลเอ็นต์และเซิร์ฟเวอร์

  2. ลองสร้างแคชมิดเลเยอร์เพื่อจัดการคำขอซ้ำซ้อนที่ไม่จำเป็น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.