Saturday, August 29, 2009

JBoss'a Ne Kadar Bellek Ayirmak Lazim?

EC2 makinamizi baslattik, ve JBoss app server'i kosturmak istiyoruz. JBoss'a ne kadar hafiza ayirmak lazim?

Komut satirina gidelim ve tum servis programlarini durduralim. Ne varsa. MySql, Apache, JBoss; makina ilk acildigi, kuruldugu anki gibi olmali. Bu durumdaki makinada "free -m" komutunu isletelim. Geriye gelen "used" yani makina hicbir sey yapmazken kullanilan bellek miktarini ikiyle carpalim. Sonra bu sayiyi "free" sayisindan cikartalim. Geri kalan JBoss'a ayirilabilecek hafiza miktaridir.

Bu hesabin mantigi nerede? Oncelikle makina hicbir sey yapmazken kullanilan hafizaya bakiyoruz, bu hafiza inaktif haldeyken bile gereken miktar, demek ki "en az" bu kadar gerekiyor. Sonra, ikiyle carparak makina kullanim halindeyken isletim sisteminin biraz daha fazla hafizaya ihtiyac duyabilecegini varsayiyoruz. Eh bundan arta kalan artik JBoss'a verilebilir.

Nasil verilecek? JBOSS/bin/run.conf icinde JAVA_OPTS icin -Xms[EN AZ]m ve -Xmx[EN FAZLA]m tanimlarini yaparak. Burada bir tavsiye; ustteki aritmetikten elimize gecen sayiyi hem en az, hem en fazla icine set edelim. JBoss baslar baslamaz eldeki tum hafizaya alsin yani, boylece hafizayi "arttirmaya" ugrasmakla vakit kaybetmesin.

Kaynak

Wednesday, August 26, 2009

Cookie, ab ve Yuk Testleri

Apache ab ile bir kullaniciyi taklit etmek icin URL uzerinden bilgi gonderilebilecegi gibi cookie uzerinden de uygulamaya bilgi gonderebilir.
ab -kc 1 -n 1 -C [COOKIE ISMI]=[COOKIE DEGERI] http://www.site.com/sayfa.seam
Cogu Web uygulamasi kullanici kimligini bir cookie uzerinde tutarak, bu cookie servis tarafina gelince kullaniciyi otomatik olarak sisteme giris yaptirir. Bu kavrami test amacli olarak kullanamaz miyiz? Hatta yuk testleri icin, pek cok kullaniciyi taklit etmek icin diyelim ki bir girdi text dosyasi icinde kimlik degerlerini tutuyoruz (kimlikleri uygulamanin veri tabanindan cikarttik) ve bir Python script bu idleri oradan okuyup, id sayisi kadar Thread baslatip (cunku kullanicilarin sisteme eszamanli girmesini simule ediyoruz) kullanici basina bir sayga istegini N kadar arka arkaya isletebilir.

Script cookied_page_load.py suna benzer:
import threading, re, os, sys

times = int(sys.argv[1])

class Caller(threading.Thread):
def __init__(self, userId):
threading.Thread.__init__(self)
self.userId = userId
def run(self):
url = "http://www.site.com/sayfa.seam"
cmd = "ab -kc 1 -n " + str(times) + " -C userId=" + self.userId + " " + url
print cmd
os.system(cmd)

ts = []
f = open ("[ID DOSYASI]")
for id in f.readlines():
id = id.replace("\n","")
a = Caller(id)
a.start()
ts.append(a)

for t in ts:
t.join()

Bu script
python cookied_page_load.py 10
ile [ID DOSYASI] icinde buldugu kimlikleri userId adli Cookie'ye koyup sistemin belirttigimiz bir sayfasina kullanici basina 10 kere girmeye calisacaktir.

HProf

"Fakir adamin Profiler araci" Java kutuphanesinin icinden cikan HProf araci. Bu arac cok sukseli bir cikti vermiyor fakat ciddi problemleri ilk bastan gormek, saptamak icin kullanilabilir. Kurumsal Java kitabinda bu aractan bahsetmistik. Profiler'i kullanmak icin bir uygulamayi o aracin kontrolunde baslatmak lazim; sonra uygulama bitince (CTRL-C, kill -9 ile durdurulunca) size java.hprof.txt adli bir dosya uretiliyor. Bu dosya icinde uygulamamizin kullanimi ile alakali bir suru istatistik mevcut.

Kullanmak icin JBoss bin/run.sh dosyasini bin/run_with_hprof.sh diye kopyalayalim. Sonra satir #60'a gidip JAVA_OPTS diyen satiri
JAVA_OPTS="-agentlib:hprof=cpu=samples,depth=7 ... "
olarak diye degistirelim. Bu sekilde JBoss'u run_with_hprof.sh script'i ile baslatinca, kullanildigi anda profile edilen bir uygulamamiz olacak. Secenekler ne diyor? CPU kullanimina bak, sadece orneklem yaparak olc (her seyi olcme) ve metot cagrilari zincirini 7 seviye derine gidecek sekilde kayitla. Rapor dosyasi uretilince en altta uygulamamizin en coktan aza dogru sayida, yuzdede cagrilan metotlarinin listesini goreceksiniz. En cok cagrilanlar performans iyilestirmesi icin bir aday olabilir elbette.

Yuk yaratmak icin ise, daha once Selenium ve Python temelli bir aractan bahsettik. Cok basit yuk ihtiyaclari icin Apache ab adli arac kullanilabilir. Diyelim ki uygulamamiz URL uzerinden parametre kabul edebiliyor, bu durumda bu parametreleri /tmp/params adli bir dosyaya yazariz, "anahtar1=deger1&anahtar2=deger2" gibi.. Bu parametreleri ab ile uygulamaya gondermek icin
ab -kc [KAC PARALEL] -n [KAC TANE] -p /tmp/params -T application/x-www-form-urlencoded 
http://www.bizimsite.com/sayfa.seam
Ustteki komut [KAC PARALEL] tane sanal kullanici (thread) yaratacak, ve bu kullanicilari [KAC TANE] kadar en sonda verilen URL adresine saldirtacak. Bu sekilde uygulamamiz uzerinde iyi bir yuk yaratabiliriz. java.hprof.txt dosyasinda ise text editorumuz ile kendi uygulamamizin Java paket ismini arariz, en alttaki metot listesine bakariz, problem noktalarini saptamaya calisiriz.

Sunday, August 23, 2009

Yourkit Java Profiler

Kurumsal Java kitabi icin 2006 senesinde profiler araclarini incelemistik. O zamandan beri piyasa pek degismemis; halen Profiler piyasasi open source baglaminda genis secenekler sunmuyor. Eger para verilecek bir alan varsa, Profiler bunu verecek yegane yerlerden biri herhalde. Bizim sahsi kullanimimiz birkac gunun otesine gecmiyor, o sebeple ticari bir urunu deneme lisansi altinda alarak para vermeden kullaniyoruz. Daha uzun sureli ve sirket temelli kullanimlar icin durum daha degisik olabilir.

Halen Profiler'lar arasinda en iyi urun Yourkit Java Profiler. Kurulumu en son versiyonunda daha da basitlesmis. Indirip unzip ettikten sonra dizine girip lib altina bakin, ve

java -jar yjp.jar -integrate

komutunu isletin. Bu komut bir menu getiriyor, YJP'yi entegre edecek app server listesi secenek olarak sunuluyor. Biz 2) sectik, yani JBoss 2.x/3.x/4.x. Daha sonra JBoss'un kullandigi run.sh dosyasinin yeri size soruluyor, bu dosyanin tam dizinli yerini veriyoruz, ve diger sorulara olagan (default) degerleri giriyoruz. Sonuc olarak bu entegrasyon programi JBoss bin dizini altinda bir run_with_yjp.sh dosyasi yaratiyor. Bu script'in ne yaptigi bariz; program JBoss'u YJP kontrolu altinda baslatiyor ve boylece Profiler GUI ile bu JVM'e baglanip bilgi toplayabilmeye basliyoruz. Her turlu cetrefil PATH ayari vs otomatik olarak bu script icinde yapilmis. Bizim ek secenekler sokusturmamiza gerek yok. Ayrica normal run.sh script'i temiz birakilmis, o hala normal ihtiyaclar icin kullanilabilir.

GUI malum, YJP kontrolundeki herhangi bir JVM belli bir port uzerinden bilgi paylasir (olagan deger 10000); Eger JBoss'u sonuc ortamindaki bir makinada ise, bu makinaya her port acik olmayabilir, o zaman SSH tunelleme gerekir. Makinaya

ssh -L10000:localhost:10000 kullanici@makina

ile baglanirsak, artik "bizim makinamizadaki" 10000 port degeri uzaktaki makinaya tunellenir, ve sonuc ortamini bu sekilde profillememiz mumkun olur. JBoss YJP ile baslatilinca, YJP/bin altindaki yjp.sh ile GUI baslatilir ve gerekli servise baglanip bilgi toplanmaya baslanabilir.

Saturday, August 22, 2009

JBoss'u Zayiflatmak

