Có phải sử dụng dịch vụ trong khi thực sự để giữ một kịch bản sống là một ý tưởng tốt?


19

Tôi chỉ nhảy vào unix từ một thế giới khác, và muốn biết nếu

while true
do
  /someperlscript.pl
done

Bản thân perl script có một trình theo dõi thư mục / tệp thực thi khi các tệp được thay đổi ở vị trí đích.

Đây có phải là while truemột ý tưởng tốt? Nếu không, một cách tiếp cận mạnh mẽ ưa thích là gì?

TIA

EDIT: Vì điều này dường như đã tạo ra một chút quan tâm, đây là kịch bản hoàn chỉnh. Tập lệnh perl tự xem một thư mục bằng cách sử dụng trình xem tập tin. Khi nhận được tệp mới (chúng đến thông qua rsync), nó sẽ chọn tệp mới và xử lý nó. Bây giờ các tệp đến có thể bị hỏng (đừng hỏi .. đến từ mâm xôi pi) và đôi khi quá trình có thể không thể xử lý được. Tôi không biết chính xác tại sao, vì chúng tôi chưa nhận thức được tất cả các kịch bản.

NHƯNG - nếu vì lý do nào đó không thành công, chúng tôi muốn nó hoạt động và chạy và xử lý tệp tiếp theo, vì tệp tiếp theo hoàn toàn không liên quan đến tệp trước đó có thể gây ra lỗi.

Thông thường tôi sẽ sử dụng một số loại bắt tất cả và bọc toàn bộ mã xung quanh nó để nó KHÔNG BAO GIỜ bị hỏng. Nhưng không chắc chắn cho perl.

Từ những gì tôi đã hiểu, sử dụng một cái gì đó như giám sát là một cách tiếp cận tốt cho việc này.


9
Đây là Linux hay UNIX? Nếu là Linux, bạn có thể muốn kiểm tra inotifyAPI để có thể tránh vòng lặp bận rộn chờ các tệp thay đổi trong thư mục đích của bạn.
roaima

Linux của nó, Ubuntu
Abhinav Gujjar

Nếu bạn biết tệp này là tốt trước khi nó rời khỏi pi, thì bạn có thể chạy một tổng kiểm tra như md5 hoặc sha1 và gửi nó cùng với tệp của bạn. Sau đó, người nhận sẽ biết nếu nó có một tập tin xấu trước khi nó cố xử lý nó. Nếu bạn không biết điều đó, thì bạn vẫn có thể xây dựng một loại tổng kiểm tra một phần hoặc khối hoặc một cái gì đó tương tự vào tệp dữ liệu của bạn để đảm bảo tính toàn vẹn dữ liệu, do đó bạn có thể kiểm tra mọi thứ khi bạn thực hiện trước khi bạn cam kết với một quy trình có thể thất bại. Một ounce phòng ngừa ...
Joe

Câu trả lời:


21

Điều đó phụ thuộc vào tốc độ trả về của tập lệnh perl. Nếu nó trả về nhanh, bạn có thể muốn chèn một khoảng dừng nhỏ giữa các lần thực thi để tránh tải CPU, ví dụ:

while true
do
  /someperlscript.pl
  sleep 1
done

Điều này cũng sẽ ngăn chặn một con lợn CPU nếu không tìm thấy tập lệnh hoặc gặp sự cố ngay lập tức.

Vòng lặp cũng có thể được thực hiện tốt hơn trong chính tập lệnh perl để tránh những vấn đề này.

Chỉnh sửa:

Như bạn đã viết, mục đích duy nhất của vòng lặp là khởi động lại tập lệnh perl nếu nó gặp sự cố, một cách tiếp cận tốt hơn sẽ là triển khai nó như một dịch vụ được giám sát nhưng cách chính xác để thực hiện nó phụ thuộc vào hệ điều hành. Ví dụ: Solaris smf, Linux systemd hoặc cron dựa trên cron.


10
Bạn có thể làm while sleep 1; do ...để lưu cuộc gọi thực sự.
Raphael AhDR

