17 Mayıs 2019 Cuma

Yazılımda Karmaşıklık (Complexity) Problemleri

Giriş
Bu yaz bir özet. Grady Booch'un şu cümlesi bence çok önemli
“Alas, this complexity we speak of seems to be an essential property of all large software systems. By essential we mean that we may master this complexity, but we can never make it go away."
Yazılım geliştirirken karşımıza bir çok karmaşık şey çıkıyor. Bunlardan bazıları şöyle.

Algorithmic complexity
Bu konu iki başlık altında incelenebilir.

1. P versus NP yani Polynomial olan ve olmayan problemler. Algoritma Analizi yazısına bakabilirsiniz.
2. Polynomial problemler için Big O gösterimi. Başka gösterimler de var ancak sanırım en bilineni Big O gösterimi. Big O Nedir yazısına bakabilirsiniz.

Development complexity
Agile yöntemler - örneğin Scrum - yazılım geliştirmenin karmaşıklığını azaltmaya veya daha iyi yönetmeye yardımcı olabilir. Örneğin aynı anda birden fazla projede çalışmak veya yönetmeye çalışmak karmaşayı artırır.

#6: Managing Multiple Projects and Expectations başlığı altında bazı öneriler var.

Information complexity 
Kolmogorov complexity olarak ta bilinir. Açıklaması şöyle
The information complexity of a task/object is determined by the number of characters that need to be written to solve this problem.

It means that the complexity of the problem is equal to the number of characters describing its solution. This, in fact, is the size of the code programmers need to write to solve the problem.
#4: Understanding Complex Systems başlığı altında bazı öneriler var.

Test complexity
Yazılım büyüdükçe ve karmaşıklaştıkça testi de karmaşıklaşıyor.

Karmaşıklığı Azaltmak İçin Ne Yapılabilir?
Bazı maddeler şöyle




14 Mayıs 2019 Salı

Yazılım Mimarisi - Pipe and Filter Architecture

Giriş
Bu mimari Pipeline Architecture olarak ta isimlendirilebiliyor. Girdi tek yönlü olarak aşamalardan geçer. Açıklaması şöyle.
The pipeline architecture consists of multiple stages where a stage consists of a queue and a worker. Each stage of the pipeline takes in the output from the previous stage as an input, processes it, and outputs it as the input for the next stage. One key factor that affects the performance of pipeline is the number of stages. 
Paralel Çalıştırma
Her bir aşama veya kademe (stage) paralel olarak çalıştırılabilir. Açıklaması şöyle.
One key advantage of the pipeline architecture is its connected nature, which allows the workers to process tasks in parallel. This can result in an increase in throughput. As a result, pipelining architecture is used extensively in many systems.
Benzer Seçenekler
Bu mimariye benzediğini düşündüğüm ve bence ismi  Request/Response queue olması gereken mimari de bir diğer seçenek olarak düşünülebilir.  Orijinal yazıda bu mimari Pipe and Filter başlığı altında tanıtılsa da bence gerçek Pipe and Filter mimarisinden biraz daha farklı.



13 Mayıs 2019 Pazartesi

redis-cli Komutu - Redis Sunucusu İle İletişim İçindir

Giriş
Açıklaması şöyle.
Redis server listens on a tcp connection not a http one, so it wouldn't be accessible via the browser. You could use redis-cli to communicate with the redis server, or if you prefer a GUI I'd recommend redis-commander
Açıklaması şöyle. Redis sunucusu 6379 numaralı portu dinler
A client connects to a Redis server creating a TCP connection to the port 6379.While RESP is technically non-TCP specific, in the context of Redis the protocol is only used with TCP connections (or equivalent stream oriented connections like Unix sockets).
GUI
Bu komut yerine GUI olarak redis-commander veya TablePlus kullanılabilir.

Kurulum
Şöyle yaparız
npm install -g redis-cli
Söz Dizimi
Şöyledir
rdcli -h {host} -a {password} -p {port}
Shell
Açıklaması şöyle
Hint 1: You could flush Redis at any time using the “FLUSHALL” command.

Hint 2: If you wish to monitor the handshakes on Redis for the next exercise, you can execute the command “MONITOR”.

Örnek - ping
Redis sunucusu ile olan iletişimi kontrol etmek için şöyle yaparız
# redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
Örnek - xread
Şöyle yaparız
> xread block 1000000 streams tutorial.test_schema.employee $
Örnek - keys
Veri tabanındaki tablo isimlerini gösterir
Örnek
Şöyle yaparız
127.0.0.1:6379> KEYS * 
(empty array)
127.0.0.1:6379>

