środa, 5 sierpnia 2015

Flopsar Suite

Po co to czytać? Z oczywistych powodów, ci co są zainteresowani, chcą wiedzieć jak najwięcej o tym za co będą płacić - bo będą i to sporo (choć czy dużo, to względne). Jest teraz taki zwyczaj, że najwięcej to się pisze o tym co jest złe. Mówię teraz o pisaniu opinii o jakimś produkcie. Ci co się przejechali chcą w ten sposób odreagować (tak sobie myślę). Robią przez to taką kampanie negatywną, jako zemstę. Jak już się dobrze pisze, to tak dużo w tym marketingu, że nie wiadomo czy to jest prawdą. Ci którym coś się sprawdziło to dają sobie spokój. Szkoda czasu. Trudno w takich warunkach o decyzję, bo aby ją podjąć trzeba mieć czym się kierować. Zaufanie nam tu pewnie nie wystarczy.

Ja opowiem historię, bo lubię opowiadać takie historie ... z happy endem. Nie Flopsar jest tu głównym aktorem - a nasz system. Nie napiszę bezpośrednio opinii o samym Flopsar Suite. Czemu? Nikt mi za to nie zapłacił. Czy warto o nim pisać? A może nawet - czy mogę pisać na ten temat! Tak zastanawiałem się bo to w końcu mój czas. Jednak napiszę bo pewien incydent związany z Flopsar był przyczyną sporej ilości zmian w moim wypadku. Czy sam Flopsar dał mi jakieś korzyści? Czy załatwił mój problem? I czy w ogóle ja miałem jakiś problem? To są pytania pomocnicze.
Tak naprawdę to piszę to, bo chcę pokazać że dałem radę.
Piszę też dlatego że irytuje mnie ta forma marketingu której doświadczam.
Zabiegi instytucji odpowiedzialnych za marketing dziś są tak nachalne, że bardziej przypominają wojnę, niż ofertę - propozycję. To moja subiektywna opinia. Może nie wszyscy tak postrzegają...

Zobacz sam http://flopsar.com/
Na dziś to raczej nie jest duża firma, ale to nie ma dużego znaczenia.

Jednak trochę krytycznie

Samemu nie musiałem szukać. Sami mnie znaleźli. Gdy już się skontaktowali obejrzałem ich stronkę. Taka reklamówka całkiem kolorowa - jak dla mnie - taka amerykańska (byłem to wiem), mało szczegółów - ładne opakowanie (choć to względne), na tle innych takich systemów do monitorowania. Ale czy to złe? Nie dowiesz się za wiele dopóki nie zgłosisz się do producenta. Sam osobiście się tam nie zgłosiłem, to mnie znaleźli, mnie nie zależało na tym aby to kupić. Dziwnie może się potoczyć koło fortuny. Potem okazało się że miałem okazję tego użyć. I przyznaje pomysł dobry systemu jest dobry. Nie jest innowacyjny. Podobne rozwiązania już były. Ważne to że wykonanie jest wystarczające do tego co ma zrobić. Napiszę, bardziej od siebie niż na życzenie, bo niestety mam takie wrażenie gdy o tym gdzieś czytam, a nie ma tego za wiele oprócz satysfakcji odkrycia czegoś.
Nasuwa mi się jedna opinia. To nie jest świat open source. To nie jest dla wszystkich. To komercja. Nawet nie ma też wzmianki o cenie. Bo to zależy od klienta. W moim mniemaniu to bardziej usługa niż soft. Ale pewnie czasem warto, aby ktoś za nas coś zrobił. Gdyby popatrzeć na przemysł od tuningu też jest taka nisza w miejscu gdzie standard nie wystarczy, gdzie są Ci którzy wiedzą (lub im się wydaje że wiedzą) więcej.

Jeżeli mam coś powiedzieć na temat marketingu, bo tu musiałem się z nim zderzyć - na pewno niezbyt przyjemna sprawa to fakt, że w biznesie nie ma że się kogoś lubi. I w tym miejscu gdzie nam się powinie noga, ktoś inny może zrobić kasę. My niestety wylądujemy w gorszym miejscu, gdzie poniekąd zostaniemy ze świadomością że się nie udało.
Ja mam tą nadzieję, że nie wszyscy tak robią i zdarzają się dwustronne korzyści.

O mnie i o innych trochę.

Było na początku tak: zrobiliśmy fajny system. Był pomysł - w sumie nie mój, ale to nie szkodzi. Nawet na początku wszystkie te graty ruszyły, przeszły testy i się cieszymy. Działa!! - prawie zawsze... Wszystko fajnie tylko... że mamy umowę. Dość wredną umowę. Dlatego nie można nad tym przejść obok, tyko trzeba się zmierzyć z tym dlaczego czasami nie działa. Przez tą umowę, gdy coś padnie liczy się czas, kiedy uda się naprawić. Mnie osobiście wydaje się, że jest jeszcze gorzej, gdy pada a potem samo się naprawia, zanim my zdążymy to naprawić. Wtedy robimy dochodzenie co było przyczyną. I teraz już wiem, że to męczące i żmudne. Na początku każda hipoteza jest cenna. Są pomysły. Są próby. Nie jedziemy w ciemno. Jest trochę co szukać, co zbierać i jak. Jednak gdy już kolejna diagnoza nie całkiem się sprawdza, pojawia się znużenie i ogólny brak zaufania.
Regularnie wraca pytanie co tam się dzieje? A potem, może nawet pytanie kto za to beknie. System jest duży, wydaje się skomplikowany i tylko czasami nawala. To właściwie powinno wystarczyć, ale nie wystarcza. Właściwie już zrobiłem co się dało.
Niestety to raport z autopsji.

I wtedy niestety ten ktoś, kto właściwy jest zadowolony jakimś cudem trafia na magika, który ci wszystko załatwi - łącznie z Tobą. Tylko czy aby to nie jest prawdziwy magik? - taki od sztuczek a nie od rozwiązywania problemów!

