7 Ağustos 2019 Çarşamba

Yazılım Performans İyileştirmesi İçin 5 İpucu

Merhabalar. Üzerinde çalıştığımız projede performans iyileştirmesi ihtiyacı doğdu. Bu vesileyle birçok performans iyileştirmesi yöntemini kullanma fırsatı bulduk. Bu yazımda bu yöntemlerden bahsedeceğim.


Performansını arttırmak istediğiniz yazılımın niteliği, uygulayabileceğiniz yöntemleri de etkileyebilmektedir. Öneğin, bir web uygulamasını, çalışan sunucu sayısını arttırıp yük dengelemesi (load balance) yöntemiyle optimize edebilirsiniz. Fakat, bir masaüstü uygulamasında tek bir makineyle sınırlısınızdır. Aynı şekilde, masaüstü uygulamalarında tüm hafızayı tek bir kullanıcıya tahsis edilmiş gibi geliştirme yapabilirken, web uygulamarında aynı anda birden fazla kullanıcının isteklerine cevap bekleyeceğini göz önüne alarak hafızayı tutumlu kullanmanız gerekmektedir. İşler mobil uygulamaya geldiğinde ise, uygulamanın yiyeceği internet miktarı, kısıtlı fiziksel hafıza, güvenlik gibi bambaşka parametreleri göz önüne almalısınız. 

Burada listelenecek ipuçlarının, bu parametrelerin akılda tutularak değerlendirilmesini tavsiye ederim.
 
  1. Doğru Thread Yönetimi

    Yeni thread başlattığınız noktaları mercek altına alın. Bu alanlar O(n) bir algoritma içinde mi başlatılıyor? Yani başlatılan yeni thread sayısı n ile doğru orantılıysa, küçük sayılarda mesele olmaz iken, n sayısı arttıkça, thread içinde yapılan iş çok küçük bir iş olmasına rağmen, aşırı thread başlatılmasından dolayı işlemcinin kitlenmesine sebep olabilmektedir. 100 bin threadin bir anda başlatıldığını bir hayal edin... Böyle bir durum söz konusuysa algoritmayı her n için 1 adet thread oluşturacak şekilde güncellemeye çalışın.
  2. Veritabanı Katmanı İyileştirmesi

    İşlemci, veri kendisine ne kadar yakınsa bir işi o kadar hızlı bitirecektir. Sırasıyla önbellek, RAM, ve harici disk olmak üzere veri gittikçe işlemciden uzaklaşmaktadır. Bu nedenle performans iyileştirmesi istiyorsak verileri olabildiğince işlemciye yaklaştırmalıyız. Veritabanı işlemleri de bu bakımdan mercek altına alınması gereken yerlerin başında geliyor. Hibernate dünyasında second level cache (örn Ehcache) , daha genelde ise in memory database cache ürünleri (örn. Redis) kullanarak veriler işlemciye bir katman daha yaklaştırılabilir. Hatta uygulamanıza biraz uzaktan bakıp bir veritabanına ihtiyaç duyup duymadığını da sorgulayabilirsiniz. Aynı işlevi in-memory veri yapıları kullanarak gerçekleştirebilecekseniz komple veritabanı katmanını aradan çıkarmak büyük avantaj sağlayacaktır.
  3. Masraflı Hesaplama İşlerini Tekrarlamaktan Sakınma 

    Kodunuzun zaman yiyen hesaplama kod parçacıklarını belirleyin. Örneğin, ağır grafiksel işlemler, dosyadan veri okuma, ağır matematiksel hesaplamalar vb. Bu hesaplamaların sonucunu in-memory şekilde bir yerlerde örneğin sınıfın global bir alan değişkeninde, singleton bir cache sınıfında vb tutun. Hesaplamaların sonucu değişmediği sürece bu hesaplanmış bulunan sonucu hesaplama koduna hiç bulaşmadan dönün. Hesaplamaların sonucunun değişeceğini bildiren bir olay gerçekleştiğinde bu cache'deki değeri geçersiz kılın ve bu değer bir sonraki istendiği vakit tekrar hesaplama kodlarının çalışmasını sağlayın.

  4. Doğru Veri Yapılarını Kullanma 

    Kullandığınız dilin veri yapılarını arka planda nasıl çalıştığını akılda tutarak kullanın. Örneğin bir LinkedList üzerinde for ile gezerken her seferinde get(index) metoduyla veriyi çağırırsanız, her item getirme O(n) maliyetinde olacaktır. Çünkü LinkedList üzerinde n. item'a kadar tek tek gezmesi gerekecektir. Başka bir örnek de, bir hesaplama sonucu itemlerı bir koleksiyonda toplayıp sonra bu koleksiyon üzerinde dolaşılacaksa, koleksiyonda itemların tekrarlanmasını engellemek, en fazla bir kez yer almasını sağlamak için Set türünden bir koleksiyon kullanılması faydalı olacaktır.

  5. Masraflı İşleri Başka Thread'lere Atama

    Buna tembel hesaplama yöntemi diyebiliriz. Program akışımızda bizi yavaşlatan bir işlem var ise, bu işlem olabildiğince akış threadinden kaldırılıp başka threadlerin sorunu haline getirilmelidir. Örneğin, bir veri yapısından bir itemın silinmesi işlemi pahalı ise, item'ı "silindi" şeklinde işaretleyip yola devam etmeli, başka bir threadin de asenkron olarak "silindi" işaretli itemları gerçekten veri yapısından silmesi sağlanmalıdır.

    Sizlerin de performans iyileştirmesi için kullandığınız yöntemler var ise yorum olarak eklerseniz çok sevinirim.

