目录
CUBA:如何准备上线
“在我电脑上是好的呢!”现在看来,这句话更像是调侃开发人员的一个段子,但是“开发环境与生产环境”之间的矛盾依然存在。作为开发者,你需要记住,你写的应用总会有在生产环境上线的一天。在本文中,我们将讨论一些 CUBA 的特性,能帮助你避免在应用程序上线时遇到问题。
编码准则
使用服务(Services)
几乎每一个 CUBA 应用程序都会实现一些业务逻辑算法。实现业务逻辑的最佳实践就是所有的业务代码都在 CUBA 服务(CUBA Services)中实现。所有其他的类,包括界面控制器、应用程序监听器等等,应当将业务逻辑全部交由服务来代理执行。这个方式的好处是:
- 对于一个业务逻辑来说,只在一处实现。
- 可以在不同的地方调用此业务逻辑,并能将其作为开放的 REST 服务。
请注意,业务逻辑中一般会包括条件、循环等。在理想情况下,对服务的调用应该是单线的。比如,我们假设在控制器中有如下代码:
Item item = itemService.findItem(itemDate);
if (item.isOld()) {
itemService.doPlanA(item);
} else {
itemService.doPlanB(item);
}
如果你看到这种代码,可以考虑在 itemService
服务中创建一个单独的方法 processOldItem(Date date)
,将这段分支判断逻辑迁移到服务中实现,因为这段代码看上去更像是业务逻辑的一部分。
由于界面和 API 可能会是不同的团队开发,所以保持业务逻辑只存在一个地方可以避免生产环境中应用程序行为的不一致。
使用无状态
当开发 web 应用程序时,需要时刻记得将会有多个用户同时使用你的程序。在代码中,意味着有些代码会被多个线程同时执行。几乎所有的应用程序组件,比如服务、bean 以及事件监听器都会受到多线程执行的影响。这里的最佳实践就是让组件保持无状态。 也就是说不要引入共享的可变类成员。 使用局部变量并将会话级特定的信息保留在用户之间不共享的应用程序存储中。 例如,可以在用户会话中保留少量可序列化的数据。
如果需要共享更多的数据,可以使用数据库或者专门的内存共享存储,比如 Redis。
使用日志
有时,生产环境会出一些问题。当问题产生时,很难搞清楚到底是什么引起的问题,因为没办法在生产环境调试。所以为了使你自己、团队以及支撑团队将来的工作更加轻松,为了能明白问题怎么发生的,以便能重现,最好在应用程序中添加适量的日志。
此外,日志还扮演了被动监测的角色。在应用程序重启、升级或者重新配置之后,管理员通常通过查看日志确保所有的程序都启动成功。
日志还能帮助解决可能不是在你的应用程序中发生的问题,而是应用程序集成的其他服务中。 例如,要弄清楚为什么支付网关会拒绝某些交易,可能需要记录所有数据,然后在与支撑团队沟通时提供这些数据。
CUBA 使用了经过验证的 slf4j 库作为日志切面和 logback 实现。 只需要向类代码中注入日志工具类,就可以用了:
@Inject
private Logger log;
然后直接调用:
log.info("Transaction for the customer {} has succeeded at {}", customer, transaction.getDate());
请记住,日志消息需要有意义并包含足够的信息以便了解在应用程序中到底发生了什么。
还有,CUBA 自带性能统计日志,所以你可以看到应用程序对服务器资源的消耗情况。当收到用户抱怨应用程序很慢时,这些日志非常有用,用它们能更快的找到性能瓶颈。
异常处理
异常是非常重要的,在应用程序发生错误时,它们能提供非常有价值的信息。因此,第一条规则就是在代码中永远不要忽视任何异常。使用 log.error()
方法创建一个有含义的消息,添加上下文和 stack trace。这个消息是你能用来定位错误的唯一信息。
如果你有编码规范,那么添加一章关于错误处理的章节吧。
我们看一个例子 - 上传一张用户的资料照片至应用程序。这个资料照片会使用上传 API 服务并存储在 CUBA 的文件存储中。
如果不处理异常,代码是这样:
try {
fileUploadingAPI.putFileIntoStorage(uploadField.getFileId(), fd);
} catch (Exception e) {
}
如果发生了错误,鬼才会知道。用户会很奇怪,我的照片怎么上不去?
下面这个好一些,但是还不理想:
try {
fileUploadingAPI.putFileIntoStorage(uploadField.getFileId(), fd