Vertx event bus can not send message to different verticle - event-bus

I am pretty new to Vertx, but I am pretty interested in testing its integration with Spring. I used Spring boot to boost the project, and deployed two verticles. I want them to communicate with each other using event bus, but failed. This is what I did:
In Main application:
#SpringBootApplication
public class MySpringVertxApplication {
#Autowired
MyRestAPIServer myRestAPIServer;
#Autowired
MyRestAPIVerticle MyRestAPIVerticle;
public static void main(String[] args) {
SpringApplication.run(MySpringVertxApplication.class, args);
}
#PostConstruct
public void deployVerticles(){
System.out.println("deploying...");
Vertx.vertx().deployVerticle(MyRestAPIVerticle);
Vertx.vertx().deployVerticle(myRestAPIServer);
}
}
In APIVerticle:
#Component
public class MyRestAPIVerticle extends AbstractVerticle {
public static final String ALL_ACCOUNT_LISTING = "com.example.ALL_ACCOUNT_LISTING";
#Autowired
AccountService accountService;
EventBus eventBus;
#Override
public void start() throws Exception {
super.start();
eventBus = vertx.eventBus();
MessageConsumer<String> consumer = eventBus.consumer(MyRestAPIVerticle.ALL_ACCOUNT_LISTING);
consumer.handler(message -> {
System.out.println("I have received a message: " + message.body());
message.reply("Pretty Good");
});
consumer.completionHandler(res -> {
if (res.succeeded()) {
System.out.println("The handler registration has reached all nodes");
} else {
System.out.println("Registration failed!");
}
});
}
}
Finally ServerVerticle:
#Service
public class MyRestAPIServer extends AbstractVerticle {
HttpServer server;
HttpServerResponse response;
EventBus eventBus;
#Override
public void start() throws Exception {
server = vertx.createHttpServer();
Router router = Router.router(vertx);
eventBus = vertx.eventBus();
router.route("/page1").handler(rc -> {
response = rc.response();
response.setChunked(true);
eventBus.send(MyRestAPIVerticle.ALL_ACCOUNT_LISTING,
"Yay! Someone kicked a ball",
ar->{
if(ar.succeeded()){
System.out.println("Response is :"+ar.result().body());
}
}
);
});
server.requestHandler(router::accept).listen(9999);
}
But after I started it, and visit to /page1, message can not be sent from ServerVerticle to APIVerticle at all. If I move event bus consumer into same verticle as Sender, then event can be received.
Are there anything wrong here in sending message between two verticles? How can I make it work?
Thanks in advance.

You deployed them in separate vertx instance:
Vertx.vertx().deployVerticle(MyRestAPIVerticle);
Vertx.vertx().deployVerticle(myRestAPIServer);
Try this:
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(MyRestAPIVerticle);
vertx.deployVerticle(myRestAPIServer);

Vertx event bus is not shared across different Vertx instances like how you are trying ( But clustered Vert.x applications can do it). In your case change it to use a single Vert.x instance like below in your MySpringVertxApplication.
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(MyRestAPIVerticle.class.getName());
vertx.deployVerticle(MyRestAPIServer.class.getName());

Related

How to get Azure Service Bus message id when sending a message to a topic using Spring Integration

After I send a message to a topic on Azure Service Bus using Spring Integration I would like to get the message id Azure generates. I can do this using JMS. Is there a way to do this using Spring Integration? The code I'm working with:
#Service
public class ServiceBusDemo {
private static final String OUTPUT_CHANNEL = "topic.output";
private static final String TOPIC_NAME = "my_topic";
#Autowired
TopicOutboundGateway messagingGateway;
public String send(String message) {
// How can I get the Azure message id after sending here?
this.messagingGateway.send(message);
return message;
}
#Bean
#ServiceActivator(inputChannel = OUTPUT_CHANNEL)
public MessageHandler topicMessageSender(ServiceBusTopicOperation topicOperation) {
DefaultMessageHandler handler = new DefaultMessageHandler(TOPIC_NAME, topicOperation);
handler.setSendCallback(new ListenableFutureCallback<>() {
#Override
public void onSuccess(Void result) {
System.out.println("Message was sent successfully to service bus.");
}
#Override
public void onFailure(Throwable ex) {
System.out.println("There was an error sending the message to service bus.");
}
});
return handler;
}
#MessagingGateway(defaultRequestChannel = OUTPUT_CHANNEL)
public interface TopicOutboundGateway {
void send(String text);
}
}
You could use ChannelInterceptor to get message headers:
public class CustomChannelInterceptor implements ChannelInterceptor {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
//key of the message-id header is not stable, you should add logic here to check which header key should be used here.
//ref: https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/spring/azure-spring-cloud-starter-servicebus#support-for-service-bus-message-headers-and-properties
String messageId = message.getHeaders().get("message-id-header-key").toString();
return ChannelInterceptor.super.preSend(message, channel);
}
}
Then in the configuration, set this interceptor to your channel
#Bean(name = OUTPUT_CHANNEL)
public BroadcastCapableChannel pubSubChannel() {
PublishSubscribeChannel channel = new PublishSubscribeChannel();
channel.setInterceptors(Arrays.asList(new CustomChannelInterceptor()));
return channel;
}

