node.js running alongside Apache PHP? - php

I am trying to get my head round node.js...
I am very happy with my LAMP set up as it currently fulfils my requirements. Although I want to add some real-time features into my PHP app. Such as showing all users currently logged into my site and possible chat features.
I don't want to replace my PHP backend, but I do want scalable real-time solutions.
1. Can I throw node.js into the mix to serve my needs without rebuilding the whole application server-side script?
2. How best could node.js serve my 'chat' and 'currently logged in' features?
Great to hear your views!
W.

I suggest you use Socket.io along side node.js. Install and download the libs from http://socket.io/. You can run it along side your Apache server no problems.
First create a node server:
var http = require('http')
, url = require('url')
, fs = require('fs')
, io = require('../')//path to your socket.io lib
, sys = require(process.binding('natives').util ? 'util' : 'sys')
, server;
server = http.createServer(function(req, res){
var path = url.parse(req.url).pathname;
}),
server.listen(8084);//This could be almost any port number
Second, run your server from the command line using:
node /path/to/your/server.js
Third, connect to the socket using client side js:
var socket = new io.Socket(null, {port: 8084, rememberTransport: false});
socket.connect();
You will have to have include the socket.io lib client side aswell.
Send data from client side to the node server using:
socket.send({data:data});
Your server.js should also have functions for processing requests:
io.on('connection', function(client){
//action when client connets
client.on('message', function(message){
//action when client sends msg
});
client.on('disconnect', function(){
//action when client disconnects
});
});
There are two main ways to send data from the server to the client:
client.send({ data: data});//sends it back to the client making the request
and
client.broadcast({ data: data});//sends it too every client connected to the server

I suspect the chat as well as the logged in listing would work via Ajax.
The chat part would be pretty easy to program in Node.js, use one of the mysql modules for Node to connect to your existing database and query login information and such and then do all the actual chatting via Node.js, I recommend you to check out Socket.io since it makes Browser/Node.js communcation really trivial, this should allow you to focus on the actual chat logic.
Also, you could take a look at the "official" chat demo of Node.js, for some inspiration.
As far as the currently online part goes, this is never easy to implement since all you can do is to display something along the lines of "5 users active in the last X minutes".
Of course you could easily add some Ajax that queries the chat server and display the userlist from that on the homepage.
Or you completely crazy and establish a Socket.io connection for every visitor and monitor it this way, although it's questionable whether this is worth the effort.

What about using a socket file
just like pedro did with ngnx ?
http://nodetuts.com/tutorials/25-nginx-and-nodejs.html

You can run php from node js using node-php: https://github.com/mkschreder/siteboot_php

I'm running a wss (secure websocket) server alongside my LAMP setup.
Node.js can easily run alongside any other web server (apache) you want. In #KitCarrau example, he lets node run on port 8084 - that's where it's running and listening to, not 80 or 443 etc (those are usually taken by apache anyway). But you can still use the same port to also serve http/https (in my case just stating some conf and general info that the service is up).
Starting from the console isn't the best way (remotely, node stops when closing the console).
I recommend taking a look at Running node as service
Easy to track log in realtime (log with console.log("hello"); in your application) with:
tail -f /var/.../SocketServer.log
Sample script (node-server.conf):
author ....
description "node.js server"
# used to be: start on startup
# until we found some mounts weren't ready yet while booting:
start on started mountall
stop on shutdown
# Automatically Respawn:
respawn
respawn limit 99 5
# Max open files are # 1024 by default. Bit few.
limit nofile 32768 32768
script
# Not sure why $HOME is needed, but we found that it is:
export HOME="/root"
exec node /var/.../SocketServer.js >> /var/www/node/.../SocketServer.log 2>&1
end script
post-start script
# Optionally put a script here that will notifiy you node has (re)started
# /root/bin/hoptoad.sh "node.js has started!"
echo "\n*********\nServer started\n$(date)\n*********" >> /var/.../SocketServer.log
end script

Related

Handle concurrent puppeteer requests through php

