#ifndef __MultiCast_h_ #define __MultiCast_h_ #include #include #include #include #include "MiscSupport.h" #include "SmarterP.h" #include "ContainerThread.h" /* This is a simple way to broadcast messages to all servers. * * Use case: You are trying to administer a group of computers. You have * a lot of servers and you are having trouble keeping up with them. Send * out a PING message asking all servers to respond. * * Or: You are trying to investigate a problem on a live server. There are * several machines in the cluster. You have done the client part of the * test setup on the client, but you are not sure which server your requests were routed * to. Send a PING_USER and every server that is handling the given user * will respond. * * Or: You update data in a database. Many servers watch that database, * polling every once in a while to see if anything's change. For example * tikiller will read a lot of candle data from the database and keep it * cached for an arbitrary amount of time in the range of 5-6 hours. Send * a message to anounce your chance, and any server that's listening will * immediately invalidate any affected cache entries. * * Warning: When I first ran this I got no errors, but I saw no messages. * The firewall was stopping the messages. I ran * systemctl stop firewalld * and I immediately started to see messages arriving. I didn't have to * restart the sender or the receiver. I don't know the specific rule causing * the problem so I suggest totally disabling the firewall. * * You can use netstat -g to see how many programs are listening to a multi- * cast group on a machine. This worked even when the firewall was blocking * messages. * * Note: In non-technical terms "multicast" is just a fancy name for * "broadcast". Multicast has the ability to be forwarded outside of the * local subnet, but we don't try to use that feature. Mostly we use * multicast instead of broadcast because you can't have two programs on the * same machine listening to the same broadcast port. You won't get any * error messages, but the data will be unreliable. */ class MultiCast : private ForeverThreadUser { public: typedef SmarterP< PropertyList > Message; typedef SmarterCP< PropertyList > ConstMessage; typedef std::function Callback; private: enum { mtMultiCastPing }; typedef std::vector< Callback > Callbacks; SmarterPVar< Callbacks > _callbacksForAll; typedef std::map< std::string, Callbacks > CallbacksByType; SmarterPVar< CallbacksByType > _callbacksByType; SmarterPVar< PropertyList > _basicInfo; void send(std::string const &message, int port) const; void send(PropertyList const &message, int port) const; void sendRequest(PropertyList const &message) const; int _sendHandle; int _receiveHandle; void onMessageReceived(std::string const &raw); MultiCast(); protected: virtual void beforeSleep(IBeforeSleepCallbacks &callbacks); virtual void awake(std::set< int > const &woken); virtual void handleRequestInThread(Request *original); public: static MultiCast &getInstance(); static const std::string TYPE; void addInfo(std::string const &key, std::string const &value); void addCallback(Callback callback); void addCallback(Callback callback, std::string const &messageType); ConstMessage getBasicInfo() const { return _basicInfo.get(); } void sendResponse(PropertyList const &message) const; // Execute the given function. If multiple threads all call this // function at the same time, we will make sure they run one at a // time. The function might be run in a different thread, and // serialize() can return before during or after the function is // run. // // (In fact serialize() is currently implemented by just // sending the function to a well known thread, and relying on // that thread's incoming queue to serialize the results. But I'm // trying to avoid any unnecessary promises at this time.) // // To see why this is useful consider NewConnections. That class // had no static members. Each object was totally seperate. Until // we decided that NewConnections should call MultiCast::addInfo(). // The message sent to addInfo() should include a summary of all // NewConnections objects. NewConnections would have to add some // lock or thread or something to serialize the access. But I hate // adding unnecessary locks, and MultiCast already has the thread, // so why shouldn't MultiCast expose this feature? void serialize(std::function< void() > toDo); // PING_USER is implemented by the user info module. Unfortunately there // are 4 different versions of this module floating around. So this is // a collection of code that might be shared by all of those modules. static const std::string PING_USER; static std::string getUserId(PropertyList const &message); static void respond(PropertyList const &message); class PURequest : public Request { public: const PropertyList message; PURequest(PropertyList const &orig, int callbackId) : Request(NULL), message(orig) { this->callbackId = callbackId; } std::string getUserId() const { return MultiCast::getUserId(message); } void respond() const { MultiCast::respond(message); } }; void addDebugCommands(); }; #endif