I am processing a huge xml document (which contains around a million entries) and subsequently importing a formatted version to the db using rabbitmq. Each time after publishing around 200,000 entries I receive a broken pipe error , and rabbitmq is unable to recover from it.
Notice Error: fwrite(): send of 2651 bytes failed with errno=11
Resource temporarily unavailable in
[/var/www/ribbon/app/Console/Command/lib/php_amqplib/amqp.inc, line
439]
Notice Error: fwrite(): send of 33 bytes failed with errno=104
Connection reset by peer in
[/var/www/ribbon/app/Console/Command/lib/php_amqplib/amqp.inc, line
439]
Notice Error: fwrite(): send of 19 bytes failed with errno=32 Broken
pipe in [/var/www/ribbon/app/Console/Command/lib/php_amqplib/amqp.inc,
line 439]
This subsequently causes a node down error and the process needs to be manually killed to recover from it.
These are my class methods:-
public function publishMessage($message) {
if (!isset($this->conn)) {
$this->_createNewConnectionAndChannel();
}
try {
$this->ch->basic_publish(
new AMQPMessage($message, array('content_type' => 'text/plain')),
$this->defaults['exchange']['name'],
$this->defaults['binding']['routing_key']
);
} catch (Exception $e) {
echo "Caught exception : " . $e->getMessage();
echo "Creating new connection.";
$this->_createNewConnectionAndChannel();
$this->publishMessage($message); // try again
}
}
protected function _createNewConnectionAndChannel() {
if (isset($this->conn)) {
$this->conn->close();
}
if(isset($this->ch)) {
$this->ch->close();
}
$this->conn = new AMQPConnection(
$this->defaults['connection']['host'],
$this->defaults['connection']['port'],
$this->defaults['connection']['user'],
$this->defaults['connection']['pass']
);
$this->ch = $this->conn->channel();
$this->ch->access_request($this->defaults['channel']['vhost'], false, false, true, true);
$this->ch->basic_qos(0 , 20 , 0); // fair dispatching
$this->ch->queue_declare(
$this->defaults['queue']['name'],
$this->defaults['queue']['passive'],
$this->defaults['queue']['durable'],
$this->defaults['queue']['exclusive'],
$this->defaults['queue']['auto_delete']
);
$this->ch->exchange_declare(
$this->defaults['exchange']['name'],
$this->defaults['exchange']['type'],
$this->defaults['exchange']['passive'],
$this->defaults['exchange']['durable'],
$this->defaults['exchange']['auto_delete']
);
$this->ch->queue_bind(
$this->defaults['queue']['name'],
$this->defaults['exchange']['name'],
$this->defaults['binding']['routing_key']
);
}
Any help will be appreciated.
Make sure you have added virtualhost access for your user on Rabbit MQ. I've created new user and forgot set access rights for "/" host which is used by default.
You can do that via management panel yourhost:15672 > Admin > click on user > Look for "Set permission".
P.S. I assume your RabbitMQ service is running, user exists and password is correct.
Actually this problem happens when you have a big content inside your message and your consumer expend too much time to process only one message, that is problem to response "ACK" to rabbit and try to consume another message.
When I have this problem for example I try to "fit" my messages, because its a products worker and each message had some like 1k products id, so I change to 100 products and it works very well.
You can read more about Detecting Dead TCP Connections with Heartbeats here
This problem happened to me when my connection to RabbitMQ was broken (the reason does not matter, in my case I intentionally stopped RabbitMQ service for some failure tests), and I was trying to reconnect to the RabbitMQ again by closing the old connection and initializing a new one, but I received Broken pipe or closed connection error.
The way I solved this problem was to use reconnect() method on my connection:
$channel->reconnect();
Related
Summary
I am trying to set up heartbeats with my Consumer so it can detect a dropped connection - if the broker is rebooted for example so it may reconnect to the failover.
I tested the code with the Consumer/Dispatcher running locally and the queue running in AWS and everything worked fine. However, when moving the code to AWS the Consumer sets up heartbeats with the server/broker, but the heartbeat is either never sent by the server/broker or never received by the client/Consumer. As a result the HeartbeatException is thrown as soon as the requested server heartbeat interval has passed.
I based my code on the examples in the stomp-php-examples github
Repo: https://github.com/stomp-php/stomp-php-examples
Heartbeat example: https://github.com/stomp-php/stomp-php-examples/blob/support/version-4/src/heartbeats_server.php
My next best guess to as why this isn't working is something related to the queue config as I'm using the default config provided by AWS (I think). I have googled and searched all over about config settings for heartbeats but haven't got very far as this is a new topic. Any help would be appreciated!
Setup
Amazon MQ (ActiveMQ 5.15.14)
stomp-php 5.0 (latest version as of today)
I'm happy to provide any more details of my setup.
Code
Consumer (stripped down version)
abstract class AmqConsumerAbstract
{
/** #var StatefulStomp */
protected $consumer;
const HEARTBEAT = 5000;
public function listen(): void
{
$observer = new ServerAliveObserver();
$this->client->getConnection()->getObservers()->addObserver($observer);
$this->client->setHeartbeat(0, self::HEARTBEAT); // heartbeats setup here
// Note: the heartbeat above is longer than the read-timeout below
$this->client->getConnection()->setReadTimeout(2, 0);
$this->client->connect();
$this->consumer = new StatefulStomp($this->client);
$this->consumer->subscribe(
$this->getQueueName(),
null,
'client'
);
if (!$observer->isEnabled()) {
// we never get here so I assume everything is working OK
echo 'The Server is not supporting heartbeats.');
exit(1);
} else {
echo sprintf('The Server should send us signals every %d ms.', $observer->getInterval() * 1000);
}
try {
while (true) {
$frame = $this->consumer->read(); // I assumed this line would read the heartbeat?
// there is then some logic that deals with payload and does
// $this->consumer->begin();
// $this->consumer->ack($frame);
// $this->consumer->commit();
if ($observer->isDelayed()) {
$this->echoLog('ServerAliveObserver: Server has been delayed.');
}
}
} catch (HeartbeatException $e) {
echo 'AMQ (STOMP) Error, the server failed to send us heartbeats within the defined interval: ' . $e->getMessage()
$this->reconnect();
} catch (ConnectionException $e) {
echo $e->getMessage();
$this->reconnect();
} catch (Exception $e) {
echo 'AMQ (STOMP) Queue error: ' . $e->getMessage();
exit(1);
}
}
}
I have reproduced on a single-instance broker with version 5.15.14.
I have tried switching on STOMP debugging for the broker, but it appears the uri attribute under <transportConnector> is not allowed. The config will save but pulls out the uri attribute.
<transportConnector> Elements and Their Attributes Permitted in Amazon MQ Configurations
Working with Spring XML configuration files (xsd files)
I am working on an eCommerce site that sends a number of emails to the customer when they complete their order using G Suite SMTP relay service. But a large number of these emails are failing. There does not seems to be any pattern to it either - sometimes all emails will send, some times just one or two, and sometimes none.
I am getting the following error: 421, "4.7.0", Try again later, closing connection.
Looking here: https://support.google.com/a/answer/3726730?hl=en doesn't really help me debug this or figure out why some emails fail.
I am using the phpmailer class (https://sourceforge.net/projects/phpmailer/)
The issue seems to occur when the first handshake fails:
function Hello($host="") {
$this->error = null; # so no confusion is caused
if(!$this->connected()) {
$this->error = array(
"error" => "Called Hello() without being connected");
return false;
}
# if a hostname for the HELO was not specified determine
# a suitable one to send
if(empty($host)) {
# we need to determine some sort of appopiate default
# to send to the server
$host = "localhost";
}
// Send extended hello first (RFC 2821)
//If this fails then the second attempt will always fail
if(!$this->SendHello("EHLO", $host))
{
//when this fails it generates the try again later error
if(!$this->SendHello("HELO", $host))
return false;
}
return true;
}
So what is the best approach for debugging this?
The error message is pretty explicit. You call a 3rd party web service, which returns an error code which says the server you are calling is at capacity, try later. Is this a free service which allows you to upgrade to a paid plan? Usually whee you see his kind of thing.
I want to connect to IBM Bluemix through the MQTT protocol using PHP to subscribe to messages come from IoT Foundation.
I use this code:
<?php
require("../phpMQTT.php");
$config = array(
'org_id' => 't9m318',
'port' => '1883',
'app_id' => 'phpmqtt',
'iotf_api_key' => 'my api key',
'iotf_api_secret' => 'my api secret',
'device_id' => 'phpmqtt'
);
$config['server'] = $config['org_id'] .'.messaging.internetofthings.ibmcloud.com';
$config['client_id'] = 'a:' . $config['org_id'] . ':' .$config['app_id'];
$location = array();
// initialize client
$mqtt = new phpMQTT($config['server'], $config['port'], $config['client_id']);
$mqtt->debug = false;
// connect to broker
if(!$mqtt->connect(true, null, $config['iotf_api_key'], $config['iotf_api_secret'])){
echo 'ERROR: Could not connect to IoT cloud';
exit();
}
$topics['iot-2/type/+/id/phpmqtt/evt/+/fmt/json'] =
array("qos"=>0, "function"=>"procmsg");
$mqtt->subscribe($topics, 0);
// process messages
while ($mqtt->proc(true)) {
}
// disconnect
$mqtt->close();
function procmsg($topic, $msg) {
echo "Msg Recieved: $msg";
}
?>
But the browser show this message:
Fatal error: Maximum execution time of 30 seconds exceeded in /Library/WebServer/Documents/phpMQTT/phpMQTT.php on line 167
subscribe is not meant to run in the web browser as it has an infinite look, its best being run from the command line.
If you are using the subscribe method to receive messages you can look at persistent msgs and breaking out of the loop on msg receipt.
There is an example of how to use phpMQTT in the web browser in the file
web-app.php of this respository https://github.com/vvaswani/bluemix-iotf-device-tracker
You don't provide very much information about what you want to achieve by doing this; do you want to keep sending messages to the browser until the page is closed in the browser?
Server Sent Events or Websockets might be a better bet, and PHP might not be the best choice for this, because it uses up quite a lot of memory per connection (compared to node.js for example).
However if you just want to remove the 30 second PHP timeout, then you can use this function:
http://php.net/manual/en/function.set-time-limit.php
Or set max_execution_time in php.ini:
http://php.net/manual/en/info.configuration.php
Setting the maximum execution time to 0 should stop it from timing out.
But be warned that PHP and/or your webserver will have a limited number of concurrent HTTP connections.
I'm working on trace logger of sorts that pushes log message requests onto a Queue on a Service Bus, to later be picked off by a worker role which would insert them into the table store. While running on my machine, this works just fine (since I'm the only one using it), but once I put it up on a server to test, it produced the following error:
HTTP_Request2_MessageException: Malformed response: in D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2\Adapter\Socket.php on line 1013
0 HTTP_Request2_Response->__construct('', true, Object(Net_URL2)) D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2\Adapter\Socket.php:1013
1 HTTP_Request2_Adapter_Socket->readResponse() D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2\Adapter\Socket.php:139
2 HTTP_Request2_Adapter_Socket->sendRequest(Object(HTTP_Request2)) D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2.php:939
3 HTTP_Request2->send() D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\Common\Internal\Http\HttpClient.php:262
4 WindowsAzure\Common\Internal\Http\HttpClient->send(Array, Object(WindowsAzure\Common\Internal\Http\Url)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\Common\Internal\RestProxy.php:141
5 WindowsAzure\Common\Internal\RestProxy->sendContext(Object(WindowsAzure\Common\Internal\Http\HttpCallContext)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\Common\Internal\ServiceRestProxy.php:86
6 WindowsAzure\Common\Internal\ServiceRestProxy->sendContext(Object(WindowsAzure\Common\Internal\Http\HttpCallContext)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\ServiceBus\ServiceBusRestProxy.php:139
7 WindowsAzure\ServiceBus\ServiceBusRestProxy->sendMessage('<queuename>/mes…', Object(WindowsAzure\ServiceBus\Models\BrokeredMessage)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\ServiceBus\ServiceBusRestProxy.php:155
⋮
I've seen previous posts that describe similar issues; Namely:
Windows Azure PHP Queue REST Proxy Limit (Stack Overflow)
Operations on HTTPS do not work correctly (GitHub)
That imply that this is a known issue regarding the PHP Azure Storage libraries, where there are a limited amount of HTTPS connections allowed. Before requirements were changed, I was accessing the table store directly, and ran into this same issue, and fixed it in the way the first link describes.
The problem is that the Service Bus endpoint in the connection string, unlike Table Store (etc.) connection string endpoints, MUST be 'HTTPS'. Trying to use it with 'HTTP' will return a 400 - Bad Request error.
I was wondering if anyone had any ideas on a potential workaround. Any advice would be greatly appreciated.
Thanks!
EDIT (After Gary Liu's Comment):
Here's the code I use to add items to the queue:
private function logToAzureSB($source, $msg, $severity, $machine)
{
// Gather all relevant information
$msgInfo = array(
"Severity" => $severity,
"Message" => $msg,
"Machine" => $machine,
"Source" => $source
);
// Encode it to a JSON string, and add it to a Brokered message.
$encoded = json_encode($msgInfo);
$message = new BrokeredMessage($encoded);
$message->setContentType("application/json");
// Attempt to push the message onto the Queue
try
{
$this->sbRestProxy->sendQueueMessage($this->azureQueueName, $message);
}
catch(ServiceException $e)
{
throw new \DatabaseException($e->getMessage, $e->getCode, $e->getPrevious);
}
}
Here, $this->sbRestProxy is a Service Bus REST Proxy, set up when the logging class initializes.
On the recieving end of things, here's the code on the Worker role side of this:
public override void Run()
{
// Initiates the message pump and callback is invoked for each message that is received, calling close on the client will stop the pump.
Client.OnMessage((receivedMessage) =>
{
try
{
// Pull the Message from the recieved object.
Stream stream = receivedMessage.GetBody<Stream>();
StreamReader reader = new StreamReader(stream);
string message = reader.ReadToEnd();
LoggingMessage mMsg = JsonConvert.DeserializeObject<LoggingMessage>(message);
// Create an entry with the information given.
LogEntry entry = new LogEntry(mMsg);
// Set the Logger to the appropriate table store, and insert the entry into the table.
Logger.InsertIntoLog(entry, mMsg.Service);
}
catch
{
// Handle any message processing specific exceptions here
}
});
CompletedEvent.WaitOne();
}
Where Logging Message is a simple object that basically contains the same fields as the Message Logged in PHP (Used for JSON Deserialization), LogEntry is a TableEntity which contains these fields as well, and Logger is an instance of a Table Store Logger, set up during the worker role's OnStart method.
This was a known issue with the Windows Azure PHP, which hasn't been looked at in a long time, nor has it been fixed. In the time between when I posted this and now, We ended up writing a separate API web service for logging, and had our PHP Code send JSON strings to it over cURL, which works well enough as a temporary work around. We're moving off of PHP now, so this wont be an issue for much longer anyways.
I'm using HttpRequest and HttpRequestPool to send parallel http requests, the number of requests is up to 200 or something, the thing is that some of the receivers might be offline, so that HttpRequestPool::send() will wait until it gets a response or until it times out.
Example of usage:
$query = "CALL GetBoardsURLS()";
$result = mysql_query($query) or die("ERROR:QUERY_FAILED 8" . mysql_error());
$pool = new HttpRequestPool();
//Add the data to the request pool.
while($row = mysql_fetch_row($result))
{
$req = new HttpRequest($row[0].'/', HTTP_METH_POST);
$req->setBody($message);
$pool->attach($req);
}
$pool->send();
In my php error log i get errors, can someone tell me what I have to do to avoid them? I'm guessing that this happens mostly because of timed out requests to invalid destinations, becaues all receivers that are valid do get the message and act accordingly.
Any suggestions?
Ty in advance.
**ERRORS**:
[13-Nov-2012 14:20:00 UTC] PHP Fatal error: Uncaught exception HttpRequestPoolException' with message 'Exception caused by 2 inner exception(s)' in C:\inetpub\wwwroot\DeusTesting\TimeSet.php:130
inner exception 'HttpInvalidParamException' with message 'Empty or too short HTTP message: ''' in C:\inetpub\wwwroot\DeusTesting\TimeSet.php:0
inner exception 'HttpRequestException' with message 'Timeout was reached; Connection time-out (http://sim6261.agni.lindenlab.com:12046/cap/11b23456-63bd-1c56-8692-b640ac992a76/)' in C:\inetpub\wwwroot\DeusTesting\TimeSet.php:130
Stack trace:
#0 C:\inetpub\wwwroot\DeusTesting\TimeSet.php(0): HttpRequestPool->send()
#1 {main}
thrown in C:\inetpub\wwwroot\DeusTesting\TimeSet.php on line 130
[13-Nov-2012 14:30:03 UTC] PHP Fatal error: Uncaught exception 'HttpRequestPoolException' with message 'Exception caused by 12 inner exception(s)' in C:\inetpub\wwwroot\DeusTesting\TimeSet.php:130
Okay, this question was around here for a while and not answered. So I'll say what I did to get rid of the errors.
$pool = new HttpRequestPool();
//Add the data to the request pool.
foreach($boardsURLS as $boardURL)
{
$req = new HttpRequest($boardURL.'/', HTTP_METH_POST);
$req->setBody($message);
$req->setOptions(array('connecttimeout' => 3, 'timeout' => 3));
$pool->attach($req);
}
$ErrorLog = fopen('Logs/GameEnd.txt', "a+");
try
{
$pool->send();
}
catch(HttpException $ex)
{
fprintf($ErrorLog, '%s', $ex);
}
fclose($ErrorLog);
The "errors" were exceptions, and since I didn't handle them they were added in the php53_errors.log. Now by adding try and catch, the exceptions could be handled so that solved it. Now I store the exceptions(timeout exceptions) in a separate file (that's because i wanted to) someone else can handle them differently of course. So not much I can do about it.
NEXT
$req->setOptions(array('connecttimeout' => 3, 'timeout' => 3));
This is the part I like. Since I don't really care about the response, I just want my http requests to get transmited to the receivers. I put a timeout of 3 seconds. That prevents my script from keep running and waiting for the responses.
Hope this will help someone. Cheers