I'm building a web app that retrieves dynamic generated content through puppeteer. I have set up (apache + php) docker containers, one for the p5js project that generates an svg based on a (large, 2MB) json file, and one container with PHP that retrieves that svg. Dockers runs in an Nginx config (nginx for routing, apache for quicker PHP handling). I'm using the cheapest CENTOS server available on digitalocean. So upgrading would definitley help.
I don't want the javascript in the p5js project to be exposed to the public, so I thought a nodejs solution would be best in this scenario.
The PHP page does a shell_exec("node pup.js"). It basically runs in approx 1-3 seconds which is perfect.
Problem is when I try to test a multi user scenario and open 5 tabs to run this PHP page, the loadtime drops to even 10+ seconds, which is killing for my app.
So the question would be how to set up this architecture (php calling a node command) for a multi user environment.
===
I've tried several frameworks like x-ray, nightmare, jsdom, cheerio, axios, zombie, phantom just trying to replace puppeteer. Some of the frameworks returned nothing, some just didn't work out for me. I think I just need a headless browser solution, to be able to execute the p5js. Eventually puppeteer gets the job done, only not in a multi-user environment (I think due to my current php shell_exec puppeteer architecture).
Maybe my shell_exec workflow was the bottleneck, so I ended up building a simple node example.js which waits 5 seconds before finish (not using puppeteer), and I ran this with several tabs simultaneously, works like a charm. All tabs load in about 5-6 seconds.
I've also tried pm2 to test if my node command was the bottleneck, I did some testing on the commandline, with no major results and I couldn't get PHP to run a pm2 command, so I dropped this test.
I've tried setting PuPHPeteer up, but couldn't get it to run.
At some time I thought it had something to do with multiple puppeteer browsers launched, but I've read that this should be no problem.
The PHP looks like:
<?php
$puppeteer_command = "node /var/www/pup.js >&1";
$result = shell_exec($puppeteer_command);
echo $result;
?>
My puppeteer code:
const puppeteer = require('puppeteer');
let url = "http://the-other-dockercontainer/";
let time = Date.now();
let scrape = async () => {
const browser = await puppeteer.launch({
args: ['--no-sandbox']
});
const page = await browser.newPage();
await page.goto(url);
await page.waitForSelector('svg', { timeout: 5000 });
let svgImage = await page.$('svg');
await svgImage.screenshot({
path: `${time}.png`,
omitBackground: true,
});
await browser.close();
return time;
}
scrape().then((value) => {
console.log(value); // Success!
});
I was thinking about building the entire app in nodejs if that is the best solution, but I've put so many hours in this PHP infrastructure, I'm at the point of really like getting some advice :)
Since I have full control over the target and destination site one brainfart would be to have node to serve a server which accepts a json file and return the svg based on a local p5js site, but don't now (yet) if this would be any different.
UPDATE
So thanks to some comments, I've tried a new approach: not using p5js, but native processing code (java). I've exported the processing code to a linux 64bit application and created this little nodejs example:
var exec = require('child_process').exec;
var cmd = '/var/www/application.linux64/minimal';
exec(cmd, processing);
// Callback for command line process
function processing(error, stdout, stderr) {
// I could do some error checking here
console.log(stdout);
};
When I call this node example.js within a shell_exec in PHP, I get this:
First call takes about 2 seconds. But when I hit a lot of refreshes, time is again building up by a lot of seconds. So, clearly, my understanding of multithreading is not that good, or am I missing something crucial in my testing?
I have a setup similar to you. The biggest problem is your droplet. You should use at minimum the cheapest CPU optimized droplet ($40 per month from memory). The cheapest generic droplets on DO are in a shared environment (noisy neighbours create performance fluctuations). You can easily test this out by making a snapshot of your server and cloning your drive.
Next as someone else suggested is to reduce the cold starts. On my server a cold start adds around 2 extra seconds. I take 10 screenshots before opening a new browser. Anything more than that and you may run into issues with memory.
If you are trying to use puppeteer with php you should use make sure you have composer installed then while inside the project folder open terminal windows and type composer require nesk/puphpeteer
also install nesk/rialto, then require autoload everything should work. One thing when working with puppeteer setting const use $ and skip using await command plus replace "." with ->
<?php
require 'vendor/autoload.php';
use Nesk\Puphpeteer\Puppeteer;
use Nesk\Rialto\Data\JsFunction;
$puppeteer = new Puppeteer;
$browser = $puppeteer->launch(['headless'=>false,'--proxy-server=000.00.0.0:80']);// Type Proxy
$bot = $browser->newPage();
$data = $bot->evaluate(JsFunction::createWithBody('return document.documentElement.innerHTML'));
$urlPath = $bot->url();
$bot->goto('https://google.com');//goes to google.com
$bot->waitForTimeout(3000);// waits
$bot->type('div', '"cellphonemega"');//searches
$bot->keyboard->press('Enter');//presses enter
$bot->waitForTimeout(8000);//waits
$bot->click('h3', 'https:');//clicks webpage
$bot->waitForTimeout(8000);//waits while site loads
$bot->screenshot(['path' => 'screenshot.png']);// TAKES SCREENSHOT!
$browser->close();//Shuts Down

