4

I am working on a networking project. Where I am creating a dashboard to view real time status (CPU/memory usage, up/down traffic and few others) of multiple routers by calling API request to server which will call another API request to the routers ( Mikrotik Routers, they offer api to do configuration and get statuses ).

Problem: In testing each request surge my VPS CPU usage up to 20%. This is only one user viewing the dashboard and doing request. What if I have multiple customers and each one of them viewing the dashboard and doing multiple request to the servers. It is going to go down for sure, right?

Here is part of back end code:

$routers = //Get all the routers belong to that users

foreach($routers as $router){

            $api = connectRouter();
            if($api === false){
                continue;
            }

            $api->write('/ip/hotspot/user/getall');
            $users = $api->read();


            $api->write('/ip/hotspot/active/getall');
            $actives = $api->read();

            $resources = $api->comm('/system/resource/print');
            $resources = $resources[0];


            $api->write('/interface/getall');
            $router->interfaces = $api->read();

} 

Question: What option do I have to reduce the CPU usage and be able to provide this services to multiple customers?

Up to what I had researched:

  1. I should try Node.js ?
  2. Change ways I am calling from front end: instead of calling API to getting all routers data at once, change it to get one by one?

NOTE 1. Those values in routers change every second (if not millisecond). I want to get value in real time as much as possible. So caching is not a solution here I think.

  1. Every users have their own set of routers. They don't share routers. So doing some duplicate router query check won't be a benefit I think. ( Yeah can be useful if the same logged in user trying to view on multiple devices or browser tabs. ) But I am trying to optimize for multiple users.
  • What are the routers? Do you have access to their code? – Andy Mar 15 '17 at 22:56
  • @DavidPacker routers are physical network device ( Mikrotik Router ). They offer api to do various kind of configuration and get statuses. – cjmling Mar 16 '17 at 6:31
  • This is most efficiently done via WebSockets - This way your clients don't need to perform unnecessary queries when there's no new data to pull. The server instead pushes to the clients only when there's new data – Nik Kyriakides May 2 '17 at 14:43
  • @NicholasKyriakides "when there's no new data to pull" .. but i did mentioned in Note 1 . that data changes every second and even may be milliseconds :) – cjmling May 5 '17 at 15:45
  • @cjmling Websockets are still more appropriate. You can throttle the "pushing" from your server in some way. – Nik Kyriakides May 5 '17 at 15:49
3

Would you not be wanting to cache the data on backend somewhere then allow the Ui to request this, rather than setting off a chain of calls every time.

You can then reduce the internal calls to one set per 3s or whatever the refresh rate is?

Having the calls chain through the system is good for testing, caching lets you scale up.

  • Those value in router change every seconds ( if not milliseconds :S ) and I want to display real time data from router and here I already setting 3 seconds in delay. So caching is not useful in this case I believe. – cjmling Mar 11 '17 at 7:09
  • If it's real time would pushing data to the UI be a better option than pulling? Kick something off on the server to tick over gathering the data then push it out every x seconds or milliseconds. Multiple users could subscribe or if each user is unique have multiple gathering services ticking over. Quick google suggests pubnub or similar if it needs to be open source? – Ross Halliday Mar 11 '17 at 7:39
  • 1. Every user got their own set of routers(they don't share) so the art of subscribing to some channel to get same data won't make any use here i think. 2. "gathering the data" .. so it got to gather/refresh data every few seconds thats mean server it got to run those functions/api every few seconds , we got back at that same issue isn't it. I had used pusher so quite understand what u meant up there. – cjmling Mar 11 '17 at 8:06
  • @cjmling whatever the solution you use, there will be a limit to the real time resolution. E.g. if your cpu load is 20% for 1 request per second, then you can only do 5 request per second max. That means updating the cache 5 times / second would be the best you can do. My point is you still need to cache. – imel96 Mar 16 '17 at 17:44
2

A good option would be a bi-directional communication. For that you will need a server which is able to hold a persistent connection, so PHP is a no-go (even though you can hack it with something like ReactPHP/Ratchet) and Node.js would be a much better option.

Your main issue currently is, should two users have access to same router, you would be querying the router two times, even though the output data is the same for both of them. And the numbers go higher once more and more users connect.

What you should so is:

  • setup a Node.js server, which will be used for querying the routers,
  • when a users connects, check their router list and add the routers to router SET (set cannot contain duplicates, each router would only be present once),
  • establish a persistent bi-directional connection between the client and the Node.js server (using sockets),
  • query all routers in the router pool to retrieve data,
  • upon receiving response from the routers, check clients of the router you have just received data from feed/push the new data to the connected clients,
  • when a client disconnects check the router pool for their routers and if they were the only client using some of the routers, remove them from the querying pool.

This way you prevent querying the same router multiple times.

Which strategy of feeding the data from the routers to their clients you choose, whether you:

  1. query all routers and upon finishing the cycle send data of all routers to clients,
  2. or feed data to the clients upon receiving data from each router,

you must device for yourself, what works the best for you. I personally would go with the second option.

  • Looks like the answer is just trade classic HTTP API for WebSockets right ? It's possible to use WebSockets in PHP – Etsitpab Nioliv Mar 16 '17 at 10:50
  • What I understand is your solution/suggestion is based on that there will be multiple client query on the same router right? and so we can save some duplicate query on same router. But in my system every client have totally different set of routers. They don't share. :S – cjmling Mar 16 '17 at 11:00
  • @cjmling I see, in that case rather than having a global pool of connections to routers you could have a pool of connections for each user, pretty much what you have at the moment. With the exception of using the bi-directional communication instead of calling the API from the dashboard directly, because the bi-directional communication is cheaper. – Andy Mar 16 '17 at 12:00
0

The first thing that I'd like to try is to move that connectRouter() line outside the loop, that should lower the overhead of the work. After that, split the job to a data collector and request handler. In the data collector, I'd do the queries periodically and put the result somewhere ready for request handler to collect. Something like:

setupRoutersConnection();

while (true) {

    foreach ($routers as $key => $router) {
        // read from the router
        $dashboard.routers[$key] = $someRouterInfo;
    }
    usleep(200);
}

The $dashboard is something that you share between data collector and request handler.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.