4
@RaphaelAhrens Thật vậy, mặc dù điều đó sẽ thay đổi một chút hành vi ban đầu. Người thay thế until ! sleep 1; do ...; donevẫn sẽ lưu cuộc gọi dựng sẵn đó trong khi bắt đầu tập lệnh ngay lập tức.
jlliagre

4
Hoàn toàn, đồng ý rằng nên tránh một vòng lặp nóng với một cuộc gọi ngủ bên trong vòng lặp nếu bạn sẽ làm điều này. Cũng đồng ý rằng một tập lệnh hướng sự kiện (inotify, et al) sẽ là một giải pháp tốt hơn. Nhưng sử dụng vòng lặp while thực chất không phải là xấu, trừ khi nó vô hạn và nóng. Tôi nghĩ rằng vấn đề quan trọng hơn có lẽ là giải quyết tại sao kịch bản perl thất bại và cần phải được khởi động lại.
Craig

2
Tốt hơn : sleep 1& /someperlscript.pl; wait.
dùng23013

2
@EliahKagan Phát hiện tốt. TBH, tôi không bao giờ sử dụng untilhướng dẫn, tôi đã nhầm lẫn nó với một do/untilvòng lặp giả thuyết không tồn tại trong vỏ.
jlliagre

13

Các câu trả lời khác, về việc sử dụng inotify , là chính xác, nhưng không phải là một câu trả lời cho câu hỏi này.

Một giám sát viên quá trình, chẳng hạn như supervisord,upstart hoặc runit, được thiết kế cho chính xác vấn đề xem và khởi động lại dịch vụ nếu nó gặp sự cố.

Bản phân phối của bạn có thể đi kèm với một trình giám sát quy trình được tích hợp.


11

while truecũng tốt khi xây dựng "vòng lặp mãi mãi" với mục đích chung. Như các câu trả lời khác nói, phần thân của vòng lặp không nên trống hoặc trở nên trống rỗng nhờ lệnh bên trong vòng lặp không hoạt động.

Nếu bạn đang sử dụng Linux, bạn có thể muốn sử dụng một lệnh như inotifywait, điều này làm cho whilevòng lặp đơn giản hơn nhiều:

while inotifywait -qqe modify "$DIRECTORY"
do
    process_the_directory "$DIRECTORY"
done

Ở đây, inotifywaitlệnh sẽ ngồi và chờ một sự kiện hệ thống tập tin xảy ra (trong ví dụ này, khi các tệp trong thư mục được ghi vào). Tại thời điểm đó, nó thoát thành công và phần thân của vòng lặp thực thi. Sau đó nó quay lại chờ đợi một lần nữa. Bởi vì inotifywaitlệnh chờ một cái gì đó xảy ra trong thư mục, nó hiệu quả hơn nhiều so với việc liên tục thăm dò thư mục.


`(apt-get hoặc yum) cài đặt inotify-tools` +1 tuyệt vời!
JJoao

4

Di chuyển while 1 vào tập lệnh perl (Theo đề xuất @roaima)

#!/usr/bin/perl

 use Linux::Inotify2;

 my $inotify = new Linux::Inotify2 or die "unable to inotify: $!";

 $inotify->watch ("Dir", IN_MODIFY, ## or in_{acess,create,open, etc...}
   sub { my $e = shift;
     my $name = $e->fullname;
     ## whatever 
     print "$name was modified\n" if $e->IN_MODIFY;
  });

 1 while $inotify->poll;

1
Điều này không giải quyết yêu cầu khởi động lại nếu tập lệnh gặp sự cố hoặc bị giết vì một số lý do.
jlliagre 30/03/2015

@jlliagre, cảm ơn vì nhận xét. Trong câu trả lời, tôi không thấy một đặc điểm kỹ thuật rõ ràng cho hành vi dự định sau một vụ tai nạn hoặc giết chết. Đây rõ ràng là một câu hỏi thú vị và có liên quan. Hiện tại tôi sẽ giữ cho nó đơn giản (nếu kịch bản chết hoặc bị giết, hãy chết đi :)
JJoao 30/03/2015

