วิธีรันคำสั่งโดยเฉลี่ย 5 ครั้งต่อวินาที


21

ฉันมีสคริปต์บรรทัดคำสั่งที่ดำเนินการเรียก API และอัปเดตฐานข้อมูลพร้อมผลลัพธ์

ฉันมีขีด จำกัด การเรียก API 5 ครั้งต่อวินาทีกับผู้ให้บริการ API สคริปต์ใช้เวลาดำเนินการมากกว่า 0.2 วินาที

  • หากฉันรันคำสั่งตามลำดับคำสั่งนั้นจะทำงานไม่เร็วพอและฉันจะทำการเรียก API 1 หรือ 2 ครั้งต่อวินาทีเท่านั้น
  • หากฉันรันคำสั่งตามลำดับ แต่พร้อมกันจากหลายเทอร์มินัลฉันอาจเกินขีด จำกัด การโทร 5 ครั้ง / วินาที

หากมีวิธีการจัดทำชุดข้อความเพื่อให้สคริปต์บรรทัดคำสั่งของฉันทำงานเกือบ 5 ครั้งต่อวินาที

ตัวอย่างเช่นสิ่งที่จะรันด้วย 5 หรือ 10 เธรดและไม่มีเธรดใดที่จะเรียกใช้งานสคริปต์หากเธรดก่อนหน้าได้ดำเนินการน้อยกว่า 200ms ที่ผ่านมา


คำตอบทั้งหมดขึ้นอยู่กับสมมุติว่าสคริปต์ของคุณจะเสร็จสิ้นตามลำดับที่เรียกว่า เป็นที่ยอมรับสำหรับกรณีการใช้งานของคุณหรือไม่หากพวกเขาเสร็จสิ้นการสั่งซื้อ?
Cody Gustafson

@CodyGustafson เป็นที่ยอมรับอย่างสมบูรณ์หากพวกเขาเสร็จสิ้นการสั่งซื้อ ฉันไม่เชื่อว่ามีข้อสันนิษฐานดังกล่าวในคำตอบที่ยอมรับอย่างน้อย?
Benjamin

จะเกิดอะไรขึ้นหากคุณมีจำนวนการโทรต่อวินาทีเกินกว่าที่กำหนด หากผู้ให้บริการ API ควบคุมปริมาณคุณไม่ต้องการกลไกใด ๆ ในตอนท้ายของคุณ ... ใช่ไหม
Floris

@Floris พวกเขาจะส่งกลับข้อผิดพลาดที่จะแปลในข้อยกเว้นใน SDK ก่อนอื่นฉันสงสัยว่าผู้ให้บริการ API จะมีความสุขถ้าฉันสร้างข้อความเร่งด่วน 50 ข้อความต่อวินาที (คุณควรดำเนินการตามข้อความดังกล่าว) และประการที่สองฉันใช้ API เพื่อวัตถุประสงค์อื่นในเวลาเดียวกันดังนั้นฉันจึง ไม่ต้องการที่จะถึงขีด จำกัด ซึ่งสูงกว่าจริงเล็กน้อย
Benjamin

คำตอบ:


25

ในระบบ GNU และถ้าคุณมีpvคุณสามารถทำได้:

cmd='
   that command | to execute &&
     as shell code'

yes | pv -qL10 | xargs -n1 -P20 sh -c "$cmd" sh

-P20คือการดำเนินการที่มากที่สุด 20 $cmdในเวลาเดียวกัน

-L10 จำกัด อัตรา 10 ไบต์ต่อวินาทีดังนั้น 5 บรรทัดต่อวินาที

หากคุณ$cmdกลายเป็นสองช้าและทำให้ถึงขีด จำกัด 20 ถึงแล้วxargsจะหยุดอ่านจนกว่าหนึ่ง$cmdอินสแตนซ์อย่างน้อยกลับมา pvจะยังคงเขียนต่อไปยังไปป์ในอัตราเดียวกันจนกว่าไปป์จะเต็ม (ซึ่งบน Linux ที่มีขนาดไพพ์เริ่มต้นที่ 64KiB จะใช้เวลาเกือบ 2 ชั่วโมง)

ณ จุดนี้pvจะหยุดเขียน แต่ถึงอย่างนั้นเมื่อxargsกลับมาอ่านต่อpvจะพยายามจับและส่งทุกบรรทัดมันควรจะส่งเร็วที่สุดเท่าที่จะทำได้เพื่อรักษาระดับเฉลี่ย 5 บรรทัดต่อวินาทีโดยรวม

นั่นหมายความว่าตราบใดที่เป็นไปได้กับ 20 กระบวนการเพื่อให้ทำงานได้ 5 วินาทีต่อวินาทีตามความต้องการโดยเฉลี่ย อย่างไรก็ตามเมื่อถึงขีด จำกัด อัตราที่กระบวนการใหม่จะเริ่มต้นจะไม่ถูกขับเคลื่อนด้วยตัวจับเวลาของ pv แต่ด้วยอัตราที่อินสแตนซ์ cmd ก่อนหน้านี้กลับมา ตัวอย่างเช่นหาก 20 กำลังทำงานอยู่และเป็นเวลา 10 วินาทีและ 10 ในนั้นตัดสินใจที่จะเสร็จสิ้นพร้อมกันทั้งหมด 10 รายการใหม่จะเริ่มต้นพร้อมกัน

