Lagom框架中的JDBC读侧支持详解
lagom Reactive Microservices for the JVM 项目地址: https://gitcode.com/gh_mirrors/la/lagom
概述
在Lagom微服务框架中,读侧(Read-Side)处理是实现CQRS(命令查询职责分离)架构的关键部分。本文将深入讲解Lagom如何通过JDBC支持关系型数据库的读侧处理,包括数据查询和事件处理机制。
读侧数据库查询
服务实现可以通过JDBC从关系型数据库中检索数据,Lagom提供了JdbcSession
来简化这一过程。
import com.lightbend.lagom.javadsl.persistence.jdbc.JdbcSession;
public class BlogServiceImpl implements BlogService {
private final JdbcSession jdbcSession;
@Inject
public BlogServiceImpl(JdbcSession jdbcSession) {
this.jdbcSession = jdbcSession;
}
@Override
public ServiceCall<NotUsed, List<PostSummary>> getPostSummaries() {
return request -> jdbcSession.withConnection(connection -> {
try (PreparedStatement ps = connection.prepareStatement(
"SELECT id, title FROM blogsummary ORDER BY created DESC LIMIT 10")) {
ResultSet rs = ps.executeQuery();
List<PostSummary> summaries = new ArrayList<>();
while (rs.next()) {
summaries.add(new PostSummary(rs.getString("id"), rs.getString("title")));
}
return summaries;
}
});
}
}
关键点说明:
JdbcSession
通过依赖注入获取,它管理连接池和事务withConnection
方法处理阻塞JDBC调用,返回CompletionStage
以异步执行- 所有JDBC操作都在专用线程池中执行,避免阻塞主线程
读侧更新处理
为了将持久化实体产生的事件转换为可查询的数据库表,我们需要实现ReadSideProcessor
。
基本结构
import com.lightbend.lagom.javadsl.persistence.jdbc.JdbcReadSide;
public class BlogEventProcessor extends ReadSideProcessor<BlogEvent> {
private final JdbcReadSide jdbcReadSide;
@Inject
public BlogEventProcessor(JdbcReadSide jdbcReadSide) {
this.jdbcReadSide = jdbcReadSide;
}
@Override
public ReadSideHandler<BlogEvent> buildHandler() {
// 构建处理器
}
@Override
public PSequence<AggregateEventTag<BlogEvent>> aggregateTags() {
// 返回事件标签
}
}
事件标签实现
@Override
public PSequence<AggregateEventTag<BlogEvent>> aggregateTags() {
return BlogEvent.TAG.allTags();
}
构建读侧处理器
使用JdbcReadSide.builder
创建处理器构建器:
JdbcReadSide.ReadSideHandlerBuilder<BlogEvent> builder =
jdbcReadSide.builder("blogsummaryoffset");
构建器会自动管理事务和读侧偏移量。偏移量存储在Lagom自动创建的read_side_offsets
表中,表结构如下:
CREATE TABLE read_side_offsets (
read_side_id VARCHAR(255),
tag VARCHAR(255),
sequence_offset bigint,
time_uuid_offset char(36),
PRIMARY KEY (read_side_id, tag)
)
全局准备回调
全局准备回调用于创建表和准备初始数据,它在整个集群中只执行一次。
private void createTable(Connection connection) throws SQLException {
try (PreparedStatement ps = connection.prepareStatement(
"CREATE TABLE IF NOT EXISTS blogsummary (" +
"id VARCHAR(64) PRIMARY KEY, " +
"title VARCHAR(256) NOT NULL, " +
"created TIMESTAMP NOT NULL)")) {
ps.execute();
}
}
注册全局回调:
builder.setGlobalPrepare(this::createTable);
注意事项:
- 回调必须是幂等的,可能被多次执行
- 失败后会使用指数退避策略重试
- 整个集群的读侧处理会等待它成功完成
事件处理器实现
处理PostAdded
事件的示例:
private void processPostAdded(Connection connection, PostAdded event) throws SQLException {
PreparedStatement ps = connection.prepareStatement(
"INSERT INTO blogsummary (id, title, created) VALUES (?, ?, ?)");
ps.setString(1, event.getPostId());
ps.setString(2, event.getContent().getTitle());
ps.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
ps.execute();
}
注册事件处理器:
builder.setEventHandler(PostAdded.class, this::processPostAdded);
完成处理器构建
return builder.build();
注册读侧处理器
最后需要在服务实现中注册处理器:
@Inject
public BlogServiceImpl(
PersistentEntityRegistry persistentEntityRegistry,
ReadSide readSide,
JdbcSession jdbcSession) {
this.persistentEntityRegistry = persistentEntityRegistry;
this.jdbcSession = jdbcSession;
// 注册事件处理器
readSide.register(BlogEventProcessor.class);
}
最佳实践
- 对于生产环境,考虑手动管理表结构而非使用全局回调
- 确保所有数据库操作都是幂等的
- 合理设计表结构以优化查询性能
- 考虑使用批处理提高事件处理效率
- 监控读侧处理延迟,确保及时性
通过以上机制,Lagom提供了完整的JDBC读侧支持,使开发者能够轻松实现CQRS架构中的查询部分,同时保证了数据的一致性和系统的可扩展性。
lagom Reactive Microservices for the JVM 项目地址: https://gitcode.com/gh_mirrors/la/lagom
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考