Spring Cloud Eureka Server ve Feign Client Nedir? #Uygulama Örneği

Kayhan Öztürk
5 min readFeb 8, 2024

--

Spring Cloud Eureka Server’dan ve bunu uyguladığımız projemizden bahsetmeden önce, mikroservis tanımından başlamakta fayda var.

Microservice Mimari Yaklaşımı Nedir?

Microservice mimari yaklaşımından önce projemizdeki tüm servisleri aynı uygulama içerisinde barındırdığımız monolithic uygulamalar mevcuttu. Fakat monolithic projelerde; deployment’ların her servis için ayrı ayrı yapılamıyor olması, uygulama içerisindeki bir serviste yapılmış geliştirmeler için tüm uygulamanın tekrar deploy edilmesi ve geliştirme hızının yavaş olması gibi çeşitli sebeplerden, microservis yaklaşımını kullanmaya başladık.

Monolithic uygulamalar yerine tüm servislerin birbirinden bağımsız şekilde çalıştığı, deploy edilebildiği bir mimari yaklaşım olan microservice yaklaşımı ortaya çıktı. Microservice yaklaşımından ilk bahseden kişi olan Martin Fowler’ın microservice tanımına buradan ulaşabilirsiniz.

Microservis mimarisine sahip uygulamalarda, her servis bir domainin(user, cart gibi.) iş yükünü üstlenmelidir. Uygulama içerisindeki tüm servisleri parçalayıp, birbirinden bağımsız development ve deploy etme imkanı yarattığımız uygulamalar için microservice yaklaşımını kullanıyor diyebiliriz.

Microservis altyapısının avantajlarını aşağıdaki şekilde sıralayabiliriz:

  • Kolay deployment yapılabilir olması. Monolithic uygulamalarda deploymentlar/dağıtımlar haftada ya da ayda belirli dönemlerde yapılabilirken, microservice altyapısına sahip uygulamalarda ise gün içinde diğer servislerden bağımsız yalnızca geliştirme yaptığımız servisleri deploy edebilme imkanımız oluştu.
  • Geliştiriciler açısından development süreçlerinin daha rahat ve kontrol edilebilir olması.
  • Teknoloji esnekliği sunması. Örneğin; user servisimizi java ile geliştirirken, card servisimizi go dili ile geliştirebiliriz. Aynı şekilde user servisimizde mysql gibi ilişkisel veritabanı kullanırken, card servisimizde ise mongodb gibi nosql bir db kullanabiliriz.
  • Uygulamanın bakımı ve test edilebilirliği kolaylaşır.

Spring Cloud Eureka Server Nedir?

Spring cloud eureka server; Netflix şirketi tarafından geliştirilmiş “service registration” işlemlerini yapmamızı sağlayan açık kaynaklı bir modüldür diyebiliriz.

Eureka server sayesinde, birbirinden bağımsız servislerimizin her biri eureka server’a register olur ve bu servislerden herhangi birini çağıracak diğer bir servis eureka üzerinden ilgili servisin ip ve port bilgilerini bilmeden sadece service-name’i ile birlikte istediği servisi çağırabilir.Eureka server için bir nevi microservice’lerimizin bilgilerinin tutulduğu katalog/contract diyebiliriz.

Yukarıda açıkladığımız microservice yaklaşımını, geliştireceğimiz notebook ve note servislerimizi içeren uygulamamız üzerinden örnekleyelim.

Microservice bir projemiz olsun, bu projede notebook ve note adında 2 servisimizin olduğunu farzedelim. Notebook servisi üzerinden note servisine erişmek istiyoruz. Bu durumda normal şartlarda RestTemplate üzerinden direkt olarak çağıracağımız note service’in ip ve port bilgilerine yer vererek erişim sağlayabiliyoruz. Fakat note servis’in ortamında değişiklik olduğu yani ip/port gibi bilgilerinin değiştiği takdirde notebook servisimizde yeniden geliştirme ihtiyacı duyuyoruz.

