In una tipica architettura a Microservizi possono esistere servizi che espongono funzionalità attraverso la composizione delle funzionalità di altri servizi appartenenti allo stesso ecosistema. Si veda ad esempio il progetto del calculator presentato nell’articolo Invocazione di Servizi RESTful con RestTemplate. In una architettura distribuita per ogni servizio conoscere “dove sono gli altri” non è un problema banale perchè, soprattutto se siamo in un ambiente a container creati e distrutti da un container manager, gli ip e le porte che caratterizzano gli end-point dei servizi possono variare.
C’è quindi la necessità di implementare un servizio di Service Discovery che, in una architettura distribuita, è offerto da un componente chiamato Service Registry, il quale gestisce un registro dei servizi disponibili in modo che i moduli possano registrarvisi e trovarsi a vicenda.
Il servizio di service discovery nel framework Spring Cloud è offerto dal componente Eureka Server.
Programmazione del Server Eureka
Come per Spring Cloud Config, introdotto nell’articolo Spring Cloud Config, Eureka è una applicazione Spring Boot configurabile con semplici annotazioni. Creiamo quindi un’applicazione Spring Boot con Maven ed inseriamo la dipendenza:
1 2 3 4 |
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> |
Secondo la documentazione ufficiale tale dipendenza è sufficiente, ma la versione Finchley.RC2 del framework Spring Cloud, utilizzata nell’esempio, sembra non si porti dietro altre due dipendenze essenziali. Per maggiori informazioni rimando al mio post su Stack Overflow.
1 2 3 4 5 6 7 8 9 |
<dependency> <groupId>com.netflix.eureka</groupId> <artifactId>eureka-client</artifactId> </dependency> <dependency> <groupId>org.codehaus.jettison</groupId> <artifactId>jettison</artifactId> <version>1.3.5</version> </dependency> |
Nella cartella /resources
creiamo il file bootstrap.yml
di configurazione ed inseriamo i seguenti parametri:
1 2 3 4 5 6 |
server: port: 8761 eureka: client: registerWithEureka: false fetchRegistry: false |
La prima parte identifica la porta dove il servizio di service registry è esposto, mentre il resto della configurazione serve semplicemente a dire al server di non registrare se stesso nel registro dei servizi.
Infine creiamo la classe di avvio di Spring Boot ed annotiamola con @EnableEurekaServer per abilitare il server Eureka.
1 2 3 4 5 6 7 |
@EnableEurekaServer @SpringBootApplication public class ServiceregisrtyApplication { public static void main(String[] args) { SpringApplication.run(ServiceregisrtyApplication.class, args); } } |
L’avvio del server avviene con l’usuale comando maven: mvn spring-boot:run
.
Configurazione dei Client
Una volta programmato il server vediamo come modificare i servizi dell’esempio Invocazione di Servizi RESTful con RestTemplate per l’utilizzo del Service Registry Eureka. In particolare prendiamo in considerazione il microservizio calculator
che utilizza gli altri due servizi opgenerator
e numbergenerator
. Tale servizio infatti, deve registrare se stesso nel service registry ma deve anche recuperare dal server registry le informazioni per connettersi agli altri due servizi.
Procediamo inserendo la dipendenza nel pom.xml
dei tre microservizi:
1 2 3 4 |
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> |
Registrazione dei Servizi
E’ possibile abilitare la registrazione dei servizi sul service registry semplicemente annotando la classe di avvio di Spring Boot con l’annotazione @EnableDiscoveryClient
. Nel caso del calculator
avremo:
1 2 3 4 5 6 7 8 9 10 11 12 |
@EnableDiscoveryClient @SpringBootApplication public class CalculatorApplication { public static void main( String[] args ) throws Exception { SpringApplication.run(CalculatorApplication.class, args); } @Bean public RestTemplate restTemplate() { return new RestTemplateBuilder().build(); } } |
I servizi vengono così registrati utilizzando come serviceId
, o chiave di registrazione, il nome indicato nella proprietà spring.application.name
. Ovviamente deve essere indicare la URL al service registry, inserendo la relativa proprietà nel file application.yml
:
1 2 3 4 |
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ |
Se proviamo ora ad avviare i tre servizi con le modifiche apportate osserveremo nei log del server Eureka le seguenti informazioni.
1 2 3 4 5 6 |
2018-07-18 11:23:57.263 Registered instance OPGENERATOR/192.168.1.127:opgenerator:8082 with status UP (replication=false) 2018-07-18 11:23:57.953 Registered instance OPGENERATOR/192.168.1.127:opgenerator:8082 with status UP (replication=true) 2018-07-18 11:24:06.600 Registered instance NUMBERGENERATOR/192.168.1.127:numbergenerator:8081 with status UP (replication=false) 2018-07-18 11:24:07.113 Registered instance NUMBERGENERATOR/192.168.1.127:numbergenerator:8081 with status UP (replication=true) 2018-07-18 11:24:10.110 Registered instance CALCULATOR/192.168.1.127:calculator:8080 with status UP (replication=false) 2018-07-18 11:24:10.622 Registered instance CALCULATOR/192.168.1.127:calculator:8080 with status UP (replication=true) |
La duplicazione delle righe è dovuto al fatto che nel registro sono registrate due istanze dello stesso servizio di cui una è la replica dell’altro. Le configurazioni dei servizi possono essere recuperate con un semplice browser seguendo le url:
- http://localhost:8761/eureka/apps/
- http://localhost:8761/eureka/apps/NUMBERGENERATOR
- http://localhost:8761/eureka/apps/OPGENERATOR
- http://localhost:8761/eureka/apps/CALCULATOR
Invocazione dei Servizi
Per l’interrogazione dei servizi disponibili nel service registry Spring Cloud mette a disposizione l’interfaccia DiscoveryClient
, che espone, tra l’altro, il servizio List<ServiceInstance> getInstances(String serviceId)
il quale restituisce le istanze di tutti i servizi registrati dato il serviceId
di registrazione. Esistono diverse implementazioni di tale interfaccia, in particolare:
-
SimpleDiscoveryClient
: recupera le informazioni da file di configurazione. -
EurekaDiscoveryClient
recupera le informazioni dal server Eureka (è ovviamente l’implementazione che interessa a noi).
Applichiamo quanto detto considerando la classe OperatorGeneratorServiceImpl
del servizio calculator
utilizzata per l’invocazione del servizio di generazione casuale dell’operatore.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Service public class OperatorGeneratorEurekaServiceImpl implements OperatorGeneratorService { @Autowired private RestTemplate rest; @Autowired private DiscoveryClient discoveryClient; @Value("${opertorGeneratorServiceId}") String opertorGeneratorServiceId; public String getOperator() { String opertorGeneratorUrl = discoveryClient.getInstances(opertorGeneratorServiceId).get(0).getUri().toString(); return rest.getForEntity(opertorGeneratorUrl, RandomOperator.class).getBody().getValue(); } } |
Il bean discoveryClient
è ottenuto semplicemente attraverso autowiring direttamente dal framework, mentre il nome del serviceId è recuperato dal file di configurazione. Infine l’URL del servizio è recuperato dal server Eureka invocando il metodo getInstances()
ed in particolare referenziando la prima istanza restituita get(0)
. La figura sottostante mostra l’architettura finale della soluzione.
Codice Sorgente
Il codice sorgente è scaricabile su Github all’url https://github.com/gotamo/spring-microservices.
Per l’esecuzione utilizzate il profilo: eureka.