PHP Secure Websocket Client Trouble, Needs to be Non Blocking

I'm building a dashboard that allows me to visualise my crontab as it runs (Think a queue of upcoming tasks, those that are running currently and those that have finished and whether the outcome was successful.) To do this, I need to send messages from the tasks (running or monitored by PHP) on my server, to the client browsers that run the dashboard using javascript. It also has to be secure.
To solve this issue I implemented a Twisted/Autobahn socket server in Python, which worked fine once I had paid for proper security certificates. The problem I have however is getting the PHP running the crontasks to be able to send messages to the webSocket server which passes them on to the client browsers, so far I have hacked this by writing a Python client that accepts the message to send as an argument and run this as an exec from PHP.
Obviously this is not a robust solution (which is also relatively slow to execute) and I'd now like to send logfile entries from the crontasks over websockets to my dashboards so I can see what's happening on my servers as tasks run. I've been looking for a while and tried various approaches (most are too long to post) however they range from tutorials, to segments from the PHP website to libraries such as Thruway (which seems too over-engineered for my use case, specialised and hard to adapt).
The best progress I have so far is Pawl, and using the following code I'm able to successfully send three messages to the Python Socket Server using wss:
<?php
require __DIR__ . '/vendor/autoload.php';
\Ratchet\Client\connect('wss://127.0.0.1:9000')->then(function($conn) {
$conn->on('message', function($msg) use ($conn) {
echo "Received: {$msg}\n";
$conn->close();
});
$conn->send('MSG 1');
$conn->send('MSG 2');
$conn->send('MSG 3');
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});
?>
(Note that this depends on the libraries found here)
The problem I have is that I would like to be able to open and close the connection and send messages as separate steps, in the code example (which I've had difficulty adapting) it seems that as open, send and close are all wrapped in the then method and anonymous function I cannot call these methods seperately. Ideally I'd like to open the connection at the beginning of my crontask execution, each time a message is logged call the send method and close the connection at the end without wasting time opening and closing a connection to my socket server for each and every message. Please note that listening for replies isn't necessary.
Also, any solutions that work to 127.0.0.1:9000 over WSS and don't need libraries or use a different one I'm happy to consider. Please also note (after seeing other posts) this question specifically refers to a websocket client, not a server.
Many thanks,
James
Leaving this in case anybody else finds this final solution welcome:
In the end I wrapped a module called Textalk by Fredrik Liljegren et al in a small class to make it more accesible and this solved my issue.
Here is the code I used in the end:
require('vendor/autoload.php');
use WebSocket\Client;
class secureSocketClient {
private $OClient;
function __construct($VProtocol, $VLocation, $VPort, $VDir) {
$this->OClient = new Client("$VProtocol://$VLocation:$VPort" . ($VDir != null ? "/$VDir" : ""));
}
function sendMessage($ORequestData) {
$VLocalMessage = json_encode($ORequestData);
$this->OClient->send($VLocalMessage);
}
function __destruct() {
$this->OClient->close();
}
}
Which can be invoked as so:
require_once <class location>
$this->OSecureSocketClient = new secureSocketClient("wss", "127.0.0.1", "9000", null);
$this->OSecureSocketClient->sendMessage($OMSG1);
$this->OSecureSocketClient->sendMessage($OMSG2);
$this->OSecureSocketClient->sendMessage($OMSG3);
To install textTalk (on linux), you can use the following commands in the directory where the class will reside:
curl -sS https://getcomposer.org/installer | php
add the following to composer.json (in the same directory):
{
"require": {
"textalk/websocket": "1.0.*"
}
}
then run the following:
sudo php composer.phar install
Regards,
James

How to connect to ESL from a remote server?

I would like to make a web interface in PHP to see the FreeSWITCH activities (calls, etc), possibly hosted on a different server than the one where FS is running.
I've seen the server status on the FS server using command line (php single_command.php status), but now I would like to see this status from another server.
When I try to copy ESL.php file to this remote server and try to check the status, I get this error message:
Fatal error: Call to undefined function new_ESLconnection() in
/var/www/freeswitch/ESL.php on line 127
This is my index.php file:
<?php
ini_set('display_errors', 1);
$password = "ClueCon";
$port = "8021";
$host = "192.168.2.12";
require_once('ESL.php');
set_time_limit(0); // Remove the PHP time limit of 30 seconds for completion due to loop watching events
// Connect to FreeSWITCH
$sock = new ESLconnection($host, $port, $password);
// We want all Events (probably will want to change this depending on your needs)
$sock->sendRecv("status");
// Grab Events until process is killed
while($sock->connected()){
$event = $sock->recvEvent();
print_r($event->serialize());
}
?>
I undestand that the webserver doesn't have FreeSWITCH installed, so the error message is obvious, but i don't see how to access to this information from this webserver.
Thank you for your help.
Depending upon your need you can use either Inbound or Outbound socket. I do not know much about PHP and FS Event Socket but yeah tried enough with python. I highly recommended to go through thislink.
So if you just want to do small task like initiating a call, bridging any two given number etc i think you should go with Inbound socket(making cli command from your web server to freeswitch server) or mod_xml_rpc.
And if you want to have full control of everything that happens on FS server like showing live call status and modifying their states or say a complete interactive telephony dashboard then you should go with Outbound socket.(Your FS server will send all events to your web server.)
However in your case problem is I think you did not properly build the php ESL module.
this link might help you installing ESL
Rather than using ESL, you might want to consider using the XMLRPC. The connection is very straight forward:
https://wiki.freeswitch.org/wiki/Freeswitch_XML-RPC
The credentials for the XMLRPC are in your autoloads_configs/xml_rpc.conf.xml

Flash/Flex & PHP Socket Application Sandbox error

I am running a socket server using PHP. The socket server runs fine because I can connect to it using PHP.
Now, I have a flash application that is trying to connect to it:
this.socket.addEventListener(Event.CONNECT, onSocketConnect);
this.socket.addEventListener(Event.CLOSE, onSocketClose);
this.socket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
this.socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecError);
try {
this.socket.connect("myip", 9999);
} catch (ioError:IOError) {
this.debugLbl.text += "ioError1 "+ioError.message;
} catch (secError:SecurityError) {
this.debugLbl.text += "secError1 "+secError.message;
}
When I run the application locally, it works! However, when I upload it to my server I get a sandbox security error (#2048). The flash app is actually hosted on the same server as the socket server, and there is cross domain policy file in place.
Is it possibly you need to use a php proxy? I had to do that, doc'd it here. Although you did mention that the app's on the same server and theres a crossdomain.xml in place, so i'm probably off the mark there (btw, Flash 10 needs a different crossdomain.xml than prev versions as far as I know).
Are you actually loading the cross domain policy file? As far as I know, Flash Player only tries to load automatically the following file: http://www.example.com/crossdomain.xml. If your file is in another place, you should load it:
Security.loadPolicyFile("http://www.example.com/subfolder/crossdomain.xml");
Also, even if the app is on the same server, Flash Player believes "http://www.example.com" to be different from "http://example.com", so you should make sure you cover this possibility in the cross domain policy file:
<allow-access-from domain="*.example.com"/>
You need to pass the crossdomain.xml file by the socket, because when you work with socket dont work any policy file in the root of the app web.
Here the sample : http://www.blog.lessrain.com/as3-java-socket-connections-to-ports-below-1024/

GWT with -noserver

I'm making a GWT project that uses PHP to connect to a DB2 database. When I compile the project and deploy it to the server (copy the contents of the WAR directory over), it works fine, obviously in hosted mode I run into the SOP issue since GWT is on port 8888 while the php script is running on port 80.
I'm trying to get the -noserver option to work but I must be missing something.. I went back and created the basic sample app from the command line (webApplicationCreator -out /home/mike/gwt/sample1)
I edited the build.xml to include the -noserver and -port 80 arguements for devmode. I want my app to be hosted at localhost/sample1 so I edited the -startupUrl to the whole URL I want to use: http://localhost/sample1/sample1.html
I compiled (ant), copied over the sample1.html, sample1.css from war to the webserver sample1 directory, and the (md5).gwt.rpc, clear.cache.gif, sample1.nocache.js and hosted.html files from the war/sample1 to sample1/sample1 directory as described in the GWT documentation (no history.html file was created).
I then run ant devmode from the project directory (/home/mike/gwt/sample1)
I can get to the sample1.html page, but when I click the button to send the name to the server it returns with
Remote Procedure Call - Failure
Server replies:
An error occurred while attempting to contact the server. Please check your network connection and try again.
I turned on firebug and it's returning a 404 for http://localhost/sample1/sample1/greet. This is where I'm stuck.. this file obviously doesn't exist on my webserver.. but why? Isn't this something that is supposed to be getting compiled by GWT?
Can anyone give me a hand? Thanks!
So, basically you've copied over the client-side of a client/server application. When your GWT client application attempts to make a Remote Procedure Call (RPC) to the server to a greeting service that is part of the initial sample, it can't find that service.
If you wanted to copy that service over, you'd need to have a Java application server, copy over the GreetingService, the web.xml that references it and possibly a few other things (I'd have to check in more detail). That doesn't sound like what you actually want, so either you'll want to build a GWT-RPC service in PHP that responds to that URL, or remove the reference in the GWT code to RPC call to the greeting service.
With a PHP back-end, you're probably not going to use GWT-RPC, I'm guessing that you're more likely to use JSON or XML, and if that's the case, then I'd go with removing the RPC call altogether for now.
Does this all make sense? Feel free to ask for further clarification.
To solve the SOP issue, I used the HttpProxyServlet to proxy the HTTP requests to my webserver through the development server.
Download httpProxyPackage.jar, copy it into WEB-INF/lib/, and configure it like so in WEB-INF/web.xml (this is for the StockWatcher tutorial, assuming your web root is the folder that contains the StockWatcher directory):
<servlet>
<servlet-name>jsonStockData</servlet-name>
<servlet-class>com.jsos.httpproxy.HttpProxyServlet</servlet-class>
<init-param>
<param-name>host</param-name>
<param-value>http://localhost/StockWatcher/war/stockPrices.php</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>jsonStockData</servlet-name>
<!--
http://127.0.0.1:8888/stockPrices.php in dev mode
http://gwt/StockWatcher/war/stockPrices.php in prod mode
-->
<url-pattern>/stockPrices.php</url-pattern>
</servlet-mapping>
Then redefine your JSON URL as:
GWT.getHostPageBaseURL() + "stockPrices.php?q=";
instead of:
GWT.getModuleBaseURL() + "stockPrices.php?q=";
It’s maybe not the best way, but if it can get someone else started… There was another way using php-cgi, but I didn’t have it installed.

Categories