Gdyby jednak tego nie robił magik a ja. Zatem umówmy się, że ja mam ten system naprawić. Przynajmniej tak bym chciał. Taka moja ambicja. Ja po części też go pisałem. Wiem sporo co i jak działa. Nie wiem niestety wszystkiego (przynajmniej ja tak zakładam), bo gdybym wiedział to pewnie bez mojej wiedzy by nie padał. Czy to moja wina że tak się dzieje? Nie, raczej szukam kogoś winnego, bo to też mogę być niestety ja. Chcę to rozwiązać.

Jak się zabrać do takiej roboty?
Na pierwszy rzut oka wydaje się że już wszystko sprawdziłem - ale na pewno nie. Co mam?

* Logi
System jest rozproszony. Badanie logów to ciężka praca. Owszem mamy teraz już fajne wynalazki jak logstash (za free) itp. Na początek opędziłem to tak, że skorzystałem z logowania do Cassandry - ale nie od razu. Rzeczywista wymagana wydajność tego co pracuje dość duża. 400 komunikatów webservice na sekundę (w tybie ciągłym). I tak działa. Ogólnie Dużo maszyn wirtualnych. Dużo serwerów. Dużo logów.
Gdy je przeglądam widzę gdzie staje, ale nie wiem czy to wina sieci, czy bazy, czy szyny. Jednym słowem pat.

