Compatibilty axon-springcloud-extension with org.springframework.cloud

Hi Tino,

Did you already try something and run into problems? As part of the recent move to 4.7 and supporting Spring Boot 3, we did add a basic integration test. So I would think that those springcloud-extension and spring cloud version would work correctly. Testing all possible implementations is out of our scope, so please let us know if there are problems.

First of all, thank you very much for the quick answer.

I am using axon with spring cloud kubernetes and still have the issue that no bean of ‘org.springframework.cloud.client.serviceregistry.Registration’ could be found to build the SpringCloudCommandRouter.

I’m jumping back on this issue a bit late, so my apologies for that.

@Tino, I would like to reiterate a paragraph I shared on February 2022:

My opinion has not changed on the subject.
As it stands, the Spring Cloud Kubernetes solution requires a workaround for the required local Registration instance.
From the perspective of a pull request, that may be a dedicated auto-configuration class that (1) validates whether Spring Cloud Kubernetes is on the classpath and (2), based on that, constructs a simplified Registration instance of the SpringCloudCommandRouter to use.

So, moving back to this:

The answer is still yes.

Honestly, this pointer left my mind, as I assumed an issue and/or PR would be added to the Spring Cloud Extension repository.
So if you’re up for it, you’re free to provide such a PR, perhaps using @Wolfgangm’s sample even.

In all, I hope this clarifies the status of Axon Framework’s Spring Cloud Extension combined with Spring Cloud Kubernetes.

1 Like

Hello,

If anyone needs this is what we ended up doing in our project, this solution uses spring-cloud-starter-kubernetes-client if you use spring-cloud-starter-kubernetes-fabric8 the solution from @Wolfgangm will help you.

    @Bean
    @Profile("kubernetes")
    @Primary
    public Registration localRegistration(KubernetesClientPodUtils podUtils) {
        V1Pod currentPod = podUtils.currentPod().get();
        if (currentPod == null) {
            throw new IllegalStateException("No current pod found");
        }

        String podTemplateHash = currentPod.getMetadata().getLabels().get("pod-template-hash");
        String name = currentPod.getMetadata().getName();
        // -1 because the template hash is preceded with a '-'
        String serviceId = name.substring(0, name.indexOf(podTemplateHash) - 1);
        String instanceId = currentPod.getMetadata().getUid();
        String host = currentPod.getStatus().getPodIP();

        DefaultServiceInstance localServiceInstance = new DefaultServiceInstance(instanceId, serviceId, host,
            port, false);

        return new KubernetesPodRegistration(localServiceInstance);
    }

public static final class KubernetesPodRegistration implements Registration {
        private final ServiceInstance localServiceInstance;

        public KubernetesPodRegistration(ServiceInstance serviceInstance) {
            this.localServiceInstance = serviceInstance;
        }

       ...overrided methods just return the according fields from this.localServiceInstance
}

And in order for the Axon SpringCloudCommandRouter to update the membership we did the following:

@Slf4j
@Profile("kubernetes")
public class KubernetesCatalogWatch implements ApplicationEventPublisherAware {
    private final KubernetesInformerDiscoveryClient discoveryClient;
    private final AtomicReference<List<String>> catalogServicesState = new AtomicReference<>();
    private ApplicationEventPublisher publisher;

    public KubernetesCatalogWatch(KubernetesInformerDiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    @Scheduled(fixedDelayString = "${kubernetes.discovery.catalogServicesWatchDelay:30000}")
    public void catalogServicesWatch() {
        try {
            List<String> previousState = this.catalogServicesState.get();

            // not all pods participate in the service discovery. only those that have
            // endpoints.
            List<String> servicesNames = this.discoveryClient.getServices()
                .stream()
                .flatMap(serviceId ->
                    this.discoveryClient.getInstances(serviceId).stream().map(ServiceInstance::getHost))
                .collect(Collectors.toList());

            this.catalogServicesState.set(servicesNames);

            if (!servicesNames.equals(previousState)) {
                log.info("Received services update from Discovery client: {}", servicesNames);
                this.publisher.publishEvent(new HeartbeatEvent(this, servicesNames));
            }
        } catch (Exception e) {
            log.error("Error watching Kubernetes Services", e);
        }
    }
}

Hope this helps.

Best,
Bogdan

2 Likes