Spring Integration - #InboundChannelAdapter polling

I am new to Spring Integration. We are creating our application using Spring Integration Annotations.
I have configured an #InboundChannelAdapter with poller fixed delay of 5 seconds. But the problem is as soon as I start my application on weblogic, the adapter starts polling and hits endpoint with practically no message.
We need to call a rest service and then trigger this adapter.
Is there a way to implement the same?
TIA!
Set the autoStartup property to false and use a control bus to start/stop it.
#SpringBootApplication
#IntegrationComponentScan
public class So59469573Application {
public static void main(String[] args) {
SpringApplication.run(So59469573Application.class, args);
}
}
#Component
class Integration {
#Autowired
private ApplicationContext context;
#InboundChannelAdapter(channel = "channel", autoStartup = "false",
poller = #Poller(fixedDelay = "5000"))
public String foo() {
return "foo";
}
#ServiceActivator(inputChannel = "channel")
public void handle(String in) {
System.out.println(in);
}
#ServiceActivator(inputChannel = "controlChannel")
#Bean
public ExpressionControlBusFactoryBean controlBus() {
return new ExpressionControlBusFactoryBean();
}
}
#MessagingGateway(defaultRequestChannel = "controlChannel")
interface Control {
void send(String control);
}
#RestController
class Rest {
#Autowired
Control control;
#PostMapping("/foo/{command}")
public void trigger(#PathVariable String command) {
if ("start".equals(command)) {
control.send("#'integration.foo.inboundChannelAdapter'.start()");
}
}
}

How to implement distributed lock around poller in Spring Integration using ZooKeeper

Spring Integration has ZooKeeper support as documented in https://docs.spring.io/spring-integration/reference/html/zookeeper.html
However this document is so vague.
It suggests adding below bean but does not give details on how to start/stop a poller when the node is granted leadership.
#Bean
public LeaderInitiatorFactoryBean leaderInitiator(CuratorFramework client) {
return new LeaderInitiatorFactoryBean()
.setClient(client)
.setPath("/siTest/")
.setRole("cluster");
}
Do we have any example on how to ensure below poller is run only once in a cluster at any time using zookeeper?
#Component
public class EventsPoller {
public void pullEvents() {
//pull events should be run by only one node in the cluster at any time
}
}
The LeaderInitiator emits an OnGrantedEvent and OnRevokedEvent, when it becomes leader and its leadership is revoked.
See https://docs.spring.io/spring-integration/reference/html/messaging-endpoints-chapter.html#endpoint-roles and the next https://docs.spring.io/spring-integration/reference/html/messaging-endpoints-chapter.html#leadership-event-handling for more info about those events handling and how it affects your components in the particular role.
Although I agree that Zookkeper chapter must have some link to that SmartLifecycleRoleController chapter. Feel free to raise a JIRA on the matter and contribution is welcome!
UPDATE
This is what I did in our test:
#RunWith(SpringRunner.class)
#DirtiesContext
public class LeaderInitiatorFactoryBeanTests extends ZookeeperTestSupport {
private static CuratorFramework client;
#Autowired
private PollableChannel stringsChannel;
#BeforeClass
public static void getClient() throws Exception {
client = createNewClient();
}
#AfterClass
public static void closeClient() {
if (client != null) {
client.close();
}
}
#Test
public void test() {
assertNotNull(this.stringsChannel.receive(10_000));
}
#Configuration
#EnableIntegration
public static class Config {
#Bean
public LeaderInitiatorFactoryBean leaderInitiator(CuratorFramework client) {
return new LeaderInitiatorFactoryBean()
.setClient(client)
.setPath("/siTest/")
.setRole("foo");
}
#Bean
public CuratorFramework client() {
return LeaderInitiatorFactoryBeanTests.client;
}
#Bean
#InboundChannelAdapter(channel = "stringsChannel", autoStartup = "false", poller = #Poller(fixedDelay = "100"))
#Role("foo")
public Supplier<String> inboundChannelAdapter() {
return () -> "foo";
}
#Bean
public PollableChannel stringsChannel() {
return new QueueChannel();
}
}
}
And I have in logs something like this:
2018-12-14 10:12:33,542 DEBUG [Curator-LeaderSelector-0] [org.springframework.integration.support.SmartLifecycleRoleController] - Starting [leaderInitiatorFactoryBeanTests.Config.inboundChannelAdapter.inboundChannelAdapter] in role foo
2018-12-14 10:12:33,578 DEBUG [Curator-LeaderSelector-0] [org.springframework.integration.support.SmartLifecycleRoleController] - Stopping [leaderInitiatorFactoryBeanTests.Config.inboundChannelAdapter.inboundChannelAdapter] in role foo

