Chạy tập lệnh php dưới dạng tiến trình daemon


154

Tôi cần chạy tập lệnh php dưới dạng tiến trình daemon (chờ hướng dẫn và thực hiện công cụ). công việc cron sẽ không làm điều đó cho tôi bởi vì các hành động cần phải được thực hiện ngay khi hướng dẫn đến. Tôi biết PHP không thực sự là lựa chọn tốt nhất cho các tiến trình daemon do vấn đề quản lý bộ nhớ, nhưng vì nhiều lý do tôi phải sử dụng PHP trong trường hợp này. Tôi đã bắt gặp một công cụ của libslack có tên Daemon ( http://libslack.org/daemon ) nó dường như giúp tôi quản lý các quy trình daemon, nhưng không có bất kỳ cập nhật nào trong 5 năm qua, vì vậy tôi tự hỏi nếu bạn biết một số lựa chọn thay thế khác phù hợp cho trường hợp của tôi. Bất kỳ thông tin sẽ được thực sự đánh giá cao.


2
vui lòng kiểm tra câu trả lời của tôi cảm ơn
Henrik P. H ship


1
Tôi đã xem qua bài đăng này gonzalo123.com/2010/05/23/ mà tôi tin là vừa thư giãn vừa ổn định.
Teson

Điều này rất dễ thực hiện với systemd
LeonanCarvalho

Câu trả lời:


167

Bạn có thể bắt đầu tập lệnh php của mình từ dòng lệnh (tức là bash) bằng cách sử dụng

nohup php myscript.php &

các &đặt quá trình của bạn ở chế độ nền.

Chỉnh sửa:
Có, có một số nhược điểm, nhưng không thể kiểm soát? Thật tồi tệ.
Một đơn giản kill processidsẽ ngăn chặn nó. Và nó vẫn là giải pháp tốt nhất và đơn giản nhất.


Nếu thiết bị đầu cuối tồn tại, quá trình sẽ KHÔNG thoát. Đó là lý do tại sao lệnh "nohup" ở đó. Tôi đã sử dụng một tập lệnh PHP làm daemon trên tất cả các máy chủ như thế này trong nhiều năm nay. Có thể có giải pháp tốt hơn, nhưng đây là giải pháp nhanh nhất.
CDR

27
Điều này sẽ không khởi động lại daemon nếu nó thất bại, và không có cách nào dễ dàng để quản lý daemon cả.
Phil Wallach

6
Tôi đồng ý với những gì đã nói ở đây-- đây là một giải pháp tồi. Bạn nên tạo một tập lệnh init vì một vài lý do: 1) Tập lệnh init được khởi chạy tự động khi khởi động 2) Bạn có thể quản lý trình nền bằng các lệnh start / stop / restart. Dưới đây là một ví dụ từ Servfault: serverfault.com/questions/229759/
Kẻ

1
này các bạn ... có vẻ như đối với tôi nohup&cũng làm điều tương tự: tách rời quá trình khởi động khỏi ý định hiện tại của vỏ. Tại sao tôi cần cả hai? Tôi có thể không chỉ làm php myscript.php &hay nohup myscript.php?? Cảm ơn
nourdine 28/03 '

1
Nếu tập lệnh ghi vào thiết bị xuất chuẩn (thông qua echo hoặc var_dump) thì bạn có thể nắm bắt thông tin này bằng tệp nhật ký như sau:nohup php myscript.php > myscript.log &
Mischa

166

Một lựa chọn khác là sử dụng Upstart . Ban đầu nó được phát triển cho Ubuntu (và được đóng gói theo mặc định), nhưng được dự định phù hợp với tất cả các bản phát hành Linux.

Cách tiếp cận này tương tự như Giám sátdaemontools , ở chỗ nó tự động khởi động trình nền khi khởi động hệ thống và trả lời khi hoàn thành tập lệnh.

Cách thiết lập:

Tạo một tập lệnh mới tại /etc/init/myphpworker.conf. Đây là một ví dụ:

