Spring Cloud — Netflix Ribbon ile Rest Client
Netflix Ribbon, bir IPC(Inter Process Communication — İşlemler arası iletişim) cloud kütüphanesidir. Ribbon’un öncelikli görevi, client-side tarafındaki load balancing algoritmalarını sağlamaktır.
Client tarafındaki load balancing algoritmalarını sağlamanın yanında, Ribbon bazı diğer özellikler de sunar;
Servis Bulma Entegrasyonu: Ribbon load balancing, cloud ortamı gibi dinamik yapıdaki ortamlarda service discovery imkanı sunar. Ribbon kütüphanesi, ek olarak Eureka ve Netflix service discovery bileşenlerini de içerisinde barındırır.
Hata Toleransı: Ribbon API, sunucuların canlı ortamda çalışıp çalışmadığını dinamik olarak belirler ve kapalı olan veya ulaşılamayan sunucuları tespit edebilir.
Yapılandırılabilir Load-Balancing Kuralları: Ribbon, genel olarak RoundRobinRule, AvailableFilteringRule, WeightedResponseTimeRule kurallarını sağlar. Bu kuralları yazının devamında daha detaylı inceleyeceğiz. Bunun yanı sıra özel kurallar tanımlanmasını da destekler.
Ribbon API “Named Client” konseptine dayanarak çalışır. Ribbon’u uygulama yapılandırma dosyamızda yapılandırırken load balancing için dahil olan sunucuların listesine bir isim veriyoruz.
Şimdi bunun nasıl yapıldığına bir göz atalım.
pom.xml dosyamıza aşağıdaki bağımlılığı ekleyerek Netflix Ribbon API’yi projemize ekleyebiliriz.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
Örnek Uygulama
Ribbon API’nin çalışmasını görmek için Spring RestTemplate ile örnek bir mikro servis uygulaması oluşturacağız. Bu örnek mikro servis uygulamasını Spring Cloud Netflix API ve Netflix Ribbon API ile geliştiriyoruz.
Uygulamamızda, client tarafı yükünün 2 sunucu arasında dengelenmesini sağlamak için Ribbon’un load balancing stratejilerinden biri olan WeightedResponseTimeRule’ü kullanacağız.
Ribbon Yapılandırması
Ribbon API, load balancer’ın aşağıdaki bileşenlerini yapılandırmamızı sağlar;
Rule: Uygulamamızda kullanacağımız load balancing kuralını belirleyen mantık bileşenidir.
Ping: Sunucunun gerçek zamanlı olarak kullanılabilirliğini belirlemek için kullanılan bir mekanizma bileşenidir.
ServerList: Dinamik veya statik olabilir. Bu örneğimizde statik bir sunucu listesi kullanacağız ve bu yüzden onları doğrudan uygulama yapılandırma dosyasında tanımlayacağız.
Öncelikle kütüphane için basit bir yapılandırma yazıyoruz;
public class RibbonConfiguration { @Autowired
IClientConfig ribbonClientConfig; @Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
} @Bean
public IRule ribbonRule(IClientConfig config) {
return new WeightedResponseTimeRule();
}
}
Sunucunun gerçek zamanlı olarak kullanılabilirliğini belirlemek için PingUrl mekanizması olan WeightedResponseTimeRule kuralını kullandık.
Bu kurala göre, sistem her sunucunun ortalama yanıt süresine göre bir ağırlık değeri hesaplar. Yanıt süresi ne kadar az olursa, ağırlık değeri o kadar az olur. Diğer bir deyişle hızlı cevap veren sunucunun yük değeri az olacağından daha fazla yönlendirme yapılması sağlanır. Bu kural sayesinde, olasılığın sunucunun ağırlığına göre belirlendiği bir sunucuyu rastgele seçer.
Bunun yanı sıra PingUrl, sunucunun kullanılabilirliğini belirlemek için her URL’ye ping atacaktır.
Örnek uygulamamız için oluşturduğumuz application.yml yapılandırma dosyası şu şekildedir;
spring:
application:
name: spring-cloud-ribbon
server:
port: 8888
ping-server:
ribbon:
eureka:
enabled: false
listOfServers: localhost:9092,localhost:9999
ServerListRefreshInterval: 15000
Yukarıdaki application.yml dosyasındaki yapılandırmalar sırayla;
— Uygulama Adı
— Uygulamanın port numarası
— Sunucu listesi için isimlendirilmiş client: “ping-server”
— Eureka servis bulma bileşenini, “enabled: false” yaparak kapatıyoruz
— Load balancing için uygun olan sunucuların listesi tanımlandı. Bu örnekte 2 sunucu tanımladık
— Sunucu yenileme hızını ServerListRefreshInterval ile 15 saniye olarak yapılandırıyoruz
RibbonClient
Şimdi de ana uygulama sınıfı içerisinde load balancing’i etkinleştirmek için RibbonClient kullanacağız;
@SpringBootApplication
@RestController
@RibbonClient(
name = “ping-a-server”,
configuration = RibbonConfiguration.class)
public class ServerLocationApp { @LoadBalanced
@Bean
RestTemplate getRestTemplate() {
return new RestTemplate();
}
@Autowired
RestTemplate restTemplate;
@RequestMapping(“/server-location”)
public String serverLocation() {
return this.restTemplate.getForObject(
“http://ping-server/locaus”, String.class);
}
public static void main(String[] args) {
SpringApplication.run(ServerLocationApp.class, args);
}
}
@RestController anotasyonuna sahip bir controller sınıfı tanımladık. Bunun yanında bir isim ve konfigürasyon sınıfı ayarlanması için @RibbonClient anotasyonunu kullandık.
Burada tanımlanan RibbonConfiguration yapılandırma sınıfı, bu uygulama için Ribbon API yapılandırmasını sağladığımız, yukarıdaki sınıfın aynısıdır.
Ayrıca RestTemplate’i @LoadBalanced anotasyonu ile işaretledik. Bu sayede RestTemplate, Ribbon ile load balancing yaparak çalışıyor.
Ribbon Başarısızlık Durumu
Daha önce de belirttiğim gibi Ribbon API yalnızca client tarafında yük dengeleme algoritmaları sağlamaz. Aynı zamanda hata toleransı da sunar.
Ribbon API, düzenli aralıklarla sunucuların sürekli pinglenmesiyle sunucunun kullanılabilirliğini belirleyebilir ve canlı olmayan sunucuları atlama özelliğine sahiptir.
Buna ek olarak, aynı zamanda belirtilen kritere göre sunucuları filtrelemek için Circuit Breaker patterni de uygulayabilmektedir.
Circuit Breaker patterni, time-out beklemeden başarısız olan sunucuya yapılan isteği hızlı bir şekilde reddederek sunucunun arızasının performans üzerindeki etkisini en aza indirir. Bu özelliği, niws.loadbalancer.availabilityFilteringRule.filterCircuitTripped parametresini “false” yaparak devre dışı bırakabiliriz.
Tüm sunucular kapalı olduğunda, isteği karşılayacak hiçbir sunucu kullanılamıyorsa, pingUrl() başarısız olur ve java.lang.IllegalStateException “No instances are available to serve the request” mesajını içeren bir hata alırız.
Ribbon Load-Balancing Kuralları
RoundRobinRule
Bu kural en basit kuraldır. Verilen listedeki sunucuları sırayla seçerek yönlendirme yapar. Genellikle daha gelişmiş kuralların varsayılan kuralı veya geri dönüşü olarak kullanılır.
AvailabilityFilteringRule
Bu kural “circuit tripped” olan, yani devrede erişilemeyen sunucuları veya eşzamanlı bağlantı sayısı yüksek olan sunucuları atlar.
Varsayılan olarak, RestClient son 3 denemesinde bağlantı hatası alırsa bir diğer instance tetiklenir. Circuit yani devre açık konuma gelmiş olur. Devrenin tekrar kapalı duruma gelmesi için, yani hata alan instance tekrar aktifleşmesi için 30 saniye beklenecektir. Tekrar erişim sağlandığında “circuit closed” yani devre kapalı konuma geçecektir. Bununla birlikte, eğer bağlantı sağlanamamaya devam ederse “circuit tripped” durumunda kalmaya devam edecektir ve tekrar “circuit closed” yani devre kapalı konuma geçmesi için bekleme süresi, ardışık hataların sayısına kadar katlanarak artacaktır.
AvailabilityFilteringRule kuralındaki bu özellikler Archaius ConfigurationManager aracılığıyla aşağıdaki şekilde ayarlanabilir;
# Sunucu erişilemiyor durumdayken art arda yapılacak bağlantı hata eşiği, varsayılan 3'tür.
niws.loadbalancer.<clientName>.connectionFailureCountThreshold# Bir instance için "unusable(kullanılamaz)" durumda kalabileceği azami süre, varsayılan 30 saniyedir.
niws.loadbalancer.<clientName>.circuitTripMaxTimeoutSeconds# Sunucuyu atlamak için gerekli eşzamanlı bağlantıların eşiği, varsayılan değer Integer.MAX_INT'tir.
<clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit
WeightedResponseTimeRule
Bu kural için, her sunucuya ortalama yanıt süresine göre bir ağırlık değer verilir. Tepki süresi ne kadar uzun olursa, o kadar az ağırlık alır. Yukarıda da kısaca geçtiğim gibi sunucu hızlı cevap vermiyorsa bu kural ağırlık değerini yükseltip, yükünü azaltmaya gider. Aynı zamanda hızlı cevap verenin de ağırlık değerini azaltıp, yükünü artırmaya gider.
WeightedResponseTimeRule özelliğini etkinleştirmek için, API üzerinden load balancer ile ayarlanabilir veya aşağıdaki özellik ile yapılabilir;
<clientName>.<clientConfigNameSpace>.NFLoadBalancerRuleClassName= com.netflix.loadbalancer.WeightedResponseTimeRule