热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

地理分布式微服务及其数据库:对抗高延迟的方法icode9精华分享大全(2022已更新)(今日/科普)

应用层的微服务实例分散在全球范围内的云选择区域。由KongGateway提供支持的API层允许微服务通过简单的REST端点相互通信。全局负载均衡器在最近的PoP(存在点)拦截用户请

应用层的微服务实例分散在全球范围内的云选择区域。由KongGateway提供支持的API层允许微服务通过简单的REST端点相互通信。

全局负载均衡器在最近的PoP(存在点)拦截用户请求,并将请求转发到地理上离用户最近的微服务实例。

一旦微服务实例收到来自负载均衡器的请求,服务很可能需要从数据库中读取数据或向其写入更改。而这一步可能会成为一个痛苦的瓶颈——如果数据(数据库)远离微服务实例和用户,您将需要解决这个问题。

在本文中,我将选择一些多区域数据库部署选项,并演示如何保持数据库查询的读写延迟较低,而不管用户的位置如何。

所以,如果你还在我的旅途中陪我,那么,就像海盗们常说的那样,“称重锚,起锚!” 这意味着,“拉起锚,让这艘船航行!”


多区域数据库部署选项

多区域数据库部署没有灵丹妙药。这都是关于权衡的。每个选项都提供优势,而其他选项会让您付出代价。

YugabyteDB是我选择的分布式SQL数据库,它支持地理分布式应用程序使用的四种多区域数据库部署选项:




  1. 单一延伸集群:数据库集群跨多个区域“延伸”。这些地区通常位于相对较近的地方(例如,美国中西部和东部地区)。



  2. 具有只读副本的单一延伸集群:与前一个选项一样,集群部署在一个地理位置(例如,北美),跨云区域相对较近(例如,美国中西部和东部地区)。但是使用此选项,您可以将只读副本节点添加到遥远的地理位置(例如欧洲和亚洲),以提高读取工作负载的性能。



  3. 单一地理分区集群:数据库集群分布在多个遥远的地理位置(例如北美、欧洲和亚洲)。每个地理位置都有自己的一组节点,它们部署在一个或多个非常接近的本地区域(例如,美国中西部和东部地区)。根据地理分区列的值,数据会自动固定到特定的节点组。使用此部署选项,您可以在全球范围内实现读取和写入工作负载的低延迟。



  4. 具有异步复制的多个集群:多个独立集群部署在各个区域。这些区域可以位于相对较近的位置(例如,美国中西部和东部地区)或位于较远的位置(例如,美国东部和亚洲南部地区)。更改在集群之间异步复制。您将在全球范围内实现读取和写入工作负载的低延迟,与之前的选项相同,但您将处理异步交换更改的多个独立集群。


好的,伙计,让我们继续并查看地理信使用例的前三个部署选项。我将跳过第四个,因为它不适合需要单个数据库集群的信使架构。


美国的单一延伸集群

第一个集群跨越美国的三个地区——美国西部、中部和东部。

图片说明

应用程序/微服务实例和API服务器(图中未显示)在相同的位置以及欧洲西部和亚洲东部地区运行。


美国流量的数据库读/写延迟

假设Blue女士本周在美国爱荷华州工作。她打开地理信使向公司频道发送便条。她的流量将由部署在美国中部地区的微服务实例处理。

在Blue女士可以发送消息之前,USCentral微服务实例必须加载通道的消息历史记录。哪个数据库节点将为该读取请求提供服务?

在我的例子中,美国中部地区被配置为YugabyteDB的首选地区,这意味着来自该地区的数据库节点将处理来自应用层的所有读取请求。将频道的消息历史从美国中央数据库节点加载到同一区域的应用程序实例需要10-15毫秒。这是我的SpringBoot日志的输出,最后一行显示了查询执行时间:


Hibernate:selectmessage0_.country_codeascountry_1_1_,message0_.idasid2_1_,message0_.attachmentasattachme3_1_,message0_.channel_idaschannel_4_1_,message0_.messageasmessage5_1_,message0_.sender_country_codeassender_c6_1_,message0_.sender_idassender_i7_1_,message0_.sent_atassent_at8_1_frommessagemessage0_wheremessage0_.channel_id=?orderbymessage0_.idasc
INFO11744---[-nio-80-exec-10]i.StatisticalLoggingSessionEventListener:SessionMetrics{
1337790nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
1413081nanosecondsspentpreparing1JDBCstatements;
14788369nanosecondsspentexecuting1JDBCstatements;(14ms!)

接下来,当Blue女士将消息发送到通道中时,微服务和数据库之间的延迟将在90毫秒左右。比之前的操作要花更多的时间,因为Hibernate为我的方法调用生成了多个SQL查询JpaRepository.save(message)(当然可以优化),然后YugabyteDB需要在跨美国西部、中部和东部运行的所有节点上存储消息的副本. 以下是输出和延迟的样子:


Hibernate:selectmessage0_.country_codeascountry_1_1_0_,message0_.idasid2_1_0_,message0_.attachmentasattachme3_1_0_,message0_.channel_idaschannel_4_1_0_,message0_.messageasmessage5_1_0_,message0_.sender_country_codeassender_c6_1_0_,message0_.sender_idassender_i7_1_0_,message0_.sent_atassent_at8_1_0_frommessagemessage0_wheremessage0_.country_code=?andmessage0_.id=?
Hibernate:selectnextval('message_id_seq')
Hibernate:insertintomessage(attachment,channel_id,message,sender_country_code,sender_id,sent_at,country_code,id)values(?,?,?,?,?,?,?,?)
31908nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
461058nanosecondsspentpreparing3JDBCstatements;
91272173nanosecondsspentexecuting3JDBCstatements;(90ms)

亚太地区流量的数据库读/写延迟

请记住,每次多区域数据库部署都会有一些权衡。在当前的数据库部署中,美国流量的读/写延迟较低,但来自其他更远位置的流量较高。让我们看一个例子。

想象一下,Blue女士的同事Red先生收到来自Blue女士的最新消息的推送通知。由于Red先生在台湾出差,他的流量将由部署在该岛上的应用程序实例处理。

但是台湾附近没有部署数据库节点,所以微服务实例要查询美国运行的数据库节点。这就是为什么在Red先生看到Blue女士的消息之前平均需要165毫秒来加载整个频道的历史记录:


Hibernate:selectmessage0_.country_codeascountry_1_1_,message0_.idasid2_1_,message0_.attachmentasattachme3_1_,message0_.channel_idaschannel_4_1_,message0_.messageasmessage5_1_,message0_.sender_country_codeassender_c6_1_,message0_.sender_idassender_i7_1_,message0_.sent_atassent_at8_1_frommessagemessage0_wheremessage0_.channel_id=?orderbymessage0_.idasc
[p-nio-80-exec-8]i.StatisticalLoggingSessionEventListener:SessionMetrics{
153152267nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
153217915nanosecondsspentpreparing1JDBCstatements;
165798894nanosecondsspentexecuting1JDBCstatements;(165ms)

当Red先生在同一频道响应Blue女士时,Hibernate大约需要450毫秒来准备、发送和存储消息到数据库中。好吧,由于物理定律,带有消息的数据包必须从台湾穿越太平洋,然后消息副本必须存储在美国西部、中部和东部:


Hibernate:selectmessage0_.country_codeascountry_1_1_0_,message0_.idasid2_1_0_,message0_.attachmentasattachme3_1_0_,message0_.channel_idaschannel_4_1_0_,message0_.messageasmessage5_1_0_,message0_.sender_country_codeassender_c6_1_0_,message0_.sender_idassender_i7_1_0_,message0_.sent_atassent_at8_1_0_frommessagemessage0_wheremessage0_.country_code=?andmessage0_.id=?
selectnextval('message_id_seq')
insertintomessage(attachment,channel_id,message,sender_country_code,sender_id,sent_at,country_code,id)values(?,?,?,?,?,?,?,?)
i.StatisticalLoggingSessionEventListener:SessionMetrics
23488nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
137247nanosecondsspentpreparing3JDBCstatements;
454281135nanosecondsspentexecuting3JDBCstatements(450ms);

现在,对于不针对该地区用户的应用程序来说,基于亚太地区的流量的高延迟可能不是什么大问题,但在我的情况下并非如此。我的地理信使必须在全球范围内顺利运行。让我们对抗这种高延迟,伙计!我们将从阅读开始!


在远程位置部署只读副本

改善YugabyteDB中读取工作负载延迟的最直接方法是在远程位置部署只读副本节点。这是一项可以在实时集群上执行的纯操作任务。

因此,我将一个副本节点“附加”到我的基于美国的实时数据库集群,并且该副本被放置在为Mr.Red提供请求的微服务实例附近的亚洲东部地区。

图片说明

然后我请求台湾的应用程序实例使用该副本节点进行数据库查询。Mr.Red的频道历史预加载延迟时间从165毫秒降至10-15毫秒这与在美国的Blue女士一样快。


Hibernate:selectmessage0_.country_codeascountry_1_1_,message0_.idasid2_1_,message0_.attachmentasattachme3_1_,message0_.channel_idaschannel_4_1_,message0_.messageasmessage5_1_,message0_.sender_country_codeassender_c6_1_,message0_.sender_idassender_i7_1_,message0_.sent_atassent_at8_1_frommessagemessage0_wheremessage0_.channel_id=?orderbymessage0_.idasc
i.StatisticalLoggingSessionEventListener:SessionMetrics
1210615nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
1255989nanosecondsspentpreparing1JDBCstatements;
12772870nanosecondsspentexecuting1JDBCstatements;(12mseconds)

因此,使用只读副本,我的地理信使可以以低延迟服务读取请求,而不管用户的位置如何!

但现在庆祝还为时过早。写入仍然太慢。

想象一下,Mr.Red向频道发送了另一条消息。来自台湾的微服务实例将要求副本节点执行查询。并且副本会将Hibernate生成的几乎所有请求转发到存储主要记录副本的美国节点。因此,亚太地区流量的延迟仍可能高达640毫秒:


Hibernate:settransactionreadwrite;
Hibernate:selectmessage0_.country_codeascountry_1_1_0_,message0_.idasid2_1_0_,message0_.attachmentasattachme3_1_0_,message0_.channel_idaschannel_4_1_0_,message0_.messageasmessage5_1_0_,message0_.sender_country_codeassender_c6_1_0_,message0_.sender_idassender_i7_1_0_,message0_.sent_atassent_at8_1_0_frommessagemessage0_wheremessage0_.country_code=?andmessage0_.id=?
Hibernate:selectnextval('message_id_seq')
Hibernate:insertintomessage(attachment,channel_id,message,sender_country_code,sender_id,sent_at,country_code,id)values(?,?,?,?,?,?,?,?)
i.StatisticalLoggingSessionEventListener:SessionMetrics
23215nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
141888nanosecondsspentpreparing4JDBCstatements;
640199316nanosecondsspentexecuting4JDBCstatements;(640ms)

最后,伙计,让我们一劳永逸地解决这个问题!


切换到全球地理分区集群

全局地理分区集群可以跨远程位置提供快速读取和写入,但需要您在数据库模式中引入特殊的地理分区列。根据列的值,数据库将自动决定一条记录属于哪个地理区域的哪个数据库节点。


数据库架构更改

简而言之,我的表(例如Messageone)定义了country_code列:


CREATETABLEMessage(
idintegerNOTNULLDEFAULTnextval('message_id_seq'),
channel_idinteger,
sender_idintegerNOTNULL,
messagetextNOTNULL,
attachmentbooleanNOTNULLDEFAULTFALSE,
sent_atTIMESTAMPNOTNULLDEFAULTNOW(),
country_codevarchar(3)NOTNULL
)PARTITIONBYLIST(country_code);

根据该列的值,可以将记录放置在以下数据库分区之一中:


CREATETABLEMessage_USA
PARTITIONOFMessage
(id,channel_id,sender_id,message,sent_at,country_code,sender_country_code,
PRIMARYKEY(id,country_code))
FORVALUESIN('USA')TABLESPACEus_central1_ts;
CREATETABLEMessage_EU
PARTITIONOFMessage
(id,channel_id,sender_id,message,sent_at,country_code,sender_country_code,
PRIMARYKEY(id,country_code))
FORVALUESIN('DEU')TABLESPACEeurope_west3_ts;
CREATETABLEMessage_APAC
PARTITIONOFMessage
(id,channel_id,sender_id,message,sent_at,country_code,sender_country_code,
PRIMARYKEY(id,country_code))
FORVALUESIN('TWN')TABLESPACEasia_east1_ts;

每个分区都映射到其中一个表空间:


CREATETABLESPACEus_central1_tsWITH(
replica_placement='{"num_replicas":1,"placement_blocks":
[{"cloud":"gcp","region":"us-central1","zone":"us-central1-b","min_num_replicas":1}]}'
);
CREATETABLESPACEeurope_west3_tsWITH(
replica_placement='{"num_replicas":1,"placement_blocks":
[{"cloud":"gcp","region":"europe-west3","zone":"europe-west3-b","min_num_replicas":1}]}'
);
CREATETABLESPACEasia_east1_tsWITH(
replica_placement='{"num_replicas":1,"placement_blocks":
[{"cloud":"gcp","region":"asia-east1","zone":"asia-east1-b","min_num_replicas":1}]}'
);

