我的Spring Boot应用程序通过RabbitMQ将事件发送到Timescale。 如果RabbitMQ出现故障,我需要知道如何保存事件。
详细信息:
RabbitMQ发布的消息是持久的。当消息代理发生故障时,事件不会被发布,并且我计划将这些事件存储在数据库中,并在事件发生时再次将它们发布到RabbitMQ。 欢迎任何解决方案和建议。
答案 0 :(得分:0)
正如Kayaman所建议的那样,如果保留消息至关重要,则应该使用可恢复,并发,高效并且最好是本地(在同一台计算机上)的数据库系统。
我满足这些需求的第一个想法是使用H2 Database Engine。 H2是一个关系数据库,使用纯Java内置,经过积极开发,并被证明具有生产价值。
要考虑的类似产品是Apache Derby。但是我听说过各种问题,尽管您应该研究其当前状况,但这可能意味着它不值得生产。
H2的关系部分可能无关紧要,因为您可能只需要一个表来跟踪您的消息流,以便稍后将其重新发送到RabbitMQ。至于本地化,高效,有弹性,可移植和并发的其他要求,H2非常适合。而且,如果您需要添加其他表,H2可以作为完全关系数据库系统使用。
H2可以通过以下两种模式之一启动:
没有更多信息很难说哪种模式适合您的需求。如果要附加外部监视工具,则可能需要服务器模式。如果您想要简单而精益的嵌入式模式。
您的Java应用通过包含的JDBC driver连接到H2。
答案 1 :(得分:-1)
进一步说明一下:确实不需要使用数据库进行持久缓冲。
编辑:另外,无法将消息发送到RabbitMQ的原因很可能是网络连接丢失。在这种情况下,大多数DBMS几乎没有用。 编辑结束。
package io.mahlberg.stackoverflow.questions.objpersistencedemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
public class PersistenceDemo {
/*
* A decent default number of objects to be buffered.
*/
private static long COUNT = 10000L;
/*
* Default filename
*/
private static final String OBJ_DAT = "./obj.dat";
private static FileOutputStream fos;
private static ObjectOutputStream oos;
private static FileInputStream fis;
private static ObjectInputStream ois;
private static File dat;
private static Object lock;
public static void main(String[] args) throws InterruptedException, IOException {
// Get the actual number of counts
if (args[0] != null) {
COUNT = Long.parseLong(args[0]);
}
// Initialize out lock
lock = new Object();
// Ensure the datafile exists.
dat = new File(OBJ_DAT);
dat.createNewFile();
// Initialize our streams.
try {
fos = new FileOutputStream(dat);
} catch (Exception e1) {
e1.printStackTrace();
System.exit(1);
}
oos = new ObjectOutputStream(fos);
// Define the writer thread.
Thread writer = new Thread(new Runnable() {
public void run() {
Data obj;
// Make sure we have the behaviour of the queue.
synchronized (lock) {
for (int i = 0; i < COUNT; i++) {
obj = new Data(String.format("Obj-%d", i), new Date());
try {
oos.writeObject(obj);
oos.flush();
fos.flush();
// Notify the reader...
lock.notify();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
// ... and wait until the reader is finished.
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
// We need to notify the reader one last time for the last
// Object we put into the stream.
lock.notify();
}
}
});
// Initialize the streams used by reader.
fis = new FileInputStream(dat);
ois = new ObjectInputStream(fis);
Thread reader = new Thread(new Runnable() {
public void run() {
Data obj;
while (true) {
synchronized (lock) {
try {
obj = (Data) ois.readObject();
// Notify writer we are finished with reading the latest entry...
lock.notify();
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
break;
}
try {
// ...and wait till writer is done writing.
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
}
});
// For doing a rough performance measurement.
Instant start = Instant.now();
writer.start();
reader.start();
// Wait till both threads are done.
writer.join();
reader.join();
Instant end = Instant.now();
Duration timeElapsed = Duration.between(start, end);
System.out.format("Took %sms for %d objects\n", timeElapsed.toMillis(), COUNT);
System.out.format("Avg: %.3fms/object\n", ((double) timeElapsed.toMillis() / COUNT));
// Cleanup
oos.close();
fos.close();
ois.close();
fis.close();
}
}
基本上,我们使用synchronize
,notify
和wait
用文件模拟FIFO缓冲区。
请注意,为了简洁和易读起见,我采取了一些捷径,但我想您已经明白了。读者应不时检查文件大小(多久一次取决于数据大小),然后截断文件,错误处理几乎不存在。我从该类和一个数据类创建了一个jar,下面是一些示例结果:
$ for i in {1..10}; do java -jar target/objpersistencedemo-0.0.1-SNAPSHOT.jar 20000; done
20000
Took 1470ms for 20000 objects
Avg: 0,074ms/object
20000
Took 1510ms for 20000 objects
Avg: 0,076ms/object
20000
Took 1614ms for 20000 objects
Avg: 0,081ms/object
20000
Took 1600ms for 20000 objects
Avg: 0,080ms/object
20000
Took 1626ms for 20000 objects
Avg: 0,081ms/object
20000
Took 1620ms for 20000 objects
Avg: 0,081ms/object
20000
Took 1489ms for 20000 objects
Avg: 0,074ms/object
20000
Took 1604ms for 20000 objects
Avg: 0,080ms/object
20000
Took 1632ms for 20000 objects
Avg: 0,082ms/object
20000
Took 1564ms for 20000 objects
Avg: 0,078ms/object
请注意,这些值是用于编写和阅读内容的。我猜每个对象少于0.1ms比从RDBMS进行写入和随后读取要快得多。
如果让读者将消息发送到RabbitMQ实例并添加一些截断和退避逻辑,则可以基本上确保所有事件都在缓冲区文件中或写入RabbitMQ。