...

127.0.0.1:6379> KEYS * 
1) "planetCache"
2) "redisson-hibernate-timestamp"
127.0.0.1:6379>
Örnek - hgetall
Şöyle yaparız. Tablodaki tüm satırları gösterir.
127.0.0.1:6379> HGETALL planetcache
Örnek - get
get seçeneğinden sonra verilen string key değeridir. Bu key değerine ait value varsa çıktı olarak onu verir.

Seçenekler
-a seçeneği
Şifreyi belirtmek için şöyle yaparız.
$ cat ./badwords-set.txt | redis-cli -a YourRedisPassword
-c seçeneği
Örnek
Şöyle yaparız. CLUSTER MEET ile IP'si değişen bir Redis sunucusunun cluster'a girmesi sağlanıyor sanırım
redis-cli -c -a <default-user-secret-password> CLUSTER MEET IP PORT
-h seçeneği
Host ismini belirtmek için şöyle yaparız
redis-cli -h redisdb.southeastasia.redisenterprise.cache.azure.net
              -p 10000 
              -a <azure redis access key> 
             --tls
info memory seçeneği
Örnek
Şöyle yaparız
redis-cli info memory ... used_memory_human:41.22M ... used_memory_rss_human:50.01M ... mem_fragmentation_ratio:1.21 #used_memory_rss/used_memory ...
info stats seçeneği
Örnek
Şöyle yaparız
redis-cli info stats ... evicted_keys:14 #14 keys removed since operation time ...
-- latency seçeneği
Örnek
Şöyle yaparız
redis-cli --latency -h 127.0.0.1 -p 6379 min: 0, max: 16, avg: 0.15 (324531 samples)...
-u seçeneği
Şöyle yaparız
$ redis-cli -u redis://p%40ssw0rd@redis-16379.hosted.com:16379/0 ping PONG

config get databases seçeneği
Kaç tane database olduğunu gösterir.

script load seçeneği
Şöyle yaparız.
redis-cli script load lrange.lua

9 Mayıs 2019 Perşembe

Binary Heap

Giriş
Açıklaması şöyle. Binary Heap'te sadece ata düğümün küçük değere sahip olduğu garanti edilir. Kardeş düşümler arasında büyüklük küçüklük karşılaştırılması yapılmaz
A binary heap is similar to a binary search in that there is a relationship between parent and child nodes. The difference is in what that relationship is. In a BST right children are larger than their parents, and left children are smaller.

But in a binary min-heap, parent nodes are smaller than both their children, all nodes are inserted in breadth-first order, and there is no relationship between sibling nodes. The relationships can also be easily represented in an array as a mathematical formula between indices.
Formül şöyle.
For a given index i
It's two children will be at (2i + 1) and (2i + 2)
So the children of the node at index 10 will be at 21 and 22

8 Mayıs 2019 Çarşamba

RabbitMQ

Giriş
2007 yılında geliştirilmeye başlandı ve Erlang ile geliştirildi. Bu yüzden maalesef JVM içinde gömülü olarak çalıştırılamıyor. Açıklaması şöyle.
One of the oldest open source message brokers can be used with various protocols. Many web developers like this software, because of its useful features, libraries, development tools, and instructions.

In 2007, Rabbit Technologies Ltd. had developed the system, which originally implemented AMQP. It's an open wire protocol for messaging with complex routing features. AMQP ensured cross-language flexibility of using message broking solutions outside the Java ecosystem. In fact, RabbitMQ perfectly works with Java, Spring, .NET, PHP, Python, Ruby, JavaScript, Go, Elixir, Objective-C, Swift, and many other technologies. The numerous plugins and libraries are the main advantage of the software.
2013 yılında Pivotal Software'e katıldı. Açıklaması şöyle
RabbitMQ was originally created by Rabbit Technologies Ltd. The project became part of Pivotal Software in May 2013.
Protokoller
RabbitMQ bir sürü protokolü gerçekleştirir - AMQP, MQTT, STOMP. Ama en önemlisi AMQP protokolünü gerçekleştirir. AMQP protokolünün JMS'e göre avantajı şöyle
While Java has messaging standards like JMS, it’s not helpful for non-Java applications that need distributed messaging which is severely limiting to any integration scenario, microservice or monolithic. With the advent of AMQP, cross-language flexibility became real for open source message brokers.
Çünkü AMQP aynı Kafka gibi sadece byte[] taşıyor. Gerisi ile ilgilenmiyor. Açıklaması şöyle
In AMQP the messages are opaque entities; AMQP does not provide any standard way to encode/decode them.