5 Ağustos 2019 Pazartesi

Karekodlu Müşteri Takip Programı Uygulaması Yayınlandı

https://play.google.com/store/apps/details?id=com.pmobile.musteritakip


Müşterileriniz için qr kod veya barkodlarını kaydedin ve tarayın.
Tüm müşterilerinizi cep telefonunuzdan yönetin.
İhtiyacınız olan tüm müşteri bilgilerini (ad soyad, telefon, adres, ücret, serbest not alanı) kaydedin.
Veritabanınızı XLS excel dosyalarına dışa / dışa aktarın. Bu sayede yedekleme yapın!

Eğer bir barkod veya karekod etiket yazıcınız var ise bu uygulamayı seveceksiniz.
Müşterileriniz için etiket yazıcınız aracılığıyla birer barkod veya karekod etiket yazdırın.
Tüm müşterilerinizin verilerini cebinizden yönetin.

Mobil Android müşteri takip uygulamasını Google Play Store'dan indirebilirsiniz.

4 Aralık 2018 Salı

Komut Satırından Google'lamak



googler isimli komut satırı aracı ile terminalden google aramaları yapabilir, sonuçlar üzerinde dolaşabilirsiniz. Ubuntu makinenize kurmak için github'daki kodunu klonlamak ve

sudo make install 

komutunu çalıştırmanız yeterli.

Kurulumu oldukça basit olan aracın bir çok parametre seçeneği bulunuyor. Dokümanda bahsedilen özelliklerden bazıları:
  • Google Arama
  • Google anahtar kelime tabanlı site içi araması
  • Google Haberler içinde arama
  • Sonuçları reklamsız dönme
  • Sonuç sayfalarını tarayıcıda açma, tekli veya toplu olarak 
  • Sonuç sayfaları arasında dolaşma
  • Otomatik yazım düzeltmeyi devre dışı bırakma ve tam olarak anahtar kelimeleri arama
  • Google anahtar kelimelerini kullanma (ör. filetype: mime, site:somesite.com) desteği
  • İlk sonucu doğrudan tarayıcıda açma (şanslı hissediyorum özelliği)
  • Kullanışlı kullanım örnekleri ile kapsamlı dokümantasyon
  • Minimum bağımlılık

