开发高性能的微服务应用 - Gupta

22-08-18 banq

微服务设计如今变得非常流行。应用程序团队正在转向微服务架构,因为它有很多优势。
我们应该记住,微服务不仅仅是将大型单体应用程序分解为子应用程序,它还远远不止于此。微服务的概念和中心围绕着创建一个自包含的功能,提供清晰的接口并可以拥有自己的内部组件。
以下是基于微服务架构的一些高级优势:
  • 松耦合和高内聚
  • 支持水平和垂直缩放:易于放大和缩小。
  • 多样化和现代技术栈:每个微服务都可以用不同的编程语言实现,并且可以部署在异构服务器或云上。
  • 模块化和分布式:围绕业务功能组织的小型单个微服务。
  • 上市时间快:开发、测试和部署比单体服务快得多。
  • 易于部署、测试和生产。


微服务性能最佳实践

一、设计、通信与安全:

1. 选择最适合的技术并使用其最佳实践来开发微服务:根据功能和业务特性/用例选择技术堆栈。有时,我们(工程师)对技术形成偏见(情感依恋),然后我们尝试基于我们最喜欢的技术堆栈开发所有微服务。我花了一些时间来改变这种观点。示例:基于 AI/ML 的服务可以在 Python 或类似语言上构建。如果我们尝试在 JAVA 中构建 AI/ML 模型,它们在性能方面可能无法按预期工作。一旦我们确定了最适合的技术堆栈,那么我们应该按照技术的最佳实践为该技术堆栈设计和构建优化的代码。

2. 使用 SOLID 原则设计微服务:SOLID 设计原则是设计良好且高效的面向对象程序或应用程序的基础。我想提倡在设计微服务时使用 SOLID 原则

3. 为性能和安全性设计微服务架构:微服务的安全性和 API 应该是设计的开始,而不是最后的实现项目。由于与利益相比,不安全的服务对消费者造成的伤害更大。OAUTH 和 Kerberos 是常用的安全原则,它们的库在大多数编程语言中都很容易获得。

4.微服务通信:尽可能异步(非阻塞)请求:同步请求是阻塞的,可能会导致严重的性能瓶颈。异步通信是指在两个或多个服务之间交换数据/信息/消息,而不需要所有服务立即响应。简而言之,交互服务在通信期间不需要启动和运行。这可以通过消息队列或数据库轮询来实现。Kafka是微服务中广泛使用的服务内通信解决方案之一。

5. 限制内存占用:微服务占用空间和业务逻辑应该是小而原子的。微服务应该解决特定的用例而不是所有问题。这样性能会提高。一个好的微服务不应该暴露不直接相关的方法/功能(例如销售和采购)。如果我们为特定用例与微服务集成,并且我们只使用不到 30-40% 的功能,那么我们可能不会调用微服务。因为这样的服务更像是低性能的迷你单体服务。

6. OAuth 和 Kerberos 令牌缓存:安全令牌的生成成本高昂且耗时。最佳实践是通过调用系统来缓存 OAuth 和/或 Kerberos 令牌,以避免频繁访问生成令牌的 API。我更喜欢 60 分钟到 180 分钟的缓存时间。尽管这取决于我们希望在微服务中提供的安全级别。Spring 为缓存和刷新令牌作为非阻塞后台进程提供了良好的集成。如果在应用程序中使用分布式消息传递服务(如 Kafka),则还应缓存其身份验证令牌以提高性能。

数据库相关:
7.使用正确的数据库类型/技术:微服务响应时间直接取决于数据源和底层数据库响应时间。数据库选择和数据库建模非常重要。一个微服务或微服务架构可以有 RDBMS、Key-Value 存储和/或非结构化数据(例如图像、视频)等。为了提高性能,结构化数据应该存储在 RDBMS 数据库中,非结构化数据应该存储在 No SQL 数据存储,如 Mongo DB 或 Cassandra。
数据库类型(RDBMS、NO-SQL、Object DB 等)应根据用例决定,我们不应试图遵循“一刀切”的做法。

8. 数据库端缓存:对于数据库中不经常变化的数据(或参考数据),应对数据库查询和响应进行缓存。这将减少对数据库的访问并避免数据库过载。EHCache是一个很好的开源服务,与 Hibernate、Spring 和 JPA 集成得很好。此外,我们应该花一些时间来确定数据库表中良好的索引/分区策略。我更喜欢数据库与缓存大小的比率为 80/20 或 70/30,即保留 20-30% 的更多当前访问的数据可以提高性能。

9.优化数据库调用/查询:避免从数据库中获取整行(元组)。假设一个 API 命中一个数据库表并提供 10 个属性,而数据库表有 40 个(或更多)属性。如果我们执行“Select All”或“Select * by id”查询,它将返回整个行/元组。更好的方法是传递 API 响应所需的属性名称。这将节省网络成本并加快执行速度。

10. 数据库连接池: DBCP是一种通过维护连接池来降低创建和关闭连接成本的方法。由于为每个请求建立新连接既昂贵又耗时,DBCP 允许对多个请求使用相同的数据库连接。此外,花时间确定空闲/待机连接的数量会更好。这实现了良好的资源利用。

11.使用数据库集群:具有负载平衡的数据库集群允许数据库对查询做出更快的响应。有多种处理方法:

  1. 可能有一个主从配置,其中从属是只读的并且最终是一致的,或者
  2. 与主从配置相比,主主配置有点慢。


12. 数据库调优和选择好的索引和/或分区策略:微调表空间、磁盘空间和用户空间。选择正确的索引或分区策略非常重要。好的索引或分区策略可以优化查询时间,而错误的索引选择会降低性能。
如果我们使用 JPA,最好在开发阶段记录和查看 JPA 生成的查询,因为有时这些查询会添加不必要的连接和自连接。

服务器端缓存和缩放:
13. 服务器端缓存:良好的缓存带来高性能收益。而且,糟糕的缓存策略会降低性能。我建议根据请求及其参数缓存微服务的响应。如果响应不经常更改(图像、电影或项目详细信息等响应),则可以根据输入参数缓存微服务的响应。这将提高性能,因为不需要为类似/相同的请求执行业务逻辑/计算。

Memcached Redis 是在应用程序和数据库之间存储键值的内存缓存的很好例子。Redis 是一种内存中、分布式、以及允许备份和恢复功能的高级缓存工具。而且,两者都与基于 Spring 的微服务很好地集成在一起。对于视频(剪辑、电影等),CDN是一个很好的解决方案。这是内容交付网络的 wiki 链接。

14. 扩展:垂直扩展(Scaling up)和水平扩展(扩展)是处理微服务负载增加的两个建议。

  • 垂直扩展是指增加单个服务的内存。因此,垂直扩展需要重新启动微服务(停机),并且它对底层存储可用性有很强的依赖性。
  • 水平扩展是指添加新节点来服务请求。这可以在同一个主机/云池中完成,也可以在不同的主机/云池中完成。对于相同的主机/云池,自动缩放器是大多数云提供商都提供的非常常见的服务。我们可以根据 Http 吞吐量、内存使用等配置自动缩放。

对于不同的主机,需要负载均衡器将流量路由到在多个节点/云池上运行的微服务。

API 网关、速率限制器和代理:
15. 速率限制器: API网关或内部开发的API 速率限制器可防止 API 过度使用并提高微服务的可用性。负载均衡器还有助于限制或修复在特定时间点命中服务的请求数量。我建议启用自动缩放以及多个节点部署,然后添加一个负载均衡器来分发请求。