/* * predixy - A high performance and full features proxy for redis. * Copyright (C) 2017 Joyield, Inc. * All rights reserved. */ #include #include "DC.h" #include "Proxy.h" #include "ServerPool.h" #include "ServerGroup.h" ServerGroup::ServerGroup(ServerPool* pool, const String& name): mPool(pool), mName(name) { mServs.reserve(Const::MaxServInGroup); } ServerGroup::~ServerGroup() { } Server* ServerGroup::getMaster() const { FuncCallTimer(); int cnt = mServs.size(); for (int i = 0; i < cnt; ++i) { Server* s = mServs[i]; if (!s->online()) { continue; } if (s->role() == Server::Master) { return s; } } return nullptr; } struct ServCond { Server* serv = nullptr; int priority = 0; }; Server* ServerGroup::getServer(Handler* h, Request* req) const { FuncCallTimer(); Server* serv = nullptr; if (req->requireWrite()) { int cnt = mServs.size(); for (int i = 0; i < cnt; ++i) { Server* s = mServs[i]; if (!s->online()) { continue; } if (s->role() == Server::Master) { serv = s; break; } } } else if (auto dataCenter = mPool->proxy()->dataCenter()) { serv = getReadServer(h, dataCenter->localDC()); } else { serv = getReadServer(h); } logDebug("server group %s for req %ld get server %s", mName.data(), req->id(), (serv ? serv->addr().data() : "None")); return serv; } Server* ServerGroup::getReadServer(Handler* h) const { FuncCallTimer(); Server* servs[Const::MaxServInGroup]; Server* deadServs[Const::MaxServInGroup]; int n = 0; int dn = 0; int prior = 0; int dprior = 0; int pendRequests = INT_MAX; int cnt = mServs.size(); for (int i = 0; i < cnt; ++i) { Server* s = mServs[i]; if (!s->online()) { continue; } int rp = 0; if (s->role() == Server::Master) { rp = mPool->masterReadPriority(); } else if (s->role() == Server::Slave) { rp = s->isStatic() ? mPool->staticSlaveReadPriority() : mPool->dynamicSlaveReadPriority(); } if (rp <= 0) { continue; } if (s->fail()) { if (rp > dprior) { dprior = rp; deadServs[0] = s; dn = 1; } else if (rp == dprior) { deadServs[dn++] = s; } } else { if (rp > prior) { prior = rp; pendRequests = h->getPendRequests(s); servs[0] = s; n = 1; } else if (rp == prior) { int preqs = h->getPendRequests(s); if (preqs < pendRequests) { pendRequests = preqs; servs[0] = s; n = 1; } else if (preqs == pendRequests) { servs[n++] = s; } } } } Server** ss = nullptr; if (n > 0) { ss = servs; } else if (dn > 0) { ss = deadServs; n = dn; } else { return nullptr; } n = h->rand() % n; return ss[n]; } Server* ServerGroup::getReadServer(Handler* h, DC* localDC) const { FuncCallTimer(); Server* servs[Const::MaxServInGroup]; int sprior[Const::MaxServInGroup]; int pendRequests[Const::MaxServInGroup]; int num = 0; DC* dcs[Const::MaxServInGroup]; DC* deadDCs[Const::MaxServInGroup]; int n = 0; int dn = 0; int prior = 0; int dprior = 0; int cnt = mServs.size(); for (int i = 0; i < cnt; ++i) { Server* s = mServs[i]; if (!s->online()) { continue; } int rp = 0; if (s->role() == Server::Master) { rp = mPool->masterReadPriority(); } else if (s->role() == Server::Slave) { rp = s->isStatic() ? mPool->staticSlaveReadPriority() : mPool->dynamicSlaveReadPriority(); } if (rp <= 0) { continue; } DC* dc = s->dc(); if (!dc) { continue; } int dcrp = localDC->getReadPriority(dc); if (dcrp <= 0) { continue; } servs[num] = s; sprior[num] = rp; pendRequests[num++] = h->getPendRequests(s); if (s->fail()) { if (dcrp > dprior) { dprior = dcrp; deadDCs[0] = dc; dn = 1; } else if (dcrp == dprior) { deadDCs[dn++] = dc; } } else { if (dcrp > prior) { prior = dcrp; dcs[0] = dc; n = 1; } else if (dcrp == prior) { dcs[n++] = dc; } } } DC** sdc = nullptr; if (n > 0) { sdc = dcs; } else if (dn > 0) { sdc = deadDCs; n = dn; } else { return nullptr; } bool found = false; DC* dc = nullptr; if (n > 1) { int m = h->idUnique().unique(sdc, n); int weights[Const::MaxServInGroup]; int w = 0; n = 0; for (int i = 0; i < m; ++i) { int dw = localDC->getReadWeight(sdc[i]); if (dw > 0) { w += dw; sdc[n] = sdc[i]; weights[n++] = w; } } if (w > 0) { w = h->rand() % w; int i = std::lower_bound(weights, weights + n, w) - weights; dc = sdc[i]; found = true; } } else if (localDC->getReadWeight(sdc[0]) > 0) { dc = sdc[0]; found = true; } if (!found) { return nullptr; } Server* deadServs[Const::MaxServInGroup]; n = 0; dn = 0; prior = 0; dprior = 0; int preqs = INT_MAX; for (int i = 0; i < num; ++i) { Server* s = servs[i]; if (servs[i]->dc() != dc) { continue; } if (s->fail()) { if (sprior[i] > dprior) { dprior = sprior[i]; deadServs[0] = s; dn = 1; } else if (sprior[i] == dprior) { deadServs[dn++] = s; } } else { if (sprior[i] > prior) { prior = sprior[i]; preqs = pendRequests[i]; servs[0] = s; n = 1; } else if (sprior[i] == prior) { if (pendRequests[i] < preqs) { preqs = pendRequests[i]; servs[0] = s; n = 1; } else if (pendRequests[i] == preqs) { servs[n++] = s; } } } } Server** ss = nullptr; if (n > 0) { ss = servs; } else if (dn > 0) { ss = deadServs; n = dn; } else { return nullptr; } n = h->rand() % n; return ss[n]; } /*************************************************** * other thread may read ServerGroup when add or remoev, * but we don't use lock * * **************************************************/ void ServerGroup::add(Server* s) { if (mServs.size() == mServs.capacity()) { return; } for (unsigned i = 0; i < mServs.size(); ++i) { if (mServs[i] == s) { return; } } mServs.push_back(s); } void ServerGroup::remove(Server* s) { for (unsigned i = 0; i < mServs.size(); ++i) { if (mServs[i] == s) { mServs[i] = mServs.back(); mServs.resize(mServs.size() - 1); return; } } }