Oysa ki eureka server aracılığı ile birlikte bunlara gerek kalmadan, servislerin ip ve port’larını bilmeden direkt olarak servislerin isimleri üzerinden “Feign Client” ile istediğimiz servise request gönderebiliriz. Aşağıda yer vereceğimiz uygulama örneğimizde de; RestTemplate yerine feign client kullanıyor olacağız.

Spring Cloud Open Feign ve Feign Client Nedir?

“Spring Cloud Open Feign” modülünün sunduğu, servisler arasındaki iletişimi “Feign Client” interface/arayüzleri üzerinden yalnızca servislerin isimlerini bilerek(herhangi bir ip/port bilgisi olmadan) sağlayabiliyoruz. Böylelikle servisler arasındaki iletişimin daha kolay ve yönetilebilir olmasını sağlayabiliyoruz.

Spring Boot ve Cloud Eureka Microservice Uygulama Örneği:

Geliştireceğimiz microservice uygulamamız 3 servisten oluşmaktadır. Birincisi notebook-service, ikincisi note-service ve son olarak da bu servislerimizin register olacağı euraka-server service.

İlk olarak euraka-server modülümüzü oluşturarak başlayalım.

1-)Eureka Server Service

Oluşturacağımız servisin euraka server servisi olacağını belirtmemiz için ilk olarak “spring-cloud-starter-netflix-eureka-server” dependency’sini ekliyoruz.

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

Sonrasında ise main methodumuzu içeren ana class’da “@EnableEurekaServer” anotasyonuna yer veriyoruz.

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

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

}

Son olarak da application yaml/properties dosyamızda gerekli konfigurasyonlarımıza yer veriyoruz.

server.port=8012
spring.application.name=discoveryservice
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.instance.preferIpAdress=true
eureka.client.serviceUrl.defaultZone=http://localhost:8012/eureka

2-) Notebook Service

Projemizde notebook ve note servislerimizin olacağından bahsetmiştik. Her servisimizin biraz önce oluşturduğumuz eureka server’a register olabilmesi için, her birinineureka client olmaları gerekiyor. Bunun için ise öncelikle “spring-cloud-starter-netflix-eureka-client” dependency’sini ekliyoruz.

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>

