Sunday, September 23, 2007

XML: Performans Katili - Another Performance Killer

XML teknolojisi yazılım dünyasının işlerini bir çok noktada kolaylaştıran ama bir o kadar da zorlaştıran bir teknoloji. Özellikle Java ile uygulama geliştiriyorsanız XML'den olabildiğince uzak durmak gerekiyor. XML parsing & generating operasyonları sırasında çok fazla String işlemi olduğu için JVM memory ve CPU kullanımı korkunç artıyor. XML'e performans diye boşu boşuna dememişler.

Bu yazımın esas amacı aslında Java uygulamarında XML kullanmanın getirdiği maliyetleri ve Dennis Sosnoski amcamızın meşhur XML Model Benchmark Test'lerini tanıtmak.

XML'in performance killer 'dan nasıl proje katiline dönüşebileceğine bakmadan evvel öncelikle neden uygulamalarımızda XML kullanmak zorunda kaldığımıza bir bakalım.

XML kullanımı, özellikle Java dünyasında Web Service'lerin yaygınlaşması ile artmaya başladı. Web Service kullanımını arttıran da hepimizin yakından bildiği SOA (Service Oriented Architecture) oldu. 2000'li yılların başında SOA'nın moda olmaya başlamasıyla, dünyaca ünlü vendor'larımız her zaman yaptıkları gibi bu kavramı da hemen suistimal etmeye başladılar. Bir metodoloji çok tuttuğunda vendor'larımızın ilk yaptığı şey, satabilmek için ortaya zorlama bir ürün çıkarmaktır. SOA'nın bu ürünü de Web Servis implemantasyonları oldu. Web Service ailesi (XML-RPC , REST, SOAP) içinde de en fazla öne çıkarılan SOAP mesajları oldu.

Halbuki SOA'nın felsefisinde tüm servis hizmetlerinin web servisler aracılığı ile verilmesi diye bir zorunluluk yoktur. SOA'da bir servise dış veya iç sistemden erişim ihtiyacı varsa öncelikle servis hizmetini veren (server) ile alanın (client) ortamlarına bakmak gerekir.