JBoss server dizini altinda belli "kurulus tipleri" oldugunu biliyoruz. Bunlar minimal, default gibi kurulus duzenleridir; run.sh -c minimal diyerek mesela JBoss'u minimal haliyle, o dizin altindaki tanimlari kullanarak baslatabiliriz. Biz default ayarlarini kullaniyoruz; fakat bu dizin altinda hala isimize yaramayan, fazla bazi ekler var. Bu yazida bu gereksiz ekleri cikartarak JBoss'u "zayiflatma (slimming)" teknigini gorecegiz.

Bundan once kullanim amacimizi ortaya koyalim: Hedefimiz JBoss'un sadece Web sayfalarini dinamik olarak ureten, veri erisimini Schemafree (ya da diger bir anahtar/deger paketini -demek istedigimiz kap servislerini kullanmadan-) yapan bir Seam uygulamasini barindirmasidir. Bu kadar. Seam arka planda local EJB kullanir, onlar da bir sekilde dahil olacak. Bu durumda ise yaramayanlar JMS, takvimleme (scheduling) gibi ekler.. Bu, uygulamaniz JMS'e hic ihtiyac duymayacak anlamina gelmez; sadece standart tipik bir Web JBoss'u hazirlamak icin (ki bizim ihtiyacimiz buydu) gerekenlere isaret ediyoruz.

Alttaki Python script bir default kurulusu alip, gereksiz bilesenleri otomatik olarak siliyor ve onu inceltiyor. Kullandigimiz JBoss versiyonu 4.2.2 GA. Kaynak bolumundeki yazida silinecekler listesi vardi, orada bazi degisiklikler yapmak gerekti; yani alttaki script en guncel zayiflatma script'i su anda.
import os

a = '''
/server/default/deploy/jms
/server/default/deploy/jmx-console.war
/server/default/deploy/snmp-adaptor.sar
/server/default/deploy/management
/server/default/deploy/mail-service.xml
/server/default/deploy/monitoring-service.xml
/server/default/deploy/schedule-manager-service.xml
/server/default/deploy/scheduler-service.xml
/server/default/deploy/hibernate-deployer-service.xml
/server/default/deploy/http-invoker.sar
'''

JB = "[JBOSS DIZININIZ]"

for token in a.split():
f = JB + token
os.system("rm -rf " + f)
Kaynak

Java Enum Tipleri

Enum tipleri program baslamadan belli sayida olan ve bir sabit "kod" ile eslenmesi programlama acisindan rahat olacak degerleri listesini temsil etmek icin kullanilir. Atanan kodlarin "tip guvenlikli" olmasi guclu tipleme iceren dillerin ikili muhasebesini / kontrolunu devreye sokabilmis olur. Yanlislikla bir String tipini sabitiniz ile esleyemezsiniz, tipler uymaz, derleyici hata verir. Bu kontrol iyidir.

Bir Web ortaminda bu yapilardan ek olarak beklediklerimiz programciya bir sekilde "hepsini gezme" ozelligi saglamasi, bir sayi degeri olan "sirasini" verebilmesidir. Birinci ozellik bize kullaniciya bir listeden onceden tanimli bazi secenekleri sectirme baglaminda lazim oldu, ikinci ise, rasgele olarak bir listeden belli bir "sey" secmek gerektiginde gundeme geldi. Rasgele sayi dogal olarak bir numaradir ve bunu onceden tanimli listemiz ile bir sekilde ilintilendirmemiz gerekiyordu.