Geliştiricisi ayrıca eğlenceli örnekler de paylaşmış. Örneğin, yazılım hatalarını terminalden StackOverflow üzerinde aratan komut geliştirmiş.

Ayrıca, geliştiricilere ufak birkaç ipucu. --json parametresiyle sonuçlar JSON formatında alınabiliyor. googler, sonuçları Google API'leri kullanmadan, standart HTML çıktıyı parse ederek üretiyor. Bu nedenle, API token sahibi olmaya gerek kalmadan belli sayıda istek sınırına takılmadan kullanılabiliyor. Gerisi hayal gücüne kalmış.

Site içi arama örneği:

googler -w n11.com raspberry pi



PDF türünde kitap arama örneği:








27 Haziran 2018 Çarşamba

SSHJ ile Raspberry'de Uzaktan Python Çalıştırmak

Merhabalar, Bu yazımızda, Java ile uzaktaki bir makineye programatik olarak SSH ile bağlanıp Python kod çalıştırmayı göreceğiz. Örnek olarak Raspberry 3 makinesi kullanacağız.
SSH işlemleri için SSHJ kütüphanesini kullanacağız. Bu kütüphane ile lokal makinenizde bulunan önceden bağlandığınız makinelerin ssh keylerini kullanarak bağlantı kurulabildiği gibi, kullanıcı adı - şifre bilgilerini kullanarak da bağlantı kurulabilmektedir. Kullanıcı adı - şifre yöntemini kullanarak bağlantı kuracağız.

Öncelikle basit bir komut çalıştırıp çıktısını konsola basalım.
Kısaca örnek kodumuzu anlatalım. IP, port, kullanıcı adı ve şifre bilgilerini kullanarak bir bağlantı açılıyor. SSH client nesnesi ile bir session açılıp komut çalıştırılıyor. Komutun bitmesini beklemek için 5 sn bekletiliyor. Daha sonra komut çıktısı okunarak konsola basılıyor.

Şimdi de SSH client ile session açıp, shell arayüzü ile komutları inputstream şeklinde shell'in standart input'una gönderelim. Örnek basit bir Python kodunu String içerisinde ByteArrayInputStream'e çevirerek gönderiyoruz. Çıktıyı da standart output'dan okuyup konsola basalım.


 Programın çıktısı örnek kodun aşağısında yorum içerisinde bulunmaktadır.

Özetle, programatik olarak SSH ile makineye bağlantı sağlayıp uzak makinanın komut satırını elimizin altına alabiliyoruz.

12 Mayıs 2018 Cumartesi

Spring Boot ile Veri Tabanına İlk Verileri Ekleme

Önceki yazımızda oluşturduğumuz örnek projede

spring.jpa.hibernate.ddl-auto = update

özelliği ile veritabanı şemasının Hibernate tarafından otomatik oluşturulmasını sağlamıştık. Fakat, çoğu zaman veri tabanı şemasının oluşturulması yeterli olmayıp, uygulamanın kullanılabilmesi için bazı ilk verilerin de eklenmiş olması gerekmektedir. Bu yazımızda uygulama ayağa kalkarken uygulama içinden ilk verilerimizi nasıl kaydedeceğimizi göreceğiz.

Veri tabanımıza veri kaydetmenin çeşitli yöntemleri vardır. SQL script dosyaları hazırlayıp, bu scriptleri çalıştırarak da veri tabanımızı doldurabiliriz. Fakat kullandığımız veri tabanına göre değişik scriptler hazırlamamız gerekebilir. Örneğin, MySQL ile çalışırken hazırladığımız scriptleri, Oracle veya başka bir veri tabanı kullanmaya karar verdiğimizde tekrar yazmamız gerekebilir. Hibernate teknolojisi bizi kullandığımız veri tabanı teknolojisinden soyutluyorken neden bunu ilk veri kaydederken de kullanmayalım?