每个表空间都属于来自特定地理位置的一组数据库节点。例如,所有国家代码为台湾country_code=TWN如果您想了解地理分区的详细信息,请查看以下文章。


跨大陆的低读/写延迟

因此,我在美国中部、欧洲西部和亚洲东部地区部署了一个三节点地理分区集群。

图片说明

现在,让我们确保Red先生的请求的读取延迟保持不变。与只读副本部署一样,加载通道的消息历史记录仍需要5-15毫秒(对于属于APAC区域的通道和消息):


Hibernate:selectmessage0_.country_codeascountry_1_1_,message0_.idasid2_1_,message0_.attachmentasattachme3_1_,message0_.channel_idaschannel_4_1_,message0_.messageasmessage5_1_,message0_.sender_country_codeassender_c6_1_,message0_.sender_idassender_i7_1_,message0_.sent_atassent_at8_1_frommessagemessage0_wheremessage0_.channel_id=?andmessage0_.country_code=?orderbymessage0_.idasc
i.StatisticalLoggingSessionEventListener:SessionMetrics
1516450nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
1640860nanosecondsspentpreparing1JDBCstatements;
7495719nanosecondsspentexecuting1JDBCstatements;(7ms)

并且……请鼓掌……当Red先生向APAC频道发布消息时,写入延迟已从平均400-650毫秒降至6毫秒!


