TL;DR: Reinstalați prin repornirea serverului (Verificați actualizarea finală).
Aceasta este o întrebare veche, dar nu am găsit încă o soluție și în acest moment nu știu unde să caut.
Avem trei aplicații web Java (de primăvară, fără pornire) implementate pe două servere: o aplicație este pe un GCP Compute Engine implementat pe Tomcat9 (portul 80) și celelalte două sunt pe serverul nostru intern pe o instanță a Tomcat8.5 ( portul 8080 redirecționat de la portul 80). Ambele sisteme au Mysql8 și folosesc aproape aceeași configurație pentru a se conecta la el: DataSource pentru DB principal și ConnectionProvider pentru chiriași.
Problema este că, atunci când sunt redistribuite, unele conexiuni mai vechi din bazin (HikariCP) nu sunt distruse, în timp ce celelalte sunt. Aceste conexiuni care rămân sunt de la furnizorul nostru de conexiuni multi-locatari.Chiriașul principal, ca să spunem așa, ucide în mod corespunzător conexiunile mai vechi. Acest lucru duce, desigur, la cazul în care avem atât de multe conexiuni în care rămânem fără ele, aruncând SQLExceptions. Am corectat asta prin creșterea numărului de conexiuni, dar aceasta nu este o soluție.
Redistribuim doar actualizând fișierul război fără GUI. Sunt convins că acesta este ceea ce cauzează problema, dar nu explică cu adevărat de ce unele conexiuni sunt închise corect, iar altele nu.
Ce am incercat:
- Am văzut răspunsuri înrudite despre asta (care se ocupă mai ales cu PHP), unde
Conexiunile Mysql rămân în starea Sleep bine după ce munca lor este terminată.
Am încercat și remediile furnizate în acele întrebări, deoarece acestea
părea rezonabil și pentru cazul meu. Lucruri precum scăderea
cel
wait_timeout
și interactvive_timeout
la 30 de minute.
- Configurația noastră HikariCP nu are conexiunile după 10 minute și au făcut-o
maxLifetime
de 15 minute. Chiar și după ore, conexiunile nu
se închid și chiar se reîmprospătează după acele 30 de minute. De aceasta
Adică ora afișată de interogare SELECT * FROM information_schema.processlist GROUP BY db;
merge până la 1799 (chiar mai puțin) și apoi înapoi la 0. De ce? Știu că sistemul nu este folosit de utilizatori la momentul respectiv și jurnalele arată că HikariCP este conștient de doar 4 conexiuni (cele pe care le-am configurat) în loc de până la 20, uneori sunt „active”.
Folosim Spring Data JPA, așa că toată gestionarea conexiunilor este gestionată de Hibernate/JPA. Conexiunile sunt, de asemenea, reîmprospătate corespunzător de către Hikari, așa că nu cred că este ceva cu conexiunile lăsate deschise în cod.
Până acum, sunt sigur că nu este o problemă cu Hikari (și prin asta mă refer la configurația noastră). Acest lucru mă face să cred că este ceva ciudat cu configurația bazei de date sau pur și simplu nu ne redistribuim corect.
Cred că această problemă va dispărea dacă reconstruiesc aspectul serverelor (scuzați-mi lipsa de vocabular) având ambele aplicații web în propria lor instanță Tomcat și folosind Apache sau Nginx pentru a le folosi. Am făcut această configurație în mediul meu de testare și îmi doresc să o fac de ceva vreme, dar este greu să justific o astfel de schimbare a poziției mele (aproape un dezvoltator nu chiar junior, dar backend care cumva s-a ocupat de asta). Chiar și așa, este o schimbare majoră, îmi va dura câteva zile cât lucrez la mai multe lucruri și chiar mai degrabă repar (corespunzător) configurația curentă decât să reconstruiesc serverul.
Alte opțiuni sunt programarea repornirilor serverului + db. Sistemul nostru este regional și încă puținii noștri utilizatori lucrează la programul obișnuit, așa că nu vor observa niciodată o repornire zilnică, să zicem, la 3 AM. Pur și simplu nu-mi place asta și cred că este la fel de ineficient ca creșterea orbește max_connections
din fiecare zi IMO.
Există, de asemenea, opțiunea de a reconstrui modul în care ne ocupăm de chiriașii noștri multipli. Folosim ConnectionProvider și aceste conexiuni sunt cele „defecte”. Am văzut câteva exemple de alte abordări folosind DataSource și știu că DataSource nu are această problemă, deoarece conexiunile la baza de date „principală” se defectează conform așteptărilor la redistribuire. Chiar și așa, încă cred că aceasta este o problemă de configurare.
Din cauza lipsei mele de experiență și a câte lucruri trebuie să mă uit, cred că am trecut cu vederea ceva în documente sau pur și simplu nu înțeleg cu adevărat configurațiile pe care le-am atins. Și oricât de pierdut sunt, am venit să caut experiența altora în această chestiune. Mai este ceva la care ar trebui să mă uit? M-am instalat si eu jurnalele_interogărilor_lente
dar fișierul menționat este încă gol după zile.
A mai avut cineva astfel de probleme? Dacă aveți nevoie de mai multe informații despre structura sau implementarea noastră, nu ezitați să le solicitați. După cum ați putea ghici, suntem o companie mică, care încă învățăm frânghiile acestui lucru.
ACTUALIZAȚI:
Am lansat câteva metode suplimentare în backend-ul nostru care probabil ar trebui să ajute cu conexiunile suplimentare. Unele metode nu au fost suprascrise și, deoarece ne extindem de la o altă clasă, este posibil ca metoda super să nu funcționeze. Aceste metode vizează în mod specific structura de date de la care sunt accesate conexiunile.
De asemenea, după o redistribuire, am văzut conexiunile mergând de la 4 la 8 (prevăzut: cele 4 de la prima implementare și cele 4 în plus de la redistribuire) dar după câteva ore numărul de conexiuni a scăzut la 6. Speram să fie sfârșitul, dar a doua zi am avut din nou acele 8 conexiuni.
Și mai rău, astăzi am avut șansa să repornesc unele servicii și am experimentat repornirea doar a serviciului de baze de date. La început, părea să scadă conexiunile la 4 așteptați per chiriaș, dar după un timp a crescut la aceeași valoare pe care o avea înainte de a reporni. Acest lucru îmi spune că conexiunile sunt ținute ostatice(?) de către Tomcat, ceea ce înseamnă că poate există ceva în documentație care abordează acest comportament. Nu am găsit cuvintele cheie potrivite pentru a-l găsi, dar pariul meu este în jurul contextului, tărâmului sau unei supape.
Dacă nu găsesc nimic, voi lansa un ConnectionProvider personalizat pe care l-am extins de la a EntityManagerFactoryBean
. În aceasta, am stabilit un Stop()
metoda de declansare a @PreDestroy
metoda accesând structura de date cu conexiunile chiriașilor și închiderea lor manuală cu metodele proprii Hikari. În teorie, acesta este cel mai mult pe care pot face din cod pentru a închide aceste conexiuni. Dacă asta nu funcționează și nici nu găsesc nimic în documentele lui Tomcat, va trebui să vorbesc și să aleg între reporniri programate sau reconstruirea serverului + „redistribuiri corecte” (Stop, Update, Start).
UPDATE 2:
Am investit ieri încercând să închid manual conexiunile folosind metoda descrisă în ultima actualizare și cu o altă metodă ajutându-mă cu o ServletContextListener
. Nici unul nu a funcționat și am aflat că metoda închide()
în HikariCPs furnizorul de conexiuni nu se referea la conexiuni, așa că da. De asemenea, am decis să încerc și să generez în mod dinamic ConnectionProviders într-un bean, cu metoda de închidere/distrugere adecvată, dar din moment ce metoda pe care m-am gândit să o folosesc nu este destinată pentru asta, voi renunța parțial la această idee.
Următorul: Schimbați de la Furnizori de conexiune
la Surse de date
. Dacă acest lucru funcționează, atunci putem continua redistribuirea așa cum facem întotdeauna. Voi încerca cele trei metode cu care am venit (în cazul în care conexiunile prezintă aceeași problemă la redistribuire): Configurarea unui @PreDestroy
metoda pentru iterarea manuală a hărții DataSources și închiderea tuturor conexiunilor relevante, generarea și înregistrarea dinamică a tuturor Sursă de date
s ca boabe (probabil „grupându-le” cu o interfață sau ceva deci MultiTenantResolver
poate lucra cu el sau luând prima abordare dar închizând conexiunile într-un ServletContextListener
.
Un alt lucru pe care l-am găsit este că conexiunile sunt menținute la un nivel mai mare față de contextul aplicațiilor web. Acestea sunt informații cheie, dar, sincer, nu înțeleg suficient nici de ce un set de conexiuni dintr-o aplicație nu se închide în timp ce celălalt set nu de ce Tomcat nu lasă acele fire/conexiuni să moară după expirarea timpului. Sursa acestor informații este această întrebare de la StackOverflow.
Am reușit să „taie o bucată din server” în tăcere și să configurez un mediu de testare personal în mediul de testare. Deoarece eu sunt responsabil din punct de vedere tehnic de asta și asta într-un efort de a remedia lucrurile care se desfășoară în prezent în producție, cred că sunt îndreptățit.
S-ar putea să încerc întrebând în SO și Grupul Google HikariCP, deși cu scopuri diferite pentru a menține întrebarea mea relevantă pentru ambele comunități.
ACTUALIZARE 3
Trecerea de la ConnectionProvider la DataSource a rezolvat jumătate din probleme și a adus noi erori, mai confuze:
- În timp ce majoritatea pool-urilor au fost inițializate corect la 4 conexiuni la redistribuire, două dintre acele pool-uri au rămas în vechiul comportament (4 din implementarea inițială + 4 din noua implementare) și unul, cumva, a ajuns cu 12 la redistribuire. Acestea sunt cele 4 inițiale, cele 4 de la redistribuire și doar niște 4 aleatorii în plus.
- În timp ce testam orice comportament ciudat folosind sistemul, am observat că de fiecare dată când schimbam chiriașii, era creat un nou grup. Mai târziu, am aflat că, de fapt, doar două pool-uri erau create la pornire și fiecare alt pool a fost creat doar la cerere. A fost în regulă, într-adevăr, dar mai aveam un chiriaș cu niște conexiuni aleatorii la pornire care au avut loc când am folosit acea bază de date.
Apoi am încercat toate opțiunile mele și am închis manual conexiunile în timpul opririi, dar nu pot spune cu adevărat că toate acestea au funcționat.
Se pare că trebuie doar să schimb modul în care funcționează serverul. Sunt puțin surprins că par să nu găsesc un răspuns, indiferent la ce mă uit și devin frustrat că, după tot timpul pe care l-am investit în asta, totul s-ar rezolva probabil într-un fișier batch care să aibă grijă de redistribuire prin închidere, înlocuire și repornire.
În Documentația Hikari se precizează că pentru implementări la cald (Și reinstalări la cald prin extensie) trebuie să închideți conexiunile, dar se vorbește despre DataSources, nu ConnectionProvider. În acest moment mă gândesc chiar să renunț la Hikari pentru o altă soluție, dar simt că acest lucru este inutil și este rezultatul frustrării mele.
Oricum, voi continua să încerc lucruri cred. Mi-a mai rămas puțin de încercat.
Actualizare 4:
Ei bine, în sfârșit am renunțat. Am vorbit cu cine trebuia să vorbesc și am primit un termen limită pentru a termina alte lucruri, inclusiv o mică revizie a serverelor noastre. Acesta a fost o parte din motivul pentru care am început să mă uit și la asta.Oricum, având în vedere acest termen limită și având în vedere că pur și simplu nu am găsit o soluție, voi reconstrui structura serverelor: voi folosi un server Proxy pentru a oferi fiecărei aplicații o instanță Tomcat în porturi diferite, securizate. În acest fel, clienții nu trebuie să schimbe nimic. În interior, voi oferi liderilor de proiect scripturi de implementare care vor actualiza ramura lor de implementare, vor genera WAR actualizat, vor opri serviciul Tomcat specific, vor curăța versiunile anterioare, vor adăuga o nouă versiune și vor porni din nou serviciul Tomcat. În acest fel, nu trebuie să-mi fac griji cu privire la conexiuni, în cele din urmă dau fiecărui proiect independența necesară și automatizez implementările pentru a evita cât mai multe erori.
Nu o să mint, cam nasol că se termină așa, dar nu câștigăm întotdeauna, nu?