@jlliagre Đó là những ngoại lệ dành cho. Nhưng Perl có thể không phải là lựa chọn thích hợp nhất trong trường hợp như vậy vì nó không hỗ trợ cơ chế ngoại lệ. Chỉ là một phỏng đoán. Sẽ là tốt nhất để chuyển tập lệnh sang một ngôn ngữ hỗ trợ các ngoại lệ. Tất cả phụ thuộc vào mức độ lớn của công việc porting, tất nhiên.

@Nasha, Trong Perl, với bộ xử lý tín hiệu, biến lỗi, Tyr :: Tiny, v.v., chúng tôi có thể làm điều đó! ... nhưng - Tôi ghét việc xử lý ngoại lệ và khôi phục lỗi: chúng không bao giờ hoàn tất ...
JJoao 30/03/2015

1
@JJoao Tôi đồng ý với bạn về sự thật rằng việc khôi phục lỗi hiếm khi hoàn tất. Tất nhiên là tùy thuộc vào nhà phát triển để bao gồm tất cả các trường hợp có thể. Vì vậy, nó là với các PLC trong thế giới công nghiệp do đó nó hoàn toàn có thể xảy ra ;-).

3

Khi tập lệnh perl của bạn được dự định để tiếp tục chạy mọi lúc, tại sao lại sử dụng trong khi xây dựng? Khi perl thất bại trước một số vấn đề nghiêm trọng, tập lệnh perl mới bắt đầu trong khi có thể gặp sự cố nghiêm trọng. Một lần nữa và một lần nữa và trên.
nếu bạn thực sự muốn perl của bạn bắt đầu lại, hãy xem xét crontab và một tập lệnh đầu tiên kiểm tra các phiên bản đang chạy. Bằng cách này, kịch bản của bạn thậm chí sẽ được bắt đầu sau khi khởi động lại.


2

Nói chung, không có vấn đề gì while truekhi sử dụng vì đây là một thử nghiệm nhỏ chỉ được thực hiện sau khi tập lệnh perl bị chấm dứt. Hãy nhớ rằng tùy thuộc vào biến thể Linux / Unix mà bạn đang sử dụng, tập lệnh có thể bị chấm dứt khi đăng xuất. Trong trường hợp như vậy, hãy xem xét sử dụng vòng lặp trong một tập lệnh và gọi nó với nohupvà đặt nó trong nền tức lànohup myscript &

Nếu tập lệnh perl chấm dứt quá thường xuyên và gây ra tải CPU thì tải này chịu trách nhiệm với tập lệnh perl chứ không phải tập lệnh while true.

Xem thêm man nohupđể biết chi tiết.


Vấn đề duy nhất là tiềm năng cho một vòng lặp nóng thúc đẩy việc sử dụng CPU. Vì vậy, tôi hoàn toàn đồng ý với việc đặt một cuộc gọi ngủ bên trong vòng lặp để chế ngự nó.
Craig

2

Nếu bạn muốn quản lý một quy trình, bạn có thể muốn xem xét một trình quản lý quy trình để thực hiện.

Với nhiều hệ thống gần đây, bạn có thể sử dụng systemd để giám sát các quy trình của mình (đây là một trong những lợi ích của systemd so với các tập lệnh init cổ điển). Nếu bản phân phối của bạn không sử dụng systemd, bạn có thể sử dụng daemontools hoặc monit .


monlà một giải pháp thay thế đơn giản, nếu sự ồn ào của monit và systemd làm phiền bạn nhiều như họ.
Anko

2

Các whilevòng lặp thực sự không phải là một ý tưởng tốt. Không có lối thoát ở đó - nó chỉ chạy mãi mãi - tĩnh . Bất kỳ số lượng điều có thể thay đổi trong môi trường và nó sẽ không bị ảnh hưởng - và điều này có thể là xấu.

Ví dụ, nếu shell thực thi chịu trách nhiệm cho whilevòng lặp đó được nâng cấp, kernel sẽ không thể giải phóng không gian đĩa cho phiên bản cũ cho đến khi script đó thoát ra vì nó cần duy trì bộ mô tả miễn là nó chạy. Và điều đó đúng với mọi tệp mà shell có thể đã mở vì bất kỳ lý do gì để chạy vòng lặp - tất cả chúng sẽ được giữ bởi whilevòng lặp này miễn là nó chạy - mãi mãi.