# Info
description "My PHP Worker"
author      "Jonathan"

# Events
start on startup
stop on shutdown

# Automatically respawn
respawn
respawn limit 20 5

# Run the script!
# Note, in this example, if your PHP script returns
# the string "ERROR", the daemon will stop itself.
script
    [ $(exec /usr/bin/php -f /path/to/your/script.php) = 'ERROR' ] && ( stop; exit 1; )
end script

Bắt đầu và dừng trình nền của bạn:

sudo service myphpworker start
sudo service myphpworker stop

Kiểm tra xem daemon của bạn đang chạy:

sudo service myphpworker status

Cảm ơn

Xin chân thành cảm ơn Kevin van Zonneveld , nơi tôi đã học được kỹ thuật này.


2
yêu cái này Chỉ cần tự hỏi, có thể có nhiều công nhân đồng thời? Tôi chỉ có một vấn đề là một công nhân không còn đủ nữa.
Manuel

1
điều này sẽ được tự động chạy khi khởi động hệ thống?
slier

2
Sudo "dịch vụ myphpworker start" không hoạt động với tôi. Tôi đã sử dụng "sudo start myphpworker" và nó hoạt động hoàn hảo
Matt Sich

3
@Pradeepta Đó là do có lỗi trong bài đăng - Tôi không chắc chắn chính xác điều gì (và chưa được thử nghiệm này), nhưng tôi nghĩ rằng sudo service myphpworker start/stop/statuschỉ hoạt động với các dịch vụ /etc/init.dkhông phải là dịch vụ mới. @ matt-sich dường như đã phát hiện ra cú pháp chính xác. Một tùy chọn khác là sử dụng Gearman hoặc Resque, cho phép xử lý nền & khử nhiễu.
ckm

3
Bản thân Ubuntu đang chuyển sang sử dụng systemd thay vì mới bắt đầu: zdnet.com/article/after-linux-civil-war-ubfox-to-adopt-systemd
Kzqai

72

Với systemd mới, bạn có thể tạo một dịch vụ.

Bạn phải tạo một tập tin hoặc một liên kết tượng trưng trong /etc/systemd/system/, ví dụ. myphpdaemon.service và đặt nội dung như thế này, myphpdaemon sẽ là tên của dịch vụ:

[Unit]
Description=My PHP Daemon Service
#May your script needs MySQL or other services to run, eg. MySQL Memcached
Requires=mysqld.service memcached.service 
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/myphpdaemon.pid
ExecStart=/usr/bin/php -f /srv/www/myphpdaemon.php arg1 arg2> /dev/null 2>/dev/null
#ExecStop=/bin/kill -HUP $MAINPID #It's the default you can change whats happens on stop command
#ExecReload=/bin/kill -HUP $MAINPID
KillMode=process

Restart=on-failure
RestartSec=42s

StandardOutput=null #If you don't want to make toms of logs you can set it null if you sent a file or some other options it will send all php output to this one.
StandardError=/var/log/myphpdaemon.log
[Install]
WantedBy=default.target

Bạn sẽ có thể bắt đầu, nhận trạng thái, khởi động lại và dừng dịch vụ bằng lệnh

systemctl <start|status|restart|stop|enable> myphpdaemon

Kịch bản PHP nên có một loại "vòng lặp" để tiếp tục chạy.

<?php
gc_enable();//
while (!connection_aborted() || PHP_SAPI == "cli") {

  //Code Logic

  //sleep and usleep could be useful
    if (PHP_SAPI == "cli") {
        if (rand(5, 100) % 5 == 0) {
            gc_collect_cycles(); //Forces collection of any existing garbage cycles
        }
    }
}

Ví dụ làm việc:

[Unit]
Description=PHP APP Sync Service
Requires=mysqld.service memcached.service
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/php_app_sync.pid
ExecStart=/bin/sh -c '/usr/bin/php -f /var/www/app/private/server/cron/app_sync.php  2>&1 > /var/log/app_sync.log'
KillMode=mixed

Restart=on-failure
RestartSec=42s

