Comment localiser les services par version avec Netflix eureka et ribbon
nous sommes en train d'explorer Condensateur De Flux de Netflix pour étudier leur implémentation d'architectures micro-services. Pour le moment, nos intérêts sont axés sur l'enregistrement des services et la fonctionnalité de recherche dynamique.
parcourant le code, les samples, et la configuration, mais quelque chose n'est pas clair; service de gestion des versions. Si eureka fournit des services de découverte, et ruban eureka est REST client, comment un client dit qu'il a besoin de service 1.2
de service fooBar
? Où le client stocke-t-il/obtient-il ce numéro de version; à partir d'un fichier de configuration local comme , ou est-il dynamique obtenu par archaius<!--4?
2 réponses
je ne vois pas en voie de service de gestion des versions dans le documentation pour l'Eureka API REST.
par conséquent, je pense que la meilleure façon de gérer cela serait d'intégrer des informations de version dans votre IDs de service.
disons que nous avons 4 services: Utilisateur,Statistiques,Login et oAuth.
nous venons de mettre à jour le Utilisateur modification de L'API de service fonctionnalité requise pour certaines nouvelles exigences dans le Login service. Ces modifications sont cependant incompatibles avec L'API qui oAuth et personne n'a le temps de mettre à jour ce service. Statistiques le service n'utilise aucune de ces fonctionnalités et ne se soucie donc pas de la version de l'API utilisée. Cela signifie que nous avons besoin à la fois de la nouvelle version du Utilisateur service (1.2) et l'ancienne version (1.1) de la même temps.
On peut le configurer comme ceci:
- la configuration pour le Utilisateur 1.1 services dit de s'inscrire comme utilisateur "1.1" et "utilisateur"
- la configuration pour le Utilisateur 1.2 services dit de s'inscrire comme utilisateur "1.2" et "utilisateur"
- la configuration pour le oAuth le service dit que L'ID du Utilisateur service est "user-1.1"
- le configuration pour le Login le service dit que L'ID du Utilisateur service est "user-1.2"
- la configuration pour le Statistiques le service dit que L'ID du Utilisateur le service est "utilisateur"
de Cette façon, le oAuth le service ne communiquera qu'avec l'ancien Utilisateur service Login service avec le nouveau Utilisateur le service, et le Statistiques service Utilisateur service.
vous devriez pouvoir utiliser Archaius pour propager la configuration sur les serveurs.
Notez que si vous relâchez Utilisateur la version 1.3 qui introduit des Modifications incompatibles avec les parties des 1.2 et 1.1 que le Statistiques le service est utilisé vous devrez soit dire le Utilisateur 1.1 et 1.2 services pour s'enregistrer eux-mêmes comme autre chose aussi (comme "utilisateur-statistiques-safe") et de dire à l' Statistiques service à utiliser l'IDENTIFIANT, ou de dire à l' Statistiques service à utiliser l'utilisateur "1.1" ou "l'utilisateur 1.2".
1 registres v1 et v2 Eureka
Service 2 découvre et envoie des demandes à 1's v1 et v2 à l'aide de différents Ruban clients
j'ai obtenu cette démo pour travailler en utilisant Spring Cloud
bien que blog pourrait trouver à: http://tech.asimio.net/2017/03/06/Multi-version-Service-Discovery-using-Spring-Cloud-Netflix-Eureka-and-Ribbon.html
L'idée, j'ai suivi RestTemplate
pour utiliser un autre Ribbon
client pour chaque version car chaque client a son propre ServerListFilter
.
le service multi-versions inclut la version dans les métadonnées d'enregistrement.
Service 1
application.yml
...
eureka:
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:8000/eureka/
instance:
hostname: ${hostName}
statusPageUrlPath: ${management.context-path}/info
healthCheckUrlPath: ${management.context-path}/health
preferIpAddress: true
metadataMap:
instanceId: ${spring.application.name}:${server.port}
---
spring:
profiles: v1
eureka:
instance:
metadataMap:
versions: v1
---
spring:
profiles: v1v2
eureka:
instance:
metadataMap:
versions: v1,v2
...
Service 2
application.yml
...
eureka:
client:
registerWithEureka: false
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:8000/eureka/
demo-multiversion-registration-api-1-v1:
ribbon:
# Eureka vipAddress of the target service
DeploymentContextBasedVipAddresses: demo-multiversion-registration-api-1
NIWSServerListClassName: com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList
# Interval to refresh the server list from the source (ms)
ServerListRefreshInterval: 30000
demo-multiversion-registration-api-1-v2:
ribbon:
# Eureka vipAddress of the target service
DeploymentContextBasedVipAddresses: demo-multiversion-registration-api-1
NIWSServerListClassName: com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList
# Interval to refresh the server list from the source (ms)
ServerListRefreshInterval: 30000
...
Application.java
...
@SpringBootApplication(scanBasePackages = {
"com.asimio.api.multiversion.demo2.config",
"com.asimio.api.multiversion.demo2.rest"
})
@EnableDiscoveryClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
AppConfig.java (Voir Ribbon
le nom du client correspond au Ribbon
clé trouvée dans application.yml
...
@Configuration
@RibbonClients(value = {
@RibbonClient(name = "demo-multiversion-registration-api-1-v1", configuration = RibbonConfigDemoApi1V1.class),
@RibbonClient(name = "demo-multiversion-registration-api-1-v2", configuration = RibbonConfigDemoApi1V2.class)
})
public class AppConfig {
@Bean(name = "loadBalancedRestTemplate")
@LoadBalanced
public RestTemplate loadBalancedRestTemplate() {
return new RestTemplate();
}
}
RibbonConfigDemoApi1V1.java
...
public class RibbonConfigDemoApi1V1 {
private DiscoveryClient discoveryClient;
@Bean
public ServerListFilter<Server> serverListFilter() {
return new VersionedNIWSServerListFilter<>(this.discoveryClient, RibbonClientApi.DEMO_REGISTRATION_API_1_V1);
}
@Autowired
public void setDiscoveryClient(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
}
RibbonConfigDemoApi1V2.java est similaire mais en utilisant RibbonClientApi.DEMO_REGISTRATION_API_1_V2
RibbonClientApi.java
...
public enum RibbonClientApi {
DEMO_REGISTRATION_API_1_V1("demo-multiversion-registration-api-1", "v1"),
DEMO_REGISTRATION_API_1_V2("demo-multiversion-registration-api-1", "v2");
public final String serviceId;
public final String version;
private RibbonClientApi(String serviceId, String version) {
this.serviceId = serviceId;
this.version = version;
}
}
VersionedNIWSServerListFilter.java
...
public class VersionedNIWSServerListFilter<T extends Server> extends DefaultNIWSServerListFilter<T> {
private static final String VERSION_KEY = "versions";
private final DiscoveryClient discoveryClient;
private final RibbonClientApi ribbonClientApi;
public VersionedNIWSServerListFilter(DiscoveryClient discoveryClient, RibbonClientApi ribbonClientApi) {
this.discoveryClient = discoveryClient;
this.ribbonClientApi = ribbonClientApi;
}
@Override
public List<T> getFilteredListOfServers(List<T> servers) {
List<T> result = new ArrayList<>();
List<ServiceInstance> serviceInstances = this.discoveryClient.getInstances(this.ribbonClientApi.serviceId);
for (ServiceInstance serviceInstance : serviceInstances) {
List<String> versions = this.getInstanceVersions(serviceInstance);
if (versions.isEmpty() || versions.contains(this.ribbonClientApi.version)) {
result.addAll(this.findServerForVersion(servers, serviceInstance));
}
}
return result;
}
private List<String> getInstanceVersions(ServiceInstance serviceInstance) {
List<String> result = new ArrayList<>();
String rawVersions = serviceInstance.getMetadata().get(VERSION_KEY);
if (StringUtils.isNotBlank(rawVersions)) {
result.addAll(Arrays.asList(rawVersions.split(",")));
}
return result;
}
...
AggregationResource.java
...
@RestController
@RequestMapping(value = "/aggregation", produces = "application/json")
public class AggregationResource {
private static final String ACTORS_SERVICE_ID_V1 = "demo-multiversion-registration-api-1-v1";
private static final String ACTORS_SERVICE_ID_V2 = "demo-multiversion-registration-api-1-v2";
private RestTemplate loadBalancedRestTemplate;
@RequestMapping(value = "/v1/actors/{id}", method = RequestMethod.GET)
public com.asimio.api.multiversion.demo2.model.v1.Actor findActorV1(@PathVariable(value = "id") String id) {
String url = String.format("http://%s/v1/actors/{id}", ACTORS_SERVICE_ID_V1);
return this.loadBalancedRestTemplate.getForObject(url, com.asimio.api.multiversion.demo2.model.v1.Actor.class, id);
}
@RequestMapping(value = "/v2/actors/{id}", method = RequestMethod.GET)
public com.asimio.api.multiversion.demo2.model.v2.Actor findActorV2(@PathVariable(value = "id") String id) {
String url = String.format("http://%s/v2/actors/{id}", ACTORS_SERVICE_ID_V2);
return this.loadBalancedRestTemplate.getForObject(url, com.asimio.api.multiversion.demo2.model.v2.Actor.class, id);
}
@Autowired
public void setLoadBalancedRestTemplate(RestTemplate loadBalancedRestTemplate) {
this.loadBalancedRestTemplate = loadBalancedRestTemplate;
}
}