Spring Boot Data JPA teknolojisini kullanarak oluşturduğumuz Repository'leri kullanarak, ApplicationReadyEvent'i dinleyen ApplicationListener beanlerimiz ile uygulama ayağa kalktığında veri atacağımız tabloyu kontrol edeceğiz. Eğer boşsa, eklemek istediğimiz entityleri oluşturup kaydedeceğiz.

Bu sınıflarımıza Data Populating Unit (Dpu) diyebiliriz. Uygulamamız ayağa kalktığında dog, bird, cat tipleri hâl-i hazırda eklenmiş olsun istiyoruz. 

Birden fazla Dpu çalıştırmak istediğimizde dpu'ların çalışma sırası önem kazanıyor. Örneğin, dog tipinde Pet kaydedeceğimiz PetDpu çalıştırmak istiyorsak, öncelikle dog PetType nesnesinin veri tabanında kayıtlı olduğundan emin olmalıyız. Bu nedenle PetTypeDpu sınıfımızın, yazacağımız PetDpu'dan önce çalışması için @Order anotasyonu ile sıra belirliyoruz.


Tüm örnek proje kodunu Github'da şuradan indirebilirsiniz.

29 Nisan 2018 Pazar

MapStruct ile Entity Dto Dönüşümü

Merhabalar,
REST servislerimizde domain entity (hibernate) nesnelerini direk dönmek güvenlik açığı oluşturabildiği gibi, bidirectional ilişkilerde JSON'a serialize etmeye çalışırken sonsuz döngüye girip hata verme tehlikesi bulunmaktadır. Bu sorunu entity nesnelerimizi basit data transfer object (DTO) nesnelerine dönüştürüp dönerek aşabiliriz. Peki her defasında onlarca defa getX() setX() metodlarını çağırmak yorucu olmayacak mı? Bunun için de MapStruct (java bean mapping) kütüphanesini kullanabiliriz. Tanımlayacağımız basit mapper nesneler ile get set metod çağırım yükünden kurtulabiliriz.



Bu yazımızda Spring Boot projesi içinde H2 veritabanı ile, MapStruct kullanımını göreceğiz. Veri modeli olarak klasik Pet Clinic yapısını kullanacağız. Evcil hayvan, evcil hayvan sahipleri, veteriner, hayvan tipleri gibi veri yapıları ile Hibernate Mapping örneklerini de görmüş olacağız.

Tüm örnek proje kodunu Github'da şuradan indirebilirsiniz.

Veri modelimiz aşağıdaki gibidir. Owner ve Pet arasında karşılıklı (bidirectional) ilişki bulunmaktadır. Bu nedenle Owner nesnesini JSON olarak dönmek istediğimizde, içindeki Pet'leri JSON'a çevirirken, Pet içindeki Owner nesnesini de yine JSON'a çevirmeye çalışacağı için sonsuz döngüye girerek başarısız olmaktadır.

Projeye yapısına genel olarak bir bakalım.

model paketi altında domain entity nesnelerimiz bulunuyor. Dto paketi içinde test için kullanacağımız entity nesnelerinin dto nesneleri bulunuyor. mapper paketi içinde MapStruct mapper interface sınıfları bulunuyor. repository paketinde Spring Data JPA repository sınıflarımız bulunuyor. service paketi içinde ise repository nesnelerini kullanan servis sınıflarımız bulunuyor.

application.properties dosyası içeriği aşağıdaki gibidir:

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
server.port = 8080
spring.datasource.url = jdbc:h2:file:~/h2/app_db;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username = sa
spring.datasource.password =
spring.datasource.driverClassName = org.h2.Driver
spring.jpa.hibernate.ddl-auto = update


Projemiz Maven projesi olduğundan bağımlılıkları incelemek için pom.xml içerisinde bağımlılıkları tanımlıyoruz.
        <dependency>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-jdk8</artifactId>   

                <version>${org.mapstruct.version}</version>
        </dependency>