Hibernate:selectmessage0_.country_codeascountry_1_1_0_,message0_.idasid2_1_0_,message0_.attachmentasattachme3_1_0_,message0_.channel_idaschannel_4_1_0_,message0_.messageasmessage5_1_0_,message0_.sender_country_codeassender_c6_1_0_,message0_.sender_idassender_i7_1_0_,message0_.sent_atassent_at8_1_0_frommessagemessage0_wheremessage0_.country_code=?andmessage0_.id=?
Hibernate:selectnextval('message_id_seq')
Hibernate:insertintomessage(attachment,channel_id,message,sender_country_code,sender_id,sent_at,country_code,id)values(?,?,?,?,?,?,?,?)
1123280nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
123249nanosecondsspentpreparing3JDBCstatements;
6597471nanosecondsspentexecuting3JDBCstatements;(6ms)

任务完成,伙计!现在,我的geo-messenger的数据库可以跨国家和大洲以低延迟提供读取和写入服务。我只需要告诉我的信使在哪里部署微服务实例和数据库节点。


跨大陆查询的案例

现在快速评论一下为什么我跳过了具有多个独立YugabyteDB集群的数据库部署选项。

对我来说,为Messenger提供一个数据库很重要,这样:



  • 用户可以加入属于任何地区的讨论频道。

  • 任何位置的微服务实例都可以访问任何位置的数据。

例如,如果Red先生加入属于美国节点的讨论频道country_code=’USA'(-基于对应物。此操作的延迟由三个SQL请求组成,大约为165毫秒


Hibernate:selectmessage0_.country_codeascountry_1_1_0_,message0_.idasid2_1_0_,message0_.attachmentasattachme3_1_0_,message0_.channel_idaschannel_4_1_0_,message0_.messageasmessage5_1_0_,message0_.sender_country_codeassender_c6_1_0_,message0_.sender_idassender_i7_1_0_,message0_.sent_atassent_at8_1_0_frommessagemessage0_wheremessage0_.country_code=?andmessage0_.id=?
Hibernate:selectnextval('message_id_seq')
Hibernate:insertintomessage(attachment,channel_id,message,sender_country_code,sender_id,sent_at,country_code,id)values(?,?,?,?,?,?,?,?)
i.StatisticalLoggingSessionEventListener:SessionMetrics
1310550nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
159080nanosecondsspentpreparing3JDBCstatements;
164660288nanosecondsspentexecuting3JDBCstatements;(164ms)

