mirror of
https://github.com/stefanocasazza/ULib.git
synced 2025-09-28 19:05:55 +08:00
sync
This commit is contained in:
parent
4f2fe33361
commit
28a3a852a8
|
@ -63,7 +63,7 @@
|
|||
typedef void (*vPFcs) (const UString&);
|
||||
typedef void (*vPFcscs)(const UString&,const UString&);
|
||||
|
||||
class UREDISClusterClient;
|
||||
class UREDISClusterMaster;
|
||||
|
||||
class U_EXPORT UREDISClient_Base : public UClient_Base, UEventFd {
|
||||
public:
|
||||
|
@ -822,7 +822,7 @@ protected:
|
|||
}
|
||||
|
||||
void init();
|
||||
virtual void processResponse();
|
||||
void processResponse();
|
||||
bool processRequest(char recvtype);
|
||||
|
||||
bool sendRequest(const UString& pipeline)
|
||||
|
@ -898,7 +898,7 @@ protected:
|
|||
private:
|
||||
bool getResponseItem() U_NO_EXPORT;
|
||||
|
||||
friend class UREDISClusterClient;
|
||||
friend class UREDISClusterMaster;
|
||||
|
||||
// U_DISALLOW_COPY_AND_ASSIGN(UREDISClient_Base)
|
||||
};
|
||||
|
@ -925,6 +925,7 @@ public:
|
|||
#endif
|
||||
|
||||
private:
|
||||
|
||||
// U_DISALLOW_COPY_AND_ASSIGN(UREDISClient)
|
||||
};
|
||||
|
||||
|
@ -978,45 +979,56 @@ private:
|
|||
// by Victor Stewart
|
||||
|
||||
#if defined(U_STDCPP_ENABLE) && defined(HAVE_CXX17)
|
||||
# include <vector>
|
||||
|
||||
class U_EXPORT UREDISClusterClient : protected UREDISClient<UTCPSocket> {
|
||||
class U_EXPORT UREDISClusterClient : public UREDISClient<UTCPSocket> {
|
||||
private:
|
||||
|
||||
struct RedisNode {
|
||||
UString ipAddress;
|
||||
UREDISClient<UTCPSocket> client;
|
||||
uint16_t port, lowHashSlot, highHashSlot;
|
||||
|
||||
RedisNode(const UString& _ipAddress, uint16_t _port, uint16_t _lowHashSlot, uint16_t _highHashSlot) :
|
||||
ipAddress(_ipAddress), port(_port), lowHashSlot(_lowHashSlot), highHashSlot(_highHashSlot)
|
||||
{
|
||||
client.connect(ipAddress.data(), port);
|
||||
}
|
||||
UREDISClusterMaster *master;
|
||||
|
||||
// DEBUG
|
||||
public:
|
||||
|
||||
void processResponse();
|
||||
UREDISClusterClient(UREDISClusterMaster *_master) : UREDISClient<UTCPSocket>(), master(_master) {}
|
||||
};
|
||||
|
||||
struct RedisClusterNode {
|
||||
|
||||
UString ipAddress;
|
||||
UREDISClusterClient client;
|
||||
uint16_t port, lowHashSlot, highHashSlot;
|
||||
|
||||
RedisClusterNode(const UString& _ipAddress, uint16_t _port, uint16_t _lowHashSlot, uint16_t _highHashSlot, UREDISClusterMaster *master) : ipAddress(_ipAddress), client(master), port(_port), lowHashSlot(_lowHashSlot), highHashSlot(_highHashSlot)
|
||||
{
|
||||
client.connect(ipAddress.data(), port);
|
||||
}
|
||||
|
||||
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
|
||||
const char* dump(bool _reset) const { return ""; }
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
enum class ClusterError : uint8_t {
|
||||
none,
|
||||
moved,
|
||||
ask,
|
||||
tryagain
|
||||
};
|
||||
enum class ClusterError : uint8_t {
|
||||
none,
|
||||
moved,
|
||||
ask,
|
||||
tryagain
|
||||
};
|
||||
|
||||
class U_EXPORT UREDISClusterMaster {
|
||||
private:
|
||||
|
||||
friend class UREDISClusterClient;
|
||||
|
||||
ClusterError error;
|
||||
UString temporaryASKip;
|
||||
UHashMap<RedisNode *> redisNodes;
|
||||
|
||||
uint16_t hashslotForKey(const UString& hashableKey) { return u_crc16(U_STRING_TO_PARAM(hashableKey)); }
|
||||
UREDISClusterClient subscriptionClient;
|
||||
UHashMap<RedisClusterNode *> clusterNodes; // when these call they need to be processed... also when MOVED... we need to set up and recalculate
|
||||
|
||||
uint16_t hashslotForKey(const UString& hashableKey) { return u_crc16(U_STRING_TO_PARAM(hashableKey)); }
|
||||
|
||||
uint16_t hashslotFromCommand(const UString& command)
|
||||
{
|
||||
U_TRACE(0, "UREDISClusterClient::hashslotFromCommand(%V)", command.rep)
|
||||
{
|
||||
U_TRACE(0, "UREDISClusterMaster::hashslotFromCommand(%V)", command.rep)
|
||||
|
||||
// expects hashable keys to be delivered as abc{hashableKey}xyz value blah \r\n
|
||||
|
||||
|
@ -1024,58 +1036,48 @@ private:
|
|||
end = command.find('}', beginning) - 1;
|
||||
|
||||
return hashslotForKey(command.substr(beginning, end - beginning));
|
||||
}
|
||||
|
||||
UREDISClusterClient& clientForHashslot(uint16_t hashslot)
|
||||
{
|
||||
U_TRACE(0, "UREDISClusterMaster::clientForHashslot(%u)", hashslot)
|
||||
|
||||
for (UHashMapNode *node : clusterNodes)
|
||||
{
|
||||
RedisClusterNode* workingNode = (RedisClusterNode *)(node->elem);
|
||||
|
||||
if ((workingNode->lowHashSlot <= hashslot) && (workingNode->highHashSlot >= hashslot)) return workingNode->client;
|
||||
}
|
||||
|
||||
UREDISClient<UTCPSocket>& clientForHashslot(uint16_t hashslot)
|
||||
return subscriptionClient; // never reached
|
||||
}
|
||||
|
||||
UREDISClusterClient& clientForASKip()
|
||||
{
|
||||
for (UHashMapNode *node : clusterNodes)
|
||||
{
|
||||
U_TRACE(0, "UREDISClusterClient::clientForHashslot(%u)", hashslot)
|
||||
|
||||
for (UHashMapNode *node : redisNodes)
|
||||
{
|
||||
RedisNode* workingNode = (RedisNode *)(node->elem);
|
||||
|
||||
if ((workingNode->lowHashSlot <= hashslot) || (workingNode->highHashSlot >= hashslot)) return workingNode->client;
|
||||
}
|
||||
|
||||
return *this; // never reached
|
||||
}
|
||||
|
||||
UREDISClient<UTCPSocket>& clientForASKip()
|
||||
{
|
||||
for (UHashMapNode *node : redisNodes)
|
||||
{
|
||||
RedisNode* workingNode = (RedisNode *)(node->elem);
|
||||
RedisClusterNode* workingNode = (RedisClusterNode *)(node->elem);
|
||||
|
||||
if (temporaryASKip == workingNode->ipAddress) return workingNode->client;
|
||||
}
|
||||
|
||||
return *this; // never reached
|
||||
}
|
||||
|
||||
UREDISClient<UTCPSocket>& clientForHashableKey(const UString& hashableKey) { return clientForHashslot(hashslotForKey(hashableKey)); }
|
||||
|
||||
public:
|
||||
UREDISClusterClient() : UREDISClient<UTCPSocket>()
|
||||
{
|
||||
U_TRACE_CTOR(0, UREDISClusterClient, "")
|
||||
}
|
||||
|
||||
~UREDISClusterClient()
|
||||
{
|
||||
U_TRACE_DTOR(0, UREDISClusterClient)
|
||||
}
|
||||
|
||||
void processResponse() final;
|
||||
void calculateNodeMap();
|
||||
|
||||
bool connect(const char* host = U_NULLPTR, unsigned int _port = 6379);
|
||||
return subscriptionClient; // never reached
|
||||
}
|
||||
|
||||
UREDISClusterClient& clientForHashableKey(const UString& hashableKey) { return clientForHashslot(hashslotForKey(hashableKey)); }
|
||||
|
||||
template<bool silence>
|
||||
const UVector<UString>& processPipeline(UString& pipeline, bool reorderable);
|
||||
|
||||
void calculateNodeMap();
|
||||
|
||||
public:
|
||||
|
||||
bool connect(const char* host = U_NULLPTR, unsigned int _port = 6379);
|
||||
|
||||
// all of these multis require all keys to exist within a single hash slot (on the same node isn't good enough)
|
||||
|
||||
UString clusterSingle(const UString& hashableKey, const UString& pipeline) { return clientForHashableKey(hashableKey).single(pipeline); }
|
||||
UString clusterSingle(const UString& hashableKey, const UString& pipeline) { return clientForHashableKey(hashableKey).single(pipeline); }
|
||||
const UVector<UString>& clusterMulti( const UString& hashableKey, const UString& pipeline) { return clientForHashableKey(hashableKey).multi(pipeline); }
|
||||
|
||||
void clusterSilencedMulti( const UString& hashableKey, UString& pipeline) { clientForHashableKey(hashableKey).silencedMulti(pipeline); }
|
||||
|
@ -1093,18 +1095,10 @@ public:
|
|||
bool clusterUnsubscribe(const UString& channel);
|
||||
bool clusterSubscribe( const UString& channel, vPFcscs callback);
|
||||
|
||||
// DEBUG
|
||||
|
||||
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
|
||||
const char* dump(bool _reset) const { return UREDISClient_Base::dump(_reset); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
U_DISALLOW_COPY_AND_ASSIGN(UREDISClusterClient)
|
||||
UREDISClusterMaster() : subscriptionClient(this) {}
|
||||
};
|
||||
|
||||
extern template const UVector<UString>& UREDISClusterClient::processPipeline<true>(UString& pipeline, bool reorderable);
|
||||
extern template const UVector<UString>& UREDISClusterClient::processPipeline<false>(UString& pipeline, bool reorderable);
|
||||
|
||||
extern template const UVector<UString>& UREDISClusterMaster::processPipeline<true>(UString& pipeline, bool reorderable);
|
||||
extern template const UVector<UString>& UREDISClusterMaster::processPipeline<false>(UString& pipeline, bool reorderable);
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -73,7 +73,7 @@ public:
|
|||
|
||||
U_INTERNAL_ASSERT_POINTER(client)
|
||||
|
||||
return UWebSocket::sendData(client->UClient_Base::socket, type, msg);
|
||||
return UWebSocket::sendData(false, client->UClient_Base::socket, type, msg);
|
||||
}
|
||||
|
||||
void close()
|
||||
|
@ -82,7 +82,7 @@ public:
|
|||
|
||||
U_INTERNAL_ASSERT_POINTER(client)
|
||||
|
||||
(void) UWebSocket::sendClose(client->UClient_Base::socket);
|
||||
(void) UWebSocket::sendClose(false, client->UClient_Base::socket);
|
||||
|
||||
client->UClient_Base::close();
|
||||
}
|
||||
|
|
|
@ -72,13 +72,13 @@ public:
|
|||
} WebSocketFrameData;
|
||||
|
||||
static bool checkForInitialData();
|
||||
static bool sendData(USocket* socket, int type, const char* data, uint32_t len);
|
||||
static bool sendData(const bool isServer, USocket* socket, int type, const char* data, uint32_t len);
|
||||
|
||||
static bool sendData(USocket* socket, int type, const UString& data) { return sendData(socket, type, U_STRING_TO_PARAM(data)); }
|
||||
static bool sendData(const bool isServer, USocket* socket, int type, const UString& data) { return sendData(isServer, socket, type, U_STRING_TO_PARAM(data)); }
|
||||
|
||||
static bool sendClose(USocket* socket)
|
||||
static bool sendClose(const bool isServer, USocket* socket)
|
||||
{
|
||||
U_TRACE(0, "UWebSocket::sendClose(%p)", socket)
|
||||
U_TRACE(0, "UWebSocket::sendClose(%b,%p)", isServer, socket)
|
||||
|
||||
// Send server-side closing handshake
|
||||
|
||||
|
@ -87,7 +87,7 @@ public:
|
|||
unsigned char status_code_buffer[2] = { (unsigned char)((status_code >> 8) & 0xFF),
|
||||
(unsigned char)( status_code & 0xFF) };
|
||||
|
||||
if (sendControlFrame(socket, U_WS_OPCODE_CLOSE, status_code_buffer, sizeof(status_code_buffer))) U_RETURN(true);
|
||||
if (sendControlFrame(isServer, socket, U_WS_OPCODE_CLOSE, status_code_buffer, sizeof(status_code_buffer))) U_RETURN(true);
|
||||
|
||||
U_RETURN(false);
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ private:
|
|||
static bool sendAccept(USocket* socket);
|
||||
static RETSIGTYPE handlerForSigTERM(int signo);
|
||||
static int handleDataFraming(UString* pbuffer, USocket* socket);
|
||||
static bool sendControlFrame(USocket* socket, int opcode, const unsigned char* payload, uint32_t payload_length);
|
||||
static bool sendControlFrame(const bool isServer, USocket* socket, int opcode, const unsigned char* payload, uint32_t payload_length);
|
||||
|
||||
U_DISALLOW_COPY_AND_ASSIGN(UWebSocket)
|
||||
|
||||
|
|
|
@ -549,15 +549,57 @@ int UREDISClient_Base::handlerRead()
|
|||
U_RETURN(U_NOTIFIER_OK);
|
||||
}
|
||||
|
||||
#if defined(U_STDCPP_ENABLE)
|
||||
|
||||
// by Victor Stewart
|
||||
|
||||
# if defined(HAVE_CXX17)
|
||||
void UREDISClusterClient::calculateNodeMap()
|
||||
{
|
||||
U_TRACE_NO_PARAM(0, "UREDISClusterClient::calculateNodeMap()")
|
||||
|
||||
void UREDISClusterClient::processResponse()
|
||||
{
|
||||
U_TRACE_NO_PARAM(0, "UREDISClusterClient::processResponse()")
|
||||
|
||||
if (UClient_Base::response.find("-MOVED", 0, 6) != U_NOT_FOUND)
|
||||
{
|
||||
// MOVED 3999 127.0.0.1:6381 => the hashslot has been moved to another master node
|
||||
|
||||
master->error = ClusterError::moved;
|
||||
|
||||
master->calculateNodeMap();
|
||||
}
|
||||
else if (UClient_Base::response.find("-ASK", 1, 4) != U_NOT_FOUND)
|
||||
{
|
||||
// ASK 3999 127.0.0.1:6381 => this means that one of the hash slots is being migrated to another server
|
||||
|
||||
master->error = ClusterError::ask;
|
||||
|
||||
uint32_t _start = UClient_Base::response.find(' ', 8) + 1,
|
||||
end = UClient_Base::response.find(':', _start);
|
||||
|
||||
(void)master->temporaryASKip.assign(UClient_Base::response.substr(_start, end - _start));
|
||||
}
|
||||
else if (UClient_Base::response.find("-TRYAGAIN", 0, 9) != U_NOT_FOUND)
|
||||
{
|
||||
//
|
||||
// * during a resharding the multi-key operations targeting keys that all exist and are all still in the same node (either the source or destination node) are still available.
|
||||
// * Operations on keys that don't exist or are - during the resharding - split between the source and destination nodes, will generate a -TRYAGAIN error. The client can try
|
||||
// * the operation after some time, or report back the error. As soon as migration of the specified hash slot has terminated, all multi-key operations are available again for
|
||||
// * that hash slot
|
||||
|
||||
|
||||
master->error = ClusterError::tryagain;
|
||||
|
||||
UTimeVal(0L, 1000L).nanosleep(); // 0 sec, 1000 microsec = 1ms
|
||||
}
|
||||
else
|
||||
{
|
||||
master->error = ClusterError::none;
|
||||
|
||||
UREDISClient_Base::processResponse();
|
||||
}
|
||||
}
|
||||
|
||||
void UREDISClusterMaster::calculateNodeMap()
|
||||
{
|
||||
U_TRACE_NO_PARAM(0, "UREDISClusterMaster::calculateNodeMap()")
|
||||
/*
|
||||
127.0.0.1:30001> cluster slots
|
||||
1) 1) (integer) 0
|
||||
|
@ -586,14 +628,16 @@ void UREDISClusterClient::calculateNodeMap()
|
|||
3) "58e6e48d41228013e5d9c1c37c5060693925e97e"
|
||||
*/
|
||||
|
||||
// the first node in each array is the master
|
||||
|
||||
bool findHashSlots = true;
|
||||
uint16_t workingLowHashSlot;
|
||||
uint16_t workingHighHashSlot;
|
||||
|
||||
(void) UREDISClient_Base::processRequest(U_RC_MULTIBULK, U_CONSTANT_TO_PARAM("CLUSTER SLOTS"));
|
||||
|
||||
UHashMap<RedisNode *> newNodes;
|
||||
const UVector<UString>& rawNodes = UREDISClient_Base::vitem;
|
||||
(void) subscriptionClient.processRequest(U_RC_MULTIBULK, U_CONSTANT_TO_PARAM("CLUSTER SLOTS"));
|
||||
|
||||
UHashMap<RedisClusterNode *> newNodes;
|
||||
const UVector<UString>& rawNodes = subscriptionClient.vitem;
|
||||
|
||||
for (uint32_t a = 0, b = rawNodes.size(); a < b; a+=2)
|
||||
{
|
||||
|
@ -612,12 +656,10 @@ void UREDISClusterClient::calculateNodeMap()
|
|||
}
|
||||
else
|
||||
{
|
||||
// first node in the array is the master
|
||||
|
||||
UString compositeAddress(50U);
|
||||
compositeAddress.snprintf(U_CONSTANT_TO_PARAM("%v.%v"), first.rep, second.rep);
|
||||
|
||||
RedisNode *workingNode = redisNodes.erase(compositeAddress);
|
||||
RedisClusterNode *workingNode = clusterNodes.erase(compositeAddress);
|
||||
|
||||
// in the case of MOVE some nodes will be new, but others we'll already be connected to
|
||||
if (workingNode)
|
||||
|
@ -625,8 +667,8 @@ void UREDISClusterClient::calculateNodeMap()
|
|||
workingNode->lowHashSlot = workingLowHashSlot;
|
||||
workingNode->highHashSlot = workingHighHashSlot;
|
||||
}
|
||||
else workingNode = new RedisNode(first, second.strtoul(), workingLowHashSlot, workingHighHashSlot);
|
||||
|
||||
else workingNode = new RedisClusterNode(first, second.strtoul(), workingLowHashSlot, workingHighHashSlot, this);
|
||||
|
||||
newNodes.insert(compositeAddress, workingNode);
|
||||
|
||||
workingNode = newNodes[compositeAddress];
|
||||
|
@ -636,121 +678,71 @@ void UREDISClusterClient::calculateNodeMap()
|
|||
}
|
||||
|
||||
// if any nodes were taken offline, the clients would've disconnected by default
|
||||
redisNodes.assign(newNodes);
|
||||
clusterNodes.assign(newNodes);
|
||||
}
|
||||
|
||||
bool UREDISClusterClient::connect(const char* host, unsigned int _port)
|
||||
bool UREDISClusterMaster::connect(const char* host, unsigned int _port)
|
||||
{
|
||||
U_TRACE(0, "UREDISClusterClient::connect(%S,%u)", host, _port)
|
||||
U_TRACE(0, "UREDISClusterMaster::connect(%S,%u)", host, _port)
|
||||
|
||||
if (UREDISClient<UTCPSocket>::connect(host, _port))
|
||||
{
|
||||
if (subscriptionClient.connect(host, _port))
|
||||
{
|
||||
calculateNodeMap();
|
||||
|
||||
// self handles the SUB/PUB traffic. Must be a dedicated client pre-Redis 6
|
||||
UEventFd::op_mask |= EPOLLET;
|
||||
UEventFd::op_mask &= ~EPOLLRDHUP;
|
||||
|
||||
UNotifier::insert(this, EPOLLEXCLUSIVE | EPOLLROUNDROBIN); // NB: we ask to listen for events to a Redis publish channel...
|
||||
subscriptionClient.UEventFd::fd = subscriptionClient.getFd();
|
||||
subscriptionClient.UEventFd::op_mask |= EPOLLET;
|
||||
subscriptionClient.UEventFd::op_mask &= ~EPOLLRDHUP;
|
||||
|
||||
UNotifier::insert(&subscriptionClient, EPOLLEXCLUSIVE | EPOLLROUNDROBIN); // NB: we ask to listen for events to a Redis publish channel...
|
||||
|
||||
U_RETURN(true);
|
||||
}
|
||||
}
|
||||
|
||||
U_RETURN(false);
|
||||
}
|
||||
|
||||
bool UREDISClusterClient::clusterUnsubscribe(const UString& channel) // unregister the callback for messages published to the given channels
|
||||
bool UREDISClusterMaster::clusterUnsubscribe(const UString& channel) // unregister the callback for messages published to the given channels
|
||||
{
|
||||
U_TRACE(0, "UREDISClusterClient::clusterUnsubscribe(%V)", channel.rep)
|
||||
U_TRACE(0, "UREDISClusterMaster::clusterUnsubscribe(%V)", channel.rep)
|
||||
|
||||
if (processRequest(U_RC_MULTIBULK, U_CONSTANT_TO_PARAM("UNSUBSCRIBE"), U_STRING_TO_PARAM(channel)))
|
||||
{
|
||||
if (pchannelCallbackMap == U_NULLPTR)
|
||||
{
|
||||
U_NEW(UHashMap<void*>, pchannelCallbackMap, UHashMap<void*>);
|
||||
}
|
||||
|
||||
(void) pchannelCallbackMap->erase(channel);
|
||||
if (subscriptionClient.processRequest(U_RC_MULTIBULK, U_CONSTANT_TO_PARAM("UNSUBSCRIBE"), U_STRING_TO_PARAM(channel)))
|
||||
{
|
||||
(void)subscriptionClient.UREDISClient_Base::pchannelCallbackMap->erase(channel);
|
||||
|
||||
U_RETURN(true);
|
||||
}
|
||||
}
|
||||
|
||||
U_RETURN(false);
|
||||
}
|
||||
|
||||
bool UREDISClusterClient::clusterSubscribe(const UString& channel, vPFcscs callback) // register the callback for messages published to the given channels
|
||||
bool UREDISClusterMaster::clusterSubscribe(const UString& channel, vPFcscs callback) // register the callback for messages published to the given channels
|
||||
{
|
||||
U_TRACE(0, "UREDISClusterClient::clusterSubscribe(%V,%p)", channel.rep, callback)
|
||||
U_TRACE(0, "UREDISClusterMaster::clusterSubscribe(%V,%p)", channel.rep, callback)
|
||||
|
||||
if (processRequest(U_RC_MULTIBULK, U_CONSTANT_TO_PARAM("SUBSCRIBE"), U_STRING_TO_PARAM(channel)))
|
||||
if (subscriptionClient.processRequest(U_RC_MULTIBULK, U_CONSTANT_TO_PARAM("SUBSCRIBE"), U_STRING_TO_PARAM(channel)))
|
||||
{
|
||||
if (subscriptionClient.UREDISClient_Base::pchannelCallbackMap == U_NULLPTR)
|
||||
{
|
||||
if (pchannelCallbackMap == U_NULLPTR)
|
||||
{
|
||||
U_NEW(UHashMap<void*>, pchannelCallbackMap, UHashMap<void*>);
|
||||
}
|
||||
U_NEW(UHashMap<void*>, subscriptionClient.UREDISClient_Base::pchannelCallbackMap, UHashMap<void*>);
|
||||
}
|
||||
|
||||
pchannelCallbackMap->insert(channel, (const void*)callback);
|
||||
subscriptionClient.UREDISClient_Base::pchannelCallbackMap->insert(channel, (const void*)callback);
|
||||
|
||||
U_RETURN(true);
|
||||
}
|
||||
}
|
||||
|
||||
U_RETURN(false);
|
||||
}
|
||||
|
||||
void UREDISClusterClient::processResponse()
|
||||
{
|
||||
U_TRACE_NO_PARAM(0, "UREDISClusterClient::processResponse()")
|
||||
|
||||
if (UClient_Base::response.find("MOVED", 0, 5) != U_NOT_FOUND)
|
||||
{
|
||||
// MOVED 3999 127.0.0.1:6381 => the hashslot has been moved to another master node
|
||||
|
||||
error = ClusterError::moved;
|
||||
|
||||
calculateNodeMap();
|
||||
}
|
||||
else if (UClient_Base::response.find("ASK", 0, 3) != U_NOT_FOUND)
|
||||
{
|
||||
// ASK 3999 127.0.0.1:6381 => this means that one of the hash slots is being migrated to another server
|
||||
|
||||
error = ClusterError::ask;
|
||||
|
||||
uint32_t _start = UClient_Base::response.find(' ', 8) + 1,
|
||||
end = UClient_Base::response.find(':', _start);
|
||||
|
||||
(void) temporaryASKip.replace(UClient_Base::response.substr(_start, end - _start));
|
||||
}
|
||||
|
||||
else if (UClient_Base::response.find("TRYAGAIN", 0, 8) != U_NOT_FOUND)
|
||||
{
|
||||
/**
|
||||
* during a resharding the multi-key operations targeting keys that all exist and are all still in the same node (either the source or destination node) are still available.
|
||||
* Operations on keys that don't exist or are - during the resharding - split between the source and destination nodes, will generate a -TRYAGAIN error. The client can try
|
||||
* the operation after some time, or report back the error. As soon as migration of the specified hash slot has terminated, all multi-key operations are available again for
|
||||
* that hash slot
|
||||
*/
|
||||
|
||||
error = ClusterError::tryagain;
|
||||
|
||||
UTimeVal(0L, 1000L).nanosleep(); // 0 sec, 1000 microsec = 1ms
|
||||
}
|
||||
else
|
||||
{
|
||||
error = ClusterError::none;
|
||||
|
||||
UREDISClient<UTCPSocket>::processResponse();
|
||||
}
|
||||
}
|
||||
|
||||
template const UVector<UString>& UREDISClusterClient::processPipeline<true>(UString& pipeline, bool reorderable);
|
||||
template const UVector<UString>& UREDISClusterClient::processPipeline<false>(UString& pipeline, bool reorderable);
|
||||
template const UVector<UString>& UREDISClusterMaster::processPipeline<true>(UString& pipeline, bool reorderable);
|
||||
template const UVector<UString>& UREDISClusterMaster::processPipeline<false>(UString& pipeline, bool reorderable);
|
||||
|
||||
template <bool silence>
|
||||
const UVector<UString>& UREDISClusterClient::processPipeline(UString& pipeline, const bool reorderable)
|
||||
const UVector<UString>& UREDISClusterMaster::processPipeline(UString& pipeline, const bool reorderable)
|
||||
{
|
||||
U_TRACE(0, "UREDISClusterClient::processPipeline(%V,%b)", pipeline.rep, reorderable)
|
||||
U_TRACE(0, "UREDISClusterMaster::processPipeline(%V,%b)", pipeline.rep, reorderable)
|
||||
|
||||
UString workingString(U_CAPACITY);
|
||||
UString workingString(pipeline.size());
|
||||
UVector<UString> commands(pipeline, "\r\n");
|
||||
uint16_t hashslot, workingHashslot, count = 0;
|
||||
|
||||
|
@ -769,7 +761,7 @@ const UVector<UString>& UREDISClusterClient::processPipeline(UString& pipeline,
|
|||
}
|
||||
}
|
||||
|
||||
UREDISClient<UTCPSocket>& client = clientForHashslot(hashslot);
|
||||
UREDISClusterClient& client = clientForHashslot(hashslot);
|
||||
|
||||
if constexpr (silence) (void) client.sendRequest(workingString);
|
||||
else
|
||||
|
@ -787,7 +779,7 @@ replay: (void) client.processRequest(U_RC_MULTIBULK, U_STRING_TO_PARAM(workingS
|
|||
|
||||
case ClusterError::ask:
|
||||
{
|
||||
UREDISClient<UTCPSocket>& temporaryClient = clientForASKip();
|
||||
UREDISClusterClient& temporaryClient = clientForASKip();
|
||||
|
||||
(void) temporaryClient.processRequest(U_RC_MULTIBULK, U_STRING_TO_PARAM(workingString));
|
||||
}
|
||||
|
@ -796,17 +788,15 @@ replay: (void) client.processRequest(U_RC_MULTIBULK, U_STRING_TO_PARAM(workingS
|
|||
case ClusterError::none: break;
|
||||
}
|
||||
|
||||
if constexpr (silence == false) vitem.move(client.vitem);
|
||||
if constexpr (silence == false) subscriptionClient.vitem.move(client.vitem);
|
||||
|
||||
count = 0;
|
||||
workingString.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
*/
|
||||
if (reorderable) {
|
||||
|
||||
if (reorderable)
|
||||
{
|
||||
for (UVectorStringIter it = commands.begin(); it != commands.end(); ) {
|
||||
|
||||
if (it == commands.begin()) hashslot = hashslotFromCommand(*it);
|
||||
|
@ -834,11 +824,10 @@ replay: (void) client.processRequest(U_RC_MULTIBULK, U_STRING_TO_PARAM(workingS
|
|||
if (commands.size() != 0) it = commands.begin();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
else
|
||||
{
|
||||
for (uint32_t index = 0, n = commands.size(); index < n; index++)
|
||||
{
|
||||
|
||||
{
|
||||
if (index == 0) hashslot = hashslotFromCommand(commands[0]);
|
||||
|
||||
UString command = commands[index];
|
||||
|
@ -848,8 +837,8 @@ replay: (void) client.processRequest(U_RC_MULTIBULK, U_STRING_TO_PARAM(workingS
|
|||
|
||||
workingHashslot = hashslotFromCommand(command);
|
||||
|
||||
if (workingHashslot == hashslot) {
|
||||
|
||||
if (workingHashslot == hashslot)
|
||||
{
|
||||
++count;
|
||||
|
||||
// goto isADirective;
|
||||
|
@ -863,14 +852,14 @@ replay: (void) client.processRequest(U_RC_MULTIBULK, U_STRING_TO_PARAM(workingS
|
|||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return vitem;
|
||||
|
||||
return subscriptionClient.vitem;
|
||||
}
|
||||
# endif
|
||||
|
||||
// DEBUG
|
||||
|
||||
# if defined(DEBUG)
|
||||
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
|
||||
const char* UREDISClient_Base::dump(bool _reset) const
|
||||
{
|
||||
UClient_Base::dump(false);
|
||||
|
@ -888,5 +877,4 @@ const char* UREDISClient_Base::dump(bool _reset) const
|
|||
|
||||
return U_NULLPTR;
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
# endif
|
||||
|
|
|
@ -538,7 +538,7 @@ void UClientImage_Base::handlerDelete()
|
|||
if (U_ClientImage_http(this) == '0')
|
||||
{
|
||||
if (bsocket_open &&
|
||||
UWebSocket::sendClose(socket))
|
||||
UWebSocket::sendClose(true, socket))
|
||||
{
|
||||
socket->close();
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ int UProxyPlugIn::handlerRequest()
|
|||
|
||||
while (UWebSocket::handleDataFraming(UWebSocket::rbuffer, UServer_Base::csocket) == U_WS_STATUS_CODE_OK &&
|
||||
(client_http->UClient_Base::prepareRequest(*UClientImage_Base::wbuffer), client_http->UClient_Base::sendRequestAndReadResponse()) &&
|
||||
UWebSocket::sendData(UServer_Base::csocket, UWebSocket::message_type, client_http->UClient_Base::response))
|
||||
UWebSocket::sendData(false, UServer_Base::csocket, UWebSocket::message_type, client_http->UClient_Base::response))
|
||||
{
|
||||
client_http->UClient_Base::clearData();
|
||||
|
||||
|
|
|
@ -90,6 +90,10 @@ int UWebSocketPlugIn::handlerRun()
|
|||
U_INTERNAL_ASSERT_EQUALS(UWebSocket::rbuffer, U_NULLPTR)
|
||||
U_INTERNAL_ASSERT_EQUALS(UWebSocket::message, U_NULLPTR)
|
||||
|
||||
#ifndef USE_LIBSSL
|
||||
U_ERROR("Sorry, I was compiled without SSL support so I can't use websocket");
|
||||
#endif
|
||||
|
||||
U_NEW_STRING(UWebSocket::rbuffer, UString(U_CAPACITY));
|
||||
U_NEW_STRING(UWebSocket::message, UString(U_CAPACITY));
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ else
|
|||
{
|
||||
// echo
|
||||
|
||||
if (UWebSocket::sendData(UServer_Base::csocket, UWebSocket::message_type, *UWebSocket::message) == false) U_http_info.nResponseCode = HTTP_INTERNAL_ERROR;
|
||||
if (UWebSocket::sendData(false, UServer_Base::csocket, UWebSocket::message_type, *UWebSocket::message) == false) U_http_info.nResponseCode = HTTP_INTERNAL_ERROR;
|
||||
}
|
||||
#endif
|
||||
-->
|
||||
|
|
|
@ -520,7 +520,8 @@ loop:
|
|||
|
||||
case U_WS_OPCODE_PING:
|
||||
{
|
||||
if (sendControlFrame(socket, U_WS_OPCODE_PONG, application_data, application_data_offset) == false)
|
||||
// implies only client initiates ping pong... so any pong is always server -> client
|
||||
if (sendControlFrame(true, socket, U_WS_OPCODE_PONG, application_data, application_data_offset) == false)
|
||||
{
|
||||
U_RETURN(U_WS_STATUS_CODE_PROTOCOL_ERROR);
|
||||
}
|
||||
|
@ -600,18 +601,32 @@ next:
|
|||
goto loop;
|
||||
}
|
||||
|
||||
bool UWebSocket::sendData(USocket* socket, int type, const char* data, uint32_t len)
|
||||
bool UWebSocket::sendData(const bool isServer, USocket* socket, int type, const char* data, uint32_t len)
|
||||
{
|
||||
U_TRACE(0, "UWebSocket::sendData(%p,%d,%.*S,%u)", socket, type, len, data, len)
|
||||
U_TRACE(0, "UWebSocket::sendData(%b,%p,%d,%.*S,%u)", isServer, socket, type, len, data, len)
|
||||
|
||||
if (UNLIKELY(len > 0xffffffff))
|
||||
{
|
||||
status_code = U_WS_STATUS_CODE_MESSAGE_TOO_LARGE;
|
||||
|
||||
U_RETURN(false);
|
||||
}
|
||||
|
||||
uint32_t header_length = (len > 125U ? 2U : 0) + (len > 0xffff ? 8U : 0);
|
||||
uint8_t opcode, masking_key[4];
|
||||
uint32_t header_length = 6U + (len > 125U ? 2U : 0) + (len > 0xffff ? 8U : 0), ncount = header_length + len;
|
||||
|
||||
if (isServer) header_length += 2U;
|
||||
else
|
||||
{
|
||||
header_length += 6U;
|
||||
*((uint32_t*)masking_key) = u_get_num_random();
|
||||
}
|
||||
|
||||
uint32_t ncount = header_length + len;
|
||||
|
||||
UString tmp(ncount), compressed;
|
||||
unsigned char* header = (unsigned char*)tmp.data();
|
||||
|
||||
*((uint32_t*)masking_key) = u_get_num_random();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case U_WS_MESSAGE_TYPE_TEXT:
|
||||
|
@ -649,39 +664,86 @@ bool UWebSocket::sendData(USocket* socket, int type, const char* data, uint32_t
|
|||
|
||||
header[0] = (opcode | 0x80);
|
||||
|
||||
if (len <= 125)
|
||||
{
|
||||
header[1] = (len | 0x80);
|
||||
// possible client header lengths
|
||||
// 2 4 12
|
||||
// possible server header lengths
|
||||
// 6 8 16
|
||||
|
||||
u_put_unalignedp32(header+2, *((uint32_t*)masking_key));
|
||||
switch (header_length)
|
||||
{
|
||||
// server + len < 125
|
||||
case 2:
|
||||
{
|
||||
header[1] = len;
|
||||
break;
|
||||
}
|
||||
else if (len > 125 &&
|
||||
len <= 0xffff) // 125 && 65535
|
||||
// server + (len > 125 && len <= 0xffff) // 125 && 65535
|
||||
case 4:
|
||||
{
|
||||
header[1] = (126 | 0x80);
|
||||
|
||||
u_put_unalignedp16(header+2, htons(len));
|
||||
u_put_unalignedp32(header+4, *((uint32_t*)masking_key));
|
||||
header[1] = 126;
|
||||
u_put_unalignedp16(header+2, htons(len));
|
||||
break;
|
||||
}
|
||||
else if (len > 0xffff &&
|
||||
len <= 0xffffffff)
|
||||
case 12:
|
||||
{
|
||||
header[1] = (127 | 0x80);
|
||||
|
||||
u_put_unalignedp64(header+2, htonl(len));
|
||||
u_put_unalignedp32(header+10, *((uint32_t*)masking_key));
|
||||
header[1] = 127;
|
||||
u_put_unalignedp64(header+2, htonl(len));
|
||||
break;
|
||||
}
|
||||
else
|
||||
// client + len < 125
|
||||
case 6:
|
||||
{
|
||||
status_code = U_WS_STATUS_CODE_MESSAGE_TOO_LARGE;
|
||||
header[1] = (len | 0x80);
|
||||
u_put_unalignedp32(header+2, *((uint32_t*)masking_key));
|
||||
break;
|
||||
}
|
||||
// client + (len > 125 && len <= 0xffff) // 125 && 65535
|
||||
case 8:
|
||||
{
|
||||
header[1] = (126 | 0x80);
|
||||
|
||||
U_RETURN(false);
|
||||
u_put_unalignedp16(header+2, htons(len));
|
||||
u_put_unalignedp32(header+4, *((uint32_t*)masking_key));
|
||||
break;
|
||||
}
|
||||
case 16:
|
||||
{
|
||||
header[1] = (127 | 0x80);
|
||||
|
||||
u_put_unalignedp64(header+2, htonl(len));
|
||||
u_put_unalignedp32(header+10, *((uint32_t*)masking_key));
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
default: break; // never reached
|
||||
}
|
||||
switch (header_length)
|
||||
{
|
||||
// server
|
||||
case 2:
|
||||
case 4:
|
||||
case 12:
|
||||
{
|
||||
header[6+i] = (data[i] ^ masking_key[i % 4]) & 0xff;
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
{
|
||||
header[2+i] = data[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
// client
|
||||
case 6:
|
||||
case 8:
|
||||
case 16:
|
||||
{
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
{
|
||||
header[6+i] = (data[i] ^ masking_key[i % 4]) & 0xff;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: break; // never reached
|
||||
}
|
||||
|
||||
U_SRV_LOG_WITH_ADDR("send websocket data (%u+%u bytes) %.*S to", header_length, len, len, data)
|
||||
|
||||
|
@ -690,27 +752,40 @@ bool UWebSocket::sendData(USocket* socket, int type, const char* data, uint32_t
|
|||
U_RETURN(false);
|
||||
}
|
||||
|
||||
bool UWebSocket::sendControlFrame(USocket* socket, int opcode, const unsigned char* payload, uint32_t payload_length)
|
||||
bool UWebSocket::sendControlFrame(const bool isServer, USocket* socket, int opcode, const unsigned char* payload, uint32_t payload_length)
|
||||
{
|
||||
U_TRACE(0, "UWebSocket::sendControlFrame(%p,%d,%.*S,%u)", socket, opcode, payload_length, payload, payload_length)
|
||||
U_TRACE(0, "UWebSocket::sendControlFrame(%b,%p,%d,%.*S,%u)", isServer, socket, opcode, payload_length, payload, payload_length)
|
||||
|
||||
uint8_t masking_key[4];
|
||||
uint32_t ncount = 6U + payload_length;
|
||||
uint32_t ncount = (isServer ? 2U : 6U) + payload_length;
|
||||
|
||||
UString tmp(ncount);
|
||||
unsigned char* header = (unsigned char*)tmp.data();
|
||||
|
||||
*((uint32_t*)masking_key) = u_get_num_random();
|
||||
header[0] = (opcode | 0x80);
|
||||
|
||||
header[0] = ( opcode | 0x80);
|
||||
header[1] = (payload_length | 0x80);
|
||||
if (isServer)
|
||||
{
|
||||
header[1] = payload_length;
|
||||
|
||||
u_put_unalignedp32(header+2, *((uint32_t*)masking_key));
|
||||
|
||||
for (uint32_t i = 0; i < payload_length; ++i)
|
||||
for (uint32_t i = 0; i < payload_length; ++i)
|
||||
{
|
||||
header[6+i] = (payload[i] ^ masking_key[i % 4]) & 0xff;
|
||||
header[2+i] = payload[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
header[1] = (payload_length | 0x80);
|
||||
|
||||
uint8_t masking_key[4];
|
||||
*((uint32_t*)masking_key) = u_get_num_random();
|
||||
|
||||
u_put_unalignedp32(header+2, *((uint32_t*)masking_key));
|
||||
|
||||
for (uint32_t i = 0; i < payload_length; ++i)
|
||||
{
|
||||
header[6+i] = (payload[i] ^ masking_key[i % 4]) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
if (USocketExt::write(socket, (const char*)header, ncount, UServer_Base::timeoutMS) == ncount)
|
||||
{
|
||||
|
@ -776,7 +851,7 @@ loop:
|
|||
rbuffer->setEmpty();
|
||||
|
||||
if (UServices::read(UProcess::filedes[2], *rbuffer) &&
|
||||
sendData(UServer_Base::csocket, message_type, *rbuffer))
|
||||
sendData(true, UServer_Base::csocket, message_type, *rbuffer))
|
||||
{
|
||||
rbuffer->setEmpty();
|
||||
|
||||
|
@ -811,7 +886,7 @@ data: if (handleDataFraming(rbuffer, UServer_Base::csocket) == U_WS_STATUS_CO
|
|||
// Send server-side closing handshake
|
||||
|
||||
if (UServer_Base::csocket->isOpen() &&
|
||||
sendClose(UServer_Base::csocket))
|
||||
sendClose(true, UServer_Base::csocket))
|
||||
{
|
||||
UClientImage_Base::close();
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
05D2
|
||||
05D8
|
||||
|
|
Loading…
Reference in New Issue
Block a user