Profilowanie

20 marzec, 2012

 Gdy ostatnio z czystej ciekawości przeprowadziłem prosty test wydajności na pewnej aplikacji webowej, okazało się, że "coś się dzieje". Okazało się, że po kilku naprawdę bystrych enhancementach, słowo "bystry" nabrało zupełnie nowego znaczenia. Skoro "ab" daje wyniki rzędu 150 req/sek na stronie która nie robi wiele więcej niż wyrenderowanie w miarę prostej strony z kilku zagnieżdżonych szablonów Velocity to coś nie gra. Wynik zadowalający to 400req/sek. Do dzieła...

Niektórzy używają Netbeans czy też JMeter, ja skoro na codzień preferuję Eclipse to postanowiłem poszukać czegoś all-in-one. Wybór padł na TPTP. Do Eclipse Indigo musiałem doinstalować go ręcznie. Nie pamiętam na czym polegały problemy z podłączeniem się do embeded Agent Controller-a więc pobrałem pakiet TPTP All-in-one i uruchamiałem Agent Controller ręcznie. Gdy ten już działa możemy uruchomić aplikację którą chcemy profilować. Ważne są zmienne środowiskowe, czytanie dokumentacji (to mnie zgubiło) oraz (w moim przypadku) parametr do Mavena...

SET TPTP_AC_HOME=G:\tools\agentcontroller
SET JAVA_PROFILER_HOME=%TPTP_AC_HOME%\plugins\org.eclipse.tptp.javaprofiler
SET PATH=%JAVA_PROFILER_HOME%;%PATH%;%TPTP_AC_HOME%\bin
SET MAVEN_OPTS=-agentlib:JPIBootLoader=JPIAgent:server=enabled;CGProf
mvn jetty:run


Teraz można utworzyć nową konfigurację profilera, podpiąć się pod port Agent Controllera, wybrać wątek, który udostepnia interfejs profilera...

undefined

 ... i ponownie uruchomić "ab -n 1000 -c 10 xxxx" by po paru chwilach otrzymać statystykę najczęściej wykonywanych i najdłużej trwających metod...

undefined

Tak więc mamy narzędzie. Warto nauczyć się nie tyle co z niego korzystać a interpretować wyniki które daje.

Dwie aplikacje w jednym projekcie Qooxdoo

25 listopad, 2011

Przy odrobinie samozaparcia udało mi się stworzyć szablon projektu Qooxdoo, który wspiera dwie aplikacje w jednym projekcie. Chodziło o to by mieć dwie strony html z rożnymi aplikacjami (logowanie i strona aplikacji), więc żaden tam widzimiś a potrzeba chwili... ważne było by aplikacje korzystały ze wspólnych zasobów oraz jedna korzystała z kodu drugiej, nie wspomnę o tym by zachować ład i porządek w repozytorium.

Moje wskazówki:

1) skryptu generate.py nie modyfikujemy (nie ma takiej potrzeby)

2) tworzymy odrębne katalogi dla żródeł kodu, plików językowych i konfiguracji builda tj:

-- build
-- cfg-login
-- cfg-main
-- source
   `-- class-main
   `-- class-login
      |-- resource
      |-- login
      |-- main
      |-- script
   `-- translation-main
   `-- translation-login


3) plik manifestu (Manifest.json) MUSI być zawsze w tym samym katalogu co plik konfiguracji (config.json). Nie udało mi się zmusić qooxdoo do zmiany nazwy pliku manifestu co wymusiło niestety kolejne zmiany w konfiguracji. Umieszczamy je odpowiednio w folderach cfg-login i cfg-main

4) Manifest każdej z aplikacji musi deklarować odrębną nazwę aplikacji, a sekcja 'provides' deklarować odpowiednie ścieżki poprzedzone prefixem '..'

"provides" :
{
"namespace" : "openfileresa",
"encoding" : "utf-8",
"class" : "../source/class-login",
"resource" : "../source/resource",
"translation" : "../source/translation-login",
"type" : "application"
}


5) W pliku konfiguracyjnym należy predefiniować zadane 'build-files' by nadpisać property 'source' i ewentualnie wskazać właściwy plik html do skopiowania...

"build-files" :
{
  "copy-files" :
  {
    "files" :
    [
      "login.html"
    ],
    "source" : "../source"
  }
}

6) pozostaje tylko pamiętać by używając generatora podać ścieżkę do właściwego pliku konfiguracji; na przykład w zadaniu Ant-a:

<target name="compile-login-page">
<exec dir="." executable="${PYTHON_PATH}" logerror="true">
<arg value="${SOURCE_DIR}/generate.py" />
<arg value="-c" />
<arg value="cfg-login/config.json" />
<arg value="build" />
</exec>
</target>

SOLR i spatial search

07 listopad, 2011

Apache SOLR jest bardzo dobrym narzędziem do spatial searchingu. W ogóle jest bardzo dobrym narzędziem (żeby nie było) :) A zatem idealnie sprawdzi się gdy zechcemy znaleźć wszystkie punkty (tudzień knajpy, puby, ogródki piwne, winiarnie, sklepy monopolowe nocne, cokolwiek) w zasięgu np. 500m od wybranej (obecnej) lokalizacji o 1am gdy nam jeszcze mało. Jak ?