However, web applications very often use JSON as an application layer format, that is, the JavaScript serialization format that has become a de-facto standard; in this way, the RabbitMQ client Java library can include some utility functions for this task.

On the other side, this is not the only protocol; any application can choose its own protocol (XML, Google Protocol Buffers, ASN.1, or proprietary).
Kurulum
RabbitMQ Kurulum yazısına taşıdım

Push Model İle Message Order Kaybedilir
Açıklaması şöyle
In RabbitMQ, messages go through an exchange, land on a queue then gets distributed to consumers in a way that a message can go to only one consumer. It is super easy to increase the replica count of consumers, but in this topology, you loose message ordering. Because the second message can be processed before the first one is completed.
Şeklen şöyle


Push Model + Acknowledgement
RabbitMQ Broker mesajları dinleyenlere kendisi push'lar. Mesajı alan istemcinin acknowledgement göndermesi gerekir. Acknowledgement şöyledir

Positive Acknowledgement
Spring terminolojisinde buna auto ve manual acknowledgement deniliyor.
In RabbitMQ, messages are stored until a receiving application connects and receives a message off the queue. The client can either ack (acknowledge) the message when it receives it or when the client has completely processed the message. In either situation, once the message is acked, it’s removed from the queue.
Prefetch Limit
Prefetch Limit kullanılabilir. Açıklaması şöyle. Prefetch count istemciye kaç tane mesajın push'lanacağı anlamına gelir.
It’s important to configure a prefetch limit in order to prevent overwhelming the consumer (if messages arrive at the queue faster than the consumers can process them). Consumers can also pull messages from RabbitMQ, but it’s not recommended. 
Negative Acknowledgement
Gerekiyorsa Negative Acknowledgement verilebilir. Açıklaması şöyle
A RabbitMQ client can also nack (negative acknowledgement) a message when it fails to handle the message. The message will be returned to the queue it came from as if it were a new message; this is useful in case of a temporary failure on the consumer side.
Şeklen şöyle. Tüketen taraf çöktüğü için bir başkası mesajı işliyor.


Negative Acknowledgement + Retry
Açıklaması şöyle. Bazı eksikler var. 1. Retry için delay verilemiyor. 2. Retry sayısı verilemiyor. dolayısıyla kutudan gelen Nack kullanımı yetersiz.
RabbitMQ provides some kind of support for retries, out of the box, but not with delay. When consuming a message the client declares whether to use auto acknowledgement or not. Auto acknowledgement means the broker can discard messages once delivered to the consumer (but before processing). Manual acknowledgement means the broker should wait for the client (consumer) to explicitly acknowledge when the message processing completed (successfully or not) and can be discarded. Note that there are no timeouts. The message will not be redelivered to a consumer, unless a negative acknowledgement is received or the connection is closed. When using automatic acknowledgements, retries due to application errors are practically not applicable. When using manual acknowledgements, a message can be redelivered by RabbitMQ if it is rejected by the consumer (meaning a negative acknowledgement is sent) and the flag requeue is set to true.
Observability
Açıklaması şöyle. Eğer tüm kuyrukları dinlemek istersek binding key olarak "#" kullanılır
Events history
In AMQP once a message is delivered (and optionally acknowledged), it is removed from a queue, and you will never see it again. What if you need to investigate what happened at that stage?
I can think of several options, for example:
- Enabling debug level of logging in message bus.
- RabbitMQ Firehose.
- Subscribing to all messages and storing/redirecting them to separate storage or message bus (might be done via custom subscriber or via tools like Apache Flink, Kafka RabbitMQ connector, etc.).

We have chosen the last option, which works the following way:
- There is a queue, which is subscribed to all messages in topic exchange via “#” binding key (which means matching any routing key);
- There are consumers, which constantly read messages from that queue and store it in the appropriate format to persistent storage.
Durable Queue
Açıklaması şöyle. Durable kuyruklar broker yeniden başlatılsa bile tekrar yaratılırlar. Eğer kuyruktaki mesajlar da persistent ise onlar da tekrar eklenirler.
Durable queues are persisted to disk and thus survive broker restarts. Queues that are not durable are called transient. Not all scenarios and use cases mandate queues to be durable.

Durability of a queue does not make messages that are routed to that queue durable. If broker is taken down and then brought back up, durable queue will be re-declared during broker startup, however, only persistent messages will be recovered.
Örnek
Şöyle yaparız. Queue durable, aynı zamanda auto_delete özelliği false, Böylece en son consumer kapansa dahi kuyruk silinmez.
curl --user guest:guest \
-X PUT -H 'content-type: application/json' \
--data-binary '{"vhost":"/","name":"test-queue-01","durable":"true","auto_delete":"false",
"arguments":{"x-queue-type":"classic"}}' \
'http://localhost:15672/api/queues/%2F/test-queue-01'
AMQP Frame Tipleri
AMQP Frame Tipler yazısına taşıdım.

