自做淘宝客网站,网站如何做长尾词排名,镇江科技有限公司,国际新闻最新消息今天10条“它可以在我的本地机器上运行#xff01;” 如今#xff0c;这听起来像模因#xff0c;但仍然存在“开发环境与生产环境”的问题。 作为开发人员#xff0c;您应始终牢记#xff0c;您的应用程序有一天将在生产环境中开始运行。 在本文中#xff0c;我们将讨论一些特定于… “它可以在我的本地机器上运行” 如今这听起来像模因但仍然存在“开发环境与生产环境”的问题。 作为开发人员您应始终牢记您的应用程序有一天将在生产环境中开始运行。 在本文中我们将讨论一些特定于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库软件包作为外观和注销实现。 您只需要向类代码注入日志记录工具就可以了。 Inject
private Logger log; 然后只需在您的代码中调用此服务 log.info(Transaction for the customer {} has succeeded at {}, customer, transaction.getDate()); 请记住日志消息应该有意义并且包含足够的信息以了解应用程序中发生了什么。 在系列文章“干净的代码干净的日志”中您可以找到更多关于Java应用程序的日志记录技巧。 另外我们建议您参阅“ 9个记录的罪过”一文 。 另外在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 profileprodbean namecuba_FileStorage classcom.haulmont.addon.cubaaws.s3.AmazonS3FileStorage/
/beans 设置该属性后将自动启用S3实现 spring.profiles.activeprod 因此在开发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.properties test-app.properties和prod-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服务调用该线程也有可能由于网络超时而永远无法恢复执行。 超时有两种类型 连接超时 读取超时 在应用程序中这些超时类型应分开处理。 让我们使用与上一章相同的示例-付款网关。 对于这种情况读取超时可能明显长于连接超时。 银行交易可以处理很长的时间数十秒最多几分钟。 但是连接应该很快因此值得将连接超时设置为例如10秒。 超时值是要移至属性文件的良好候选者。 并始终为通过网络交互的所有服务设置它们。 以下是服务bean定义的示例 bean idpaymentGwConfig classcom.global.api.serviceConfigs.GatewayConfigproperty nameconnectionTimeout value${xxx.connectionTimeoutMillis}/property namereadTimeout 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框架创建应用程序时系统中将创建两个用户 admin和anonymous 。 始终在生产环境中更改其默认密码然后用户才能使用该应用程序。 您可以手动执行此操作也可以将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