[Install]
WantedBy=default.target

Nếu thường trình PHP của bạn nên được thực thi một lần trong một chu kỳ (như một digest), bạn có thể nên sử dụng tập lệnh shell hoặc bash để được gọi vào tệp dịch vụ systemd thay vì trực tiếp PHP, ví dụ:

#!/usr/bin/env bash
script_path="/app/services/"

while [ : ]
do
#    clear
    php -f "$script_path"${1}".php" fixedparameter ${2}  > /dev/null 2>/dev/null
    sleep 1
done

Nếu bạn chọn các tùy chọn mà bạn nên thay đổi KillMode đến mixedcác quy trình, bash (chính) và PHP (trẻ em) bị giết.

ExecStart=/app/phpservice/runner.sh phpfile parameter  > /dev/null 2>/dev/null
KillMode=process

This method also is effective if you're facing a memory leak.

Lưu ý: Mỗi khi bạn thay đổi "myphpdaemon.service", bạn phải chạy `systemctl daemon-reload ', nhưng lo lắng nếu bạn không làm, nó sẽ được cảnh báo khi cần thiết.


7
Câu trả lời không nản lòng. Bạn có +1 của tôi.
Gergely Lukacsy

2
Tuyệt vời. Ước gì chúng ta cũng có thể trả lời câu hỏi vì điều này không nên bị chôn vùi trên trang này.
Justin

1
Bạn nên kiểm tra systemctl status <your_service_name> -lđầu ra, nó sẽ cho bạn manh mối những gì đang xảy ra.
LeonanCarvalho

1
@LeandroTupone MySQL và Memcached là một minh chứng về cách sử dụng các phụ thuộc dịch vụ không cần thiết.
LeonanCarvalho

3
Điều này thực sự sẽ thay thế câu trả lời được chấp nhận như bây giờ là năm 2019.
gia vị

47

Nếu bạn có thể - lấy một bản sao của Lập trình nâng cao trong Môi trường UNIX . Toàn bộ chương 13 được dành cho lập trình daemon. Các ví dụ có trong C, nhưng tất cả các hàm bạn cần đều có các hàm bao trong PHP (về cơ bản là các phần mở rộng pcntlposix ).

Trong một vài từ - viết một daemon (điều này chỉ có thể nhìn thấy trên hệ điều hành dựa trên * nix - Windows sử dụng các dịch vụ) là như thế này:

  1. Gọi umask(0)để ngăn chặn vấn đề cho phép.
  2. fork() và có lối ra cha mẹ.
  3. Gọi setsid().
  4. Thiết lập xử lý tín hiệu của SIGHUP(thường được bỏ qua hoặc sử dụng để báo hiệu trình nền để tải lại cấu hình của nó) và SIGTERM(để báo cho quá trình thoát ra một cách duyên dáng).
  5. fork() một lần nữa và có lối ra cha mẹ.
  6. Thay đổi thư mục làm việc hiện tại với chdir().
  7. fclose() stdin, stdoutstderrvà không viết thư cho họ. Cách chính xác là chuyển hướng chúng đến một /dev/nullhoặc một tệp, nhưng tôi không thể tìm ra cách để làm điều đó trong PHP. Có thể khi bạn khởi chạy trình nền để chuyển hướng chúng bằng cách sử dụng trình bao (bạn sẽ phải tự tìm hiểu cách thực hiện điều đó, tôi không biết :).
  8. Làm công việc của bạn!

Ngoài ra, vì bạn đang sử dụng PHP, hãy cẩn thận với các tham chiếu theo chu kỳ, vì trình thu gom rác PHP, trước PHP 5.3, không có cách nào để thu thập các tham chiếu đó và quá trình sẽ bị rò rỉ bộ nhớ, cho đến khi cuối cùng nó bị hỏng.


1
Cảm ơn bạn về thông tin. Có vẻ như chương trình daemon của libslack thực hiện khá nhiều công việc chuẩn bị như bạn đã đề cập. Tôi nghĩ bây giờ tôi sẽ gắn bó với nó cho đến khi tôi tìm thấy những lựa chọn thay thế tốt khác.
Beier

1
Tìm thấy bài đăng này, mã dự kiến ​​để sao chép và dán vào một ứng dụng cũ xảo quyệt mà không đóng stdin, v.v., đã thất vọng. : p
ThiefMaster

1
Tại sao (5) ngã ba () lại?
TheFox

Làm việc cho tôi - công việc tuyệt vời!
Gautam Sharma

Đối với những độc giả tương lai hỏi tại sao phải rẽ nhánh hai lần: stackoverflow.com/questions/881388/ - - TL; DR: Ngăn chặn zombie.
Ghedipunk

24

Tôi chạy một số lượng lớn trình nền PHP.

Tôi đồng ý với bạn rằng PHP không phải là ngôn ngữ tốt nhất (hoặc thậm chí là tốt) để làm điều này, nhưng trình nền chia sẻ mã với các thành phần đối diện web vì vậy nói chung nó là một giải pháp tốt cho chúng tôi.

Chúng tôi sử dụng daemontools cho việc này. Đó là thông minh, sạch sẽ và đáng tin cậy. Trong thực tế, chúng tôi sử dụng nó để chạy tất cả các trình tiện ích của chúng tôi.

Bạn có thể kiểm tra điều này tại http://cr.yp.to/daemontools.html .

EDIT: Một danh sách nhanh các tính năng.

  • Tự động khởi động trình nền khi khởi động lại
  • Tự động khởi động lại dameon khi thất bại
  • Ghi nhật ký được xử lý cho bạn, bao gồm cuộn qua và cắt tỉa
  • Giao diện quản lý: 'svc' và 'svstat'
  • UNIX thân thiện (có lẽ không phải là một điểm cộng cho tất cả mọi người)

Cũng có thể cài đặt từ kho lưu trữ, ví dụ như trong apt!
Kzqai

14

Bạn có thể

  1. Sử dụng nohupnhư Henrik đề nghị.
  2. Sử dụng screenvà chạy chương trình PHP của bạn như một quy trình thông thường bên trong đó. Điều này cho phép bạn kiểm soát nhiều hơn so với sử dụng nohup.
  3. Sử dụng trình tiện ích như http://supervisord.org/ (nó được viết bằng Python nhưng có thể trình bày bất kỳ chương trình dòng lệnh nào và cung cấp cho bạn một điều khiển từ xa để quản lý nó).
  4. Viết trình bao bọc daemonise của riêng bạn như Emil đề xuất nhưng nó quá mức IMO.

Tôi muốn giới thiệu phương pháp đơn giản nhất (theo ý kiến ​​của tôi) và sau đó nếu bạn muốn có nhiều tính năng hoặc chức năng hơn, hãy chuyển sang các phương pháp phức tạp hơn.


Bạn có thể cung cấp một cấu hình giám sát tương tự?
Alix Axel

11

Có nhiều hơn một cách để giải quyết vấn đề này.

Tôi không biết chi tiết cụ thể nhưng có lẽ có một cách khác để kích hoạt quy trình PHP. Chẳng hạn, nếu bạn cần mã để chạy dựa trên các sự kiện trong cơ sở dữ liệu SQL, bạn có thể thiết lập một trình kích hoạt để thực thi tập lệnh của mình. Điều này thực sự dễ thực hiện trong PostgreSQL: http://www.postgresql.org/docs/civerse/static/external-pl.html .

Thành thật tôi nghĩ rằng đặt cược tốt nhất của bạn là tạo ra một quy trình Damon bằng cách sử dụng nohup. nohup cho phép lệnh tiếp tục thực thi ngay cả khi người dùng đã đăng xuất:

nohup php myscript.php &

Tuy nhiên có một vấn đề rất nghiêm trọng. Như bạn đã nói trình quản lý bộ nhớ của PHP là rác hoàn chỉnh, nó được xây dựng với giả định rằng một tập lệnh chỉ thực thi trong vài giây và sau đó tồn tại. Tập lệnh PHP của bạn sẽ bắt đầu sử dụng bộ nhớ GIGABYTES chỉ sau vài ngày. Bạn PHẢI tạo một tập lệnh cron chạy cứ sau 12 hoặc có thể 24 giờ để giết và tái tạo tập lệnh php của bạn như thế này:

killall -3 php
nohup php myscript.php &

Nhưng nếu kịch bản ở giữa một công việc thì sao? Giết -3 là một ngắt, nó giống như thực hiện ctrl + c trên CLI. Tập lệnh php của bạn có thể bắt được ngắt này và thoát một cách duyên dáng bằng thư viện pcntl PHP: http://php.oregonstate.edu/manual/en/feft.pcntl-signal.php

Đây là một ví dụ:

function clean_up() {
  GLOBAL $lock;
  mysql_close();
  fclose($lock)
  exit();
}
pcntl_signal(SIGINT, 'clean_up');

Ý tưởng đằng sau khóa $ là tập lệnh PHP có thể mở tệp bằng fopen ("tệp", "w");. Chỉ một quy trình có thể có khóa ghi trên một tệp, vì vậy sử dụng quy trình này, bạn có thể đảm bảo rằng chỉ có một bản sao của tập lệnh PHP của bạn đang chạy.

Chúc may mắn!




3

Gần đây tôi có nhu cầu về một giải pháp đa nền tảng (Windows, Mac và Linux) cho vấn đề chạy các tập lệnh PHP dưới dạng trình nền. Tôi đã giải quyết vấn đề bằng cách viết giải pháp dựa trên C ++ của riêng mình và tạo nhị phân:

https://github.com/cubiclesoft/service-manager/

Hỗ trợ đầy đủ cho Linux (thông qua sysvinit), nhưng cũng có các dịch vụ Windows NT và Mac OSX launchd.

Nếu bạn chỉ cần Linux, thì một vài giải pháp khác được trình bày ở đây hoạt động đủ tốt và tùy thuộc vào hương vị. Ngoài ra còn có Upstart và systemd những ngày này, có những dự phòng cho các kịch bản sysvinit. Nhưng một nửa quan điểm của việc sử dụng PHP là bản chất đa nền tảng, do đó mã được viết bằng ngôn ngữ có cơ hội hoạt động khá tốt ở mọi nơi. Sự thiếu sót bắt đầu hiển thị khi một số khía cạnh cấp độ hệ điều hành bên ngoài nhất định xâm nhập vào bức tranh, chẳng hạn như các dịch vụ hệ thống, nhưng bạn sẽ gặp vấn đề đó với hầu hết các ngôn ngữ kịch bản.

Cố gắng bắt các tín hiệu như một người nào đó ở đây đề xuất trong vùng người dùng PHP không phải là một ý tưởng hay. Đọc tài liệu một pcntl_signal()cách cẩn thận và bạn sẽ nhanh chóng biết rằng PHP xử lý các tín hiệu bằng cách sử dụng một số phương thức khá khó chịu (cụ thể là 'đánh dấu') để nhai một loạt các chu kỳ cho một thứ mà các quy trình (ví dụ như tín hiệu) nhìn thấy. Xử lý tín hiệu trong PHP cũng chỉ có sẵn trên các nền tảng POSIX và hỗ trợ khác nhau dựa trên phiên bản PHP. Nó ban đầu nghe có vẻ là một giải pháp tốt nhưng nó không thực sự hữu ích.

PHP cũng đã trở nên tốt hơn về các vấn đề rò rỉ bộ nhớ khi thời gian tiến triển. Bạn vẫn phải cẩn thận (trình phân tích cú pháp DOM XML có xu hướng bị rò rỉ) nhưng tôi hiếm khi thấy các quy trình chạy trốn ngày nay và trình theo dõi lỗi PHP khá im lặng khi so sánh với những ngày qua.


1

Như những người khác đã đề cập, chạy PHP như một daemon khá dễ dàng và có thể được thực hiện bằng một dòng lệnh duy nhất. Nhưng vấn đề thực tế là giữ cho nó chạy và quản lý nó. Tôi đã có cùng một vấn đề cách đây khá lâu và mặc dù đã có rất nhiều giải pháp, hầu hết chúng đều có nhiều phụ thuộc hoặc khó sử dụng và không phù hợp với các cách sử dụng cơ bản. Tôi đã viết một tập lệnh shell có thể quản lý bất kỳ quy trình / ứng dụng nào bao gồm các tập lệnh cli PHP. Nó có thể được đặt làm cronjob để khởi động ứng dụng và sẽ chứa ứng dụng và quản lý nó. Nếu nó được thực thi lại, ví dụ thông qua cùng một cronjob, nó sẽ kiểm tra xem ứng dụng có chạy hay không, nếu nó chỉ đơn giản là thoát ra và để cho phiên bản trước đó tiếp tục quản lý ứng dụng.

Tôi đã tải nó lên github, cứ thoải mái sử dụng nó: https://github.com/sinasalek/ EASDeamonizer

EasyDeamonizer

Đơn giản chỉ cần xem qua ứng dụng của bạn (bắt đầu, khởi động lại, đăng nhập, theo dõi, v.v.). một tập lệnh chung để đảm bảo rằng ứng dụng của bạn vẫn chạy đúng. Cố tình nó sử dụng tên tiến trình. Tập tin pid / lock để ngăn chặn tất cả các tác dụng phụ của nó và giữ cho tập lệnh đơn giản và dễ hiểu nhất có thể, vì vậy nó luôn hoạt động ngay cả khi chính EasyDaemonizer được khởi động lại. Đặc trưng

  • Khởi động ứng dụng và tùy chọn độ trễ tùy chỉnh cho mỗi lần khởi động
  • Đảm bảo rằng chỉ có một phiên bản đang chạy
  • Theo dõi việc sử dụng CPU và tự động khởi động lại ứng dụng khi đạt đến ngưỡng xác định
  • Đặt EasyDeamonizer chạy qua cron để chạy lại nếu nó bị dừng vì bất kỳ lý do gì
  • Ghi nhật ký hoạt động của nó

1

Mở rộng câu trả lời của Emil Ivaov , Bạn có thể thực hiện các thao tác sau để đóng STDIN, STDOUT VÀ STDERROR trong php

if (!fclose(STDIN)) {
    exit("Could not close STDIN");
}

if (!fclose(STDOUT)) {
    exit("Could not close STDOUT");
}

if (!fclose(STDERR)) {
    exit("Could not close STDERR");
}

$STDIN = fopen('/dev/null', 'r');
$STDOUT = fopen('/dev/null', 'w');
$STDERR = fopen('/var/log/our_error.log', 'wb');

Về cơ bản, bạn đóng các luồng tiêu chuẩn để PHP không có chỗ để viết. Các fopencuộc gọi sau sẽ đặt IO chuẩn thành /dev/null.

Tôi đã đọc điều này từ cuốn sách của Rob Aley - PHP ngoài web


0

Tôi đã viết và triển khai một php-daemon đơn giản, mã đang trực tuyến tại đây

https://github.com/jmullee/PhpUnixDaemon

Các tính năng: thả đặc quyền, xử lý tín hiệu, đăng nhập

Tôi đã sử dụng nó trong một trình xử lý hàng đợi (trường hợp sử dụng: kích hoạt một hoạt động dài từ một trang web, mà không làm cho php tạo trang chờ, tức là khởi chạy một hoạt động không đồng bộ) https://github.com/jmullee/PhpIPCMessageQueue


0

bạn có thể kiểm tra pm2 ở đây, http://pm2.keymetrics.io/

tạo một tệp ssh, chẳng hạn như worker.sh đưa vào tập lệnh php của bạn mà bạn sẽ xử lý.

công nhân

php /path/myscript.php

daemon bắt đầu

pm2 start worker.sh

Chúc mừng, đó là nó.

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.