Ho un server implementato in Netty che gestisce una richiesta dell'utente, parla con un middleware, e invia una risposta. L'I/O dovrebbe essere trascurabile rispetto al round-trip del middleware, quindi per minimizzare il blocco ho un ExecutionHandler nella pipeline su un OrderedMemoryAwareThreadPoolExecutor. Nessun problema finora.compiti Dare la priorità Netty in ThreadPoolExecutors
Sto cercando in che modo il server si comporta sotto carico pesante. Dalle esperienze passate con il nostro protocollo, tendiamo a essere sommersi da attacchi DOS casuali - il più delle volte, lo script di un utente è bloccato in un loop infinito o simile. Idealmente, potremmo dare la priorità ai loro canali una volta superata una determinata soglia di utilizzo, in modo che il servizio degli altri utenti non sia influenzato.
Ho implementato un semplice ThreadPoolExecutor che utilizza un PriorityBlockingQueue e imposta la priorità in base ai dati estratti dalla nostra classe Session (collegata al contesto in ChannelHandler). Di nuovo, nessun problema finora.
La difficoltà viene quando si cerca di sfruttare l'ordinamento e la memoria la consapevolezza di ThreadPoolExecutors built-in di Netty. Idealmente MyThreadPoolExecutor potrebbe semplicemente estendere OrderedMemoryAwareThreadPoolExecutor e collegare le code di priorità. Purtroppo questo non è possibile per due motivi: privato e finale. In dettaglio:
a) ThreadPoolExecutor.workQueue può essere impostato nel suo costruttore, ma MemoryAwareThreadPoolExecutor hard-coded questo come un LinkedTransferQueue, e non espone questo al suo bambino OrderedMemoryAwareThreadPoolExecutor (cioè MyThreadPoolExecutor non ha accesso per impostare esso). Se necessario, questo può essere superato con un brutto frammento di campo privato basato sulla riflessione.
b) Mi piacerebbe poter eseguire l'override di MyThreadPoolExecutor.doUnorderedExecute(), in modo che sia possibile inserire la gestione delle priorità e creare gli oggetti necessari, ma è dichiarato definitivo. Il codice che lo chiama non ha bisogno di essere cambiato.
Il risultato è che per mantenere tutte le belle caratteristiche Netty, ma usare una coda di priorità, avrei dovuto Copy'n'Paste sia OrderedMemoryAwareThreadPoolExecutor e MemoryAwareThreadPoolExecutor, modificare un paio di righe di ciascuno, e quindi estendere da lì. Questo non mi sembra una buona pratica di codifica! Anche considerando che mette in allarme campane di allarme.
Ora, per alcune domande:
1) Perchè sono la soluzione del problema che non va? Sto abbaiando completamente dall'albero sbagliato per quello che voglio ottenere?
2) In caso contrario, c'è un modo migliore per farlo rispetto a quelli discussi sopra?
3) L'approccio di cui sopra porta con sé un rischio di morire di fame per i compiti de-priorità in cui il carico totale del server è costantemente al massimo della capacità. Sono pronto a tollerarlo per gli utenti "cattivi", ma non appena vengono restituiti allo stato normale, i loro compiti esistenti continueranno a morire di fame e per preservare l'ordine devono essere aggiunti nuovi compiti con priorità più alta. Avete qualche consiglio su come meglio affrontarlo? (Ban degli utenti non è consentito dal business.)
4) Questa è la metà-domanda, mezzo-feedback. La netta documentazione per OrderedMemoryAwareThreadPoolExecutor ha un comodo diagramma per il thread X & Y - presumibilmente questi sono i thread raggruppati nel ThreadPoolExecutor e non i thread di I/O worker? Potrebbe valere la pena renderlo più chiaro. Inoltre, quando non si utilizza un ExecutionHandler, ogni canale è associato a un singolo thread di I/O worker - questo è ancora il caso quando dietro un ExecutionHandler? è l'ordine in cui le attività vengono aggiunte a ExecutionHandler e sono garantite come l'ordine in cui arrivano nel Canale?Se questo è il caso, allora non riesco a vedere come Thread X nei documenti per MemoryAwareThreadPoolExecutor possa elaborare l'evento 2 prima dell'evento 1 - Accetto che qui thread diversi possano finire il lavoro in qualsiasi ordine, ma non riesco a vedere come il lavoro può essere assegnato allo stesso thread in ordine (si apre da workQueue). I documenti di ExecutionHandler suggeriscono questo, ma trarrebbero beneficio da un piccolo dettaglio in più.
Grazie mille per la lettura e ogni aiuto è molto apprezzato.
Questo server è ancora in fase di sviluppo, ma quello (C++) che sta sostituendo è fortemente associato al database. Abbiamo risolto la possibilità di un carico elevato eliminando il database (v. Cattive notizie) con un numero limitato di thread di lavoro, ma questo ha ammesso la possibilità che un utente affamato metta a tacere tutti i lavoratori e peggiorando il servizio di tutti gli altri (abbiamo SLA piuttosto stretti da rispettare). La nostra soluzione a questo problema nel vecchio server era basata su supposizioni che non sono valide per nulla, quindi dobbiamo trovare qualcosa di meglio - da qui la domanda. Non ci preoccupiamo ancora delle dimensioni della piscina! – Liche