High Availability
Açıklaması şöyle
RabbitMQ has provided clustering and highly available queues for several major versions. Version 3.8.0 shipped with “Quorum Queues” which use the Raft consensus algorithm to provide data replication with higher performance than “classic” HA queues.
Subscription Çeşitleri Nedir
İki çeşit subscription var.

1. Durable Subscription
Consumer çalışmasa bile elle silininceye kadar abonelik yaşar

2. Ephemeral subscription
Consumer kapatılınca subscription ve beklemekte olan mesajları silinir.

Exchange Çeşitleri
Exchange Çeşitleri yazısına taşıdım

Lazy Queue
Açıklaması şöyle
People that are trying out RabbitMQ are probably not aware of the the feature lazy queues. Lazy queues are queues where the messages are automatically stored to disk, thereby minimizing the RAM usage, but extending the throughput time. In our experience, lazy queues create a more stable cluster with better predictive performance. If you are sending a lot of messages at once (e.g. processing batch jobs), or if you think that your consumers will not consistently keep up with the speed of the publishers, we recommend that you enable lazy queues.
RabbitMQ Single Active Consumer (SAC)
Açıklaması şöyle. { "x-single-active-consumer", true } parametresi ile sağlıyoruz.
The Single Active Consumer mode allows to have only one consumer at a time consuming from a queue and to fail over to another registered consumer in case the active one is cancelled or dies.

30 Nisan 2019 Salı

GoF - Prototype Örüntüsü

Giriş
Yarı bitmiş nesnenin clone'lanması içindir. Şeklen şöyle



Flyweight ile İlişkisi
Prototype yeni nesnelerin yarı bitmiş bir nesneden kopyalanarak yaratılması içindir. Örneğin bir oyundan EnemyPrototype ile bir çok düşman nesnesi yaratılabilir. Flyweight ise hazır nesnelerin paylaşılması içindir.

Tomcat

Giriş
Tomcat tam bir J2EE Application Server değil. J2EE standardındaki herşeyi gerçekleştirmiyor. Açıklaması şöyle. JSP ölü bir teknoloji olduğuna göre ben sadece Servlet Container olarak düşünüyorum.
The Apache Tomcat® software is an open source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies.
Zip Olarak Kurulum
Tomcat Zip Olarak Kurulum yazısına taşıdım.

Service Olarak Kurulum
Tomcat Servis Olarak Kurulum yazısına taşıdım.

Docker Olarak Kurulum
war dosyaları "/usr/local/tomcat/webapps/" dizinine kopyalanır

Örnek
Şöyle yaparız
FROM tomcat:9.0.64-jre11    (the chosen DockerHub image)
ADD target/provman.war /usr/local/tomcat/webapps/ 
                            (The relative location of your WAR file)
EXPOSE 8080     (The port that the Docker container will listen to) 
Çalıştırmak için şöyle yaparız
docker build -t provman .      (don’t forget the dot!)
docker run -p 80:8080 provman  
Örnek
Şöyle yaparız
From tomcat
COPY ./target/hello-world.war /usr/local/tomcat/webapps

Locale
-Duser.language=en -Duser.country=US ile Tomcat Türkçe bir makinede Amerikan locale ile çalıştırılır. Bu bazen Java'nın toUpperCase() metodu ile çıkarttığı problemi aşmamıza yardımcı olur.

URL Yolu
Tomcat http isteklerini yüklü olan war'ların URL'lerinin en uzunundan başlayarak bulmaya çalışır.
İki tane war yüklü olsun.
İlki ROOT.war, context = /
İkincisi ise api#v1.war, context = /api/v1/
olsun

Şu istek farklı bir yere GET http://<hostname>/api/someMethod
Şu istek farklı bir yere GET http://<hostname>/api/v1/someMethod
gider.

Context.xml
TOMCAT_HOME\conf\context.xml dosyasıdır.

JNDI - DataSource
Açıklaması şöyle
NOTE - The default data source support in Tomcat is based on the DBCP connection pool from the Commons project. However, it is possible to use any other connection pool that implements javax.sql.DataSource, by writing your own custom resource factory, as described below.
JNDI ayarı şöyle yapılır.
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/eatST">
<Resource 
    name="jdbc/test"
    auth="Container" 
    type="javax.sql.DataSource"
    maxActive="100" 
    maxIdle="30" 
    maxWait="10000" 
    username="root"
    password="123" 
    driverClassName="com.mysql.jdbc.Driver"
    url="jdbc:mysql://localhost:3306/test?
         useUnicode=true&amp;characterEncoding=UTF8"
    factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
 />
 </Context>
