1 // ANNA - Anna is Not 'N' Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // https://bitbucket.org/testillano/anna
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
17 // * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
37 #ifndef anna_comm_CongestionController_hpp
38 #define anna_comm_CongestionController_hpp
42 #include <anna/config/defines.hpp>
43 #include <anna/core/Singleton.hpp>
44 #include <anna/core/mt/NRMutex.hpp>
45 #include <anna/core/util/Average.hpp>
58 class LocalConnection;
62 Gestor de congestion autonomo.
64 Su funcionamiento a grandes rasgos es el siguiente:
66 -# El campo \em getsockopt (...SO_RCVBUF...) nos da el tamaño maximo del buffer de recepcion
67 para un socket dado. Segun hemos visto puede varias desde 32K hasta 80 K, dependiendo del sistema
68 operativo y la configuracion. Independientemente del valor particular este sera nuestro limite
69 maximo (100%), ya que si la longitud de la cola de espera alcanza este valor, el socket dejara de
70 estar disponible para todos los clientes.
71 -# El metodo \em ioctl (....FIONREAD...) nos da el numero de bytes pendientes de tratar en el socket.
72 -# La carga del socket estara definida por (2) * 100 / (1).
73 -# Con esta clase podemos limitar la carga maxima soportada por los sockets de nuestra
74 aplicacion. Por defecto sera 60% del valor (1), un valor mas conservador podria ser 40%.
75 -# La zona sin congestion estara entre 0% y el valor (4). La zona de congestion estara entre (valor (4) + 1)% y el 100%.
76 -# La zona de congestion se divide, a su vez, en 4 zonas logicas: Si la carga del socket esta en el
77 primer 25% se descartan mensajes con una probabilidad del 25%. Si la carga esta en el segundo 25% se
78 descartan mensajes con una probabilidad del 50%. Si la carga esta entre el 51% y el 85% se descartan
79 paquetes con una probabilidad del 85% y si esta entre el 86% y el 100% se descartan los paquetes con
80 una probabilidad del 99%.
84 \li Trata cada socket de forma independiente, con lo que ajustamos mucho mejor el rendimiento de
86 \li No hay que imaginar los 4 niveles de a8_controlniveles. Solo tendremos que establecer el nivel
87 de carga maximo y el resto lo hace el proceso por si mismo. Sea como sea, es mucho mas facil ajustar
88 un unico valor que los 8 o 10 necesarios con el sistema anterior.
89 \li No requiere ninguna configuracion en base de datos.
91 class CongestionController : public Singleton <CongestionController> {
94 * Nivel máximo de congestión.
96 static const int MaxLevel = 4;
99 Posibles resultados de CongestionController::getAdvice.
103 None, /**< No hay datos para tomar una decision. */
104 Process, /**< Se puede procesar el mensaje sin contribuir a la congestion */
105 Discard /**< Se aconseja descartar para limitar la congestion */
110 Posibles formas de calcular la carga que hara aconsejar el descarte o aceptacion de un mensaje.
114 Auto, /**< Escoge la forma de calculo que mejor se adapte al modo de compilacion. Normalmente
115 El calculo de tipo Local sera mas util en modo MT, mientras que el Global puede ser
116 mas aconsejable en modo ST.
118 Local, /**< El calculo de la carga se calcula de forma independiente para cada Socket */
119 Global /**< El calculo de la carga se calcula de forma conjunta para todos los Sockets */
123 typedef std::pair <int, int> Workload;
125 static const int DefaultLimit = 60;
128 * Valor máximo que se puede asignar al control de bytes pendientes de tratar.
130 static const int MaxPendingBytes;
133 Devuelve el modo que estamos usando para calcular la carga.
134 \return El modo que estamos usando para calcular la carga.
136 Mode::_v getMode() const throw() { return a_mode; }
139 * Devuelve el número total de mensajes recibididos.
140 * \return el número total de mensajes recibididos.
142 int getMessageCounter() const throw() { return a_messageCounter; }
145 * Devuelve el número de mensajes tratados correctamente.
146 * \return el número de mensajes tratados correctamente.
148 int getSuccessCounter() const throw() { return a_messageCounter - a_discardedCounter; }
151 Establece el limite de carga que vamos a imponer.
152 \param limit Limite de carga. Debe ser un valor entre 0 y 100.
154 void setLimit(const int limit) throw();
157 Establece el modo en que vamos a calcular la carga.
158 \param mode Modo usado para calcular la carga.
160 void setMode(const Mode::_v mode) throw() {
161 if((a_mode = mode) == Mode::Auto) {
162 WHEN_MULTITHREAD(a_effectiveMode = Mode::Local);
163 WHEN_SINGLETHREAD(a_effectiveMode = Mode::Global);
165 a_effectiveMode = mode;
169 Devuelve \em true si esta instancia ha recibido datos o \em false en otro caso.
170 \return \em true si esta instancia ha recibido datos o \em false en otro caso.
172 bool isEmpty() const throw() { return a_avgWorkload.isEmpty(); }
175 * Establece el nº máximo de bytes que deberían tener los socket en la cola de entrada.
176 * \param maxPendingBytes Número máximo de bytes que debería tener los socket en la cola de entrada.
177 * \warning El valor indicado debe estar dentro del siguiente ámbito:
179 * [#comm::Communicator::getReadingChunkSize, min (#comm::Communicator::getReadingChunkSize * 4, #MaxPendingBytes)]
182 void setMaxPendingBytes(const int maxPendingBytes) throw(RuntimeException);
185 Devuelve el consejo sobre si debemos tratar/o no el ultimo mensaje recibido
186 por el ClientSocket recibido como parametro.
187 \param clientSocket Socket cliente por el que hemos recibido el ultimo mensaje.
188 \return Advice::Process para indicar que debemos procesar el mensaje
189 , Advice::None para indicar que todavia no tiene datos para tomar una
190 decision clara o Advice::Discard para indicar que debemos descartar.
192 Advice::_v getAdvice(const ClientSocket& clientSocket) throw();
195 Devuelve el consejo sobre si debemos tratar/o no el ultimo mensaje recibido
196 por el ClientSocket recibido como parametro.
197 \param clientSocket Socket cliente por el que hemos recibido el ultimo mensaje.
198 \return Advice::Process para indicar que debemos procesar el mensaje
199 , Advice::None para indicar que todavia no tiene datos para tomar una
200 decision clara o Advice::Discard para indicar que debemos descartar.
202 Advice::_v getAdvice(const ClientSocket* clientSocket) throw() {
203 return (clientSocket == NULL) ? Advice::Process : getAdvice(*clientSocket);
207 * Devuelve información sobre las estadísticas de carga en las que se basó para hacer
210 * \warning Sólo debería invocarse a este método después de invocar a #getAdvice.
211 * \warning Este método no es MT-estricto, por lo que en un entorno MT los valores
212 * sobre los que se calculó el último #getAdvice y los datos obtenidos con este método
213 * pueden haber variado ligeramente.
218 Workload getAccumulatedWorkload() const throw();
221 * Devuelve información sobre las estadísticas de carga del socket recibido como parámetro.
224 * \param clientSocket Socket del que se quiere obtener el estado actual de ocupación.
226 * \warning Sólo debería invocarse a este método después de invocar a #getAdvice.
227 * \warning Este método no es MT-estricto, por lo que en un entorno MT los valores
228 * sobre los que se calculó el último #getAdvice y los datos obtenidos con este método
229 * pueden haber variado ligeramente.
234 Workload getCurrentWorkload(const ClientSocket& clientSocket) const throw();
237 Devuelve un documento XML con la informacion relevante sobre esta clase.
238 \return un documento XML con la informacion relevante sobre esta clase.
240 xml::Node* asXML(xml::Node* parent) const throw();
243 * Extrae la carga media que soporta es proceso. La carga es un número en [0, 100].
244 * \return la carga media que soporta es proceso. La carga es un número en [0, 100].
246 static int getLoad(const Workload& workload) throw() { return workload.second; }
249 * Extrae el nivel de carga en la que está el proceso. El nivel de carga es un número en [0, 4].
250 * \return el nivel de carga en la que está el proceso. El nivel de carga es un número en [0, 4].
252 static int getLevel(const Workload& workload) throw() { return workload.first; }
255 static const Millisecond DelayTrace;
256 static const int UnusedPendingBytes = -1;
257 static const Millisecond HeartBeat;
259 //----------------------------------------------------------------------------------
260 // a_limit: % de ocupacion maxima al que vamos a limitar los canales.
261 // a_discardLevel: Valor maximo del % de cada nivel de descarte. 0 = 25, 1 = 50,
263 //----------------------------------------------------------------------------------
265 int a_discardLevel [MaxLevel];
266 int a_percentage [MaxLevel];
269 Mode::_v a_effectiveMode;
270 Average <unsigned int> a_avgWorkload;
271 unsigned int a_messageCounter;
272 unsigned int a_discardedCounter;
273 mutable Millisecond a_timeTrace;
274 int a_maxPendingBytes;
275 int a_incomingSocketCounter;
276 Millisecond a_tickTime;
278 void incrementIncomingSocket() throw(RuntimeException);
279 void decrementIncomingSocket() throw(RuntimeException);
281 CongestionController();
282 CongestionController(const CongestionController&);
284 int calculeWorkload(const ClientSocket&) const throw();
286 friend class Singleton <CongestionController>;
288 friend class ClientSocket;
291 friend class handler::LocalConnection;
292 // incrementIncomingSocket, decrementIncomingSocket