165毫秒无疑高于6毫秒(Red先生将消息发布到基于亚太地区的本地频道时的延迟),但这里重要的是能够在必要时通过单个数据库连接发出跨大陆请求。另外,正如执行计划所示,在Hibernate级别有很大的优化空间。目前,Hibernate将我的JpaRepository.save(message)调用转换为3条JDBC语句。这是可以进一步优化的,以将跨大陆请求的延迟从165毫秒降低到低得多的值。

以上就是今天(2022年11月9日19:35:58)小编为大家整理的icode9【精华分享大全】,希望对大家有所帮助。


推荐阅读
  • 函子(Functor)是函数式编程中的一个重要概念,它不仅是一个特殊的容器,还提供了一种优雅的方式来处理值和函数。本文将详细介绍函子的基本概念及其在函数式编程中的应用,包括如何通过函子控制副作用、处理异常以及进行异步操作。 ... [详细]
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • 本文详细介绍了JQuery Mobile框架中特有的事件和方法,帮助开发者更好地理解和应用这些特性,提升移动Web开发的效率。 ... [详细]
  • 本文介绍了如何通过C#语言调用动态链接库(DLL)中的函数来实现IC卡的基本操作,包括初始化设备、设置密码模式、获取设备状态等,并详细展示了将TextBox中的数据写入IC卡的具体实现方法。 ... [详细]
  • 本文探讨了在SQL Server中处理几何类型列时遇到的INTERSECT操作限制,并提供了解决方案,包括通过转换数据类型和使用额外表结构的方法。 ... [详细]
  • 本文将从基础概念入手,详细探讨SpringMVC框架中DispatcherServlet如何通过HandlerMapping进行请求分发,以及其背后的源码实现细节。 ... [详细]
  • 本文详细介绍了如何搭建一个高可用的MongoDB集群,包括环境准备、用户配置、目录创建、MongoDB安装、配置文件设置、集群组件部署等步骤。特别关注分片、读写分离及负载均衡的实现。 ... [详细]
  • 小编给大家分享一下Vue3中如何提高开发效率,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获, ... [详细]
  • linux网络子系统分析(二)—— 协议栈分层框架的建立
    目录一、综述二、INET的初始化2.1INET接口注册2.2抽象实体的建立2.3代码细节分析2.3.1socket参数三、其他协议3.1PF_PACKET3.2P ... [详细]
  • 深入理解Java SE 8新特性:Lambda表达式与函数式编程
    本文作为‘Java SE 8新特性概览’系列的一部分,将详细探讨Lambda表达式。通过多种示例,我们将展示Lambda表达式的不同应用场景,并解释编译器如何处理这些表达式。 ... [详细]
  • 本文探讨了在Windows系统中运行Apache服务器时频繁出现崩溃的问题,并提供了多种可能的解决方案和建议。错误日志显示多个子进程因达到最大请求限制而退出。 ... [详细]
  • PHP面试题精选及答案解析
    本文精选了新浪PHP笔试题及最新的PHP面试题,并提供了详细的答案解析,帮助求职者更好地准备PHP相关的面试。 ... [详细]
  • Redis:缓存与内存数据库详解
    本文介绍了数据库的基本分类,重点探讨了关系型与非关系型数据库的区别,并详细解析了Redis作为非关系型数据库的特点、工作模式、优点及持久化机制。 ... [详细]
  • Docker安全策略与管理
    本文探讨了Docker的安全挑战、核心安全特性及其管理策略,旨在帮助读者深入理解Docker安全机制,并提供实用的安全管理建议。 ... [详细]
  • 流处理中的计数挑战与解决方案
    本文探讨了在流处理中进行计数的各种技术和挑战,并基于作者在2016年圣何塞举行的Hadoop World大会上的演讲进行了深入分析。文章不仅介绍了传统批处理和Lambda架构的局限性,还详细探讨了流处理架构的优势及其在现代大数据应用中的重要作用。 ... [详细]
author-avatar
dx152
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有