Sonrasında servisimizin main class dosyamızda, “@EnableDiscoveryClient” anotasyonunu kullanıyoruz. Böylelikle spring IOC container’a bu servisin bir eureka client servisi olduğunu belirtmiş oluyoruz.

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@ComponentScan(basePackages = "com.notebook.notebookservice")
public class NotebookServiceApplication {

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

Son olarak ise application yaml/properties dosyalarımızda gerekli euraka konfigürasyonlarına yer veriyoruz.

eureka.client.serviceUrl.defaultZone=${EUREKA_URI:http://localhost:8012/eureka}
spring.devtools.restart.enabled=true
eureka.instance.instance-id=${spring.application.name}:${spring.application.instance_id:${random.value}}

3-)Note Service

Note servisimiz için de notebook servisteki dependency, euraka client anotasyon ve application properties konfigürasyonlarımızı tamamlıyoruz.

Sonrasında ilk olarak euraka server servisimizi ayağa kaldırıyoruz, service’imiz ayağa kalktıktan sonra notebook ve note servislerimizi çalıştırıp euraka server modülümüze register olduklarını gözlemleyebiliriz. Ayağa kalkan euraka server adresindeki dashboard üzerinden euraka’ya register olmuş service instance’larını kontrol edebiliriz.

Feign Client kullanacağımız bu uygulamamızda, amacımız Notebook Service üzerinden note service’ine erişim sağlamak olacak. Notebook servisimizden feign client ile note servisimize erişim sağlayabilmek için Notebook ve note servislerimize “spring-cloud-starter-openfeign” bağımlılığını ekliyoruz. Notebook Service üzerinden, note servisi çağırabilmemiz için “@EnableFeignClients” anotasyonuna notebook-service main class üzerinde kullanmamız yeterli olacaktır.

Notebook servisimiz üzerinden note servisini çağırabilmemiz için feign client interface template örneğimiz aşağıdaki gibidir:

@FeignClient(name = "note-service", path="/v1/note")
public interface NoteServiceClient {
Logger logger = LoggerFactory.getLogger(NoteServiceClient.class);
@GetMapping
ResponseEntity<List<NoteDto>> findAllNotes();

@GetMapping("/id/{id}")
@CircuitBreaker(name = "findNoteByIdCircuitBreaker", fallbackMethod = "findNoteByIdFallback")
ResponseEntity<NoteDto> findNoteById(@PathVariable Long id);

default ResponseEntity<NoteDto> findNoteByIdFallback(Long id, Exception exception){
logger.info("Note not found by id " + id);
return ResponseEntity.ok(NoteDto.builder()
.id(3L)
.title("titleA")
.description("descriptionA")
.build());
}

@GetMapping("/title/{title}")
ResponseEntity<NoteDto> findNoteByTitle(@PathVariable String title);
}

Feign client sayesinde aşağıdaki gibi sanki Note servis, notebook servis içerisinde yer alan bir servismiş gibi rahatlıkla erişim sağlayabiliyoruz.

/**
* @author Kayhan Öztürk
* @version 0.1
* @since 0.1
*/
@Service
public class NotebookServiceImpl implements NotebookService {
private static final Logger logger = LoggerFactory.getLogger(NotebookServiceImpl.class);
private final NotebookRepository notebookRepository;
private final NotebookDtoMapper notebookDtoMapper;
private final NoteServiceClient noteServiceClient;

public NotebookServiceImpl(NotebookRepository notebookRepository, NotebookDtoMapper notebookDtoMapper, NoteServiceClient noteServiceClient) {
this.notebookRepository = notebookRepository;
this.notebookDtoMapper = notebookDtoMapper;
this.noteServiceClient = noteServiceClient;
}

@Override
public NotebookDto findAllNotesInNotebookById(Long id) {
Notebook notebook = notebookRepository.findById(id)
.orElseThrow(() -> new NotebookNotFoundException("notebook not found id info:" + id));
logger.info("searching notebook by id {}", id);
NotebookDto notebookDto = new NotebookDto();
notebookDto.setId(notebook.getId());
notebookDto.setNoteDtoList(notebook.getNoteList()
.stream()
.map(note -> noteServiceClient.findNoteById(Long.valueOf(note)).getBody())
.collect(Collectors.toList()));
return notebookDto;
}

@Override
public NotebookResponseDto findNotebookById(Long id) {
NotebookResponseDto notebookResponseDto = notebookRepository.findById(id)
.map(notebook -> notebookDtoMapper.convertEntityToRespDto(notebook))
.orElseThrow(() -> new NotebookNotFoundException(id + " idNotebook not found!"));

return notebookResponseDto;
}

@Override
public NotebookResponseDto addNoteToNotebook(NotebookCreateRequest notebookCreateRequest) {
Notebook notebook = notebookRepository.findById(notebookCreateRequest.getNotebookId())
.orElseThrow(() -> new NotebookNotFoundException("Notebook not found by id :" + notebookCreateRequest.getNotebookId()));

NoteDto noteDto = noteServiceClient.findNoteById(Long.valueOf(notebookCreateRequest.getNotes().get(0))).getBody();

notebook.getNoteList().add(noteDto.getId().toString());
notebook.setNoteList(notebook.getNoteList());
Notebook createdNotebook = notebookRepository.save(notebook);
return notebookDtoMapper.convertEntityToRespDto(createdNotebook);
}

@Override
public NotebookResponseDto createNotebook() {
Notebook createNotebook = notebookRepository.save(new Notebook());
return notebookDtoMapper.convertEntityToRespDto(createNotebook);
}

@Override
public List<Long> getAllNotebookIdInfo() {
return notebookRepository.findAll()
.stream().map(notebook -> notebook.getId())
.collect(Collectors.toList());
}
}

Spring Cloud Eureka Server Microservice Projesi Github Kaynak Kod: https://github.com/kayhanoztrk/notebook-eureka

NOT: Sample olması için github’a eklediğim microservice projesinde spring cloud eureka dışında spring cloud config, spring cloud gateway ve distributed log trace sistem için ise Zipkin kullanılmıştır. Bu teknolojilerin kullanımı ile ilgili de yazı serisi paylaşıyor olacağım.

--

--