catalina.properties dosyasındaki değerleri kullanarak şöyle yaparız.
<Resource name="${tomcat.dbpool.ups.quartz.resourcename}" auth="Container"
  type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
  url="${tomcat.dbpool.ups.quartz.connectionurl}"
  username="${tomcat.dbpool.ups.quartz.username}"
  password="${tomcat.dbpool.ups.quartz.password}"
  maxTotal="${tomcat.dbpool.ups.quartz.maxTotal}"
  maxIdle="${tomcat.dbpool.ups.quartz.maxIdle}"
  minIdle="${tomcat.dbpool.ups.quartz.minIdle}"
  maxWaitMillis="${tomcat.dbpool.ups.quartz.maxWaitMillis}"
  validationQueryTimeout="${tomcat.dbpool.ups.quartz.validationQueryTimeout}"
  testWhileIdle="true"
  removeAbandonedOnMaintenance="true"
  timeBetweenEvictionRunsMillis="..."
  minEvictableIdleTimeMillis="..."
 />
maxIdle bağlantılar boşa çıksa bile kaç tane açık tutulacağını belirtir. 2 olarak belirtmek için şöyle yaparız. maxActive en fazla kaç bağlantı açılacağını belirtir.
<Resource
   name=XXXXX
   type=xxxxx
   username=xxxxx
   password=xxxxxx
   driverClassName=xxxxx
   maxIdle=2
   url=xxxxx
   maxActive=10
/>

Realm Nedir?
Kullanıcıları yetkilendiren sisteme verilen isim.

JDBCRealm
Kullanıcıyı, veritabanındaki bir tabloya bakarak yetkilendirir.
<Realm localDataSource="true" debug="99"
    className="org.apache.catalina.realm.JDBCRealm" connectionName="user"
    connectionPassword="password" connectionURL="jdbc:mysql://localhost:3306/ ccc"
    digest="md5" driverName="com.mysql.jdbc.Driver" roleNameCol="role_name"
    userCredCol="password" userNameCol="email_id"
    userRoleTable="users_list" userTable="user_list_view" />
Combined Realm
Combined Realm istemciyi bir veya daha fazla datasource ile authenticate edebilir.

Session
Persistent session yani tomcat tekrar başlatılınca session'ları hatırlasın istemezsek, session'ların kaydedildiği dizini boş tanımlarız. Şöyle yaparız.
<!-- Uncomment this to disable session persistence across Tomcat restarts -->

<Manager pathname="" />
Eğer session bilgisini Redis üzerinde saklamak istersek şöyle yaparız.
<Manager className="org.redisson.tomcat.RedissonSessionManager"

   configPath="${catalina.base}/redisson.conf" readMode="MEMORY" updateMode="DEFAULT"/>

Server.xml
Tomcat Server.xml yazısına taşıdım.

Remote Debug
Eclipse ile uzaktaki bir Tomcat üzerindeki uygulamayı debug etmek için eclipse debug remote web application başlıklı yazıya bakılabilir.

startup.bat dosyası ayarları
Sunucu Tomcat.exe kullanılarak başlatılmaz. startup.bat ile başlatılır.
startup.bat dosyası catalina.bat dosyasını çağırıyor.

CATALINA_OPTS
startup.bat dosyası içinde CATALINA_OPTS değişkeni tanımlanarak çeşitli JVM ayarları yapmak mümkün. Örnek :
export CATALINA_OPTS=-Xms16m -Xmx256m;
catalina.bat dosyası ayarları
JAVA_OPTS
Örnek:
set JAVA_OPTS="-Djava.library.path=/usr/tomcat/shared/lib"
Bir diğer örnekte ise gelen HTTP isteklerini loglamak mümkün.
export JAVA_OPTS="$JAVA_OPTS -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true"

CATALINA_HOME\lib
Eğer ClassNotFoundException exception alınırsa gerekli olan jar dosyalarını bu dizine kopyalamak mümkün. Ancak bu dizin tomcat üzerine çalışan tüm uygulamalar tarafından ortak kullanıldığından, jar dosyaları arasında sürüm farkından dolayı sorun da çıkarabilir.

Tomcat'in ilk kurulumunda bazı jar dosyaları bu dizinden geliyor. Örneğin servlet 3.0 api'si servlet-api.jar dosyasında mevcut.