ตัวอย่าง:

$ cmd='date +%T.%N; exec sleep 2'
$ yes | pv -qL10 | xargs -n1 -P20 sh -c "$cmd" sh
09:49:23.347013486
09:49:23.527446830
09:49:23.707591664
09:49:23.888182485
09:49:24.068257018
09:49:24.338570865
09:49:24.518963491
09:49:24.699206647
09:49:24.879722328
09:49:25.149988152
09:49:25.330095169

โดยเฉลี่ยจะเป็น 5 ครั้งต่อวินาทีแม้ว่าการหน่วงเวลาระหว่างการวิ่งสองครั้งจะไม่เท่ากับ 0.2 วินาทีเสมอไป

ด้วยksh93(หรือด้วยzshถ้าsleepคำสั่งของคุณรองรับเศษส่วนวินาที):

typeset -F SECONDS=0
n=0; while true; do
  your-command &
  sleep "$((++n * 0.2 - SECONDS))"
done

ที่ทำให้ไม่มีข้อผูกมัดกับจำนวนของyour-commands พร้อมกัน


หลังจากการทดสอบเล็กน้อยpvคำสั่งดูเหมือนจะเป็นสิ่งที่ฉันกำลังมองหาและหวังว่าจะดีกว่านี้ไม่ได้! เพียงแค่ในบรรทัดนี้: yes | pv -qL10 | xargs -n1 -P20 sh -c "$cmd" shไม่shซ้ำซ้อนสุดท้ายหรือไม่
Benjamin

1
@Benjamin ที่สองshคือสำหรับ$0คุณใน$cmdสคริปต์ มันยังใช้ในข้อความแสดงข้อผิดพลาดโดยเชลล์ ถ้าไม่มีมันก็$0จะมาyจากyesดังนั้นคุณจะได้รับข้อความแสดงข้อผิดพลาดเช่นy: cannot execute cmd... คุณสามารถทำได้yes sh | pv -qL15 | xargs -n1 -P20 sh -c "$cmd"
Stéphane Chazelas

ฉันกำลังดิ้นรนเพื่อแยกย่อยสิ่งทั้งหมดออกเป็นชิ้นส่วนที่เข้าใจได้ TBH! ในตัวอย่างของคุณคุณได้ลบสุดท้ายนี้sh; และในการทดสอบของฉันเมื่อฉันลบมันฉันไม่เห็นความแตกต่าง!
Benjamin

@Benjamin มันไม่สำคัญ มันจะสร้างความแตกต่างก็ต่อเมื่อคุณ$cmdใช้งาน$0(เพราะเหตุใด) และสำหรับข้อความแสดงข้อผิดพลาด ลองยกตัวอย่างเช่นมีcmd=/; โดยไม่ต้องสองshคุณจะเห็นสิ่งที่ต้องการy: 1: y: /: Permission deniedแทนsh: 1: sh: /: Permission denied
Stéphane Chazelas

ฉันมีปัญหากับโซลูชันของคุณ: ใช้งานได้ดีไม่กี่ชั่วโมงจากนั้นในบางจุดก็เพิ่งออกโดยไม่มีข้อผิดพลาด สิ่งนี้อาจเกี่ยวข้องกับท่อที่เต็มไปด้วยผลข้างเคียงที่ไม่คาดคิดหรือไม่?
Benjamin

4

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

while sleep 1
do    for i in {1..5}
      do mycmd &
      done
done

หากคำสั่งของคุณอาจใช้เวลามากกว่า 1 วินาทีและคุณต้องการกระจายคำสั่งที่คุณสามารถลองได้

while :
do    for i in {0..4}
      do  sleep .$((i*2))
          mycmd &
      done
      sleep 1 &
      wait
done

หรือคุณสามารถมี 5 ลูปแยกต่างหากที่ทำงานอย่างอิสระโดยมีขั้นต่ำ 1 วินาที

for i in {1..5}
do    while :
      do   sleep 1 &
           mycmd &
           wait
      done &
      sleep .2
done

วิธีแก้ปัญหาค่อนข้างดีเช่นกัน ฉันชอบความจริงที่ว่ามันเรียบง่ายและเป็น 5 เท่าต่อวินาที แต่มันมีข้อเสียของการเริ่มต้น 5 คำสั่งในเวลาเดียวกัน (แทนที่จะเป็นทุก ๆ 200ms) และอาจขาดการป้องกันไม่ให้เธรดส่วนใหญ่ทำงานในเวลาเดียวกัน !
Benjamin