* Monitoring
Skoro znam się na Zabbix to czemu mam coś kombinować. Monitorowany Linux, Java, Poole do bazy, Baza, Operacje na bazie, Szyna ESB, Endpointy na szynie. Ale to za mało :[
Nie tylko ja to monitoruje. Nie tylko Zabbix. Wszyscy widzimy kiedy pada. Przesłanki które pozwalają wróżyć kiedy to się stanie pojawiają się za krótko przed zanim to się dzieje. W sumie na reakcję jest minuta - może? I to się nam pięknie zapisuje. Możemy to sobie po fakcie pooglądać. Ale gdzie jest przyczyna? Patowa sytuacja ogólnie.

Pewnie potrzeba czegoś na poparcie tych historyjek. Ale tu niestety nie wszystko można pokazać.
Tu muszę podkreślić że pomysł który wykorzystali we Flopsar Suite jest dobry, a to jako developer sobie cenię.

Dostaniesz agenta do javy. Każda maszyna wirtualna będzie ładować klasy przez wrapper który opakuje wywołanie wskazanych metod

Jak już przez to przebrnę to będę musiał zadać odpowiedzieć na kilka pytań, które się pojawiły:
- Na co uważać?

  • na opłaty
  • na to co rejestrują (do ich bazy idą jawne parametry które występują w metodach kodu), może to oznaczać, że znajdą się tam dane niepożądane takie jak hasła i piny które są wrażliwe. Normalnie te dane nie są dostępne bo są tylko na czas przetwarzania requesta aplikacji - tu mogą one zostać zapisane w bazie flopsar

- Co zobaczysz?

  • agenta do javy
  • "czarną skrzynkę" z autorską bazą
  • klienta do bazy w jsf, który jest dość intuicyjny

- Czy da radę?

  • zbierać potrafi dużo (spowolnienie kilka ms na request przy 400 requestów / sekundę - akceptowalne)   

- Czy warto?

Przykłady

Nie mogę pokazać na tyle dużo na ile bym chciał...
Myślę że mogę pokazać parę screenów które nie zdradzają szczegółów systemu.
Jednym z testów które przeprowadziłem był test impulsowej odpowiedzi.

"Osławiona" galaktyka punktów

Najbardziej wychwalanym elementem, który jest reklamowany przez flopsar to galaktyka punktów.
Na badany system składa się wiele workerów (serwerów aplikacyjnych) pełniących różną role.
Punkty które się pojawiają na wykresie galaktyka to czasy dla poszczególnych elementów stacka (dla requestów w moim wypadku) - atomowych elementów występujących w ścieżce przejścia.
W tym wypadku jest to request HTTP, na który składa się po stronie serwera wywołanie wielu metod z różnych klas.
To jakie klasy rejestrować, jakie metody z tych klas, oraz jakie atrybuty w metodach jest konfigurowalne w każdym z agentów dla javy na której pracuje serwer aplikacyjny. 
Jako serwer aplikacyjny można potraktować tomcat-a, ESB czy cokolwiek,
Pozycja pionowo na osi to czas przetwarzania konkretnej metody w ramach requesta. 
Na jeden request może być wiele takich punktów - po prostu stack wywołania.
Kolory punktów wynikają z przynależności do serwera aplikacyjnego - agenta dla flopsara który zostanie dograny do javy na której pracuje serwer aplikacyjny.

Przykładowy test impulsowy aplikacji.


Co widać na takim wykresie?

  • niektóre workery (np ten czerwony, niebieskim i zielony) przetwarzają żądania dłużej przy teście impulsowym znacznie dłużej.
  • po zaznaczeniu fragmentu obszaru z punktami, który znajduje się wyżej na osi Y będzie można obejrzeć requesty które zawierają punkty powyżej - to jest właśnie siła. Tu da się przejrzeć który punkt (czas przetwarzania jakiegoś kawałka - metody) trwał najdłużej i za co odpowiada. Tu będzie można np zobaczyć sql (jak jest ustawione rejestrowanie w agencie) lub parametry

Czego nie widać na tym wykresie?

  • Ile jest faktycznie żądań - czy jest ich dużo czy mało, Na podstawie ilości punktów można powiedzieć tylko że jest dużo punktów (metod) zarejestrowanych. Pośrednio jak żądań jest więcej to punktów też jest więcej, ale nie daje to możliwości bezpośredniej organoleptycznej oceny ilości żądań
  • Gdy request się nie zakończy (zawiśnie) to go nie zobaczymy
  • Co jest przyczyną trzeba samemu wyciągnąć wnioski, a one nie koniecznie zawszę są na tacy
Można przy pomocy takiego narzędzia dobrać konfigurację systemu przy modyfikacji parametrów.
Wykonujemy te same testy dla różnych konfiguracji, a potem badamy to co możemy - czyli to co się zarejestrowało.

Poniżej zachowanie impulsowe systemu przy zmianach wielkości pooli dla datasource do bazy.


W zastawieniu zarejestrowanych requestów po wybraniu obszaru który nas interesuje wygląda to tak... (oczywiście nie pokazuje tego co nie trzeba)


Stack wywołania dla jednego ze wskazanych requestów


Na pewno takie przejrzenie historycznej odpowiedzi daje ciekawe pole do popisu do analizy. Co do tego na pewno nie mam wątpliwości.
Czy oglądanie tego na bieżąco jest potrzebne? Raczej nie.
Czy warto to ciągle rejestrować? Jak są zasoby to czemu nie. Wtedy można się "cofnąć w czasie".
Na podstawie zarejestrowanych danych będzie ciężko jednak wysnuć wnioski jeżeli przechodzą przez wiele workerów. 
Diagnostyka jest skuteczna w ramach jednego workera - a w złożonych systemach rzadko kiedy tak jest. Nie zawsze długi czas to dobre kryterium do alarmu.

czwartek, 9 lipca 2015

Firefox wersja v39 problem z połączeniem SSL np do Tomcat (Error code: ssl_error_weak_server_ephemeral_dh_key)

Jak to ostatnio bywa "ktoś"... "psuje" przeglądarkę FireFox
Teraz mamy w standardzie (przymusowym) nowy bajer pod kryptonimem bezpieczeństwa które aż tak nam nie jest potrzebne...

Objawia się

 (Error code: ssl_error_weak_server_ephemeral_dh_key)

W moim wypadku łączę się z moim lokalnym Tomcatem (o dziwo jest niebezpieczny)

Pomaga u mnie zmiana konfiguracji Tomcat związana z negocjacją cipher suite

    <Connector  
         port="12443" protocol="HTTP/1.1" SSLEnabled="true"...
               sslEnabledProtocols="TLSv1, TLSv1.1, TLSv1.2"
               ciphers="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA,SSL_RSA_WITH_RC4_128_SHA"               
               />

</connector>

na podstawie https://jamfnation.jamfsoftware.com/discussion.html?id=15032

Inna opcja ustawić w about:config na false

security.ssl3.dhe_rsa_aes_128_sha
security.ssl3.dhe_rsa_aes_256_sha

poniedziałek, 15 grudnia 2014

Java OQL

Nie będę się rozwodził czy i po co warto oglądać co znajduje się na stercie Java. W praktyce korzystam z 2 narzędzi


Daruje sobie instrukcję jak korzystać z MAT - tego narzędzia da się używać bez specjalnej znajomości i pozwala w wielu wypadkach pokazać szybciej problem z pamięcią. Właściwie na rzecz tego bajeru zarzuciłem korzystanie jvisualvm dostępnego w Java SE od wersji 1.6. Spotkałem się jednak z kilkoma przypadkami kiedy nie potrafiłem dokładnie zbadać zawartości kolekcji lub zbioru obiektów przy pomocy zapytania OQL z MAT (oczywiście też posiada) Na wstępie dodam że całość publikacji jest właśnie poświęcam właśnie takiemu sposobowi exploracji Heap-a.

Czego używać do przeglądania i analizy?

  • Zasadnicza zasada OQL dla MAT to nie ten sam co dla jvisualvm. 
  • MAT pod wieloma względami jest lepszy i szybszy niż jvisualvm
  • jvisualvm ma OQL, który działa wolno jednak posiada porządne wsparcie dla wyrażeń javascript i tego właśnie mi brakuje (albo nie znam jak to użyć) w MAT
Przejrzałem sporo postów na temat tego jak ludzie sobie radzą z OQL. Nie ma jakiejś rewelacji temat jest niszowy w niewielu miejscach można znaleźć pasjonatów, którzy wiedzą co się dzieje w środku. Ci właśnie używają OQL.

Dokumentacja OQL jvisualvm

Należy dokumentację traktować jak reference. W moim odczuciu pokazane przypadki użycia nie pokazuja tego że to jest JavaScript i co można tu zrobić. Przykłady koncentrują się na zapytaniu. Projekt można znaleźć w http://visualvm.java.net/ Aplikacja jednak jest bardzo podobna do standardowej dołączanej do java sun jdk1.7\bin\jvisualvm.exe http://visualvm.java.net/oqlhelp.html Podczas przeglądania znalazłem bardziej użyteczną dokumentację https://blogs.oracle.com/poonam/resource/OQL.htm

Przykłady użycia

Trywialne przypadki opisane w dokumentacji nie oddają użyteczności narzędzia. Lista wszystkich obiektów to za mało aby coś wiedzieć. Ale od tego się wszystko zaczyna trzeba mieć jakąś kolekcje którą chcemy explorować i filtrować.

Listowanie obiektów jakiejś klasy

select x from java.util.HashMap$Entry x
Zamiast tego można zrobić
select można się posłużyć heap.objects
heap.objects("java.util.HashMap$Entry")

Listowanie String do pliku

Dla mnie osobiście to najbardziej użyteczny kawałek.
Podam klika przypadków kiedy dostałem to co chciałem zobaczyć.
Przejrzeć to w MAT nie sposób, z poziomu opcji jest dostępna opcja copy i ona pozwala też exportować dane do pliku, ale nie przypadła mi do gustu. Dane nie są zapisywane w takim formacie który mnie interesuje.
Właśnie w jvisualvm znalazłem to co szukałem!
map(heap.objects("java.lang.String"),function(it) {
 return null != it ? it.toString() :"null";
});
var m = map(heap.objects("java.lang.String"),function(it) {
 return null != it ? it.toString() :"null";
});
var out = new java.io.BufferedWriter(
new java.io.FileWriter("c:/temp/result14.txt", true)
);
var i=0;
out.write("id|wartosc\n");
while(m.hasMoreElements()) {
 var e=m.nextElement();
 out.write(i+"|" + e.replace("\n"," ") + "\n");
 out.flush();
 i++;
}
out.close();

W ten sposob do pliku mogę zapisać nawet gigabajty a potem w zewnętrznym sofcie sobie to policzyć (zagregować jak chce). OQL jest specyficzny i na razie te narzędzia są średnio wydajne.

Przeglądanie MapEntry

Kolejny przykład w którym dane z kolekcji lądują do pliku.
var m = map(filter(heap.objects("java.util.HashMap$Entry"), 
 'null != it.key'),function(it) {
 return {
  k:it.key.toString(), 
  vc:(it.value == null? "null": classof(it.value).name), 
  v:(it.value != null?it.value.toString():"null")
 };
});
var out = new java.io.BufferedWriter(
 new java.io.FileWriter("c:/temp/result13.txt", true)
 );
var i=0;
 out.write("id|klucz|klasa|wartosc\n");
while(m.hasMoreElements()) {
 var e=m.nextElement();
 out.write(i+"|" + e.k + "|" + e.vc + "|" + e.v + "\n");
 out.flush();
 i++;
}
out.close();
Można wyeksportować elementy mapy do postacji id|klucz|klasa|wartosc

Obiekt po ID

Pobranie identyfikator obiektu
select objectid(o) from java.lang.Object o
Odnalezienie obiektu po id
heap.findObject(32402378344)
Dlaczego to może być użyteczne? Dlatego że gdy już mamy element z kolekcji zawsze możemy się do niego ponownie odwołać.

Dostęp do atrybutów obiektu

Nic prostszego. Wszystko to widać na bieżąco podczas oglądania obiektów.
heap.findObject(32402378344).attributes.table

Listowanie elementów mapy

Normalnie pokazują się tylko wybrane pozycje z kolekcji. Gdy chcemy coś zobaczyć trzeba dodać do zapytania coś co pozwoli aby pokazało się te co chcemy... ale wtedy zapytanie musi wszystko przemielić a to trawa strasznie długo. Ja potrzebowałem przejrzeć mapę która miała 570000 pozycji. Nie udało mi się doczekać na to z OQL-a bezpośrednio. Tym sposobem no problem (oczywiście widać na bieżąco co leci do pliku)
var t = heap.findObject(32402378344).attributes.table;
var size1 = heap.findObject(32402378344).attributes.size;

var out = new java.io.BufferedWriter(
 new java.io.FileWriter(
  "c:\temp\file_03.txt", true));
 out.write("nr|object");
for (var i=0; i<size1; i++) 
{
  var e = t[i];
  if (e==null) continue;
  var k=e.key;
  var v=e.value;
  if (k==null) {k="";} 
   else {k = k.toString();};
  if (v==null) {v="";}
   else {v = v.toString();};
  out.write(i+"|"+ k.toString().replace("\n"," ") 
    + "|" + v.toString().replace("\n"," ") +"\\n");
  out.flush();
}
out.close();

// lista contextow aplikacji
map(heap.objects("org.apache.catalina.loader.WebappClassLoader"),function(it) {
 return null != it ? it.canonicalLoaderDir.toString() :"null";
});




W Przypadku MAT
Zwracane obiekty z zapytania są określonego typu i można na ich rzecz również wywoływać metody
http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Ftasks%2Fqueryingheapobjects.html

SELECT s.getClazz() FROM INSTANCEOF pl.kamsoft.common.monitor.KamMonitors s

SELECT s.resolveValue("timeMonitors") FROM INSTANCEOF pl.kamsoft.common.monitor.KamMonitors s

piątek, 11 kwietnia 2014

Zabbix - Openfire monitoring

Po dłuższym okresie użytkowania aplikacji Openfire, okazało się że serwer na którym jest uruchomiona miewa mocne zadyszki procesora i na maszynie wirtualnej powoduje zamieszanie. Aplikacji sam nie pisałem. Dostałem do obejrzenia tzw. "Czarną skrzynkę".

Od jakiegoś czasu próbuje zbierać takie historyjki. Nawiązując więc do wcześniejszego http://kczerw.blogspot.com/2014/01/zabbix-daje-rade.html postaram się pokazać nieco szczegółów tylko pod kątem Openfire i Zabbix.

System aktualnie był monitorowany tylko na poziomie Linux. Nieregularnie, ale zazwyczaj w godzinach porannych, można było obserwować duże obciążenie procesora. Przypadkiem po zalogowaniu do konsoli ssh udało się złapać że to jest proces openfire.

Po zalogowaniu do konsoli administracyjnej Openfire nie zastałem zbyt wiele. Wersja prehistoryczna 3.6.4. w oryginale nie miała statystyk. Jedyne co było widać do objawy na postawie systemu Linux.
Pierwsze co zrobiłem to rozszerzyłem statystyki o monitorowanie socketów.
Z tych gniazdek wyszła pierwsza relacja która pokazała, że jest wyjątkowo dużo połączeń ESTABILISHED. Zaskoczyło mnie to, że jest ich znacznie więcej niż samych userów.

Openfire Linux socket


Postanowiłem poszukać źródeł do Openfire. W przypadku tej wersji nie było łatwo... w końcu to prawie zabytek. Udało się! Nawet takie starocie mają na serwerze. 

http://download.igniterealtime.org/openfire/openfire_3_6_4.tar.gz
http://download.igniterealtime.org/openfire/openfire_src_3_6_4.zip

Zabrałem się za oglądanie źródeł dla openfire.

W pierwszej kolejności dla tej wersji skompilowałem wszystko co się dało. Teraz już miałem to co ważne - pluginy do monitorowania.
Nie pozostało nic jak przymierzyć się do uruchomienia dodatkowych elementów które pokazują statystyki tego co się dzieje w środku. Po krótkich poszukiwaniach okazało się, że dodatkowe informacje można uzyskać na podstawie pluginów. Całkiem ciekawy okazał się plugin "Monitoring Service".

Czysta instalacja Openfire (bez dodatkowych pluginów) nie pokazuje zbyt wiele co się daje w aplikacji.
Wykonane modyfikacje dotyczyły pluginów do autoryzacji i grup. W największym skrócie powodowały, że struktura firmy w postaci wydziałów (zapisana w bazie) jest odwzorowana na grupy w Openfire. Po rewizji kodu pluginu okazuje się, że również zmienia się sposób informowania o widoczności. W ramach firmy przyjęto, że każda osoba widzi nawzajem zmiany statusu w komunikatorze.

Po wykonaniu wywiadu i przejrzeniu bazy dowiedziałem się że mamy około 400 userów, ale w praktyce korzysta około 200 osób.

I na tym skończyła się moja wiedza, reszty mogłem się domyślać, ale nie potrafiłem tego potwierdzić.
Moja hipoteza już na wstępie zakłada, że przyczyna dużego obciążenia procesora jest związana z podłączaniem (logowaniem) nowych operatorów. W dużej mierze można to było potwierdzić. Po wykonaniu restartu openfire zjawisko się również mocno nasilało.

Linux CPU Usage

Linux CPU Usage no iddle

Linux network traffic

Linux CPU Load


Kontakt z autorem pluginów niewiele dał. Ogólnie aplikacja jest w porządku działa - trzeba dać mocniejszą maszynę. Taka argumentacja mnie specjalnie nie przekonała, a co jak dodamy 100 userów?

Postanowiłem podłączyć do aplikacji monitor Java. Generalnie do aplikacji Java gdzie mogę to pcham zapcat-a. Jest znacznie prostszy we współpracy z zabbix niż natywny connector JMX w zabbix.
Openfire korzysta z Tanuki wrapper. Nie mam prostego sposobu na wpięcie się do tego wrapera aby wystartować proces zapcat-a. Tu można to prościej załatwić. Zapcat ma plugin który można wgrać do openfire.

Obserwowałem obrazki z monitora Linuxa i ze smutkiem spowodowany bezsilnością. Prawie pewne że kolejny dzień rano znów przypali się procesor w wirtualce.

Co do monitorowania Java nie można mieć zastrzeżeń. Ilość zjadanej pamięci jest spora ale nie dramatyczna. Nie widać też jakiś jawnych wycieków.





Spróbowałem podglądać wątki przy pomocy jconsole oraz jvisualvm. Niestety rezultaty nie były zbyt odkrywcze. Nawet załączenie monitorowania wywoływanych metod nie rzuciło światła na to co jest przyczyną obciążenia. Próbowałem wykorzystać sampler aby sprawdzić która z metod jest najbardziej kosztowana.

jvisualm openfire sampler snapshot

jvisualm openfire sampler
Ciągle rosła wartość czasu przetwarzania NamePreservingRunnable.run(). Po sprawdzeniu kodu tej klasy okazuje się, że to tylko wraper do uruchomienia wątku (na podstawie tego czegoś nie wiadomo co ten wątek robi).

Znaczna aktywność bazy danych w tym momencie skłoniła mnie do sprawdzenia ilości wywołań metod związanych z operacjami na bazie danych autoryzacji. Zrobiłem to w prosty sposób grepując loga.
Moje podejrzenia się potwierdziły. Ilość operacji była ogromna. Najbardziej dawał popalić plugin dostarczający informację o userach. Rekordowa była metoda

public class XXXUserProvider implements  UserProvider... {
 public Collection<String> getUsernames() {
 ...

Bardzo mnie to zastanawiało dlaczego operacje na bazie obciążają procesor. Normalnie to nie do pomyślenia, przecież wtedy procesor czeka na dostarczenie danych, to baza co najwyżej mogła być obciążona (ale to zupełnie inny system w tym wypadku iSeries), jednak baza dawała radę. Zatem to niekoniecznie jest przyczyną całego problemu.

Trochę światła się pojawiło po zainstalowaniu pluginu "Monitoring Service"
Pokazuje on 3 ważne parametry:

  • liczba userów, 
  • aktywne konwersacje, 
  • liczba pakietów na minutę


Liczba pakietów które się pojawiają po restarcie Openfire sporo daje do myślenia. Z dużym prawdopodobieństwem można powiedzieć, że są to pakiety z informacją o widoczności. 
Przy założeniu że 1 klient jest źródłem powiadomienia do wszystkich innych mamy 1 -> 200 a klientów jest 200 - wiec mamy 200 * 200 co daje 40 000 pakietów. A ponieważ nie jest to tylko ten pakiet wiec rachunek w miarę się zgadza. Model który został przyjęty przy definiowaniu widoczności jest na pewno kosztowny przy masowej zmianie widoczności operatorów. 

Oglądając wykresy statystyk openfire na bierząco nie miałem jakiś specjalnych zastrzeżeń. Gdy chciałem sprawdzić stan który był wczoraj, albo kilka dni wcześnie było kiepsko. Dane zostały uśrednione. 
Plugin "Monitoring Service" w tej wersji trochę mnie zawiódł. Niestety nie podłącza danych które zbiera do JMX. Pozostaje na początek zatem tylko konsola admina do oglądania tego co się dzieje.

Obejrzałem również wartości dla cache Openfire. Tu po chwili obserwacji okazało się że wartość cache dla "Roster" zdecydowanie była zbyt mała. Zwiększyłem ją. I tu pojawił się skok. Liczba wywołań metody  getUsernames() dla UserProvider-a kolosalnie spadła. To mnie przekonało o znaczeniu cache. Niemniej jednak nie spowodowało to zmniejszenia zużycia procesora w jakiś znaczący sposób. Podobnie miała się sprawa w przypadku cache "VCard"

Ponieważ chciałem mieć te elementy w jednym słusznym miejscu zabrałem się do przeróbki pluginu zabbix dla Openfire. Postanowiłem dodać do JMX elementy które są widoczne w konsoli administracyjnej Openfire po dodaniu pluginu "Monitoring".

Przyjąłem sobie założenie aby nie zepsuć aktualnego pluginu. Czyli jeżeli nie ma zainstalowanego pluginu "Monitoring" to po prostu nie mam drzewka ze statystyka w JMX.

Do w/w elementów można się dostać w wykorzystując wymienione poniżej klasy:

XMPPServer xmppServer = XMPPServer.getInstance();
org.jivesoftware.openfire.plugin.MonitoringPlugin
org.jivesoftware.openfire.archive.ConversationManager
org.jivesoftware.openfire.reporting.stats.StatsViewer
org.jivesoftware.openfire.reporting.graph.GraphEngine
org.jivesoftware.openfire.reporting.stats.StatsAction

Postaram się udostępnić zmodyfikowane źródła pluginu zapcat-openfire oraz template dla Zabbix.

Rezultat jest wystarczający. W drzewku JMX pojawiają się pozycje openfire/session/cache oraz openfire/session/stats
openfire-zabbix plugin JMX tree (cache)

openfire-zabbix plugin JMX tree (stats)

Teraz już wystarczy przygotować template dla Zabbix.

openfire-zapcat zabbix cache roster

openfire-zapcat zabbix cache routing-user-session

openfire-zapcat zabbix cache vcard

openfire-zapcat zabbix conversation

openfire-zapcat zabbix pocket-count

openfire-zapcat zabbix server-bytes

openfire-zapcat zabbix session
Teraz może pora pochwalić się czy udało się zmienić tą maszynę z piekarnika z powrotem w normalnie pracujący system. Okazało się że przyczyną jest trywialny problem. Na dysku znajdował się katalog, który zawierał 500 000 plików. Dostęp do tego katalogu (inicjowany przez zupełnie inną aplikację) pochłaniał takie zasoby procesora. Ale skutek uboczny również przymulał openfire.
W takim modelu powiadamiania jaki mamy wybrany nie da się pewnie zrealizować tego bez obciążenia procesora. Można wykonać dokładniejszą eksploracje mechanizmu komunikacji Openfire.

W tym jednak przypadku ja postawiłem sobie za cel tylko zrobienie statystyk w Zabbix dla Openfire.

środa, 5 marca 2014

Zabbix - Interface WEB

Modyfikacje kodu interface WEB

Już od dawna przymierzałem się do drobnych modyfikacji interface webowego Zabbix. Za każdym razem odstraszało mnie przygotowanie środowiska do pracy z PHP. 

Z tego co widzę projekt się nadal rozwija, jednak przy każdych zmianach mam wątpliwości czy aby na pewno słuszny kierunek to obiera.

Na początek powiem, że ogólna koncepcja nie jest zła i dość daleko zaszła. Brakuje zdecydowanie estetycznego dopracowania i stabilności (szczególnie w interface od strony użytkownika). Nie będę sobie zawracał teraz głowy gdzie i kiedy się sypie. W wielu miejscach brakuje prostych filtrów. Przy dużej ilości elementów typu Template, Screen bieda się odnaleźć - właśnie tam nie ma filtrów. Mam wątpliwości co do użyteczności takiego czegoś jak makro.

Takie właśnie drobne rzeczy chciałem sprawdzić czy da się poprawić.

Da się!

Ale o zgrozo zajrzałem w jaki sposób jest budowana formatka i po prostu wolna ameryka!

Wziąłem sobie na pulpit screenconf.php i wzorując się na hosts.php próbowałem go wzbogacić o filtr.

Postaram się opisać jak to zrobiłem i opisać ogólną koncepcję, która jakoś tam się rysuje.

Gdzie sesja

Nie jest to dziwne... w java - Servlet itp takie coś jest jako obiekt. Tu na próżno szukać. Z tego co widzę to do przechowywania takich rzeczy jest CProfile
// aktualizacja wartości w profilu
CProfile::update('web.template.filter_template', $_REQUEST['filter_template'], PROFILE_TYPE_STR);

// pobranie wartości z profilu
CProfile::get('web.template.filter_template')

Filtrowanie parametrów wejściowych

Podejście które autorzy zastosowali polega na przepuszczeniu przez $_REQUEST tylko zadeklarowanych parametrów. Już na wstępie jest robiona walidacja dopuszczalnych wartości w oparciu o tablicę $fields.
Bardzo niewygodne podejście bo już na samym wstępie trzeba wiedzieć jakie będą dopuszczalne parametry i ich typy. Wymaga to wielokrotnej i rozproszonej deklaracji. W pierwszej kolejności na $fields, potem w przepisywaniu parametrów do określonych kolekcji takich jak CProfile reprezentujących profil, czy pola.
Trzeba to mieć na uwadze. Ja się przejechałem nie mogłem dotrzeć dlaczego pomimo tego, że parametry się przesyłają nie widać ich w $_REQUEST. Załatwiała mnie check_fields($fields). Właśnie ta funkcja waliduje co może być w $_REQUEST.

Template

Są ... ale nie jakieś tam cudo. Po prostu jest to taki sam plik php jak główny powiedzmy kontroler. Może różnica jest umowna że w tym template powinniśmy korzystać z $data do dekorowania zawartości.

// render view
$screenView = new CView('configuration.screen.list', $data);
$screenView->render();
$screenView->show();

Jak widać żadnych sensacji. Pliki są czytane z include/views w tym wypadku będzie to

include/views/configuration.screen.list.php

Renderowanie filtrów i elementów

Takie podejście jak tu już się spotkałem w wielu projektach. Klasy odpowiedzialne za generowanie konkretnych elementów HTML. Jednym z trochę już archaicznych rozwiązań z Java to ECS http://jakarta.apache.org/ecs/

Filtrowanie danych

Dane do list są dostarczane przez API. Można powiedzieć że na tym poziomie w ogóle odcinamy się od bazy. Nie posługujemy się ani jak w Java obiektami Query, Connection. Warstwa abstrakcji na tym poziomie wydaje się wystarczająca. 
// pobranie listy temlate
API::Template()->get($options);
Warunki są przekazywane w $options mogą być dość złożone.
Na pierwszy rzut może wydawać się to skomplikowane, ale na szczęście API jest dość dobrze opisane. Można znaleźć na https://www.zabbix.com/documentation/2.0/manual/appendix/api/api
Jeżeli mam się odnieść do pobierania listy template będzie to https://www.zabbix.com/documentation/2.0/manual/appendix/api/template/get, Niestety trzeba się tu zapoznać z pozycjami, które możemy używać w options bo są one różne dla różnych rodzajów danych. Ogólna zasada jest taka: filtrowanie z like wykorzystuje search
// przygotowanie options do wywołania API
$options = array(
 'editable' => 1,
 'sortfield' => $sortfield,
 'sortorder' => $sortorder,
 'limit' => ($config['search_limit']+1),
 'search' => array()
);
// inicjowanie pozycji po których szukamy z like
// tu gdy jest ustawione filter_template to dodane jest szukanie po host
$local_template_name = $_REQUEST['filter_template'];
if (!zbx_empty($local_template_name)) {
 $options['search']['host'] = $local_template_name;
}
W moim odczuciu z opisu API nie do końca wynika co podać jako warunki szukania. Wydaje się jednak dość sensowna metoda polegająca na sprawdzeniu jakie zostanie przygotowane zapytanie.
Można to zrobić wykonując breakpoint-a w klasie wykonującej zapytanie. Ponieważ aktualnie korzystam z mysql-a zrobiłem to w include/db.inc.php w funkcjach DBexecute oraz DBselect niezawodnie można tam sprawdzić query które idzie. 

Standardy

W całym projekcje słabo widać nową technologię - ale nie to jest przecież najważniejsze. Czasem działa dlatego, że nie zostało przeenżynierowane. Ale jednak raczej są to stare techniki MVC i to własnej konstrukcji. Nie mówię że traktuje to jako wadę. Ale patrząc na ilość pracy przy napisaniu większej ilości formatek już może być problem aby to ogarnąć. Jest ogólna separacja, kontroler-a, dostarczenia danych, renderowania rezultatów ale bardzo umowna i raczej słabo przestrzegana.

Wyświetlanie na screen wartości typu plain text

W moim odczuciu w przypadku tego typu informacji powinna być wykonana konwersja podobnie jak w przypadku "Last value" z Latest data (latest.php). Ogólnie wydaje mi się że jest to niespójność dlatego że na wykresie item który ma zdefiniowane unit jest prezentowany z uwzględnieniem konwersji do podanych jednostek (wybierany jest adekwatny dekorator - jeżeli da się to zrobić) W tym miejscu po wybraniu jednostek jako sekundy (wszędzie jest wyświetlane jako czas) ale na screen ze zdefiniowanym "plain text" wyświetla się bez dekoratora (jawny timestamp).

// latest.php
$change=convert_units($db_item['lastvalue']-$db_item['prevvalue'],$db_item['units']);
// include\func.inc.php
// gdy s 
convertUnitsS
 
// include\views\monitoring.screen.php

// include\screens.inc.php
$resourcetype = $screenItem['resourcetype'];
$resourcetype == SCREEN_RESOURCE_PLAIN_TEXT
get_screen_plaintext
$element = get_screen($screen, 0, $effectiveperiod);


get_screen_plaintext
get_item_by_itemid
... oczywiście będzie ciąg dalszy
Zamierzam dodać w tym przykładzie filtry do miejsc które wspomniałem wcześniej.



środa, 26 lutego 2014

Ant - Selektory plików dla fileset

Na wstępie powiem tak - to nie jest tutorial. Znacznie lepsze określenie do tego co jest niżej to rodzaj pamiętnika. Wiele rzeczy można zrobić na wiele sposobów. Niektóre są dobrze opracowane i łatwo je znaleźć. Niektóre nie wydają się zbyt popularne i drążenie zaczyna się od suchej dokumentacji. To co próbuje zrobić niżej można zrobić na wiele innych sposobów (pewnie to prawda). Ja akurat się uwziąłem aby zrobić to przy pomocy ANT-a. Chodzi o wybieranie elementów do fileset-a. fileset jest przekazywany jako argument do innych task-ów i dlatego jest popularny.

W przypadku prostych definicji nie trzeba sobie zawracać głowy, wystarczy tylko wskazać katalog i się robi. To jest dobre. Co zrobić gdy trzeba w jakiś złożony sposób spreparować listę do skopiowania?
Zamiast zmagać się z jakimś kosmicznym wyrażeniem regularnym pisanym w xml-u można spróbować dodać do fileset selektor który będzie wybierał pliki przy pomocy kodu. Wydaje się że to będzie prostsze. Czy to prawda? Z tego co piszą można to zrobić korzystając z języków skryptowych. Można też napisać klasę która to realizuje wykorzystując Custom Selectors.

Może najpierw spróbuje sprawdzić selektory skryptowe. Wybrałem jako język JavaScript.
Przykład selektora który wybiera każdy plik
<scriptselector language="javascript">
    self.setSelected(true);
</scriptselector>
Przykład selektora który ma nazwę o długości parzystej
<scriptselector language="javascript">
    self.setSelected((filename.length%2)==0);
</scriptselector>
Jak to wykorzystać w taskach które akceptują fileset? Poniżej przykład kopiowania z takim selektorem
<copy todir="dir-to">
    <fileset dir="..">
        <scriptselector language="javascript">
            self.setSelected((filename.length%2)==0);
        </scriptselector>
    </fileset>
</copy>
Po wykonaniu widać że kopiowana jest cała hierarchia. Wcale nie jest to takie proste. Na pierwszy rzut oka nie wiadomo dlaczego i skąd bierze się self i co to w ogóle jest. No na szczęście w tym temacie wyjaśnia się sprawa po lekturze manual-a Script Selector (patrz Selektory w ANT i radzę dokładnie przeczytać pisze co i jak).

Mam już jakieś narzędzie. Postawiłem więc sobie takie zadanie. Chcę wybrać tylko katalogi (nie pliki), które są w katalogu poniżej i zaczynają się od liczby.
Moja struktura może to wyglądać tak:
[all]
    build.xml
    test.xml
[00]
    [properties]
    ...
    build.xml
[00_auth]
    [properties]
    ...
    build.xml
[02]
    [properties]
    ...
    build.xml
...
[17]
    [properties]
    ...
    build.xml
[17_auth]
    [properties]
    ...
    build.xml
Jest to typowe zadanie gdy buduje wersję dla różnych oddziałów. W każdym katalogu który wybiorę jest skrypt, który buduje aplikację. Nie jest ważne jak to się buduje. Ważne jest to że chce mieć jakiś customizowalny sposób na wybieranie tych katalogów. Jakby się zastanowić to wcale może nie trzeba korzystać z selektora skryptowego! Może skorzystać z Filename Selector oraz Depth Selector. Mój testowy skrypt Ant-a to test.xml. Przyjąłem założenie, że w każdym katalogu jest plik o nazwie build.xml. Jako częściowa próba zrealizowania tego może to wyglądać tak:
<copy todir="dir-to" verbose="true">
    <fileset dir="..">
        <and>
            <depth max="1" />
            <filename regex="^\d+" />
            <filename regex="build.xml" />
        </and>
    </fileset>
</copy>
Wydaje się to działać. Ale czy to koniec? Normalne copy kopiuje wszystko to kopiuje tylko build.xml. Działa ale tylko dlatego że jest jeden build.xml. Co by było gdyby  build.xml był w kolejnym podkatalogu [00]/xx/build.xml? Skopiuje się! Nie okazuje się że właśnie depth przed tym zabezpieczył!

Selectory filename

W selektorze przekazywana jest nazwa pliku. W dokumentacji bezpośrednio nie ma informacji że to jest pełna ścieżka od miejsca podanego jako atrybut dir dla fileset. To dość ważne. Pisząc warunek który wybiera po nazwie chcemy np zdefiniować tak aby brało 17/ ale już nie 17_auth/. W zależności od tego czy ant jest odpalony na Windows czy Linux warunek jest interpretowany inaczej i wartość filename przekazana do badania warunkiem może mieć ścieżkę z separatorem / lub \.
Stosując wyrażenie regularne można to opędzić definiując np. wyważenie
<filename regex="^17[\\/]" />
Taki patent działa na obu systemach.

Eclipse

Uruchomienie takiego skryptu w Eclipse wymaga ustawienia ANT_HOME w Eclipse na właściwą wersję ANT. Wymagana też jest wskazanie odpowiedniej wersji JAVA. W moim przypadku korzystałem ze starej wersji Eclipse dlatego wskazałem moją własną - zewnętrzną wersję ANT (Window / Preferences / Ant / Runtime / Ant Home). Uruchomnienie przy pomocy Eclipse wymagało wskazania JAVA 1.6 (prawoklik - menu - Debug As / Ant Build / JRE / minumum 1.6)
W przypadku JAVA 1.5 dostałem komunikat że nie można załadować BSF
Nie udało mi się na razie debugować wyrażenia w JavaScript. Debuguje się jedynie skrypt ANT-a.
W sumie mało użyteczne odpalenie tego z Eclipse!

Selektory w ANT http://ant.apache.org/manual/Types/selectors.html
Opis języka skryptowego http://ant.apache.org/manual/Tasks/script.html

poniedziałek, 24 lutego 2014

Windows - batch

Manager urządzeń

Można odpalić z cmd przez komenę devmgmt.msc

Pobranie do zmiennej bieżącego katalogu

Przypadkowo zdarzyło mi się że musiałem napisać prosty plik bat. Nie przepadam za skryptami w batch-ach... ale gdy trzeba trudno.
Chciałem sprawdzić bieżący katalog.
Postąpiłem w sposób analogiczny jak dla Linux.
Pewnie jest to jakaś zmienna systemowa.
Okazało się że to nie tak samo...

W przypadku Linux można to sprawdzić przeglądając zawartość zmiennych komendą set
Wartość mamy w PWD
set |less
set |grep PWD=
Chciałem to sprawdzić dla Windows. I co? Okazuje się że szukam a tu nie ma!
set |more
set >c:\test
Można sobie taką zmienną spreparować. Komenda cd zwraca tę wartość.
set PWD="%cd%"
echo %PWD%

... i gotowe

Źródło http://stackoverflow.com/questions/6789491/batch-script-to-set-a-variable-with-the-current-path-location

Szukanie pliku który się uruchomił

I znów pracuję w Windows.
Odpalam konsole cmd. Wydaje polecenie np: java i wykonuje. 
Działa... ale czemu?
Może na początku zaznaczę że polecenia wydaje bez podania pełnej ścieżki. Wygląda tak jak komenda systemu. Niektóre polecenia się wywołują - ot tak - bez podania pełnej ścieżki.
Gdyby się zastanowić, to tak do końca nie wiem co się uruchomiło.
Sprawdzając managera zadań, pewnie zobaczę pełną ścieżkę, ale już po uruchomieniu.
A jak zobaczyć co system wybierze zanim to wykonam?

Wracając do teori w systemie Windows programy są odnajdywane na podstawie ścieżki PATH. Skąd tak na prawdę ten program jest uruchamiany? Jak znasz scieżkę PATH możesz spróbować to prześledzić, ale czy to proste i komu by się to chciało robić.

Okazuje się że można zobaczyć co właściwie system znajduje gdy się poda polecenie - służy do tego where.
where java
 C:\Windows\System32\java.exe
 D:\java\jdk\jdk1.6\bin\java.exe
Teraz już wiem.... wykona C:\Windows\System32\java.exe bo jest pierwsze na liście

W przypadku Linux jest whereis. Wydaje się że where z Windows to właśnie jego odpowiednik.

Rozszerzone polecenia cmd

Chciałem wykonać prosty skrypt do odpalania programu java. Jestem w katalogu X, wywołuje skrypt program.bat z podaniem bezwzględnej ścieżki katalogu Y. W ścieżce Y znajdują się wymagane jary. Trzeba je dodać do wywołania jar-a. Okazuje się że zrobiłem sobie zagadkę.
c:\X> c:\Y\program.bat
Kod program.bat jest prosty:
java -cp .\lib1.jar Class1
Program przy takim wywołaniu się wykłada. Wina to podanie bezwzględnej ścieżki. Ścieżka reprezentowana przez . to c:\X a tu nie ma lib1.jar.

Jak zatem to zrobić aby działało również dla takiego przypadku?
Nie chce przecież w skrypcie mieć zapisanych ścieżek na sztywno!
Czytam więc reference dla cmd. Zabrałem się do tego po lekturze linka poniżej. Należy zwrócić uwagę że I to numer od 0 - n
%~fI        - expands %I to a fully qualified path name
%~dI        - expands %I to a drive letter only
%~pI        - expands %I to a path only
Co ciekawe podobny rezultat dostałem gdy sprawdziłem zawartość %0 podobnie jak dla %~f0 

Idąc tym tropem spróbowałem ustawić zmienną. Wydaje się działać:
set BATH_PATH=%~d0%~p0
Po tych zmianach kod program.bat wygląda tak
set BATH_PATH=%~d0%~p0
java -cp %BATH_PATH%\lib1.jar Class1
Teraz działa tak jak chciałem!