Spring Cloud — Netflix Eureka

Ahmet Cokgungordu
4 min readDec 22, 2018

--

Spring Cloud Netflix Eureka, servislerin makina adı ve bağlantı noktasına ihtiyaç olmaksızın birbirleriyle iletişim kurmasını sağlar. Bu mimarideki tek önemli nokta, her servisin kayıt olması gereken bir servis olmasıdır.

Netflix Eureka ile tüm bağlı clientların listesini alınır ve load balancing algoritması ile servislere istekler dağıtılır. Bunun için her servis ayağa kalktığında eureka servera bir heartbeat sinyal gönderir ve kayıt olur.

Eureka Server

Service registry kullanmak için Eureka Server’ı ‘spring-cloud-starter-netflix-eureka-server’ ile bağımlılıklara ekliyoruz. Eureka Server’ı aktifleştirmek için ‘SpringBootApplication’ anotasyonu ile birlikte ‘EnableEurekaServer’ anotasyonunu kullanıyoruz.

Öncelikle yeni bir Maven projesi oluşturacağız ve bağımlılıkları buna ekleyeceğiz. Tüm projelerde spring-cloud-starter-parent bağımlılığı ekliyoruz.

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Ardından, EurekaServerApplication ana uygulama sınıfını oluşturuyoruz.

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}

Eureka Server özelliklerini YAML biçiminde application.yml dosyası oluşturup yapılandırıyoruz.

server:
port: 8761
eureka:
client:
registerWithEureka: false
fetchRegistry: false

Burada uygulama portunu belirtiyoruz. Eureka Server için varsayılan port 8761 olarak verilmiştir. Devamında Eureka Client’ına kendisi ile register olmamasını belirttik. Şimdilik uygulamanın sadece bir server gibi davranmasını istiyoruz.

Şimdi Eureka kontrol panelini görüntülemek için tarayıcımızda http://localhost:8761 adresine gidiyoruz.

Eureka anasayfasında aşağıdaki gibi bazı temel göstergeleri görüyoruz.

Eureka Client

Discovery Client yaratmak için bazı Spring Discovery Client’ları (spring-cloud-starter-netflix-eureka-client) bağımlılıklarımıza ekliyoruz.

@Configuration @EnableDiscoveryClient veya @EnableEurekaClient ile eklememiz gerekir. Fakat spring-cloud-starter-netflix-eureka-client bağımlılığı eklediğimizde bizim yerimize bunları ekleyecektir.

Uygulamada bir örnek olması açısından REST kullanabilmemiz için spring-boot-starter-web bağımlılığını da ekliyoruz.

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-starter</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>

Varolan bir projeye eureka-client yeteneği kazandırmak için ise aşağıdaki gibi spring-cloud-starter-netflix-eureka-client bağımlılığını da ekliyoruz.

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

Burada ana uygulama sınıfını oluşturuyoruz.

@SpringBootApplication
@RestController
public class EurekaClientApplication implements GreetingController {

@Autowired
@Lazy
private EurekaClient eurekaClient;

@Value("${spring.application.name}")
private String appName;

public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}

@Override
public String greeting() {
return String.format("Hello from '%s'!", eurekaClient.getApplication(appName).getName());
}
}

GreetingController interface yapısı;

public interface GreetingController {
@RequestMapping("/greeting")
String greeting();
}

Client uygulamamızı server üzerinde kaydetmek için YAML dosyamızı aşağıdaki şekilde yapılandırıyoruz.

Spring Boot’un bizim için rastgele bir port seçip ayağa kalkması için port seçimini ‘0’ yapıyoruz. Nihayetinde bir bu servise, servis ismi ile ulaşıyor olacağız. Daha sonra Eureka bilgilerini vererek Client’ın Server’ı bulmasını sağlıyoruz.

spring:
application:
name: spring-cloud-eureka-client
server:
port: 0
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
instance:
preferIpAddress: true

Eureka Client’ımızı bu şekilde ayarladığımızda, böyle bir servisin daha sonra kolayca ölçeklenebilir olmasını sağlamış olduk.

Şimdi tekrar http://localhost:8761 adresine gittiğimizde Client’ın server’a bağlandığını göreceğiz.

Feign Client

Projemizde son olarak birden fazla uygulama ile Feign Client’ı inceleyeceğiz. Örnek olarak Spring Netflix Feign Client ile RestTemplate yazacağız.

Feign endpointler arasında iletişim kurmak için interface kullanır. Bu interfaceler ile servis urlleri yerine, runtime da otomatik olarak implement edilen servis isimleri üzerinden iletişim sağlanmaktadır.

