설정: 백그라운드 프로세스 파이프라인에서 생성된 파일이 포함된 bash 시스템이 있습니다. 파이프라인의 첫 번째 단계에서는 여러 프로세스가 단일 명명된 파이프에 씁니다. 파이프의 다음 단계에는 명명된 파이프를 읽고 해당 데이터를 다른 작업자 프로세스에 전달하여 데이터 작업을 수행하는 판독기 프로세스가 있습니다. (아래 샘플 스크립트를 참조하세요). https를 통해 원격 리소스와 통신하여 데이터가 준비되지 않았다고 판단되면 이러한 작업자는 데이터를 stage2 파이프라인으로 반환할 수 있습니다.
문제: 파이프라인의 두 번째 단계에서 작업자 프로세스 내부에 ssh 호출을 추가할 때까지 이 설정은 사소한 로드에서도 잘 작동했습니다. 이렇게 하면 Named Pipe를 통과하는 많은 데이터가 사라지기 시작했습니다. 이는 이전 작업 부하의 2%로 부하를 크게 줄여도 발생합니다.
나는 약간의 독서를 했고 ssh 클라이언트가 fd를 파괴하는 것에 대한 모호한 참조를 발견했기 때문에 이것이 관련이 있는지 확실하지 않습니다.
환경: 현재 Ubuntu 20.04에서 bash 5.0.17을 사용하고 있습니다. 또한 Ubuntu 22.04 시스템에서 테스트했는데 동일한 동작을 확인했습니다.
단순화된 스크립트
#!/bin/bash
function stage1_worker()
{
# Do work including network calls via a 3rd party program which uses python
flock --exclusive $QUEUE -c "print \"%s\n\" $DATA > $QUEUE"
}
function stage2_reader_v1()
{
local QUEUE_DATA=""
while true ;
do
# logic
if read QUEUE_DATA ; then
stage2_worker $QUEUE_DATA &
fi
# logic
done < $QUEUE
}
function stage2_worker_v1()
{
local QUEUE_DATA=$1
local retry_needed=false
# logic
# Add back to the stage 2 queue if retry needed (reader rate limits to stage 2 worker to keep process count down)
if $retry_needed ; then
flock --exclusive $QUEUE -c "print \"%s\n\" $DATA > $QUEUE"
fi
}
QUEUE=$(mktemp -u)
mkfifo $QUEUE
trap "rm -f $QUEUE" EXIT
# Launch stage 2 reader in background process
stage2_reader &
# Launch several stage 1 workers...
stage1_worker $ARG &
# wait for all background processes to complete
wait
이렇게 변경했을 때 갑자기 대기열의 데이터가 손실되기 시작했고 항목이 처리되지 않았습니다.
function stage2_worker_v2()
{
local QUEUE_DATA=$1
local retry_needed=false
# logic
local IP_FROM_QUEUE_DATA=... #logic
ssh -o ConnectTimeout=1 -o BatchMode=yes user@$IP_FROM_QUEUE_DATA '<remote command' >/dev/null 2>&1
if [[ $? -ne 0 ]] ; then
#handle failure
fi
# Add back to the stage 2 queue if retry needed (reader rate limits to stage 2 worker to keep process count down)
if $retry_needed ; then
flock --exclusive $QUEUE -c "print \"%s\n\" $DATA > $QUEUE"
fi
}
영구적으로 열린 fd를 제한하려는 노력에서 리더를 이것으로 변경하여 처리량이 향상되었지만 여전히 데이터가 누락되었습니다.
function stage2_reader_v1()
{
local QUEUE_DATA=""
while true ;
do
# logic
if read -t 1 QUEUE_DATA <> $QUEUE ; then
stage2_worker $QUEUE_DATA &
fi
# logic
done
}
stage2_worker_v2에서 ssh 호출을 제거하면 모든 데이터가 예상대로 파이프를 통해 흐르게 됩니다.
왜 이런 일이 발생하는지, 해결 방법을 알려주시면 감사하겠습니다.
이 단순화된 스크립트를 신속하게 작성했으며 실제 코드에는 존재하지 않는 작은 구문/이름 지정 오류가 있을 수 있습니다.
답변1
명명된 파이프를 방해해서는 안 되지만 표준 입력(예: 파이프에서 데이터를 읽으려는 시도)을 방해합니다. 호출하면 stage2_worker
표준 입력이 파이프에서 프로세스로 리디렉션됩니다 ssh
.
while true; do
if read QUEUE_DATA ; then
stage2_worker $QUEUE_DATA &
fi
done < $QUEUE
function stage2_worker_v2()
{
...
ssh -o ConnectTimeout=1 -o BatchMode=yes user@$IP
ssh
표준 입력을 다른 곳에서 리디렉션하거나 ssh ... < /dev/null
(또는 기본적으로 동일한 기능을 사용 ssh -n
) 루프 내부 항목에 대한 표준 입력을 방해하지 않도록 while 루프 내에서 리디렉션을 수행합니다. 몇 가지 변경 사항이 있습니다:
read
fd 3에서 읽고 입력을 거기로 리디렉션하도록 지시합니다 (Bash):
while true; do
if read QUEUE_DATA -u 3; then
stage2_worker $QUEUE_DATA &
fi
done 3< $QUEUE
동일하지만 다른 리디렉션이 있습니다.
while true; do
if read QUEUE_DATA <&3; then
stage2_worker $QUEUE_DATA &
fi
done 3< $QUEUE
아니면 파이프에서 리디렉션하면 됩니다 read
.
while true; do
if read QUEUE_DATA < $QUEUE; then
stage2_worker $QUEUE_DATA &
fi
done
어떤 경우든 값에 공백이나 와일드카드가 포함된 경우를 대비하여 이러한 변수 확장을 인용하는 것을 고려할 수 있습니다. 또한 함수를 선언하는 표준 방법은 ksh 스타일과 표준 방법을 혼합한 funcname()
while 이며 Bash(AFAIR)에서만 지원됩니다.function funcname()