MapStruct, derleme aşamasında tanımlanan mapper interface veya abstract sınıflarım implementasyonlarını otomatik olarak oluşturmaktadır. Bunun için maven compile plugin içerisine bir mapstruct-processor tanımı yapmak gerekmektedir. 

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.5.1</version>
            <configuration>
                <source>1.8</source> <!-- or higher, depending on your project -->
                <target>1.8</target> <!-- or higher, depending on your project -->
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>

            </configuration>
        </plugin>
        </plugins>
    </build>


Domain ve Dto sınıflarımıza bir bakalım:




Mapper interfacelerimize bir bakalım. Dikkat edilirse Owner entity'si içinde Pet entity'si var. Bu nedenle Pet için de mapper tanımlamalı ve OwnerMapper sınıfını bu maperdan haberdar etmeliyiz:
Bunun için @Mapper anotasyonu içindeki uses alanını kullanıyoruz. Ayrıca Spring'in dependency injection kabiliyetinden faydalanıp mapper'ları bean olarak kullanabilmek için @Mapper anotasyonu içinde componentModel = "spring" tanımını yapıyoruz.

OwnerMapper 

PetMapper


PetTypeMapper

Maven ile derlendiğinde target klasörü altına otomatik olarak üretilmiş mapper impl dosyaları görünür olacaktır. 
mvn clean compile yapılarak derlenmelidir.


 Otomatik üretilmiş OwnerMapperImpl içeriği aşağıdaki gibidir:


Şimdi OwnerMapper'ı kullandığımız OwnerService koduna bakalım.


Repository'den dönen entity'lerin direk dönüldüğü ve mapper ile Dto'ya çevrilip dönüldüğü iki listeleme metodu tanımladık.

Test Vakti

Gelelim projeyi test etmeye. OwnerService sınıfında örnek Owner kaydedecek metod tanımladık. OwnerController içinde /owner/add ile istek gelirse bu metod çağrılacak. Listelemek için de controller içinde iki metod tanımladık. /owner/list ve /owner/listCorrect. /owner/list metodu normal Owner entity listesi dönmektedir. Bu sayede sonsuz döngüye girme hatasını görebileceğiz. /owner/listCorrect ile de OwnerDto listesi dönecek ve başarılı bir şekilde ownerların JSON dökümünü görebileceğiz.


Spring Boot projemizi çalıştıralım ve http://localhost:8080/owner/add adresine gidelim. Add metodu da aslında OwnerDto listesi döndüğünden JSON serialize hatasıyla karşılaşmadığımızı görebiliriz. 
Veritabanına bakmak için http://localhost:8080/h2-console linkine gidip jdbc adresi alanına application.properties kısmında yazan jdbc adresini yazarak giriş yapabiliriz.





http://localhost:8080/owner/list adresine giderek hatayı üretelim.


Konsola düşen Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException hata loglarına bakalım:




 http://localhost:8080/owner/listCorrect adresine giderek sorunsuz dönen cevaba bakalım:

MapStruct kullanırken unutmaması gereken ufak bir not: Mapper sınıfların implementasyonları maven derleme sırasında üretildiği için, kullandığınız IDE'nin otomatik build ettiği sırada üretmeyecektir. Bu sebeple uygulama ayağa kalkarken bean bulunamadı hatası alabilirsiniz. Bu nedenle impl dosyalarının üretildiğinden emin olup uygulamayı öyle başlatınız.

Tüm örnek proje kodunu Github'da şuradan indirebilirsiniz.




17 Mart 2018 Cumartesi

Selenium ve Python ile Otomatik Ekran Testine Giriş

Yazılım projelerimizde birim testleri önemli bir yer tutuyor. Birim test denilince nedense servis katmanına yazılan testler akla ilk gelen oluyor. Fakat, ekranların da otomatik olarak test edilmesi geliştirme sırasında farkedilemeyen alakasız bir yerdeki hatanın farkedilmesini sağlar. Ekranların test edilmesi işini manuel olarak halletmeye çalıştığımızda harcayacağımız adam-gün'lük işin büyük bölümünü kazanmış oluyoruz.
Bu yazımızda Selenium web otomasyon kütüphanesi ve yazacağımız Python scriptlerini kullanarak basit bir html formunu otomatik olarak doldurup, tuşa tıklatıp sonucu test edeceğiz. Tüm örnek kodları şuradan indirebilirsiniz.




