0

When trying to implement a DistributedCommandBus using Spring Cloud, I am getting the following error intermittently. I have reason to believe that there is some sort of race condition happening with the auto-configuration of my aggregate root class, its command handlers, and my configuration bean class.

org.axonframework.commandhandling.NoHandlerForCommandException: No node known to accept.

I am using Axon Version 3.3.5.

Here is my configurations class:

@Configuration
@AutoConfigureBefore(CustomerAggregate.class)
public class AxonConfig {
    @Value("${mongo.servers}")
    private String mongoUrl;

    @Value("${mongo.db}")
    private String mongoDbName;

    @Value("${axon.events.collection.name}")
    private String eventsCollectionName;

    @Value("${axon.snapshot.collection.name}")
    private String snapshotCollectionName;

    @Value("${axon.saga.collection.name}")
    private String sagaCollectionName;

    @Bean
    @Primary
    public CommandGateway commandGateway(@Qualifier("distributedBus") DistributedCommandBus commandBus) throws Exception {
        return new DefaultCommandGateway(commandBus, new IntervalRetryScheduler(Executors.newSingleThreadScheduledExecutor(), 1000, 10));
    }

    @Bean
    @Primary
    @Qualifier("springCloudRouter")
    public CommandRouter springCloudCommandRouter(DiscoveryClient client, Registration localServiceInstance) {
        return new SpringCloudCommandRouter(client, localServiceInstance, new AnnotationRoutingStrategy());
    }

    @Bean
    @Primary
    @Qualifier("springCloudConnector")
    public SpringHttpCommandBusConnector connector() {
        return new SpringHttpCommandBusConnector(new SimpleCommandBus(), new RestTemplate(), new JacksonSerializer());
    }

    @Bean
    @Primary
    @Qualifier("distributedBus")
    public DistributedCommandBus springCloudDistributedCommandBus(@Qualifier("springCloudRouter") CommandRouter router) {
        return new DistributedCommandBus(router, connector());
    }

    @Bean
    @Primary
    public AggregateFactory<CustomerAggregate> aggregateFactory(){
        return new GenericAggregateFactory<CustomerAggregate>(CustomerAggregate.class);
    }

    @Bean
    @Primary
    public EventCountSnapshotTriggerDefinition countSnapshotTriggerDefinition(){
        return new EventCountSnapshotTriggerDefinition(snapShotter(), 3);
    }

    @Bean
    @Primary
    public Snapshotter snapShotter(){
        return new AggregateSnapshotter(eventStore(), aggregateFactory());
    }

    @Bean
    @Primary
    public EventSourcingRepository<CustomerAggregate> customerAggregateRepository(){
        return new EventSourcingRepository<>(aggregateFactory(), eventStore(), countSnapshotTriggerDefinition());
    }

    @Bean(name = "axonMongoTemplate")
    public MongoTemplate axonMongoTemplate() {
        return new DefaultMongoTemplate(mongoClient(), mongoDbName)
            .withDomainEventsCollection(eventsCollectionName)
            .withSnapshotCollection(snapshotCollectionName)
            .withSagasCollection(sagaCollectionName);
    }

    @Bean
    public MongoClient mongoClient() {
        MongoFactory mongoFactory = new MongoFactory();
        mongoFactory.setMongoAddresses(Arrays.asList(new ServerAddress(mongoUrl)));
        return mongoFactory.createMongo();
    }

    @Bean
    @Primary
    public MongoEventStorageEngine engine() {
        return new MongoEventStorageEngine(new JacksonSerializer(), null, axonMongoTemplate(), new DocumentPerEventStorageStrategy());
    }

    @Bean
    @Primary
    public EventStore eventStore() {
        return new EmbeddedEventStore(engine());
    }
}

And here is my aggregate class with command handlers:

@Aggregate(repository = "customerAggregateRepository")
public class CustomerAggregate {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @AggregateIdentifier
    private String id;
    private String firstName;
    private String lastName;
    private String email;

    private CustomerAggregate() {}

    public String getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String getEmail() {
        return email;
    }

    @CommandHandler
    public CustomerAggregate(CreateCustomer cmd) {
        logger.debug("Received creation command: " + cmd.toString());
        apply(new CustomerCreated(cmd.getId(),cmd.getFirstName(),cmd.getLastName(), cmd.getEmail()));
    }

    @CommandHandler
    public void on(UpdateCustomer cmd) {
        logger.debug("Received update command: " + cmd.toString());
        apply(new CustomerUpdated(this.id,cmd.getFirstName(),cmd.getLastName(), cmd.getEmail()));
    }

    @CommandHandler
    public void on(UpdateCustomerEmail cmd) {
        logger.debug("Received update command for existing customer: " + cmd.toString());
        apply(new CustomerUpdated(cmd.getId(), this.firstName, this.lastName, cmd.getEmail()));
    }

    // Various event handlers...
}

Any help is much appreciated.

  • It's important to realize that the discovery mechanism takes a while to discover nodes. It can take up to 1 minute, actually. Also, there seems to be a lot of configuration. If you use Spring boot, consider using the distributed starter and let Axon autoconfigure things for you. – Allard Sep 13 '18 at 22:07
  • Do you mind sharing which implementation of Spring Cloud Discovery you are using? This might changes the answer I'd like to give on this issue. Apart from that, I think @allard has a valid point. Try to leverage the auto configuration provided by the framework as much as possible, that should simplify your life. – Steven Sep 14 '18 at 08:41
  • Allard, I thought that this may be the case so I tested many different scenarios, including a single instance of the application. In several of the tests I waited an extended period of time and confirmed that the service was registered with the eureka server I was using only to receive this message. You bring up a good point that I should try to use the auto configuration from the axon components. In fact, that could be my entire problem. – Charlie Thomas Sep 15 '18 at 12:59
  • Steven, the implementation I am using is Eureka. – Charlie Thomas Sep 15 '18 at 13:00

0 Answers0