1. Durum Servis hizmetini veren ve alanın aynı java container'ında olması.
2. Durum Servis hizmetini verenin ve alanın farklı java container'larında olması
3. Durum Servis hizmetini verenin java fakat alanın farklı bir dile (ASP,C#,vs.) ait container'da olması.

1. Durumda istemci ve sunucu aynı JVM'de olduğu için zaten web servis kullanmaya gerek yoktur. Fakat dikkat edilmesi gereken konu, SOA'nın şartlarından biri olan servisler arası bağımsızlığın (loose coupling) korunmasıdır. Spring Framework 'unun da kullandığı Inversion of Control diye adlandırılan patterni bu duruma güzel bir örnektir.

2. Durumda istemci ve sunucu farklı container'da olmasına rağmen iki taraf da java olduğu için yine web servis kullanmaya gerek yoktur. İlk bakışta servis çağırmak için RMI kullanmak en mantıklı çözüm gibi gözüksede, RMI teknolojisinin http protokolü kullanmaması nedeniyle firewall ve proxy'lerde problem yaşaması, farklı java versiyonları kullanan istemci/sunucu mimarilerinde sorun yaşanması gibi sebeplerden pek tercih edilmemektedir.

Bunun yerine en mantıklı çözüm serialized java objects kullanmaktır. Serializable java nesnelerini aynı web servis mantığı ile örnekteki gibi byte array'e çevirip container'lar arasında http üzerinden transfer edebilirsiniz. Objelerin serialVersionUID 'lerini değiştirmediğiniz sürece JDK 1.3 - 1.5 arasındaki container'lar arasında bile servis çağırabilirsiniz. Serialization&DeSerialization, XML parsing'e göre çok daha hızlı, daha az memory kullanan, binary olduğu için daha az data büyüklüğü oluşturan bir işlemdir. Vakti zamanında yaptığımız testlerde özellikle büyük datalarda 30 kata yakın performans elde etmiştik. Eğer bu konuda daha yetenekli, productionda kullanmaya hazır bir API'ye ihtiyaç duyarsanız JBoss Remoting 'e bir göz atabilirsiniz.

3. Durumda istemci java'dan başka bir container olduğunda web servis kullanmaktan başka bir çare yok. Fakat bu konuda da alınabilecek önlemler var. Eğer standart web servis implementasyonlarından herhangi birini seçme imkanınız var ise SOAP'dan kesinlike uzak durun derim. SOAP çok genel amaçlar için tasarlanmış ve sistem performansını en çok düşüren bir XML protokolüdür.

XML'in sisteminizin performansına olan etkisini belirleyen 3 önemli unsur vardır:

1. XML'in yapısı

Varsayalım elimizde 100KB. büyüklüğünde 2 farklı yapıda XML dosyası olsun. Birincisinin ağaç yapısında varsayalım:

XML teknolojisi yazılım dünyasının işlerini bir çok noktada kolaylaştıran ama bir o kadar da zorlaştıran bir teknoloji. Özellikle Java ile uygulama geliştiriyorsanız XML'den olabildiğince uzak durmak gerekiyor. XML parsing & generating operasyonları sırasında çok fazla String işlemi olduğu için JVM memory ve CPU kullanımı korkunç artıyor. XML'e performans diye boşu boşuna dememişler.

Bu yazımın esas amacı aslında Java uygulamarında XML kullanmanın getirdiği maliyetleri ve Dennis Sosnoski amcamızın meşhur XML Model Benchmark Test'lerini tanıtmak.

XML'in performance killer 'dan nasıl proje katiline dönüşebileceğine bakmadan evvel öncelikle neden uygulamalarımızda XML kullanmak zorunda kaldığımıza bir bakalım.

XML kullanımı, özellikle Java dünyasında Web Service'lerin yaygınlaşması ile artmaya başladı. Web Service kullanımını arttıran da hepimizin yakından bildiği SOA (Service Oriented Architecture) oldu. 2000'li yılların başında SOA'nın moda olmaya başlamasıyla, dünyaca ünlü vendor'larımız her zaman yaptıkları gibi bu kavramı da hemen suistimal etmeye başladılar. Bir metodoloji çok tuttuğunda vendor'larımızın ilk yaptığı şey, satabilmek için ortaya zorlama bir ürün çıkarmaktır. SOA'nın bu ürünü de Web Servis implemantasyonları oldu. Web Service ailesi (XML-RPC , REST, SOAP) içinde de en fazla öne çıkarılan SOAP mesajları oldu.

Halbuki SOA'nın felsefisinde tüm servis hizmetlerinin web servisler aracılığı ile verilmesi diye bir zorunluluk yoktur. SOA'da bir servise dış veya iç sistemden erişim ihtiyacı varsa öncelikle servis hizmetini veren (server) ile alanın (client) ortamlarına bakmak gerekir.

1. Durum Servis hizmetini veren ve alanın aynı java container'ında olması.
2. Durum Servis hizmetini verenin ve alanın farklı java container'larında olması
3. Durum Servis hizmetini verenin java fakat alanın farklı bir dile (ASP,C#,vs.) ait container'da olması.

1. Durumda istemci ve sunucu aynı JVM'de olduğu için zaten web servis kullanmaya gerek yoktur. Fakat dikkat edilmesi gereken konu, SOA'nın şartlarından biri olan servisler arası bağımsızlığın (loose coupling) korunmasıdır. Spring Framework 'unun da kullandığı Inversion of Control diye adlandırılan patterni bu duruma güzel bir örnektir.

2. Durumda istemci ve sunucu farklı container'da olmasına rağmen iki taraf da java olduğu için yine web servis kullanmaya gerek yoktur. İlk bakışta servis çağırmak için RMI kullanmak en mantıklı çözüm gibi gözüksede, RMI teknolojisinin http protokolü kullanmaması nedeniyle firewall ve proxy'lerde problem yaşaması, farklı java versiyonları kullanan istemci/sunucu mimarilerinde sorun yaşanması gibi sebeplerden pek tercih edilmemektedir.

Bunun yerine en mantıklı çözüm serialized java objects kullanmaktır. Serializable java nesnelerini aynı web servis mantığı ile örnekteki gibi byte array'e çevirip container'lar arasında http üzerinden transfer edebilirsiniz. Objelerin serialVersionUID 'lerini değiştirmediğiniz sürece JDK 1.3 - 1.5 arasındaki container'lar arasında bile servis çağırabilirsiniz. Serialization&DeSerialization, XML parsing'e göre çok daha hızlı, daha az memory kullanan, binary olduğu için daha az data büyüklüğü oluşturan bir işlemdir. Vakti zamanında yaptığımız testlerde özellikle büyük datalarda 30 kata yakın performans elde etmiştik. Eğer bu konuda daha yetenekli, productionda kullanmaya hazır bir API'ye ihtiyaç duyarsanız JBoss Remoting 'e bir göz atabilirsiniz.

3. Durumda istemci java'dan başka bir container olduğunda web servis kullanmaktan başka bir çare yok. Fakat bu konuda da alınabilecek önlemler var. Eğer standart web servis implementasyonlarından herhangi birini seçme imkanınız var ise SOAP'dan kesinlike uzak durun derim. SOAP çok genel amaçlar için tasarlanmış ve sistem performansını en çok düşüren bir XML protokolüdür.

XML'in sisteminizin performansına olan etkisini belirleyen 3 önemli unsur vardır:

1. XML'in yapısı

Varsayalım elimizde 100KB. büyüklüğünde 2 farklı yapıda XML dosyası olsun. Birincisinin ağaç yapısında olduğunu,ikincinin ise tek nod'dan oluştuğunu varsayalım:
Herhangi bir XML parser ile (DOM, SAX, PULL farketmez) yukarıdaki aynı büyüklükteki 2 farklı yapıdaki dosyayı parse ettiğinizde çok farklı sonuçlar (hız, cpu ve memory kullanımı) elde ettiğinizi göreceksiniz. Birazdan bahsedeceğimiz XML Parser'in seçimi ile XML'in yapısı arasında çok yakından bir ilişki vardır.

2. XML'in büyüklüğü

XML data'sı büyüdükçe sistem performansı doğal olarak düşer. Fakat XML verisinin büyüklüğü ile sistem performansı arasındaki oran logaritmiktir. Bunun sebebi ise XML'in daha evvel bahsettiğimiz gibi çok fazla memory ve cpu kullanmasıdır. Bir JVM'de aynı anda çok fazla XML parsing işlemi olduğunda CPU bu işlemleri sıraya koymaya başlıyacak, sırada bekleyen XML nesneleri çok fazla memory kullandığından heap'i doldurmaya başlıyacak, JVM heap'in dolduğunu görünce sık sık GC (Garbage Collector) çalıştırmaya başlıyacak, GC çalışırken çok fazla CPU kullandığı ve tüm sistemi çalıştığı sürece suspend ettiği için tüm sistem tabir yerinde ise ağır çekimde ilerleyecektir. Tabii bu durum son kullanıcıya program çöktü olarak yansıyacaktır.

3. XML Model

Buraya kadar anlattıklarımıza rağmen data alışverişlerinde hala XML kullanmaya kararlı iseniz o zaman yapmanız gereken doğru XML parseri seçmek. Aynı zamanda bir SOA danışmanı olan Dennis Sosnoski 'nin eski ama hala meşhur XML Model Benchmark sonuçlarını incelerseniz, farklı test tipleri için meşhur XML API'lerin nasıl değişik test sonuçları verdiğini görebilirsiniz.

Ben genelde parser olarak çok daha hızlı olan ve az memory kullanan PULL parser'ları tercih ediyorum. Bu konuda size de XPP3 'i önerebilirim. Fakat son yıllarda VTD-XML isminde XML datasını String olarak değilde byte olarak işleyen çok hızlı bir XML parser ön plana çıktı. (world's fastest XML processor benchmark results).

Sonuç

Buraya kadar anlattıklarımızı özetlersek çok çok mecbur kalmadıkça konfigürasyon dosyası dışında XML kullanmayın derim. (Bu konfigürasyon dosyalarını okumak için de lütfen kendiniz bir API yazmaya kalkmayın, Apache Commons Configuration'ı kullanın.) Diyelimki çok mecbur kaldınız o zaman aşağıdaki önerilere kulak asmanızda fayda var:

1) SOAP'tan uzak durun ve XML'in yapısını oldukça basit tutun. XML'in yapısı ne kadar basit ise o kadar hızlı işlenir.
2) Farklı yapılarda XML veriniz var ise hepsi için aynı XML Model'ini kullanmayan. Hatta process etmeden evvel, XML'in büyüklüğüne ve yapısına bakıp en uygun XML Model'ini seçen generic bir API bile yazabilirsiniz.
3) Veri büyüklüğünün üst sınırını bilemediğiniz servislerinizi XML ile sunmayın ya da limit koyun. Test ortamlarında ortalama 100Kb.'lık datalar ile çalışırken, production'da bir servis sonucu 10MB.'lık bir XML oluşursa çok ciddi üzülürsünüz :)
4) XML operasyonlarını servisi hazırladığınız veya karşıladığınız katmanda yapın ve XML objelerini hemen basit java objelerine çevirin. Business metodları içine XML objelerini geçirmeyin.
5) Vendorların her söylediğine hemen kanmayın (hatta şüphe ile yaklaşın). Unutmayın onların hedefi daha fazla ürün satmak, sizin hedefiniz ise projenin başarılı olması.

Bu aralar AJAX'ın çok moda olması nedeniyle XML konusunda bir hatırlatma daha yapmak istiyorum. Bildiğiniz gibi AJAX 'da client/server arasındaki data alışverişini mecburen XML ile yapıyor. Developer'lar AJAX kullanırken XML yüzünden sisteme binen yükün farkında değiller. Konu açılmışken AJAX'a henüz giriş yapmayanlar Sezer Yeşiltaş'ın AJAX konusundaki blog'una bir göz atabilirler

Performans katili, projelerimizin katili olmamalı! Önümüzdeki projelerde de buna dikkat etmeliyiz!

Konuyla ilgilenmek isteyen hem .NET'ciler hem de JAVA'cılar için birkaç link vermek istiyorum:

Comparing Web service perfomances(1):
http://msdn2.microsoft.com/en-us/vstudio/aa700840.aspx

Comparing Web Service performances (2):
http://www.sosnoski.com/presents/cleansoap/comparing.html

Checklist Web Service performances:
http://msdn2.microsoft.com/en-us/library/ms979173.aspx

Ve Sun 'ın olaya yanıtı:
http://java.sun.com/performance/reference/whitepapers/WS_Test-1_0.pdf

No comments: