古巴比伦乘法_古巴:为生产做准备

古巴比伦乘法

“它可以在我的本地机器上运行!” 如今,它听起来像模因,但是“开发环境与生产环境”的问题仍然存在。 作为开发人员,您应始终牢记,您的应用程序有一天将在生产环境中开始运行。 在本文中,我们将讨论一些特定于CUBA的事情,这些事情将帮助您避免在应用程序投入生产时出现问题。

编码准则

优先服务

几乎每个CUBA应用程序都实现一些业务逻辑算法。 这里的最佳实践是在CUBA Services中实现所有业务逻辑。 所有其他类:屏幕控制器,应用程序侦听器等应将业务逻辑执行委托给服务。 此方法具有以下优点:

  1. 一处只有一个业务逻辑实现
  2. 您可以从不同位置调用此业务逻辑,并将其公开为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库软件包作为外观和注销实现。 您只需要向类代码注入日志记录工具,就可以了。

@Inject
private Logger log;

然后只需在您的代码中调用此服务:

log.info("Transaction for the customer {} has succeeded at {}", customer, transaction.getDate());

请记住,日志消息应该有意义并且包含足够的信息以了解应用程序中发生了什么。 在“干净的代码,干净的日志”系列文章中,您可以找到有关Java应用程序的更多日志记录技巧。 另外,我们建议您看看“ 9 Logging Sins”一文 。

另外,在CUBA中,我们有性能统计日志,因此您始终可以看到应用程序如何消耗服务器资源。 当客户支持开始收到用户对应用程序运行缓慢的投诉时,这将非常有帮助。 通过此登录,您可以更快地找到瓶颈。

处理异常

异常非常重要,因为异常会在您的应用程序出现问题时提供有价值的信息。 因此,第一条规则-永远不要忽略异常。 使用log.error()方法,创建有意义的消息,添加上下文和堆栈跟踪。 该消息将是您用来标识发生了什么的唯一信息。

如果您有代码约定,请在其中添加错误处理规则部分。

让我们考虑一个示例–将用户的个人资料图片上传到应用程序。 此个人资料图片将保存到CUBA的文件存储和文件上传API服务。

这是您不得处理异常的方式:

try {fileUploadingAPI.putFileIntoStorage(uploadField.getFileId(), fd); } catch (Exception e) {}

如果发生错误,则没人会知道,当用户看不到个人资料照片时,用户会感到惊讶。

这好一些,但远非理想。

try {fileUploadingAPI.putFileIntoStorage(uploadField.getFileId(), fd); } catch (FileStorageException e) {log.error (e.getMessage)}

日志中将出现错误消息,我们将仅捕获特定的异常类。 但是不会有关于上下文的信息:文件的名称是谁,谁曾尝试上传文件。 而且,将没有堆栈跟踪,因此很难找到异常发生的位置。 还有一件事–用户不会收到有关该问题的通知。

这可能是一个好方法。

try {fileUploadingAPI.putFileIntoStorage(uploadField.getFileId(), fd); } catch (FileStorageException e) {throw new RuntimeException("Error saving file to FileStorage", e);}

我们知道错误,不要丢失原始异常,添加一条有意义的消息。 调用方法将收到有关异常的通知。 我们可以将当前用户名以及可能的文件名添加到消息中,以添加更多上下文数据。 这是CUBA Web模块的示例。

在CUBA应用程序中,由于其分布式特性,您可能对核心模块和Web模块具有不同的异常处理规则。 文档中有一个关于异常处理的特殊部分。 实施该政策之前,请先阅读它。

特定于环境的配置

开发应用程序时,请尝试隔离应用程序代码中特定于环境的部分,然后使用功能切换和配置文件根据环境切换这些部分。

使用适当的服务实施

CUBA中的任何服务都由两部分组成:接口(服务API)及其实现。 有时,实现可能取决于部署环境。 例如,我们将使用文件存储服务。

在CUBA中,您可以使用文件存储来保存已发送到应用程序的文件,然后在服务中使用它们。 默认实现使用服务器上的本地文件系统保留文件。

但是,当您将应用程序部署到生产服务器时,此实现可能不适用于云环境或集群部署配置 。

为了启用特定于环境的服务实现,CUBA支持运行时配置文件 ,该配置文件允许您根据启动参数或环境变量来使用特定服务。

对于这种情况,如果我们决定在生产中使用文件存储的Amazon S3实现,则可以通过以下方式指定bean:

<beans profile="prod"><bean name="cuba_FileStorage" class="com.haulmont.addon.cubaaws.s3.AmazonS3FileStorage"/>
</beans>

设置该属性后,将自动启用S3实现:

spring.profiles.active=prod

因此,当您开发CUBA应用程序时,请尝试识别特定于环境的服务并为每个环境启用正确的实现。 尽量不要编写看起来像这样的代码:

If (“prod”.equals(getEnvironment())) {executeMethodA();
} else {executeMethodB();
}

尝试实现一个单独的服务myService ,该服务具有一个方法executeMethod()和两个实现,然后使用配置文件对其进行配置。 之后,您的代码将如下所示:

myService.executeMethod();

更清洁,更简单,更易于维护。

外部化设置

如果可能,将应用程序设置提取到属性文件中。 如果参数将来可以更改(即使概率很小),请始终对其进行外部化。 避免将连接URL,主机名等作为纯字符串存储在应用程序的代码中,切勿将其复制粘贴。 更改代码中的硬编码值的成本要高得多。 邮件服务器地址,用户的照片缩略图大小,没有网络连接时的重试次数–所有这些都是您需要外部化的属性的示例。 使用[配置接口] https://doc.cuba-platform.com/manual-latest/config_interface_usage.html )并将它们注入您的类中以获取配置值。

利用运行时配置文件将特定于环境的属性保存在单独的文件中。

例如,您在应用程序中使用支付网关。 当然,您不应在开发过程中花费大量金钱来测试功能。 因此,您有一个用于本地环境的网关存根,一个在网关端用于生产前测试环境的测试API,以及一个用于产品的真实网关。 显然,这些环境的网关地址不同。

不要这样写代码:

If (“prod”.equals(getEnvironment())) {gatewayHost = “gateway.payments.com”;
} else if (“test”.equals(getEnvironment())) {gatewayHost = “testgw.payments.com”;
} else {gatewayHost = “localhost”;
}
connectToPaymentsGateway(gatewayHost);

而是定义三个属性文件: dev-app.propertiestest-app.propertiesprod-app.properties并在其中定义database.host.name属性的三个不同值。

之后,定义一个配置接口:

@Source(type = SourceType.DATABASE)
public interface PaymentGwConfig extends Config {@Property("payment.gateway.host.name")String getPaymentGwHost();
}

然后注入接口并在代码中使用它:

@Inject
PaymentGwConfig gwConfig;//service codeconnectToPaymentsGateway(gwConfig.getPaymentGwHost());

该代码更简单,并且不依赖于环境,所有设置都在属性文件中,并且如果更改了某些内容,则不应在代码中搜索它们。

添加网络超时处理

始终认为通过网络进行的服务调用不可靠。 当前用于Web服务调用的大多数库都基于同步阻塞通信模型。 这意味着,如果您从主执行线程调用Web服务,则应用程序将暂停直到收到响应为止。

即使您在单独的线程中执行Web服务调用,该线程也有可能由于网络超时而永远无法恢复执行。

超时有两种类型:

  1. 连接超时
  2. 读取超时

在应用程序中,这些超时类型应分开处理。 让我们使用与上一章相同的示例-付款网关。 对于这种情况,读取超时可能比连接超时长得多。 银行交易可以处理很长的时间,数十秒,最多几分钟。 但是连接应该很快,因此,值得将连接超时设置为例如10秒。

超时值是要移至属性文件的良好候选者。 并始终为通过网络交互的所有服务设置它们。 以下是服务bean定义的示例:

<bean id="paymentGwConfig" class="com.global.api.serviceConfigs.GatewayConfig"><property name="connectionTimeout" value="${xxx.connectionTimeoutMillis}"/><property name="readTimeout" value="${xxx.readTimeoutMillis}"/>
</bean>

在您的代码中,您应该包括一个特殊部分来处理超时。

数据库准则

数据库是几乎所有应用程序的核心。 在生产部署和更新方面,不破坏数据库非常重要。 除此之外,开发人员工作站上的数据库工作负载显然与生产服务器不同。 这就是为什么您可能要实施以下描述的一些做法。

生成特定于环境的脚本

在CUBA中,我们生成用于创建和更新应用程序数据库SQL脚本。 在生产服务器上首次创建数据库之后,一旦模型更改,CUBA框架就会生成更新脚本。

关于生产中的数据库更新,有一个特殊的部分 ,请在首次生产之前阅读它。

最终建议:始终在更新之前执行数据库备份。 如果出现任何问题,这将节省大量时间和精力。

考虑多租户

如果您的项目将成为多租户应用程序 ,请在项目开始时将其考虑在内。

CUBA通过该插件支持多租户,它对应用程序的数据模型和数据库的查询逻辑进行了一些更改。 例如,将一个单独的列tenantId添加到所有特定于Tenant的实体。 因此,所有查询都隐式修改为使用此列。 这意味着在编写本机SQL查询时应考虑此列。

请注意,由于上面提到的特定功能,向在生产环境中运行的应用程序添加多租户功能可能很棘手。 为了简化迁移,请将所有自定义查询保留在同一应用程序层中,最好在服务中或在单独的数据访问层中。

安全注意事项

对于可以被多个用户访问的应用程序,安全性起着重要的作用。 为了避免数据泄漏,未经授权的访问等,您需要认真考虑安全性。 您可以在下面找到一些原则,这些原则可以帮助您改善安全性。

安全编码

安全性始于防止问题的代码。 您可以在此处找到有关Oracle提供的安全编码的很好的参考。 您可以在下面找到本指南中的一些(可能是显而易见的)建议。

准则3-2 / INJECT-2:避免使用动态SQL

众所周知,动态创建SQL语句(包括不受信任的输入)会受到命令注入的影响。 在CUBA中,您可能需要执行JPQL语句,因此,也应避免使用动态JPQL。 如果需要添加参数,请使用适当的类和语句语法:

try (Transaction tx = persistence.createTransaction()) {// get EntityManager for the current transactionEntityManager em = persistence.getEntityManager();// create and execute QueryQuery query = em.createQuery("select sum(o.amount) from sample_Order o where o.customer.id = :customerId");query.setParameter("customerId", customerId);result = (BigDecimal) query.getFirstResult();// commit transactiontx.commit();}

准则5-1 / INPUT-1:验证输入

在使用前,必须验证来自不受信任来源的输入。 恶意制作的输入可能会导致问题,无论是通过方法参数还是通过外部流。 一些示例是整数值溢出和通过在文件名中包含“ ../”序列的目录遍历攻击。 在CUBA中,除了签入代码外,您还可以在GUI中使用验证器 。

以上只是安全编码原理的几个示例。 请仔细阅读该指南,它将以多种方式帮助您改进代码。

保护个人资料的安全

由于法律要求,某些个人信息应受到保护。 在欧洲,我们有GDPR ,在美国的医疗应用中,有HIPAA要求等。因此,在实施您的应用时要考虑到这一点。

CUBA允许您设置各种权限,并使用角色和访问组限制对数据的访问。 在后者中,您可以定义各种约束 ,以防止未经授权访问个人数据。

但是提供访问权限只是确保个人数据安全的一部分。 数据保护标准和行业特定要求有很多要求。 在规划应用程序的体系结构和数据模型之前,请先查看这些文档。

更改或禁用默认用户和角色

使用CUBA框架创建应用程序时,将在系统中创建两个用户: adminanonymous 。 始终在生产环境中更改其默认密码,然后用户才能使用该应用程序。 您可以手动执行此操作,也可以将SQL语句添加到30-....sql初始化脚本中。

使用CUBA文档中的建议,这些建议将帮助您正确配置生产中的角色。

如果您具有复杂的组织结构,请考虑为每个分支机构创建本地管理员 ,而不是在组织级别上创建多个“超级管理员”用户。

将角色导出到生产

在第一次部署之前,通常需要将角色和访问组从开发(或登台)服务器复制到生产服务器。 在CUBA中,您可以使用内置的管理UI来执行此操作,而不必手动执行。

要导出角色和特权,您可以使用Administration -> Roles屏幕。 下载文件后,可以将其上传到应用程序的生产版本。

对于访问组,有一个类似的过程,但是您需要使用Administration -> Access Groups屏幕。

配置应用

生产环境通常与开发环境以及应用程序配置不同。 这意味着您需要执行一些其他检查,以确保您的应用程序在生产时能够平稳运行。

配置日志

确保已针对生产环境正确配置了日志记录子系统:日志级别设置为所需级别(通常为INFO),并且在应用程序重新启动时不会删除日志。 您可以参考文档以获取正确的日志设置和有用的记录器参考。

如果使用Docker,请使用Docker卷将日志文件存储在容器外部。

为了进行正确的日志记录分析,您可以部署特殊的工具来收集,存储和分析日志。 例如ELK stack和Graylog 。 建议将日志记录软件安装到单独的服务器上,以避免对应用程序性能造成影响。

在群集配置中运行
可以将CUBA应用程序配置为在群集配置中运行。 如果决定使用此功能,则需要注意您的应用程序体系结构,否则,您可能会从应用程序中得到意外的行为。 我们希望引起您对专门针对集群环境进行调整的最常用功能的注意:

任务调度
如果要在应用程序中执行预定任务(例如每日报告生成或每周电子邮件发送),则可以使用相应的框架内置功能( https://doc.cuba-platform.com/manual-latest /scheduled_tasks.html )。 但是,请想象自己是一位获得了三封相同营销电子邮件的客户。 你快乐吗? 如果您的任务在三个群集节点上执行,则可能会发生这种情况。 为避免这种情况,最好使用CUBA任务计划程序 ,该程序使您可以创建单例任务。

分布式缓存
缓存是可以提高应用程序性能的东西。 有时开发人员尝试缓存几乎所有内容,因为现在内存非常便宜。 但是,当您的应用程序部署在多台服务器上时,缓存将在服务器之间分配,并且应该同步。 同步过程发生在相对较慢的网络连接上,这可能会增加响应时间。 这里的建议–在决定添加更多缓存之前(尤其是在集群环境中),执行负载测试并衡量性能。

结论

CUBA平台简化了开发,您可能会完成开发并开始考虑比预期更早的投入生产。 但是,无论您是否使用CUBA,部署都不是一件容易的事。 并且,如果您开始考虑在开发的早期阶段就进行部署并遵循本文中所述的简单规则,那么您的生产方式很可能会很顺利,所需的工作量很小,并且不会遇到严重的问题。

翻译自: https://www.javacodegeeks.com/2020/03/cuba-getting-ready-for-production.html

古巴比伦乘法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java编译器API——使用编译工具

【0】README 0.1&#xff09;以下内容转自&#xff1a; http://suntips.iteye.com/blog/69002 0.2&#xff09;for basic java compiler API, please visit http://blog.csdn.net/pacosonswjtu/article/details/50718494 1&#xff09;当你需要更好的处理这些结果时,你可以使…

Java 线程同步总结

一、synchronized&#xff08;1&#xff09;synchronized方法 &#xff08;2&#xff09;synchronized块 二、Lock 注意&#xff1a;及时释放Lock锁&#xff0c;否则会出现死锁&#xff0c;通常在finally代码释放锁 &#xff08;1&#xff09;ReentrantLock 实现…

aws es方案_AWS Elasticsearch后模式

aws es方案碰巧我们在SaaS版本的LogSentinel上遇到了生产问题–我们的Elasticsearch停止了对新数据编制索引。 由于Elasticsearch只是辅助存储&#xff0c;因此没有数据丢失&#xff0c;但这给我们的客户带来了一些问题&#xff08;他们无法在其仪表板上看到实时数据&#xff0…

java注解总结

【0】README 0.1&#xff09;本文主要对 java 注解做总结&#xff1b; 【1】处理注解的级别 level1&#xff09; 在运行期级别处理注解&#xff1a; http://blog.csdn.net/pacosonswjtu/article/details/50719361level2&#xff09;在源码级别处理注解&#xff1a; http://b…

Java 类加载总结

一、类加载过程 装载链接验证准备解析初始化二、类初始化情况 1&#xff09;创建类的实例&#xff0c;也就是new一个对象 2&#xff09;访问某个类或接口的静态变量&#xff0c;或者对该静态变量赋值 3&#xff09;调用类的静态方法 4&#xff09;反射&#xff08;Class.…

maven依赖管理_依赖管理和Maven

maven依赖管理Maven伟大而成熟。 几乎所有事物都总有解决方案。 您可能在组织项目上遇到的主要情况是依赖管理。 而不是每个项目都具有自己的依赖关系&#xff0c;您需要一种集中的方式来继承那些依赖关系。 在这种情况下&#xff0c;您可以在父舞会上声明托管依赖项。 在我的…

JavaBean 持久化

【0】README 0.1&#xff09;本文文字描述转自 core java volume 2&#xff0c;旨在学习 JavaBean 持久化 的基础知识&#xff1b; 0.2&#xff09;本文所有源代码荔枝均为原创&#xff1b; 0.3&#xff09; for complete souce code, please visit https://github.com/pa…

apache kafka_Apache Kafka消费者再平衡

apache kafka消费者重新平衡决定哪个消费者负责某个主题的所有可用分区的哪个子集。 例如&#xff0c;您可能有一个包含20个分区和10个使用者的主题。 在重新平衡结束时&#xff0c;您可能希望每个使用者都从2个分区中读取数据。 如果关闭了这些使用者中的10个&#xff0c;则可…

Java 注解总结

一、注解定义 注解早在J2SE1.5就被引入到Java中&#xff0c;主要提供一种机制&#xff0c;这种机制允许程序员在编写代码的同时可以直接编写元数据。 二、元注解 Target 说明了被修饰的注解的应用范围&#xff0c;也就是被修饰的注解可以用来注解哪些程序元…

编译原理三大经典书籍(龙书 虎书 鲸书)

以下内容转自&#xff1a; http://blog.csdn.net/skymingst/article/details/7436892 1、龙书&#xff08;Dragon book&#xff09; 英文名&#xff1a;Compilers: Principles,Techniques,and Tools 作者&#xff1a;Alfred V.Aho,Ravi Sethi,Jeffrey D.Ullman 中文名&…

两个时间之间是多少小时_那是两个小时我不会回来

两个时间之间是多少小时正如我之前关于linting主题所说的 &#xff0c;花时间修改代码的好处很有限&#xff0c;因为自动工具告诉您这样做。 更糟糕的是&#xff0c;这些工具并非万无一失。 例如&#xff0c;我们一直在针对完美无害的try-with-resources构造周围的SpotBugs警告…

java的类载入器

【0】README 0.1&#xff09;本文文字转自&#xff1a; 深入剖析tomcat&#xff0c; 旨在 理解 jvm 的类载入器&#xff1b; 【1】 jvm的类载入器相关 1&#xff09;jvm 使用了3种类载入器来载入所需要的类&#xff1a;分别是引导类载入器&#xff08;bootstrap class load…

Java 代理总结

一、代理 为其他对象提供一种代理以便控制对这个对象的访问。 &#xff08;1&#xff09;静态代理 &#xff08;2&#xff09;动态代理 1&#xff09;JDK自带的动态代理 2&#xff09;javaassist字节码操作库实现 3&#xff09;CGLIB 4&#xff09; ASM&#xff08;底层…

分解因数 递归_递归分解WAR文件

分解因数 递归抽象 是否曾经需要分解WAR文件以及分解WAR文件中的所有JAR文件&#xff1f; 是的&#xff0c;我也是&#xff01; 我写了ferris-war-exploder来爆炸&#xff1a; 一个JAR文件 一个WAR文件&#xff0c;它找到的每个JAR文件也会爆炸。 包含每个JAR文件&#xff…

jvm(2)-java内存区域

【0】README 0.1&#xff09;本文转自 深入理解jvm&#xff0c; 旨在学习 java内存区域 的基础知识&#xff1b; 【1】运行时数据区域 1&#xff09;jvm 所管理的内存将会包括以下几个运行时数据区域 1.1&#xff09;方法区&#xff1b;&#xff08;线程共享&#xff09; 1.2&…

Java Socket编程总结

一、网络API InetAddress     用于标识网络上的硬件资源&#xff0c;主要是IP地址 URL         统一资源定位符&#xff0c;通过URL可以直接读取或写入网络上的数据Sockets      使用TCP协议实现的网络通信Socket相关的类Datagram     使用UDP协议&am…

java插入排序_Java程序要插入排序

java插入排序Java程序插入示例的排序。 显示了示例仿真以及时间复杂度。 插入排序是一种简单的排序算法&#xff0c;可以一次构建一个最终的排序数组&#xff08;或列表&#xff09;。 它比冒泡排序有效得多&#xff0c;并且在大型列表上的效率比快速排序 &#xff0c;堆排序或…

jvm(2)-JVM内存的设置(解决eclipse下out of memory问题)

【0】README 0.1&#xff09;本文转自&#xff1a; http://blog.csdn.net/sjf0115/article/details/8889201 一、JVM内存的设置的原理 默认的java虚拟机的大小比较小&#xff0c;在对大数据进行处理时java就会报错&#xff1a;java.lang.OutOfMemoryError。 设置jvm内存的方…

Java Servlet总结

一、Servlet简介 Servlet&#xff08;Server Applet&#xff09;是Java Servlet的简称&#xff0c;称为小服务程序或服务连接器&#xff0c;用Java编写的服务器端程序&#xff0c;主要功能在于交互式地浏览和修改数据&#xff0c;生成动态Web内容。狭义的Servlet是指Java语言实…

java oca_OCA第1部分中的Java难题

java oca我在业余时间正在阅读Mala Gupta的Oracle认证Java SE程序员助理书&#xff0c;我对所学到的一些新知识感到惊讶。 有时候他们真的没有道理&#xff0c;有时候他们说得通&#xff0c;但真的让人惊讶。 因此&#xff0c;在本系列文章中&#xff0c;我想将它们共享为“ Ja…