[英]ZeroMQ send/receive on PHP works if script launched from CLI/shell but not from APACHE/web request
我正在為 PHP 測試 ZeroMQ。 我的目標是將消息發送到 Python 腳本。 如果我從 PHP cli 啟動傳輸腳本,一切正常
php /path/to/myscript.php
如果它是 web 請求,則會失敗。 我已經嘗試從上面的 PHP cli 執行服務器腳本(這似乎是更合乎邏輯的方式)並使用 web 請求。
我通過 PECL 安裝安裝了 PHP 7.2 和 ZeroMQ 1.1.3 的 Centos 7 服務器。
我什至嘗試在客戶端腳本中使用 shell_exec/exec 啟動上述命令,但它不起作用。 連接工作正常,但它不發送也不接收。
客戶端代碼:
$context = new ZMQContext();
// Socket to talk to server
echo "Connecting to hello world server...\n";
$requester = new ZMQSocket($context, ZMQ::SOCKET_REQ);
$currentObject = $requester->connect("tcp://localhost:5555");
for ($request_nbr = 0; $request_nbr != 10; $request_nbr++) {
printf ("Sending request %d...\n", $request_nbr);
$risSend = $requester->send("Hello", ZMQ::MODE_NOBLOCK);
print_r($risSend);
$reply = $requester->recv();
printf ("Received reply %d: [%s]\n", $request_nbr, $reply);
}
服務器代碼:
$context = new ZMQContext(1);
// Socket to talk to clients
$responder = new ZMQSocket($context, ZMQ::SOCKET_REP);
$responder->bind("tcp://*:5555");
while (true) {
// Wait for next request from client
$request = $responder->recv();
printf ("Received request: [%s]\n", $request);
// Send reply back to client
$responder->send("World");
}
瀏覽器卡住了,沒有任何錯誤。 即使使用超時,它也會達到限制並退出,但我無法收到任何錯誤消息。
觀察:瀏覽器卡住了,沒有任何錯誤。
這是非常合法的 state。 為了讓它發生,“錯過”第一個REQ
端已經發送的請求的到來就足夠了,並且由於很高興確實依賴於分布式有限狀態自動機,我們陷入了無法挽救的死鎖,其中REQ
端等待一個永遠不會到達的答案(參見下一個),而REP
端等待一個永遠不會到達的請求(參見REQ
端已經在等待),這樣的 state 永遠保持不變。
最好的下一步:
如果從未使用過 ZeroMQ,
或者從來沒有遇到過零之禪的藝術概念,
在深入了解更多細節之前,您可能會喜歡先看看“ZeroMQ原則在不到 5 秒內”
開始
具有無條件工作的原型 - 一對PUSH / PULL
單純形通道,不需要REQ-REP-REQ-REP-REQ-REP-...-{deadlock}
的 dFSA 兩步...不可避免的終端 state,關於哪一個永遠不確定它何時會發生,但它會......在以后的某個時間:o)
下一個,
可以使用zmq_setsockopt( ZMQ_IMMEDIATE, 1 )
來增加消息流的穩健性,以避免將消息移動到對等點之間的不完整連接上。
總是
更喜歡.recv()
方法的非阻塞 forms,最好使用.poll()
方法對消息存在進行預測試。 Poller
類雖然在許多語言綁定中都可用,但並不總是像直接在Socket
實例上使用顯式.poll()
方法那樣方便和靈活。
也可以隨意閱讀更多關於微調 ZeroMQ 工具和零之藝術的其他含義的信息。
服務器端模型:作為 { PASS | FAIL } - .send()---.recv()
鏈有效嗎?
<?php /* Create new PUSH-ing end */
$aCTX = new ZMQContext();
try { /* Try: things may turn wreck havoc */
$PUSHer = $aCTX->getSocket(, ZMQ::SOCKET_PUSH );
echo "POSACK'd: .getSocket() was made\n";
}
catch ( ZMQSocketException $e ){
echo " NACK'd: I told you ...\n"; /* Handle with care ... */
if ( $e->getCode() === ZMQ::ERR_... ) {
echo " - Got ERR_..., read ZeroMQ API documentation for details\n";
} else {
die( " - Get ERR: " . $e->getMessage() );
}
}
try { /* Try: things may turn wreck havoc */
$PUSHer->bind( "tcp://A.B.C.D:NNN" ); /* IP address to .connect() */
echo "POSACK'd: .bind() was made\n";
}
catch ( ZMQSocketException $e ){
echo " NACK'd: I told you ...\n"; /* Handle with care ... */
if ( $e->getCode() === ZMQ::ERR_... ) {
echo " - Got ERR_..., read ZeroMQ API documentation for details\n";
} else {
die( " - Get ERR: " . $e->getMessage() );
}
}
$retries = 1234567;
do { /* Start a loop */
try { /* Try: to PUSH.send() */
echo "Trying to send a message #" . ( 1234568 - $retries ) . "\n";
$PUSHer->send( "This is a message", ZMQ::MODE_DONTWAIT );
echo "POSACK'd: PUSHer.send() was made\n";
}
} catch ( ZMQSocketException $e ) {
echo " NACK'd: I told you ...\n"; /* Handle with care ... */
if ( $e->getCode() === ZMQ::ERR_... ) {
echo " - Got ERR_..., read ZeroMQ API documentation for details\n";
} else { /* For all ERR_... states */
die( " - Got ERR_...: " . $e->getMessage() );
}
}
/* --------------------------------------------------------------------
Here one may add an attempt to .recv( $PULLer, ZMQ::MODE_DONTWAIT );
and test for a non-empty string returned
-------------------------------------------------------------------- */
usleep( 1 ); /* Sleep a bit between operations */
} while ( --$retries );
?>
客戶端模型,用於測試 PUSH-er 生命和.send()
-s
import time, datetime, zmq; print( "Thissssss Sssssssssssssssssssssssssssssssssssssssnake uses ZeroMQ ver:{0:}".format( zmq.__version__ ) )
aCtx = zmq.Context()
aPull= aCtx.Socket( zmq.PULL )
aPull.setsockopt( zmq.LINGER, 0 ) # always ... be explicit
aPull_address2c = "tcp://A.B.C.D:NNN"
M0 = "{0:} try a .connect( {1:} ), if it gets to PUSH-er side"
M1 = "{0:} try a .recv(), if it gets any message"
M2 = "{0:} got a .recv()->[[[ {1:} ]]]"
M3 = "{0:} EXC'd will gracefully release resources and terminate..."
M4 = "{0:} did"
try:
print( M0.format( datetime.datetime.isoformat( datetime.datetime.now() ),
aPull_address2c
)
)
aPull.connect( aPull_address2c );
while True:
print( M1.format( datetime.datetime.isoformat( datetime.datetime.now() ) )
m = aPull.recv( zmq.NOBLOCK ) # always ... avoid blocking waits
if ( len( m ) > 0 ):
print( M2.format( datetime.datetime.isoformat( datetime.datetime.now() ),
str( m ) # always ... fused to str()
)
)
time.sleep( 5 )
else:
time.sleep( 1 )
pass
################################################################
# Here one may add an attempt to aPush.send( M4, zmq.NOBLOCK )
# and test if the reverse path aPush->$PULLer goes well
################################################################
except:
print( M3.format( datetime.datetime.isoformat( datetime.datetime.now() ) )
finally:
aPull.close() # always ... be explicit
aCtx.term() # always ... be explicit
print( M4.format( datetime.datetime.isoformat( datetime.datetime.now() ) )
好的,我終於找到了解決方案。 感謝@user3666197,我設法得到了一個錯誤。 這是一個“權限被拒絕”錯誤。
使用 CentOS(可能還有所有帶有 SELinux 的 linux 系統),您必須為 ZMQ用於 webserver的端口添加一條規則。
例子:
semanage port -a -t http_port_t -p tcp 5555
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.