IO.select 처리에 유용한 자동 관리 기술
개시하다
IO.나는 select를 처리할 때 겪는 문제로 차단 문제가 존재한다고 생각한다.차단이란 버퍼에 데이터가 전혀 없고 데이터 읽기 처리 중 대기가 발생하며 버퍼 메모리에 데이터가 있지만 읽으려는 바이트의 데이터가 없고 데이터 읽기 처리 대기 등으로 인한 현상(쓰기도 마찬가지)을 말한다.IO.select 처리 중 블로킹이 발생하면 후속 처리를 기다리며 효율이 낮다.이 문제를 해결하는 방법에는 셀프 파이프 기술이 있다.자관 기술의 역사는 1990년께부터 시작됐으며, 현재도 우리가 흔히 사용하는 총서에서 사용되고 있다.자동 파이프 기술
IO.select에서 막힌 예와 해결 예
예를 들어 루비에서 입출력을 합니다.select 처리를 하고 싶을 때 입출력입니다.pipe를 사용하면 이렇게 쓸 수 있습니다.이것은 블로킹이 발생한 예다.
예1
eg_1 = -> {
async_heavy_process = -> { puts "heavy" }
r, w = IO.pipe
fork { sleep 5; w.puts "hoge" }
IO.select([r])
async_heavy_process.()
puts r.gets
}
eg_1.()
5초 서지 않으면, asyncheavy_프로세스 처리는 달리기 효율이 낮은 프로그램이 되었다.가능한 한 빨리 비동기적인 집행의 무거운 처리를 집행하기를 바란다.그럼 문제를 해결해 봅시다.IO.selectに渡されたIOオブジェクトの中に常に準備完了状態のものがあればIO.selectでブロッキングされることは無くなります。
이 아이디어의 출처는 자기 파이프 기술이다.그럼 이 아이디어例1
가 삽입된 수정판 코드 예2를 살펴보자.예2
eg_2 = -> {
async_heavy_process = -> { puts "heavy"; }
self_reader, self_writer = IO.pipe
self_writer.puts 0
r, w = IO.pipe
fork { sleep 5; w.puts "hoge" }
IO.select([r, self_reader])
async_heavy_process.()
puts r.gets
}
eg_2.()
IO"heavy"
가 즉시 표시됩니다.select 부분에 의해 차단되지 않습니다.다른 용도도 있을 것 같은데 아시면 알려주세요.자동 파이프 기술을 사용한 라이브러리 예시
예에서 소개한 예는 매우 간단하고 실용성이 부족하다. 마지막으로 짧은 장서고에서 이런 자관 기술을 어떻게 사용했는지 소개한다.
foreman
foreman은 시작할 명령을 정의하는 Procefile을 읽습니다. 다중 프로세스에서 실행되며, 각 프로세스에서 발생하는 표준 출력 (표준 오류) 은 파이프를 통해 주 프로세스에서 시작하는 프로그램으로 전달되며, 표준 출력을 통해 표시되는 도구입니다.구체적으로 이런 느낌의 물건이에요.
Procfile
app: sleep 5 && echo 'app' && exit 1; # 子プロセス1
web: while :; do sleep 1 && echo 'web'; done; # 子プロセス2
bash$ foreman start
00:57:43 app.1 | started with pid 21149 # メインプロセス/メインスレッドで出力
00:57:43 web.1 | started with pid 21150 # メインプロセス/メインスレッドで出力
00:57:44 web.1 | web # 子プロセス2にwriterを渡して書き込ませ、readerを通して、メインプロセス/スレッド2で出力
00:57:45 web.1 | web # 子プロセス2にwriterを渡して書き込ませ、readerを通して、メインプロセス/スレッド2で出力
00:57:46 web.1 | web # 子プロセス2にwriterを渡して書き込ませ、readerを通して、メインプロセス/スレッド2で出力
00:57:47 web.1 | web # 子プロセス2にwriterを渡して書き込ませ、readerを通して、メインプロセス/スレッド2で出力
00:57:48 app.1 | app # 子プロセス1にwriterを渡して書き込ませ、readerを通して、メインプロセス/スレッド2で出力
00:57:48 web.1 | web # 子プロセス1にwriterを渡して書き込ませ、readerを通して、メインプロセス/スレッド2で出力
00:57:48 app.1 | exited with code 1 # メインプロセス/スレッド2で子プロセス1の終了を確認
00:57:48 system | sending SIGTERM to all processes # メインプロセス/メインスレッドから子プロセスへSIGTERMが送られた時の出力(windowsだとSIGKILL)
00:57:48 web.1 | terminated by SIGTERM # メインプロセス/メインスレッドから全ての子プロセスのterminatedを確認した時の出力
.어느 처리에 사용되는지 하위 처리에 전달된 파이프의 표준 출력 (표준 오류) 처리 (wait for output) 에서 사용합니다.전선으로 말하자면 여기다. # https://github.com/ddollar/foreman/blob/5b815c5d8077511664a712aca90b070229ca6413/lib/foreman/engine.rb#L406-L420
def watch_for_output
Thread.new do
begin
loop do
io = IO.select([@selfpipe[:reader]] + @readers.values, nil, nil, 30)
read_self_pipe
handle_signals
handle_io(io ? io.first : [])
end
rescue Exception => ex
puts ex.message
puts ex.backtrace
end
end
end
io = IO.select([@selfpipe[:reader]] + @readers.values, nil, nil, 30)
이 자체 파이프가 없으면 어떻게 될까.select가 영구적으로 차단되면watchfor_output의 후속 처리인 하위 프로세스의 끝 확인 검사 처리 (wait for shoutdown or child termination) 가 더 이상 실행되지 않습니다.이것은foreman에서kill자 프로세스를 할 수 없다는 것을 의미하며 최악의 사태이다. # https://github.com/ddollar/foreman/blob/5b815c5d8077511664a712aca90b070229ca6413/lib/foreman/engine.rb#L54-L63
def start
register_signal_handlers
startup
spawn_processes
watch_for_output
sleep 0.1
wait_for_shutdown_or_child_termination
shutdown
exit(@exitstatus) if @exitstatus
end
unicorn
코드를 자세히 읽지 않았기 때문에 자세히 말하지는 않지만, 코드grep를 사용하면 자동관을 사용했다는 것을 알 수 있다.
# https://github.com/defunkt/unicorn/blob/2c347116305338710331d238fefa23f00e98cf54/lib/unicorn/http_server.rb#L82-L91
# We use @self_pipe differently in the master and worker processes:
#
# * The master process never closes or reinitializes this once
# initialized. Signal handlers in the master process will write to
# it to wake up the master from IO.select in exactly the same manner
# djb describes in https://cr.yp.to/docs/selfpipe.html
#
# * The workers immediately close the pipe they inherit. See the
# Unicorn::Worker class for the pipe workers use.
@self_pipe = []
그리고 나는 이것이 막힘 처리를 피하는 데 도움이 될 것이라고 생각한다.# https://github.com/defunkt/unicorn/blob/2c347116305338710331d238fefa23f00e98cf54/lib/unicorn/http_server.rb#L748
def worker_loop(worker)
#
# 省略
#
ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
#
# 省略
#
end
관심 있으면 코드를 읽어보세요.총결산
IO.다음은 select 처리에서 발생하는 막힘을 피하는 자관 기술을 소개한다.평소에 저를 배려해 주신 포맨과 유니콘 등 총서에서 자주 사용하는 기교입니다. 이럴 때 배워보세요.일본에서 기사가 전혀 없어서 기사를 써봤어요.나는 세심하지 못한 점이 있을 것이라고 생각한다. 만약 도움을 줄 수 있다면 매우 좋겠다.
참고 자료
Reference
이 문제에 관하여(IO.select 처리에 유용한 자동 관리 기술), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/yukihirop/articles/c4a9f2fc2df6bf2dc192텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)