Và nếu, trời cấm, có một rò rỉ bộ nhớ ở bất cứ đâu trong vỏ chạy vòng lặp đó - thậm chí là nhiều phút nhất - nó sẽ chỉ tiếp tục rò rỉ - một cách tĩnh . Nó sẽ xây dựng không được kiểm soát và cách duy nhất là giết chết nó một cách mạnh mẽ, sau đó bắt đầu lại chỉ để làm điều tương tự sau đó.

Đây thực sự không phải là cách bạn nên thiết lập một quy trình nền - ít nhất, theo ý kiến ​​của tôi. Thay vào đó, như tôi nghĩ, nên có một điểm đặt lại - làm mới tập lệnh và trạng thái của nó. Cách dễ nhất để làm điều này là với exec. Bạn có thể thay thế quy trình hiện tại bằng quy trình mới - trong khi vẫn duy trì cùng một PID - nhưng vẫn chạy quy trình mới .

Ví dụ: nếu perltập lệnh của bạn sẽ trả về đúng sau khi xử lý thành công sửa đổi tệp trong một số thư mục được theo dõi:

#!/bin/sh
trap 'rm -rf -- "${ldir%%*.}"' 0 INT
_exec() case    $#      in
        (0)     exec env -  "PID=$$" "ldir=${TMPDIR:-/tmp}/." \
                            "$0" "$@";;
        (*)     export "$@" "boff=0" "lmt=30"
                exec        "$0" "$@";;
        esac
[ "$PID" = "$$" ] || _exec
[ -w "$ldir" ]  &&
case    $ldir   in
(*.)    until   mkdir -- "$ldir"
        do      :& ldir=$ldir$$$!
        done    2>/dev/null
;;
(*)     until   /someperlscript.pl ||
                [ "$((boff+=1))"  -ge "$lmt" ]
        do      [ -d "$ldir" ]     &&
                sleep "$boff"      || ! break
        done
;;esac  &&      _exec ldir PID

...hoặc điều tương tự. Một cái gì đó cho phép các máy móc cơ bản đằng sau vòng lặp làm mới mỗi lần.


1

Tôi nêu lên một vài trong số những câu trả lời này, nhưng theo bản năng tôi có những cảm xúc nồng nhiệt nhất cho câu trả lời của @ WalterA. Vâng, tôi đã làm cho đến khi tôi tạo ra câu trả lời của riêng mình ...

Cá nhân, tôi có xu hướng cập nhật tập lệnh Perl để nó viết một mục nhật ký mô tả về sự thất bại và gửi thông báo cho quản trị viên.

Nếu tập lệnh Perl có nghĩa là tiếp tục chạy, chờ đợi các sự kiện thay đổi từ hệ thống tệp, tại sao nó bị lỗi?

Nếu nó không thất bại, tại sao bạn lại quan tâm đến việc gói nó trong một kịch bản để khởi động lại nó vô tận?

Nếu có vấn đề về cấu hình hoặc một số phụ thuộc bị hỏng khiến tập lệnh Perl bị hủy bỏ, chỉ cần khởi động lại nó nhiều lần sẽ không khiến nó đột nhiên bắt đầu hoạt động.

Bạn biết định nghĩa điên rồ, phải không? (làm điều tương tự lặp đi lặp lại, mong đợi một kết quả khác). Chỉ cần nói. ;-)


haha- tôi biết, tôi biết nhưng bạn biết đấy - thế giới thực hút. Dù sao đi nữa - lý do là nó xử lý các tệp và một số tệp thậm chí có thể không được truyền chính xác. Chúng tôi chưa biết nhiều lý do có thể thất bại. Tôi thấy quan điểm của bạn về việc ghi lại lỗi, nhưng tôi vẫn sẽ cần sao lưu để xử lý tệp tiếp theo thông qua một cái gì đó như giám sát
Abhinav Gujjar

Nếu bạn có thể bắt ngoại lệ, bạn có thể đăng nhập chúng sau đó tiếp tục xử lý và thậm chí quay lại và thỉnh thoảng thử lại các tệp không thành công? Có thể tệp không thành công đã bị khóa bởi người dùng hoặc quá trình khác khi bạn cố xử lý tệp, v.v.
Craig
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.