@Benjamin ฉันเพิ่ม 200ms sleep ในลูปของเวอร์ชันที่สอง รุ่นที่สองนี้ไม่สามารถมีมากกว่า 5 cmds ในเวลาเดียวเราทุก ๆ เริ่ม 5 แล้วรอพวกเขาทั้งหมด
meuh

ปัญหาคือคุณไม่สามารถเริ่มได้มากกว่า 5 ต่อวินาที หากสคริปต์ทั้งหมดใช้เวลาในการดำเนินการมากกว่า 1 วินาทีทันใดนั้นคุณก็อยู่ไกลเกินขีด จำกัด API นอกจากนี้หากคุณรอทั้งหมดสคริปต์การบล็อกเดียวจะบล็อกผู้อื่นทั้งหมดหรือไม่
Benjamin

@Benjamin เพื่อให้คุณสามารถเรียกใช้ 5 ลูปอิสระแต่ละคนมีการนอนหลับขั้นต่ำ 1 วินาทีดูรุ่นที่ 3
meuh

2

ด้วยโปรแกรม C

ตัวอย่างเช่นคุณสามารถใช้เธรดที่หลับเป็นเวลา 0.2 วินาทีในขณะที่

#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

pthread_t tid;

void* doSomeThing() {
    While(1){
         //execute my command
         sleep(0.2)
     } 
}

int main(void)
{
    int i = 0;
    int err;


    err = pthread_create(&(tid), NULL, &doSomeThing, NULL);
    if (err != 0)
        printf("\ncan't create thread :[%s]", strerror(err));
    else
        printf("\n Thread created successfully\n");



    return 0;
}

ใช้เพื่อทราบวิธีสร้างเธรด: สร้างเธรด (นี่คือลิงค์ที่ฉันใช้เพื่อวางโค้ดนี้)


ขอบคุณสำหรับคำตอบของคุณแม้ว่าฉันจะมองหาสิ่งที่ไม่เกี่ยวข้องกับการเขียนโปรแกรม C แต่ควรใช้เครื่องมือ Unix ที่มีอยู่เท่านั้น!
Benjamin

ใช่คำตอบที่ StackOverflow ยังอาจจะเช่นนี้จะใช้ถังโทเค็นร่วมกันระหว่างผู้ปฏิบัติงานเธรดหลาย แต่ขอให้อยู่บน Unix.SE แสดงให้เห็นมากขึ้นของผู้ใช้ "อำนาจ" มากกว่า "โปรแกรมเมอร์" วิธีการเป็นที่ต้องการ :-) ยังคงccเป็น เครื่องมือ Unix ที่มีอยู่และนี่ไม่ใช่รหัสจำนวนมาก!
Steve Jessop

1

ใช้Node.jsคุณสามารถเริ่มต้นหัวข้อเดียวที่รันสคริปต์ทุบตีทุก 200 มิลลิวินาทีไม่ว่านานการตอบสนองจะใช้เวลาที่จะกลับมาเพราะการตอบสนองมาผ่านฟังก์ชั่นการโทรกลับ

var util = require('util')
exec = require('child_process').exec

setInterval(function(){
        child  = exec('fullpath to bash script',
                function (error, stdout, stderr) {
                console.log('stdout: ' + stdout);
                console.log('stderr: ' + stderr);
                if (error !== null) {
                        console.log('exec error: ' + error);
                }
        });
},200);

จาวาสคริปต์นี้จะทำงานทุก 200 function (error, stdout, stderr)มิลลิวินาทีและการตอบสนองจะได้ผ่านฟังก์ชั่นการเรียกกลับ

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


ผมชอบวิธีนี้ก็จะเริ่มตรง 5 คำสั่งต่อวินาทีในช่วงเวลาปกติ ข้อเสียเปรียบเพียงอย่างเดียวที่ฉันเห็นก็คือมันขาดการป้องกันไม่ให้มีกระบวนการมากที่สุด n กระบวนการทำงานในเวลาเดียวกัน! หากเป็นสิ่งที่คุณสามารถรวมได้อย่างง่ายดาย ฉันไม่คุ้นเคยกับ node.js
Benjamin

0

ฉันเคยใช้โซลูชันของStéphane Chazelas pvมาระยะหนึ่งแล้ว แต่ก็พบว่ามันออกมาแบบสุ่ม (และเงียบ ๆ ) หลังจากเวลาผ่านไปไม่กี่นาทีจากที่ใดก็ได้ - แก้ไข: สาเหตุที่สคริปต์ PHP ของฉันเสียชีวิตเนื่องจากการดำเนินการเกินเวลาสูงสุดออกจากสถานะ 255

ดังนั้นฉันจึงตัดสินใจที่จะเขียนเครื่องมือบรรทัดคำสั่งง่ายๆที่ทำสิ่งที่ฉันต้องการ

การบรรลุเป้าหมายดั้งเดิมของฉันนั้นง่ายเหมือน:

./parallel.phar 5 20 ./my-command-line-script

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

เครื่องมือนี้ไม่ไวต่อการออกจากสถานะ 255

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