为了提高系统吞吐率,也就是提高生产效率,核心观点如下,系统设计也是如此

在微服务或任何其他基于事件的架构(event-driven-architecture)中,在一些用例中,一个服务可能需要我们对他们自己的本地数据库进行修改,同时发布一个事件。然后,该事件会被其他服务所消费。为了拥有一个一致的软件系统,这两个动作必须以原子方式执行。这两个动作都需要成功完成,或者都不需要完成。不应该有其他方法。 电商网站的事件驱动型架构(event-driven-architecture)示例。此架构使网站能够对需求峰值期间的各种来源变动做出反应,而不会导致应用程序崩溃或过度预置资源:

解决这个问题的一个优雅方法是使用收件箱模式(Outbox Pattern)。它通过使用一个数据库表(如果你的服务使用关系型数据库),通常称为发件箱表,来存储这些事件。在这种情况下,你能够将事件的插入SQL语句纳入用例本地事务。另一个运行器可以定期检查收件箱表是否为空,并通过将事件发布到消息代理中来处理这些事件。社区已经实现了一个Java库,称之为JQueue,它使这种模式的实现更加容易。JQueue使用关系型数据库表实现了一个FIFO数据结构,所以目前,如果你的服务的数据库是关系型的,它就可以工作。

JQueue有两个模块,推送模块和运行器模块。要把一个事件(或任务)推入队列,你可以这样做:

JTxQueue.queue(/*a JDBC Data Source or a JDBC Connection */)
.push(
“{\”type\”: \”job_type1\”, \”event\”:{\”id\”: \”an id\”, \”value\”: \”\” }}”);

请注意,作为一个参数,你必须传递当前在你的本地事务中正在使用的数据源或连接。在这种情况下,如果你的事务被提交,推送也将被提交。如果任何事情失败了,一切都会被回滚。你推入队列的事件或任务可以是任何文本。然后,为了从队列中消费事件或任务,你必须写一些类似这样的东西:

JQueueRunner.runner(/* a JDBC DataSource */)
.executeAll(new Job() {
@Override
public void run(String data) {
//do something with data
}
});

上面的代码将在一个循环中逐一处理队列中的所有条目,直到它是空的。它将读取队列数据并调用Job接口的一个实例,你必须提供该实例来做你需要的任何数据。这可能是将一个消息推送到消息代理中,或者只是调用任何其他的外部服务API。你可以使用任何调度器库(比如Quartz)来安排JQueue运行器,以所需的频率来保持队列为空。

该运行器使用 “select for update skip locked “SQL语句,这是一些关系型数据库实现的一种新功能,用于(除其他外)在关系型表中实现队列。这是目前使JQueue在PostgreSQL v9.5+和MySQL 8.0+中工作的原因之一(JQueue对Oracle和MS SQL的支持即将到来,因为它们都支持跳过锁定功能)。

现在让我们来展示一些例子,说明你如何在本地事务中推送事件。假设你的服务创建了用户,当这种情况发生时,你需要发布NewUserEvent。 如果你的服务使用的是普通的JDBC,你可以做这样的事情:

Connection conn = connection();
try {
conn.setAutoCommit(false);
//your business logic first
final PreparedStatement st = conn.prepareStatement(
“insert into user(id, user_name, pwd, email) values(108, ‘user1′,’anyPassword’,’user1@dot.com’)”);
st.executeUpdate();

//then push an event
JTxQueue.queue(conn)
.push(new NewUserEvent(108, “user1”, “user1@dot.com”).toJson());

conn.commit();
} catch (SQLException | JQueueException e) {
try {
conn.rollback();
throw new RuntimeException(e);
} catch (SQLException e1) {
throw new RuntimeException(e1);
}
} finally {
try {
conn.setAutoCommit(true);
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}

}

而且,如果你的服务使用Spring,你可以这样做:

@RestController
@RequestMapping(“/api/users”)
public class UserController {
@Autowired
private UserRepository userRepository;

@Autowired
private DataSource dataSource;

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@Transactional
public User create(@RequestBody User user) throws SQLException {
//your business logic first
User u = userRepository.save(user);
//then push an event
JTxQueue.queue(dataSource)
.push(new NewUserEvent(u.id(), u.getUserName(), u.email()).toJson());
return u;
}
}

在上面所有的例子中,事务包裹了你的业务逻辑,加上推入队列。

JQueue的灵感来自于Yii2 Queue这个优秀的PHP库。希望这有助于使Outbox模式的实现变得更容易和简单。


今天先到这儿,希望对云原生,技术领导力, 企业管理,系统架构设计与评估,团队管理, 项目管理, 产品管管,团队建设 有参考作用 , 您可能感兴趣的文章:
领导人怎样带领好团队
构建创业公司突击小团队
国际化环境下系统架构演化
微服务架构设计
视频直播平台的系统架构演化
微服务与Docker介绍
Docker与CI持续集成/CD
互联网电商购物车架构演变案例
互联网业务场景下消息队列架构
互联网高效研发团队管理演进之一
消息系统架构设计演进
互联网电商搜索架构演化之一
企业信息化与软件工程的迷思
企业项目化管理介绍
软件项目成功之要素
人际沟通风格介绍一
精益IT组织与分享式领导
学习型组织与企业
企业创新文化与等级观念
组织目标与个人目标
初创公司人才招聘与管理
人才公司环境与企业文化
企业文化、团队文化与知识共享
高效能的团队建设
项目管理沟通计划
构建高效的研发与自动化运维
某大型电商云平台实践
互联网数据库架构设计思路
IT基础架构规划方案一(网络系统规划)
餐饮行业解决方案之客户分析流程
餐饮行业解决方案之采购战略制定与实施流程
餐饮行业解决方案之业务设计流程
供应链需求调研CheckList
企业应用之性能实时度量系统演变

如有想了解更多软件设计与架构, 系统IT,企业信息化, 团队管理 资讯,请关注我的微信订阅号:

作者:Petter Liu
出处:http://www.cnblogs.com/wintersun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 该文章也同时发布在我的独立博客中-Petter Liu Blog。