Öncelikle Selenium'dan bahsedelim. Selenium web tarayıcılarını kodsal olarak otomatik kullanmak için geliştirilmiş bir kütüphanedir. Selenium WebDriver modülü ile Firefox, Chrome vb tarayıcıları otomatik olarak kullanabilmekteyiz. Bu tarayıcıların her biri için Selenium web driver bulunuyor. Tabii ki seçilen web tarayıcı sistemde kurulu olmalıdır. Sadece test için değil, sürekli tekrar eden sıkıcı işleri otomatikleştirmek için de kullanılabilir.
Geniş bir programlama dili desteği bulunuyor. Java, C#, Ruby, Python, Javascript resmi olarak desteklenen dillerden.

Kurulumlar

Python 2.7 kurulu olduğu varsayılarak aşağıdaki komutlarla Selenium'u bilgisayarımıza kuralım.

pip install selenium

Selenium'un tarayıcıları otomatik açabilmesi için geckodriver denen bir araç da kurulmalıdır. Mac OS'da aşağıdaki gibi kurulabilir.

brew install geckodriver

Örnek Kodlar

Öncelikle HTML form kodunu görelim.


Submit'e tıklandığında input alanlarının değerlerini alıp birleştirip tekrar sayfaya basan Javascript kodu.
Ad Soyad alanlarına girilen metin toplanarak, outputDiv id'li bir div elementin içine eklenerek sayfada gösteriliyor.
Şimdi de form alanlarını otomatik olarak doldurup tuşu tıklatacak Python koduna bir göz atalım.

Firefox Web Driver nesnesi oluşturuyoruz. Bu nesne ile yerel dosya sisteminde açmak istediğimiz html dosyanın full pathini veriyoruz.
Selenium selector fonksiyonlarını kullanarak HTML dosyadaki input alanları ve tuş nesnelerine ulaşıyoruz. Biz burada HTML elementlerin id ve name alanlarına göre elementi getiren fonksiyonları kullandık. Fakat Selenium HTML elementlerine ulaşmak için çok çeşitli seçenekler sunuyor. XPath'e göre, class alanına göre, CSS içeriğine göre, text içeriğine göre, hatta direk javascript çalıştırarak dahi HTML elementlerine ulaşılabiliyor.
İnput alanlarına örnek metinler girip tuşa tıklatıyoruz.
Sonuç olarak ekrana basılan output ile beklediğimiz output'u karşılaştırıyoruz.

Tüm kodların aynı klasörde olduğundan emin olduktan sonra aşağıdaki komutla testimizi çalıştırabiliriz.

python autotest.py

Çalıştırdığımızda Selenium yeni bir Firefox açacak, index.html dosyasını görüntüleyecek. İnput alanlarını doldurup tuşa tıklayacak. Tüm işlemler tamamlandığında da firefox kapatılacak.

Bu kodda driver ile yerel dosya sisteminde bulunan bir dosyayı açtık. Fakat istersek herhangi bir URL vererek tarayıcıda açabiliriz. Örneğin, driver.get("http://www.kodblog.net/"); gibi.

Selenium görüldüğü gibi oldukça güçlü bir araç. Klavye veya mouse kullanmadan tarayıcıda formlar doldurulabiliyor, tuşlar tıklanabiliyor. Projenizde oluşturacağınız test caseler ile Selenium ekranlarınızı test ederken siz de çayınızı yudumlayıp ekranda akıp giden uygulamanızı izleyebilirsiniz.


Yazılım Performans İyileştirmesi İçin 5 İpucu

Merhabalar. Üzerinde çalıştığımız projede performans iyileştirmesi ihtiyacı doğdu. Bu vesileyle birçok performans iyileştirmesi yöntemini k...