Spring Integration Kafka Manual Acknowledgment

I'm having issues using manual acknowledgements with the KafkaTopicOffsetManager. When acknowledge() is called, the topic begins to get spammed repeatedly. Kafka has log.cleaner.enable set to true and the topic is using cleanup.policy=compact. Thanks for any help.
Config:
#Bean
public ZookeeperConfiguration zookeeperConfiguration() {
ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration(kafkaConfig.getZookeeperAddress());
zookeeperConfiguration.setClientId("clientId");
return zookeeperConfiguration;
}
#Bean
public ConnectionFactory connectionFactory() {
return new DefaultConnectionFactory(zookeeperConfiguration());
}
#Bean
public TestMessageHandler messageListener() {
return new TestMessageHandler();
}
#Bean
public OffsetManager offsetManager() {
ZookeeperConnect zookeeperConnect = new ZookeeperConnect(kafkaConfig.getZookeeperAddress());
OffsetManager offsetManager = new KafkaTopicOffsetManager(zookeeperConnect, kafkaConfig.getTopic() + "_OFFSET");
return offsetManager;
}
#Bean
public KafkaMessageListenerContainer kafkaMessageListenerContainer() {
KafkaMessageListenerContainer kafkaMessageListenerContainer = new KafkaMessageListenerContainer(connectionFactory(), kafkaConfig.getTopic());
kafkaMessageListenerContainer.setMessageListener(messageListener());
kafkaMessageListenerContainer.setOffsetManager(offsetManager());
return kafkaMessageListenerContainer;
}
Listener:
public class TestMessageHandler implements AcknowledgingMessageListener {
private static final Logger logger = LoggerFactory.getLogger(TestMessageHandler.class);
#Override
public void onMessage(KafkaMessage message, Acknowledgment acknowledgment) {
logger.info(message.toString());
acknowledgment.acknowledge();
}
}
The KafkaTopicOffsetManager needs its own topic to maintain the offset of the actual topic being consumed.
If you don't want to deal with decoding the message payload yourself (its painful in my opinion), extend listener from abstract class AbstractDecodingAcknowledgingMessageListener and provide org.springframework.integration.kafka.serializer.common.StringDecoder as the decoder.
public class TestMessageHandlerDecoding extends AbstractDecodingAcknowledgingMessageListener {
public TestMessageHandlerDecoding(Decoder keyDecoder, Decoder payloadDecoder) {
super(keyDecoder, payloadDecoder);
}
#Override
public void doOnMessage(Object key, Object payload, KafkaMessageMetadata metadata, Acknowledgment acknowledgment) {
LOGGER.info("payload={}",payload);
}

How to persist Entity in separate thread with JPA on J2EE App server?

I have a chat application that needs to store messages to DB. But connection with DB is a little bit slow, therefore it delays response to chat client.
Is it possible to persist Message entity in separate thread? What I'm actually need in: reduce delay before send-recieve message on client.
I try to do it, but it doen't work.
Dao object:
#Stateless
public class MessagesDAO {
#PersistenceContext(type= PersistenceContextType.EXTENDED)
private EntityManager entityManager;
private PersistenceThread persistenceThread = new PersistenceThread();
//another methods
public void addMessage(Message message) {
Thread thread = new Thread(persistenceThread);
persistenceThread.setMessage(message);
thread.start();
}
private class PersistenceThread implements Runnable {
private Message message;
public void setMessage(Message message) {
this.message = message;
}
public void run() {
entityManager.persist(message);
}
}
}
Interface service that calls DAO to persist new message and then return it to clients:
#Stateless
#Path("/messages")
#Produces("application/xml")
#Consumes("application/xml")
public class MessagesServiceImpl {
#EJB
private MessagesDAO messagesDAO;
#POST
#Broadcast(resumeOnBroadcast = true)
public Message postMessage(Message message) {
messagesDAO.addMessage(message);
return message;
}
#GET
#Path("/wait")
#Suspend(outputComments = false)
public Message waitForLastMessage() {
return null;
}
//another methods
}
Thanks.
Give the #Asynchronous annotation a try:
#Stateless
public class MessagesDAO {
#PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
#Asynchronous
public void addMessage(Message message) {
entityManager.persist(message);
}
}
Just bear in mind that it requires EJB 3.1.

Resources