I was finally able to get this to work…sort of! It still does not work with Spring Cloud Eureka by itself and I had to fall back to the SpringCloudHttpBackupCommandRouter to make it work. And even there I ran into a few issues:
Issue 1:
The SpringCloudHttpBackupCommandRouter uses RestTemplate to make a request to the endpoint defined in application.yml:
fallback-url: /message-routing-information
However, due to our corporate policy, all HTTP endpoints have to be secured with JWT tokens and I had to pass a JWT token to the above endpoint to make it work.
I had to create a Http Request interceptor in order to insert the token for every call:
public class RestSecurityInterceptor implements ClientHttpRequestInterceptor {
String authToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1OTUxMzM3ODcsImV4cCI6MTYyNjY2OTc4NywiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsInJvbGVzIjpbIkFOT05ZTU9VUyJdLCJhdXRoZW50aWNhdGVkIjp0cnVlfQ.iHODq9RKtosejJgh6RDyKzn18a_OuWJTKGA1RYxPQ0Q";
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("User-Agent", "resttemplate"); //Set the header for each request
headers.set("Authorization", "Bearer "+authToken );
return execution.execute(request, body);
}
}
and then add this interceptor to resttemplate:
@Bean
public CommandRouter springCloudHttpBackupCommandRouter(
DiscoveryClient discoveryClient,
RestTemplate restTemplate,
Registration localServiceInstance,
@Value("${axon.distributed.spring-cloud.fallback-url}")
String messageRoutingInformationEndpoint) {
restTemplate.setInterceptors(Collections.singletonList(new RestSecurityInterceptor()));
return SpringCloudHttpBackupCommandRouter.builder()
.discoveryClient(discoveryClient)
.routingStrategy(new AnnotationRoutingStrategy())
.enforceHttpDiscovery()
.restTemplate(restTemplate)
.localServiceInstance(localServiceInstance)
.messageRoutingInformationEndpoint(messageRoutingInformationEndpoint)
.build();
}
This solved the problem from an Axon perspective, but there was another issue…
Issue 2:
Since this was a POC, I was running the Axon services and Spring Cloud Eureka on my localhost. However, sine most of us are working remotely due to Covid, I was logged into the company VPN. I noticed that once my Axon services started up and registered with Eureka, the IP address for localhost was shown as 192.xx.xx.xx. This was the IP assigned to my Ethernet interface. I noticed there was another IP assigned to the VPN tunnel: 10.xx.xx.xx. Due to strict VPN policy, none of these IP addresses worked when trying to hit my Axon services using these IP addresses. The ONLY address that worked was either “localhost” or “127.0.0.1”.
However, in the Eureka console, the health check and info endpoints for my Axon services was listed as 192.xx.xx.xx:8080 … Since SpringCloudHttpBackupCommandRouter uses Eureka to get the routing information for the Axon services, it was using RestTemplate with a URL like: http://192.xx.xx.xx:8080/message-routing-information in order to retrieve the Command routing information. This was obviously not going to work with my VPN setup…
To get around this, I used the following Eureka configuration in application.yml:
server:
port: 8081
eureka:
instance:
lease-renewal-interval-in-seconds: 1
lease-expiration-duration-in-seconds: 2
health-check-url: http://localhost:8081/actuator/health/
prefer-ip-address: true
status-page-url: http://localhost:8081/actuator/info/
ip-address: 127.0.0.1
hostname: localhost
instance-id: localhost:${server.port}
client:
serviceUrl:
defaultZone: http://eureka:123456@localhost:8761/eureka/
healthcheck:
enabled: true
enabled: true
use-dns-for-fetching-service-urls: false
on-demand-update-status-change: true
After making the above config change for all my Axon services and restarting, I see that the services are correctly registered in Eureka as localhost:8081:
After this, everything worked as expected i.e. using SpringCloudHttpBackupCommandRouter but sadly, not with Spring Cloud Eureka.
Hope this info will help anyone tearing their hair out trying to make the Distributed Command Bus work with Eureka 