Bu Feign olmadan EurekaClient üzerinden de yapabilirdik. Application sınıfı üzerinden bütün clientları çekip, herhangi birinin bilgilerine InstanceInfo üzerinden ulaşabiliriz. Bu sayede elimizde host ve port bilgileri olacağından Rest çağrısında bulunabiliriz.

Örneğin;

@Autowired
private EurekaClient eurekaClient;

public void doRequest() {
Application application
= eurekaClient.getApplication("spring-cloud-eureka-client");
InstanceInfo instanceInfo = application.getInstances().get(0);
String hostname = instanceInfo.getHostName();
int port = instanceInfo.getPort();
// ...
}

Feign Client’ı yapılandırmak için pom.xml’imize aşağıdaki bağımlılıkları ekliyoruz.

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>

Feign Client spring-cloud-starter-feign paketinden bulunmaktadır. Etkinleştirmek için @Configuration’a @EnableFeignClients eklememiz gerekmektedir. Kullanımı ise yukarıda yazdığımız GreetingClient interface içerisine @FeignClient(‘servis-adı’) ekleyerek olmaktadır.

Feign Client oluşturmak için en iyi yöntem, @RequestMapping anotasyonu ile interfaceler oluşturmak ve bunları ayrı bir modül haline getirmektir. Böylece onlar client-server arasında paylaşılabilir. Server tarafında, bunları @Controller olarak uygulayabilir ve client tarafında @FeignClient anotasyonu kullanılabilir ve extend edilebilir.

Ayrıca spring-cloud-starter-eureka package bağımlılığını projemize eklememiz ile @EnableEurekaClient anotasyonunu otomatik kullanmış olduk.

spring-boot-starter-web ve spring-boot-starter-thymeleaf bağımlılıklarını, Rest servis ile getirilen verileri sunmak için kullanıyoruz.

Örnek olarak Feign Client interface’imiz aşağıdaki gibi olacaktır.

@FeignClient("spring-cloud-eureka-client")
public interface GreetingClient {
@RequestMapping("/greeting")
String greeting();
}

Main class ise şu şekilde olacaktır.

@SpringBootApplication
@EnableFeignClients
@Controller
public class FeignClientApplication {
@Autowired
private GreetingClient greetingClient;

public static void main(String[] args) {
SpringApplication.run(FeignClientApplication.class, args);
}

@RequestMapping("/get-greeting")
public String greeting(Model model) {
model.addAttribute("greeting", greetingClient.greeting());
return "greeting-view";
}
}

Basit bir HTML şablonu ekliyoruz.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Greeting Page</title>
</head>
<body>
<h2 th:text="${greeting}"/>
</body>
</html>

application.yml yapılandırma dosyası ise bir önceki örnektekine benzer yapıdadır.

spring:
application:
name: spring-cloud-eureka-feign-client
server:
port: 8080
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}

Daha sonra servisimizi ayağa kaldırdığımızda ve http://localhost:8080/get-greeting adresine istek attığımızda aşağıdaki çıktıyı üretecektir.

Hello from SPRING-CLOUD-EUREKA-CLIENT!

TransportException: Cannot execute request on any known server

Eureka sunucusunu çalıştırırken, genellikle aşağıdaki gibi hatayla karşılaşabiliriz.

com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server

Bu hata genellikle application.properties veya application.yml içerisindeki yanlış yapılandırmadan kaynaklı oluşmaktadır. Eureka, client için konfigüre edilebilen iki özellik sunar.

registerWithEureka: Bu özelliği true olarak yaparsak, sunucu içerisinde client başlatılırken Eureka sunucusuna kendini kaydetmeyi deneyecektir.

fetchRegistry: Bu özelliği true olarak yapılandırırsak, client Eureka kayıt defterini almaya çalışacaktır.

Artık Eureka sunucusunu başlattığımızda, kendini sunucuyla yapılandırmak için client kaydını yapmak istemiyoruz.

Sunucu başlatılırken yukarıdaki özellikleri true olarak işaretlersek(varsayılan olarak true gelmekte), client Eureka sunucusuyla kendini kaydetmeye çalışır ve henüz mevcut olmayan kayıt defterini almaya çalışır. Bunun sonucunda, TransportException’ı alırız.

Bu yüzden Eureka sunucu uygulamalarında bu özellikleri asla true olarak yapılandırmamalıyız. Application.yml içine konması gereken doğru ayarlar şu şekilde olmalıdır.

eureka:
client:
registerWithEureka: false
fetchRegistry: false

Artık Spring Netflix Eureka Sunucusu’nu kullanarak bir servis kaydı yapabiliyoruz ve bazı Eureka Clientlarını bununla birlikte kaydedebiliyoruz.

Eureka Clientlarımızı rastgele portlarla ayağa kaldırıp sunucuya kaydettik. Feign Client sayesinde port bilgisi değişse bile bulabilir ve çağırabiliriz.

--

--