Java Enum tipleri burada yardimci olabilir. Bizim kullandigimiz kalip (pattern) suna benziyor.
public static enum EnumType {
A("Bu bir A"), B("Bu bir B");
private String name;
EnumType(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int getOrdinal() {
return ordinal();
}
}
Artik A, B birer "tip" olarak tanimlanmistir. Soyle bir kullanim mumkundur.
EnumType a = EnumType.A;

log.debug("a.getName()=" + a.getName());
log.debug("a.getOrdinal()=" + a.getOrdinal());
Tum listeyi gezmek icin
for (EnumType t : EnumType.values()) {
log.debug("t.getName()=" + t.getName());
}
Ustteki liste rahatlikla Seam uzerinden @Out ile de-enjekte edilebilir ve kullaniciya icinden sebebilecegi bir secenek sunulabilir. Son bir nokta; Enum tiplerini Seam tarafinda == isareti ile esitlik testing tutmak gerekirsa, o zaman tiplerin String karsiligi kullanilmali, mesela yukaridaki ornekte kullanim .. == 'A' olacaktir.

Tuesday, August 18, 2009

S3 ve Yedeklemek

Bir EC2 makinasi kapandiginda bir imaja yakilmamis her verisini kaybeder. O zaman bir MySql tabanini EC2 uzerinde isletiyorsak, dinamik olarak surekli guncellenen verisini kaybetmemenin bir yolunu bulmamiz lazim. S3 servisi buyuk dosyalarinizi Amazon sistemi icinde depolamanizi sagliyor. O zaman EC2 makinasindan rutin olarak (cron uzerinden mesela) MySql'in yedegini alip, bu yedegi S3'e gondermek bir cozum olabilir.

S3'e script ortamindan erisebilmek icin Ubuntu EC2 makinanizda

sudo apt-get install s3cmd

komutunu isletin. Bu komut s3cmd adli bir yardimci programi kuracak. Bu programi sc3cmd --configure ile isletin; sorulan sorular EC2 acik ve gizli anahtarlariniz. Bu anahtarlar girildikten sonra s3cmd onlari hep hatirlayacak ve bundan sonra isletilen tum s3cmd komutlari o anahtarlar uzerinden gerceklestirilecek, yani tum islemler ayni hesabin isaret ettigi kullanici uzerinden yapilacak. Bu kullanicinin kullanmasi icin yeni bir dizin yaratmak;

s3cmd mb s3://[BUCKET ISMI]

Herhangi bir dosyayi oraya kopyalamak icin

s3cmd put /tmp/test s3://[BUCKET ISMI]

[BUCKET ISMI] isminin "global" bir isim oldugunu belirtmistik, yani yeterince tekil olan bir isim secelim.

Bu kadar; artik

mysqldump -u root --quick [DB] | gzip > /vs/vs/db-backup.gz

gibi bir komutla rutin olarak yaratilan yedek dosyasini (db-backup.gz yani) s3cmd ile S3 hesabimiza gonderebiliriz.

Kaynak

Sunday, August 16, 2009

EC2 Elastik Ip Adresi

Amazon EC2 "elastik IP adresi" diye bir servis sunuyor. Aslinda bu servisle elde ettiginiz bir "statik IP" (yani servisin sundugu kabiliyetin tam yaptigi sey elastik kelimesi ile bir tezat olusturuyor olabilir) ama bu adres makinadan makinaya esnek bir sekilde "baglanabildigi (associate)" icin elastik bir kullanim sunmakta.. Sonuc olarak tek bir IP adresini istediginiz kadar elinizde tutabiliyorsunuz, ve bu adresi istediginiz EC2 makinasina baglayabiliyorsunuz. Baglantinin etki etmesi saniyeler aliyor; ve arkasindan hic degismeyen IP adresiniz yeni makinaya baglanabilmeye basliyor. EC2 ortaminda makinalarin (instance) ne kadar rahat acilip, kapatilabilecegini bildigimize gore, boyle bir ortamda elastik IP adresinin cok faydali bir servis olacagi acik. Teorik olarak eski siteniz islemeye devam ederken, yeni bir EC2 kumesnde yeni kodunuzu, yapinizi test edebilirsiniz, ve tatmin olunca, ana adresi, elastik IP adresini yeni makinaniza degistirdiginiz anda sitenizin trafigi hemen yeni makinalara akmaya baslayacaktir.

Elastik IP adresleri bir makinaya bagli oldugu zaman ucret odenmiyor, fakat rezerv edilmis ama "baglanmamis" halde ise Amazon sizden bir ucret kesiyor. Bunun sebebi Internet'te IP adreslerinin kisitli bir kaynak (precious resource) olmasidir; bu sebeple Amazon eger bir IP adresini rezerve ettiyseniz onu kullanmanizi istiyor. Bu sayede bir adresi "kapip" onu hic kullanmayan kisilerin tesvik edilmesi engelleniyor.

Adres nasil alinir? Cok basit:
ec2-allocate-address
Bu komut bir IP adresi geri dondurecektir ve bu adres iptal etmediginiz surece artik sizin EC2 hesabiniza bagli olacaktir. Adresi bir EC2 makinasina baglamak icin
ec2-associate-address -i [MAKINA ISMI] [IP]
komut yeterli. [MAKINA ISMI] ec2-describe-instances komutundan gelen listede "i-" ile baslayan makina ismi olacak, [IP] reserve edilmis olan IP adresiniz. Makina dogal olarak daha once ec2-run-instances ile baslattiginiz bir EC2 makinasidir. Herhangi bir anda reserve ettiginiz IP adreslerini gormek icin
ec2-describe-addresses
komutu yeterlidir.

WWW ile baslayan site ismini IP adresi ile baglamak, site ismini aldiginiz sirketle halledilecek bir sey; elastik IP adresinizi bu sirkete bildirdiginiz anda site isminiz ile IP adresiniz arasindaki baglanti da kurulmus olur, ve bu baglantinin bir daha degistirilmesi gerekmez. Makinalar degistikce ec2-associate-address ile ayni adres yeni makinalara baglanabilir, ve ayni isim ayni adres ile calismaya devam eder. Site ismi -> IP adresi baglantisi genelde surekli degistirilen bir sey degildir (etki etmesi uzun suruyor) o sebeple fazla olabilecek degisimi EC2 tarafinda yapabilerek optimal bir cozumu bulmus oluyoruz.

Monday, August 10, 2009

Sabitler

Web uygulamasi sabit degerleri (constants) nasil almali? Bir properties dosyasindan, muhakkak, fakat bu dosya EAR icinde mi disinda mi olsa daha iyi?

Disinda olmasi daha iyi; boylece bu dosyanin set edilmesi tamamen derleme sistemi disinda, gelistirme ile alakali degil, sonuca gonderme (deployment) ile ilgili. Sonuc ortaminda "ant" olmasa bile, properties dosyasinda degisim yapabiliriz. Alttaki class sabitleri okumak icin yazildi. Uygulamamizin sabitleri, diyelim ki app.properties dosyasi, her zaman $HOME/etc/ altinda aranir. Mesela JBoss baslatan "ubuntu" adli bir kullanici ise, /home/ubuntu/etc/app.properties okunacak.
import org.apache.log4j.Logger;
import java.io.IOException;
import java.io.FileInputStream;
import java.util.Properties;

public class Constants {

transient Logger log = Logger.getLogger("logger");

private static Constants _instance = null;

Properties properties = new Properties();

public static Constants instance() {
if (_instance == null) {
_instance = new Constants();
}
return _instance;
}
public Constants() {
String userHome = System.getProperty("user.home").toLowerCase();
try {
log.debug("userHome =" + userHome + "/etc/pocketbudda.properties");
properties.load(new FileInputStream(userHome + "/etc/app.properties"));
} catch (IOException e) {
Util.log(e);
}
}
public String getString(String arg) {
return properties.getProperty(arg);
}
public int getInt(String arg) {
return new Integer(properties.getProperty(arg)).intValue();
}
public String[] getList(String arg) {
String s = properties.getProperty(arg);
return s.split(",");
}
}
Sabit degerleri okumak icin uygulamamizin herhangi bir yerinde Constants.instance().getString("anahtar") gibi bir cagri yeterli. Dosya
anahtar1=deger1
anahtar2=deger2
formatinda... Constants class'i listeler, sayilari "tip guvenlikli (type safe)" bir sekilde okuyabilecek halde.

Sabitleri Seam'e vermek mumkun; uygulama baslayinca muhakkak islemesi garanti bir Action class'i icine:
@Out
String showAds = Constants.instance().getString("showAds");
gibi bir tanim "showAds" sabitini dis dunyaya de-enjekte edecektir. Artik
<s:div rendered="#{showAds == 'false'}">
<img src="[imaj]"/>
</s:div>
gibi tanimlar xhtml sayfalari icinde kullanilabilir.

Sunday, August 9, 2009

Apache 2, mod_jk, Ubuntu ve Seam

JBoss onunde bir Apache Web Server programi hem yuk dagiticisi olarak hem statik icerik saglayici olarak gorev yapsin istiyorsak, Ubuntu Server uzerinde hem Apache 2 hem de mod_jk kurmak apt-get ile mumkun. Kurmak ve ayar icin sunlar gerekli:

Once

sudo apt-get install apache2 libapache2-mod-jk

Sonra
/etc/apache2/conf.d/jk.conf dosyasi yaratin, sunlar olsun:

JkWorkersFile /etc/apache2/jk-workers.properties
JkLogFile /var/log/apache2/mod_jk.log
JkShmFile /var/log/apache2/mod_jk.shm
JkLogLevel warn
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
JkRequestLogFormat "%w %V %T"
JkMount /*seam* ajp13
JkMountCopy all

Yukarida *seam* ibaresine dikkat. Sadece *.seam demiyoruz, *seam* diyoruz cunku Ajax cagrisi amaciyla EJB js kodlari dahil ederken bunu seam/remoting/../remote.js gibi bir kelime kullanarak yapariz, ve bu js dosyasi dinamik olarak "uretilen" bir dosya oldugu icin bu cagrinin app server'a gitmesi lazim, Apache o isi halledemez. Eger sadece *.seam deseydik, bu tur cagrilar JBoss'a gitmeyecekti.

Simdi /etc/apache2/jk-workers.properties dosyasi yaratin, sunlar olsun:

worker.list=ajp13
worker.ajp13.port=8009
worker.ajp13.host=localhost
worker.ajp13.type=ajp13
worker.ajp13.lbfactor=1

Statik iceriklerinizin (imaj, css, html dosyalari gibi) hepsini /var/www altina kopyalamaniz gerekiyor. Artik

sudo /etc/init.d/apache2 restart

ve JBoss run.sh ile baslatin (-b [IP ADRESI] ibaresine gerek yok cunku Apache arka planda JBoss ile localhost uzerinden konusuyor zaten).

Artik Seam sayfalarini gorebiliyor olmaniz lazim.

Kaynak:

Saturday, August 8, 2009

EC2 Imajlari, Ubuntu Server

Uygulamamizi sonuc ortamina (production) gondermeye karar verdik; diyelim ki barindirma servisi olarak Amazon EC2 servisini sectik. Daha onceki su yazida genel EC2 komutlarini tanidik.

Simdi bir Ubuntu bazli servis makinasini nasil yaratacagimiza gelelim:

Bildigimiz gibi, EC2 "makina imajlari (machine image)" kavramiyla isliyor. Bir makinayi sifirdan yaratmak icin gerekecek tum dosyalar (bunlar oldukca buyuk yer tutuyor dogal olarak) yine Amazon'un buyuk olcekli dosyalari tutmak icin sundugu S3 servisi uzerinden herkese sunuluyor. Bu servisteki tum Ubuntu AMI (makina imajlarini) gormek icin
ec2-describe-instances -a | grep ubuntu
isletmek yeterli. Soyle bir liste geri gelecek:
...
IMAGE ami-5059be39 canonical-cloud-us/ubuntu-intrepid-20090422-i386.manifest.xml
099720109477 available public i386machine aki-714daa18 ari-6a5bbc03 I
...
Ustteki imaj bizim sectigimiz imajlardan biri. Canonical Ubuntu sistemini yaratan sirket zaten, bu sebeple "kaynaktan" gelecek imajin saglam olacagi dusuncesinden hareketle onu seciyoruz. Bu konuyu onceki yazida isledigimiz icin daha fazla detaya girmiyoruz. Simdi, ek olarak yine diyelim ki, "sudo apt-get install" ile bu imajdan yaratilan makinaya bir suru yeni program ekledik. Bu programlari kaybetmek istemiyoruz, cunku ec2-terminate-instances ile biraz onceki yarattigimiz makinayi kapatinca imaja dahil olmayan her turlu yeni dosya kaybolacaktir. O zaman bu makinanin son halini yeni bir imaj olarak "yakmamiz" gerekecek. Bunu yapmak icin EC2 makinasinda sunlari isletebiliriz.
sudo ec2-bundle-vol -d /mnt -k /mnt/[ACIK SSH ID DOSYASI] -c /mnt/[CERT DOSYASI] -u [KULLANICI ISMI] -r i386 -p [IMAJ ISMI]
[] icindeki dosyalari EC2 hesabinizda anahtarlarinizi gosteren yerden ya indirebilir, ya da kopyalayarak yapistirabilirsiniz. Acik ssh id dosyasi kisisel bilgisayarinizda EC2 sistemlerine erismek icin surekli kullandigimiz dosya aslinda. Tum bu dosyalari alip EC2 makinanizdaki /mnt/ altina kopyalayin. Niye /mnt? Cunku yakma islemi sirasinda /mnt altindaki dosyalar imaja yazilmiyor ve bu "gizli" dosyalarin imaj icinde olmamasi daha iyi.

Imaji yarattiktan sonra onu alip S3 depolama sisteminize yazmaniz lazim, kaybolmamasi icin. EC2 hesabi olan herkes bir S3 hesabi acabilir. Bunu yaptiktan sonra;
ec2-upload-bundle -b [BUCKET ISMI] -m /mnt/[IMAJ ISMI].manifest.xml -a [ACCESS KEY] -s [SECRET KEY]
Bucket ismi, sizin sececeginiz bir "dizin" ismi olacak. S3 bu isimleri "global" isimler olarak aliyor, yani "mybundle" gibi isimler cok genel isimler secilirse cakisma olur, projenize ozel bir isim olmasi daha iyi olur.

S3 dosyalarinizi gormek icin S3 Organizer iyi bir Firefox eklentisi.

Bunun ertesinde, artik EC2 makinanizi kapatsaniz bile, yarattiginiz imaj dosyalarini baz alarak hemen ayni makinayi sifirdan yaratabilirsiniz. Bunu yapmadan once imaji EC2 hesabinizdan "kayit etmis olmaniz (register image)" lazim. [IMAJ ISMI].manifest.xml ile bunu hemen yapabilirsiniz, ardindan komut satirinde ec2-describe-images komutunu isletirseniz, kayit ettigimiz imajin "i-" ile baslayan kimligini alabilirsiniz, bundan sonra ec2-run-instances komutu ile bildigimiz islemleri gerceklestirebiliriz.

Bir kaynak:

Wednesday, August 5, 2009

Seam, Selenium ve Yuk Testleri

Web uygulamamiz uzerinde yuk testleri isletmek icin Kurumsal Java kitabinda jMeter aracini tavsiye etmistik. Fakat Seam uzerinde JMeter'in islemedigini farkedince alternatif bir arac bulmak gerekti. Web egitim slaytlarimizda kabul testleri (acceptance test) icin Selenium adli bir arac gosteriliyordu; Bu arac bir Firefox eklentisidir, ve kaydetmeye baslayinca Firefox uzerinden yaptiginiz tum aksiyonlari, girilen verileri alip tekrar geri calma (replay) gibi servisler saglamaktadir. Tek kullanici, tek test icin guzel bir ortamdir. Kurmak icin indirme sayfasindan Selenium IDE xpi eklentili dosyaya tiklayin, Firefox otomatik olarak programi kuracaktir.

Fakat simdi bizim istedigimiz Selenium ile bir testi kaydedip, N tane sanal kullanicinin ayni testi uygulama uzerinde isletebilmesi. Selenium, herhangi bir testi Python script olarak kaydedebiliyor. Bu iyi, scriptleme isleri bu sayede kolaylasabilir.

Test kaydetmek icin Firefox Tools | Selenium IDE menusunden uygulamayi aciyoruz, kayit isleminiz bittiginde ise File | Save Test Case As .. | Python secimini yapiyoruz, bir dosya ismi giriyoruz. Uretilen kod suna benziyor:
from selenium import selenium
import unittest, time, re

class exploretest(unittest.TestCase):
def setUp(self):
self.verificationErrors = []
self.selenium = selenium("[makina]", [PORT], "*chrome", "http://[site.ismi]")
self.selenium.start()

def test_exploretest(self):
sel = self.selenium
sel.open("/home.seam?conversationId=708")
sel.click("link=Explore")
sel.wait_for_page_to_load("30000")
sel.type("j_id4:day", "3")
...

def tearDown(self):
self.selenium.stop()
self.assertEqual([], self.verificationErrors)

if __name__ == "__main__":
unittest.main()
Bu kodu komut satiri ortamindan geri caldirmak icin, cunku paralel sekilde test isletmek icin once geri caldirma islemini scriptleyebilmemiz lazim, Selenium indirme sayfasindan Selenium RC adli paketi aliyoruz ve bir dizinde aciyoruz. Actiktan sonra dizinde selenium-server-x ve selenium-python-client-driver-x gibi dizinler goreceksiniz. Bunlardan birincisi Python scriptlerini geri isletirken konusulan servis, ikincisi ise uretilen Python kodlarinin import ettigi kodlarin oldugu yer. Bu kodlari uretilen Python scriptine tanitmak icin "import sys" ve "sys.path.append('[SELENIUM RC DIZINI]')" kodlarini en basa koymaniz gerekecek (ya da Python cevre degiskenleriyle biraz takla lazim). Bu olduktan sonra Sel RC baslatmak icin server dizinine gidip
java -jar selenium-server.jar -port [PORT]
girip servisi baslatabilirsiniz. [PORT] icin buyuk bir rakam iyi olabilir, 12000 mesela. Bu arada, uzerinden kayit yaptiginiz Seam programiniz isliyor olacak tabii. Artik uretilen test scriptine gidip direk python komutu ile isletirseniz, arka planda bir Firefox baslatildigini, kaydettiginiz hareketlerin aynen orada yapildigini, ve sonra Firefox'un kapatilip testin bitirildigini goreceksiniz. Testin basarili olup olmadigi python programi tarafindan text bazli olarak ekrana basilacaktir.

Buraya kadar basit. Fakat paralel testler soz konusu olunca problem surada: Yuk testleri icin bu testlerden 100 tane isletmemiz gerekebilir. O zaman 100 tane Firefox programi ekrana cikacaktir! Bu hem performans acisindan kotu hem de gereksiz cunku biz otomize bir sekilde bu testleri isletirken gorsel hicbir seyi umursamiyoruz. Bizi tek ilgilendiren bildigimiz bir senaryo uzerinden bir yuk yaratmak ve programin performansina bakmak. Programin "dogrulugunu" zaten Selenium IDE ile tekil geri calma yaptigimizda goruyorduk. Ayrica paralel testler genelde (her zaman) hicbir gorsel birimi olmayacak bir Linux server makinasi uzerinde isletilecek, o zaman isin GUI tarafini iptal etmek / kapatabilmek onemli bir ihtiyac.

Bunu nasil yapacagiz? Gorsel olmadan (headless) sekilde Selenium isletmek icin guzel bir teknik bulduk. Bu arkadas xvfb diye bir program kullanarak Selenium'dan gelen tum X komutlarini monitora gondermeden yakalattiriyor, ve iptal ediyor. Selenium X var zannediyor ama aslinda yok. Kurmak icin "apt-get install xvfb". Isletmek icin iki tane xterm acin: birinde "Xvfb :99 -ac" komutunu isletin. Ikinci xterm icinde "export DISPLAY=:99" deyip, arkasindan ayni xterm icinde hemen ustteki gibi Selenium RC server baslatin. Artik python ile kaydedilen testi geri caldigimizda hicbir Firefox acilmadigini gorecegiz.

Simdi paralelizasyona gelelim. Bundan sonraki basamak, bizim ekimiz. Birden fazla, paralel sekilde ayni testi isletmenin bir teknigi. Bu amacla RC server sureclerinden N tane baslatacagiz ve test isletici program, tek surec ama N thread icinden bu ayri sureclere baglanacak, ve testleri paralel sekilde isletecek. Isin temiz olmasi icin, tum bunlari tek Python script'i icinden yaptirtacagiz. Ama ilk once Selenium IDE tarafindan urettirdigimiz test programina gidelim; burada ufak bazi degisiklikler lazim. Diyelim ki uretilen ustteki exploretest.py script'i icindeki exploretest class'i olsun. Bu class'in ustteki hali suna cevrilmeli:
class exploretest(unittest.TestCase):
def __init__ (self, port):
self.port = port
def setUp(self):
self.selenium = selenium("[makina]", self.port, "*chrome", "http://[site]")
self.selenium.start()
...
Degisen ne? setUp ile class tanimi arasina bir kurucu method (constructor) eklemisiz. Bu lazim cunku pek cok Thread pek cok exploretest objesi yaratacak ve bu objelerin her birinin ayri bir RC server portuna isaret ediyor olmasi lazim. Ikinci degisimle, selenium() cagrisi artik disaridan verilen self.port parametresini kullaniyor.

Bu script disinda runselserver.sh adli bir script lazim. runselserver.sh suna benzer:
#!/bin/bash
export DISPLAY=:99
cd [SELENIUM RC SERVER DIZINI]
java -jar selenium-server.jar -port $1
Tamam. Bu programi chmod u+x ile isletilebilir hale getirmeyi unutmayin. Simdi gelelim esas okkali programa. testrunner.py adli bu programi server sureclerini, threadleri baslatma, isletme islerinin hepsini gerceklestiren ana program.
import os
import subprocess
import time
import threading
from exploretest import exploretest
import unittest

class Caller(threading.Thread):
def __init__(self, port):
threading.Thread.__init__(self)
self.port = port
def run(self):
print "Calling"
a = exploretest(self.port)
a.setUp()
a.test_exploretest()
a.tearDown()

# number of parallel test
testnumber = 4

# start port
port = 12000

for i in range(testnumber):
print str(port+i)
cmd = ['[DIZIN]/runselserver.sh %s' % str(port+i)]
p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

time.sleep(2)
print "running tests"

ts = []
for i in range(testnumber):
a = Caller(port+i)
a.start()
ts.append(a)

for t in ts:
t.join()
Bu programi kullanarak yeni urettiginiz kendi testlerinizi cagirttirmak icin exploretest yazan her yerde kendi test isminizi koyabilirsiniz. testnumber parametresi ile kac tane paralel test isletilecegini kontrol edebilirsiniz. Bu kadar!

Notlar:

o N surec N thread icin sadece bir tane Xvfb isletmek yeterli. Bu komut ayri bir xterm icinde bir kere isletilir.
o Bir potansiyel problem: Xvfb programi pek stabil degil, her isletim sonrasi Segmentation Fault ile cokuyor, ama ondan once isi basariyla tamamliyor. O zaman her test oncesi bu programi yeniden baslatmak lazim. Biraz hammaliye (ki zaten otomize edilebilir), bu da aklimizda olsun.
o Yuk testleri isleten makinanin guclu bir makina olmasi iyi olur cunku bu makinanin N tane firefox programini baslatmasi gerekecektir. Her ne kadar firefox'un gorselligini Xvfb ile iptal etmis olsak bile, sonucta firefox isler kodu yine de calisacak, ve bu pek ufak bir program degil.