Krok pierwszy to zaopatrzenie się w bazę danych geo. Odradzam darmową bazę z serwisu http://www.geonames.org/ . Ktoś celowo udostępnia ją za free. Dokładność dla Polski jest rzędu 15 kilometrów :) Czyli np. kody pocztowe z Ursynowa są gdzieś kawałek za Muranowem. Więc... jeśli chcemy robić coś komercyjnie to wykładamy te kilkadziesiąt dolców...


Krok drugi to załadowanie danych do dowolnej bazy danych. Dane są w formacie więc najprościej będzie użyć SSIS, ewentualnie BULK INSERT w MySQLu...

LOAD DATA LOCAL INFILE '/path/allbooks.csv' INTO TABLE geopostcodes FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n' (country_code,postal_code,place_name,admin_name1,admin_code1,admin_name2,admin_code2,admin_name3,admin_code3,latitude,longitude,accuracy);

W powyższy sposób ładujemy jeden z plików danych do tabeli, o której wcześniejszym utworzeniu należy pamięŧać :)


Teraz trochę od tyłu... ładujemy dane. Po uruchomieniu instancji SOLR-a w przeglądarce wchodzimy na url http://localhost:8983/solr/geodataimport?command=full-import. Strona zwróci wynik natychmiast, a w konsoli SOLRa będzie można obserwować faktyczny progress. Proces usuwa przed importem bieżącą zawartość.


Konfigurujemy zawczasu handler, który mapuje proces importujący dane do odpowiedniego URLa.

<requestHandler name="/geodataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
  <lst name="defaults">
    <str name="config">db-data-config.xml</str>
  </lst>
</requestHandler>

Jak widać konfiguracja data-source powinna znaleźć się w pliku db-data-config.xml . Gdy już jar ze sterownikiem JDBC trafi do katalogu 'lib' SOLR-a, możemy przystąpić do zdefiniowania źródła danych.

<dataConfig>
  <dataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/geosolr" user="root" password="secretpass" />
  <document>
    <entity name="item" query="SELECT id,country_code,postal_code,place_name,admin_name1,admin_code1,admin_name2,admin_code2,admin_name3,admin_code3,CONCAT(CAST(latitude AS CHAR),',',CAST(longitude AS CHAR)) AS location,accuracy FROM geopostcodes">
      <field column="id" name="id" />
      <field column="country_code" name="country_code" />
      <field column="postal_code" name="postal_code" />
      <field column="place_name" name="place_name" />
      <field column="admin_name1" name="admin_name1" />
      <field column="admin_code1" name="admin_code1" />
      <field column="admin_name2" name="admin_name2" />
      <field column="admin_code2" name="admin_code1" />
      <field column="admin_name3" name="admin_name3" />
      <field column="admin_code3" name="admin_code3" />
      <field column="location" name="location" />
      <field column="accuracy" name="accuracy" />
    </entity>
  </document>
</dataConfig>

Bardzo ważny jest CONCAT który skleja koordynaty w jeden string rozdzielony znakiem spacji. Tylko taki format akceptuje SOLR. Do tego jeszcze prosta konfiguracja schema...

<fields>
  <field name="id" type="string" indexed="true" stored="true" required="true"/>
  <field name="country_code" type="string" indexed="true" stored="true"/>
  <field name="postal_code" type="string" indexed="true" stored="true"/>
  <field name="place_name" type="string" indexed="true" stored="true"/>
  <field name="location" type="location" indexed="true" stored="true"/>
  <field name="admin_name1" type="string" indexed="false" stored="true"/>
  <field name="admin_code1" type="string" indexed="false" stored="true"/>
  <field name="admin_name2" type="string" indexed="false" stored="true"/>
  <field name="admin_code2" type="string" indexed="false" stored="true"/>
  <field name="admin_name3" type="string" indexed="false" stored="true"/>
  <field name="admin_code3" type="string" indexed="false" stored="true"/>
  <field name="accuracy" type="plong" indexed="false" stored="true"/>
  <dynamicField name="*_coordinate" type="tdouble" indexed="true" stored="false"/>
</fields>
<uniqueKey>id</uniqueKey>
<defaultSearchField>id</defaultSearchField>

Nadszedł czas na odpytanie bazy. Prosta klasa parametryzuje query by wyszukać wszystkie obiekty w określonej odległości od wskazanego punktu...

public class SolrGeoQuery extends SolrQuery {

public SolrGeoQuery(String latitude, String longitude, int distance, int limit) { super(); this.setRows(limit); this.add("sfield","location");
this.add("sort","geodist() asc"); this.add("indent","true"); this.add("fq","{!geofilt}"); this.add("d", Integer.toString(distance)); this.add("pt", latitude + "," + longitude); this.setQuery("*:*"); } }
Home ← Starsze wpisy