J'essaie d'exécuter Spring Boot (avec Spring Cloud) + Eureka Server + Hystrix Dashboard and Turbine stream, mais je rencontre un problème que je n'ai trouvé aucune solution jusqu'à présent. J'utilise Spring Boot 1.2.1.RELEASE et Spring Cloud 1.0.0.RC2 . Voici ce que j'ai:
La première instance exécute le serveur Eureka et le tableau de bord Hystrix:
@Configuration
@EnableAutoConfiguration
@EnableEurekaServer
@EnableHystrixDashboard
@EnableDiscoveryClient
class Application {
public static void main(String[] args) {
SpringApplication.run Application, args
}
}
Ici vous pouvez trouver build.gradle
pour cette instance - https://Gist.github.com/wololock/570272ad7cf2d14a4d3c
Le serveur Eureka fonctionne bien, je peux voir les instances enregistrées sur le tableau de bord du serveur eureka, je peux également utiliser LoadBalancer
pour obtenir l'URL de l'instance enregistrée en utilisant son identifiant. Jusqu'à présent, tout va bien.
J'ai quelques instances exécutées avec @EnableHystrix
annotation et utilisation @HystrixCommand
pour définir quelles méthodes doivent être contrôlées par Hystrix. Lorsque je transmets l'URL à hystrix.stream d'une seule instance au tableau de bord Hystrix, je peux le voir fonctionner sans problème.
J'ai également un serveur Turbine séparé, pas compliqué:
@EnableAutoConfiguration
@EnableTurbine
@Configuration
@EnableDiscoveryClient
class Application {
public static void main(String[] args) {
SpringApplication.run Application, args
}
}
Ici vous pouvez trouver build.gradle
pour l'instance de serveur Turbine - https://Gist.github.com/wololock/ff0d855b8a890232851e
Il utilise une configuration très simple, basée principalement sur celle fournie par l'exemple d'application de turbine - https://github.com/spring-cloud-samples/turbine
info:
component: Turbine
endpoints:
restart:
enabled: true
shutdown:
enabled: true
turbine:
appConfig: pdf-creator-service
InstanceDiscovery:
impl: io.spring.platform.netflix.turbine.EurekaInstanceDiscovery
server:
port: 8989
management:
port: 8990
eureka:
instance:
leaseRenewalIntervalInSeconds: 10
client:
serviceUrl:
defaultZone: ${vcap.services.${PREFIX:}eureka.credentials.uri:http://user:password@localhost:8761}/eureka/
Après avoir exécuté ces instances dans l'ordre:
J'ai une deuxième et une troisième instance enregistrées sur le serveur eureka, indique le journal du serveur de turbine, qu'il y a une instance en place:
[2015-02-06 12:35:04.162] boot - 20495 INFO [Timer-0] --- EurekaInstanceDiscovery: Fetching instance list for apps: [pdf-creator-service]
[2015-02-06 12:35:04.162] boot - 20495 INFO [Timer-0] --- EurekaInstanceDiscovery: Fetching instances for app: pdf-creator-service
[2015-02-06 12:35:04.162] boot - 20495 INFO [Timer-0] --- EurekaInstanceDiscovery: Received instance list for app: pdf-creator-service = 1
[2015-02-06 12:35:04.162] boot - 20495 INFO [Timer-0] --- InstanceObservable: Retrieved hosts from InstanceDiscovery: 1
[2015-02-06 12:35:04.162] boot - 20495 INFO [Timer-0] --- InstanceObservable: Found hosts that have been previously terminated: 0
[2015-02-06 12:35:04.162] boot - 20495 INFO [Timer-0] --- InstanceObservable: Hosts up:1, hosts down: 0
[2015-02-06 12:36:04.162] boot - 20495 INFO [Timer-0] --- EurekaInstanceDiscovery: Fetching instance list for apps: [pdf-creator-service]
[2015-02-06 12:36:04.162] boot - 20495 INFO [Timer-0] --- EurekaInstanceDiscovery: Fetching instances for app: pdf-creator-service
[2015-02-06 12:36:04.162] boot - 20495 INFO [Timer-0] --- EurekaInstanceDiscovery: Received instance list for app: pdf-creator-service = 1
[2015-02-06 12:36:04.162] boot - 20495 INFO [Timer-0] --- InstanceObservable: Retrieved hosts from InstanceDiscovery: 1
[2015-02-06 12:36:04.162] boot - 20495 INFO [Timer-0] --- InstanceObservable: Found hosts that have been previously terminated: 0
[2015-02-06 12:36:04.162] boot - 20495 INFO [Timer-0] --- InstanceObservable: Hosts up:1, hosts down: 0
L'appel de hystrix.stream à partir d'une seule instance fonctionne, par exemple curl http://localhost:8885/hystrix.stream
Retour:
data: {"type":"HystrixCommand","name":"post","group":"PdfController","currentTime":1423223614259,"isCircuitBreakerOpen":false,"errorPercentage":0,"errorCount":0,"requestCount":0,"rollingCountCollapsedRequests":0,"rollingCountExceptionsThrown":0,"rollingCountFailure":0,"rollingCountFallbackFailure":0,"rollingCountFallbackRejection":0,"rollingCountFallbackSuccess":0,"rollingCountResponsesFromCache":0,"rollingCountSemaphoreRejected":0,"rollingCountShortCircuited":0,"rollingCountSuccess":0,"rollingCountThreadPoolRejected":0,"rollingCountTimeout":0,"currentConcurrentExecutionCount":0,"latencyExecute_mean":0,"latencyExecute":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"latencyTotal_mean":0,"latencyTotal":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"propertyValue_circuitBreakerRequestVolumeThreshold":20,"propertyValue_circuitBreakerSleepWindowInMilliseconds":5000,"propertyValue_circuitBreakerErrorThresholdPercentage":50,"propertyValue_circuitBreakerForceOpen":false,"propertyValue_circuitBreakerForceClosed":false,"propertyValue_circuitBreakerEnabled":true,"propertyValue_executionIsolationStrategy":"THREAD","propertyValue_executionIsolationThreadTimeoutInMilliseconds":8000,"propertyValue_executionIsolationThreadInterruptOnTimeout":true,"propertyValue_executionIsolationThreadPoolKeyOverride":null,"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"propertyValue_requestCacheEnabled":true,"propertyValue_requestLogEnabled":true,"reportingHosts":1}
data: {"type":"HystrixCommand","name":"generate","group":"WkHtmlToPdfGenerator","currentTime":1423223614259,"isCircuitBreakerOpen":false,"errorPercentage":0,"errorCount":0,"requestCount":0,"rollingCountCollapsedRequests":0,"rollingCountExceptionsThrown":0,"rollingCountFailure":0,"rollingCountFallbackFailure":0,"rollingCountFallbackRejection":0,"rollingCountFallbackSuccess":0,"rollingCountResponsesFromCache":0,"rollingCountSemaphoreRejected":0,"rollingCountShortCircuited":0,"rollingCountSuccess":0,"rollingCountThreadPoolRejected":0,"rollingCountTimeout":0,"currentConcurrentExecutionCount":0,"latencyExecute_mean":0,"latencyExecute":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"latencyTotal_mean":0,"latencyTotal":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"propertyValue_circuitBreakerRequestVolumeThreshold":20,"propertyValue_circuitBreakerSleepWindowInMilliseconds":5000,"propertyValue_circuitBreakerErrorThresholdPercentage":50,"propertyValue_circuitBreakerForceOpen":false,"propertyValue_circuitBreakerForceClosed":false,"propertyValue_circuitBreakerEnabled":true,"propertyValue_executionIsolationStrategy":"THREAD","propertyValue_executionIsolationThreadTimeoutInMilliseconds":8000,"propertyValue_executionIsolationThreadInterruptOnTimeout":true,"propertyValue_executionIsolationThreadPoolKeyOverride":null,"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"propertyValue_requestCacheEnabled":true,"propertyValue_requestLogEnabled":true,"reportingHosts":1}
data: {"type":"HystrixThreadPool","name":"PdfController","currentTime":1423223614259,"currentActiveCount":0,"currentCompletedTaskCount":4,"currentCorePoolSize":10,"currentLargestPoolSize":4,"currentMaximumPoolSize":10,"currentPoolSize":4,"currentQueueSize":0,"currentTaskCount":4,"rollingCountThreadsExecuted":0,"rollingMaxActiveThreads":0,"propertyValue_queueSizeRejectionThreshold":5,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"reportingHosts":1}
Mais quand j'attache turbine.stream au tableau de bord hystrix, je ne reçois rien. Les journaux disent:
[2015-02-06 12:42:48.922] boot - 24816 INFO [Timer-0] --- EurekaInstanceDiscovery: Received instance list for app: pdf-creator-service = 1
[2015-02-06 12:42:48.922] boot - 24816 INFO [Timer-0] --- InstanceObservable: Retrieved hosts from InstanceDiscovery: 1
[2015-02-06 12:42:48.922] boot - 24816 INFO [Timer-0] --- InstanceObservable: Found hosts that have been previously terminated: 0
[2015-02-06 12:42:48.922] boot - 24816 INFO [Timer-0] --- InstanceObservable: Hosts up:1, hosts down: 0
[2015-02-06 12:43:26.237] boot - 24816 INFO [XNIO-2 task-4] --- TurbineStreamServlet: FilterCriteria: []
[2015-02-06 12:43:26.237] boot - 24816 INFO [XNIO-2 task-4] --- TurbineStreamServlet: StatsType filters: []
[2015-02-06 12:43:26.237] boot - 24816 INFO [XNIO-2 task-4] --- TurbineStreamingConnection: Relevance config: []
[2015-02-06 12:43:26.237] boot - 24816 INFO [XNIO-2 task-4] --- TurbineStreamingConnection: Relevance metrics config: {}
[2015-02-06 12:43:26.237] boot - 24816 INFO [XNIO-2 task-4] --- ClusterMonitor: Registering event handler for cluster monitor: StreamingHandler_f1308dda-58c5-47a5-b1e2-5a0bea32226b
[2015-02-06 12:43:26.237] boot - 24816 INFO [XNIO-2 task-4] --- TurbineDataDispatcher:
Just added and starting handler Tuple: StreamingHandler_f1308dda-58c5-47a5-b1e2-5a0bea32226b
[2015-02-06 12:43:26.238] boot - 24816 INFO [XNIO-2 task-4] --- AggDataFromCluster: Per handler dispacher started for: StreamingHandler_f1308dda-58c5-47a5-b1e2-5a0bea32226b
[2015-02-06 12:43:26.238] boot - 24816 INFO [XNIO-2 task-4] --- ClusterMonitor: All event handlers for cluster monitor: [StreamingHandler_637572ab-acda-4bf4-81cd-6a658adb73eb, StreamingHandler_f1308dda-58c5-47a5-b1e2-5a0bea32226b, StaticListener_For_Aggregator, StreamingHandler_5ec12ee8-3fcd-4a6f-9006-d2a6ecc309d0, StreamingHandler_72d7b9e2-ad98-42a0-9ac3-abe4aa57cc7a]
[2015-02-06 12:43:26.238] boot - 24816 INFO [XNIO-2 task-4] --- ClusterMonitor: Starting up the cluster monitor for default_agg
Si je fais curl http://localhost:8989/turbine.stream
Je reçois seulement:
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223006935}
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223010935}
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223013936}
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223017936}
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223020937}
: ping
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223024937}
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223028938}
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223032938}
: ping
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223036938}
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223039939}
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223043939}
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223046940}
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223050940}
: ping
: ping
data: {"reportingHostsLast10Seconds":0,"name":"meta","type":"meta","timestamp":1423223054941}
Ma question est: ai-je raté quelque chose lors de sa configuration et de son fonctionnement? Auparavant, j'essayais de gérer cela en ayant eureka et turbine server en une seule instance, mais dans ce cas, turbine ne pouvait même pas trouver l'application enregistrée dans eureka en utilisant le nom d'application approprié. J'ai fait des progrès après avoir divisé eureka et turbine, mais cela ne fonctionne toujours pas correctement.
Je serai reconnaissant pour toute suggestion. Si vous avez besoin de plus d'informations, faites le moi savoir, je pourrais manquer quelque chose d'important.
Suite à la suggestion de Dave, j'ai appliqué de petits changements dans application.yml
fichier de turbine-server
. Maintenant, le fichier ne contient que:
info:
component: Turbine
turbine:
appConfig: pdf-creator-service
clusterNameExpression: 'default'
server:
port: 8989
management:
port: 8990
eureka:
instance:
leaseRenewalIntervalInSeconds: 10
client:
serviceUrl:
defaultZone: ${vcap.services.${PREFIX:}eureka.credentials.uri:http://user:password@localhost:8761}/eureka/
Mais cela ne fait pas fonctionner turbine.stream. Une fois que turbine-server a eu connaissance d'un client enregistré sur le serveur eureka, il échoue avec une exception donnée:
[2015-02-09 21:25:03.516] boot - 4808 INFO [Timer-0] --- EurekaInstanceDiscovery: Fetching instance list for apps: [pdf-creator-service]
[2015-02-09 21:25:03.516] boot - 4808 INFO [Timer-0] --- EurekaInstanceDiscovery: Fetching instances for app: pdf-creator-service
[2015-02-09 21:25:03.516] boot - 4808 INFO [Timer-0] --- EurekaInstanceDiscovery: Received instance list for app: pdf-creator-service = 1
[2015-02-09 21:25:03.520] boot - 4808 ERROR [Timer-0] --- EurekaInstanceDiscovery: Failed to fetch instances for app: pdf-creator-service, retrying once more
org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Property or field 'default' cannot be found on object of type 'com.netflix.appinfo.InstanceInfo' - maybe not public?
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.Java:226)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.Java:93)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.Java:81)
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.Java:120)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.Java:242)
at org.springframework.cloud.netflix.turbine.EurekaInstanceDiscovery.getClusterName(EurekaInstanceDiscovery.Java:183)
at org.springframework.cloud.netflix.turbine.EurekaInstanceDiscovery.marshallInstanceInfo(EurekaInstanceDiscovery.Java:141)
at org.springframework.cloud.netflix.turbine.EurekaInstanceDiscovery.getInstancesForApp(EurekaInstanceDiscovery.Java:123)
at org.springframework.cloud.netflix.turbine.EurekaInstanceDiscovery.getInstanceList(EurekaInstanceDiscovery.Java:88)
at com.netflix.turbine.discovery.InstanceObservable.getInstanceList(InstanceObservable.Java:327)
at com.netflix.turbine.discovery.InstanceObservable.access$500(InstanceObservable.Java:66)
at com.netflix.turbine.discovery.InstanceObservable$1.run(InstanceObservable.Java:258)
at Java.util.TimerThread.mainLoop(Timer.Java:555)
at Java.util.TimerThread.run(Timer.Java:505)
J'ai essayé d'écrire 'default'
ainsi que default
, mais le résultat est malheureusement le même.
Merci à Dave Syer d'avoir trouvé la bonne solution. Fondamentalement, ce qui a fait l'affaire était d'ajouter:
turbine:
clusterNameExpression: new String("default")
à application.yml
fichier dans l'instance d'application du serveur turbine. Cela peut sembler bizarre, je ne pensais pas que cela fonctionnerait, mais c'est le cas. Maintenant, lorsque j'appelle mon application client hystrix, j'obtiens les informations appropriées dans hystrix.stream
qui est desservi par cette application et dans turbine.stream
du serveur Turbine également. Mon actuel application.yml
dans le serveur de turbine se présente comme suit:
info:
component: Turbine
turbine:
clusterNameExpression: new String("default")
appConfig: pdf-creator-service
server:
port: 8989
management:
port: 8990
eureka:
instance:
leaseRenewalIntervalInSeconds: 10
client:
serviceUrl:
defaultZone: ${vcap.services.${PREFIX:}eureka.credentials.uri:http://user:password@localhost:8761}/eureka/
turbine.appConfig
contient les informations sur les clients hystrix (par leur ID). Pour ajouter un autre client à votre serveur turbine, vous devrez simplement mettre un autre identifiant d'instance, séparé par le coma du précédent. Et c'est tout le monde :)
Cela fonctionne pour moi si j'ajoute une configuration pour le cluster, par exemple.
turbine:
appConfig: customers,stores
clusterNameExpression: new String('default')
Turbine doit savoir comment construire le nom du "cluster" (une clé d'agrégation pour des ensembles d'applications). La valeur par défaut est d'utiliser le nom de l'application, donc si vous ne définissez pas le clusterNameExpression
, vous devez utiliser un paramètre de requête dans l'URL du flux, par exemple /turbine.stream?cluster=CUSTOMERS
(nom d'application en majuscule).
Dans mon cas, j'avais: 7979/mock.stream comme client pour tester l'application turbine. La turbine vérifie l'heure de l'événement (timeOfEvent) afin de n'afficher que les événements de date et d'heure actuels.
Il peut être désactivé à l'aide de
turbine.InstanceMonitor.eventStream.skipLineLogic.enabled = false