Ne comprenez pas les transactions distribuées, ne dites pas que vous comprenez les microservices!

1. Gestion des transactions pour les applications traditionnelles

1.1 Affaires locales

Avant d'introduire la cohérence des données sous les microservices, introduisons brièvement le contexte des transactions. Les applications autonomes traditionnelles utilisent un SGBDR comme source de données. L'application ouvre une transaction, exécute CRUD, valide ou annule la transaction, qui se produisent tous dans la transaction locale, et le gestionnaire de ressources (RM) fournit directement la prise en charge des transactions. La cohérence des données est garantie dans une transaction locale.

1.png

1.2 Transaction distribuée

1.2.1 Engagement en deux phases (2PC)

Lorsque l'application se développe progressivement et qu'une application utilise plusieurs sources de données, les transactions locales ne peuvent plus répondre aux exigences de cohérence des données. En raison de l'accès simultané de plusieurs sources de données, les transactions doivent être gérées sur plusieurs sources de données et des transactions distribuées ont vu le jour. L'un des plus populaires est la validation en deux phases (2PC), et les transactions distribuées sont gérées de manière uniforme par le gestionnaire de transactions (TM).

La validation en deux phases est divisée en phase de préparation et phase de validation.

2.png

3.png

Cependant, la validation en deux phases ne peut pas garantir pleinement le problème de cohérence des données, et il y a le problème du blocage de la synchronisation, donc sa version optimisée de la validation en trois phases (3PC) a été inventée.

1.2.2 Soumission en trois phases (3PC)

4.png

Cependant, 3PC ne peut garantir la cohérence des données que dans la plupart des cas.

2. Gestion des transactions sous microservices

Alors, la transaction distribuée 2PC ou 3PC est-elle adaptée à la gestion des transactions sous microservices? La réponse est non, pour trois raisons:

  1. Étant donné que les microservices ne peuvent pas accéder directement aux données, les appels mutuels entre microservices sont généralement effectués via RPC (dubbo) ou API Http (SpringCloud), il n'est donc plus possible d'utiliser TM pour gérer la RM des microservices.
  2. Les types de sources de données utilisés par différents microservices peuvent être complètement différents. Si le microservice utilise une base de données qui ne prend pas en charge les transactions, telle que NoSQL, les transactions seront tout simplement impossibles.
  3. Même si les sources de données utilisées par les microservices prennent toutes en charge les transactions, si une grande transaction est utilisée pour gérer les transactions de plusieurs microservices, le temps de maintenance de cette grande transaction sera de plusieurs ordres de grandeur plus long que celui de la transaction locale. Ces transactions à long terme et les transactions interservices généreront de nombreux verrous et une indisponibilité des données, ce qui affectera gravement les performances du système.

On constate que les transactions distribuées traditionnelles ne peuvent plus répondre aux besoins de gestion des transactions dans le cadre de l'architecture de microservice. Ainsi, comme les transactions ACID traditionnelles ne peuvent pas être satisfaites, la gestion des transactions sous microservices doit suivre une nouvelle théorie de base de règles.

La théorie BASE a été proposée par Dan Pritchett, l'architecte d'eBay. La théorie BASE est une extension de la théorie CAP. L'idée centrale est que même si une forte cohérence ne peut être obtenue, l'application doit être en mesure d'atteindre une cohérence finale de manière appropriée. BASE fait référence à Basiquement disponible, état souple, cohérence éventuelle.

基本可用: Désigne la perte de disponibilité partielle en cas de panne du système distribué, c'est-à-dire pour s'assurer que le cœur est disponible.

软状态: Permet au système d'avoir un état intermédiaire, et l'état intermédiaire n'affectera pas la disponibilité globale du système. Dans le stockage distribué, il existe généralement au moins trois copies d'une donnée, le délai qui permet la synchronisation des copies entre différents nœuds est une manifestation de l'état souple.

最终一致性: La cohérence finale signifie que toutes les copies de données du système peuvent enfin atteindre un état cohérent après un certain laps de temps. Une consistance faible est l'opposé d'une consistance forte. La consistance ultime est un cas particulier de consistance faible.

BASE est l'exigence fondamentale de la gestion des transactions sous microservices. La 最终一致性gestion des transactions basée sur des microservices ne peut pas atteindre une cohérence forte, mais la cohérence la plus importante doit être garantie. Alors, quelles méthodes existe-t-il pour garantir la cohérence ultime de la gestion des transactions dans les microservices? Selon le principe de mise en œuvre, il existe deux types principaux: le type de notification d'événement et le type de compensation. Le type de notification d'événement peut être divisé en mode de notification d'événement fiable et meilleur effort Le mode de notification et le mode de compensation peuvent être divisés en deux types: le mode TCC et le mode de compensation d'entreprise. Ces quatre modes peuvent tous atteindre la cohérence ultime des données sous microservices.

3. Moyens d'assurer la cohérence des données dans les microservices

3.1 Mode de notification d'événement fiable

3.1.1 Événement de synchronisation

Le concept de conception du mode de notification d'événement fiable est plus facile à comprendre, c'est-à-dire qu'une fois le service principal terminé, le résultat est transmis au service esclave via l'événement (généralement une file d'attente de messages) et le service esclave consomme le message après avoir reçu le message pour terminer l'entreprise, de manière à atteindre le service principal et Cohérence des messages entre les services esclaves. La première et la plus simple chose à laquelle on puisse penser est la notification d’événement synchrone. Le traitement commercial et l’envoi des messages sont exécutés de manière synchrone. La logique d’implémentation est illustrée dans le diagramme de code et de séquence ci-dessous.

public void trans () { 
    try { 
    // 1. Exploiter la base de données 
        bool result = dao.update (data); // 
    Si l'opération de base de données échoue, une exception sera levée // 2. Si l'opération de base de données réussit, envoyer un message 
        if (result) { 
            mq.send (data); // Si la méthode échoue à s'exécuter, une exception sera levée 
        } 
    } catch (Exception e) { 
        roolback (); // Si une exception se produit, revenir en arrière 
    }}

5.png

La logique ci-dessus semble transparente. Si l'opération de base de données échoue, elle se terminera directement sans envoyer le message; si l'envoi du message échoue, la base de données est annulée; si l'opération de base de données réussit et que le message est envoyé avec succès, l'entreprise réussit et le message est envoyé aux consommateurs en aval. Ensuite, après mûre réflexion, il existe en fait deux lacunes dans la notification de message synchrone.

  1. Sous l'architecture de microservice, il peut y avoir des problèmes d'E / S réseau ou des problèmes de temps d'arrêt du serveur. Si ces problèmes surviennent à l'étape 7 du diagramme de séquence, le service principal ne peut pas être notifié normalement après la remise du message (problème de réseau) ou la soumission ne peut pas être poursuivie. Transaction (temps d'arrêt), alors le service principal pensera que la livraison du message a échoué et déploiera l'activité principale du service, mais en fait le message a été consommé par le service esclave, les données du service maître et du service esclave seront incohérentes. Les scénarios spécifiques peuvent être vus dans les deux chronogrammes suivants.


6.png

7.png

  1. Le service événementiel (service de messagerie dans ce cas) est trop associé à l'entreprise. Si le service de messagerie n'est pas disponible, l'entreprise ne sera pas disponible. Le service d'événements doit être découplé de l'entreprise et exécuté de manière asynchrone indépendamment, ou essayer d'envoyer le message une fois après l'exécution de l'entreprise. Si le message ne parvient pas à être envoyé, il est rétrogradé en envoi asynchrone.

3.1.2 Événements asynchrones

3.1.2.1 Service d'événements locaux

Afin de résoudre le problème des événements synchrones décrit au 3.1.1, le mode de notification d'événement asynchrone a été développé. Le service métier et le service événementiel sont découplés et l'événement est exécuté de manière asynchrone. Un service événementiel séparé garantit une livraison fiable de l'événement.

8.png

异步事件通知-本地事件服务


当业务执行时,在同一个本地事务中将事件写入本地事件表,同时投递该事件,如果事件投递成功,则将该事件从事件表中删除。如果投递失败,则使用事件服务定时地异步统一处理投递失败的事件,进行重新投递,直到事件被正确投递,并将事件从事件表中删除。这种方式最大可能地保证了事件投递的实效性,并且当第一次投递失败后,也能使用异步事件服务保证事件至少被投递一次。

然而,这种使用本地事件服务保证可靠事件通知的方式也有它的不足之处,那便是业务仍旧与事件服务有一定耦合(第一次同步投递时),更为严重的是,本地事务需要负责额外的事件表的操作,为数据库带来了压力,在高并发的场景,由于每一个业务操作就要产生相应的事件表操作,几乎将数据库的可用吞吐量砍了一半,这无疑是无法接受的。正是因为这样的原因,可靠事件通知模式进一步地发展-外部事件服务出现在了人们的眼中。

3.1.2.2 外部事件服务

外部事件服务在本地事件服务的基础上更进了一步,将事件服务独立出主业务服务,主业务服务不在对事件服务有任何强依赖。

9.png

异步事件通知-外部事件服务


业务服务在提交前,向事件服务发送事件,事件服务只记录事件,并不发送。业务服务在提交或回滚后通知事件服务,事件服务发送事件或者删除事件。不用担心业务系统在提交或者会滚后宕机而无法发送确认事件给事件服务,因为事件服务会定时获取所有仍未发送的事件并且向业务系统查询,根据业务系统的返回来决定发送或者删除该事件。

外部事件虽然能够将业务系统和事件系统解耦,但是也带来了额外的工作量:外部事件服务比起本地事件服务来说多了两次网络通信开销(提交前、提交/回滚后),同时也需要业务系统提供单独的查询接口给事件系统用来判断未发送事件的状态。

3.1.2.3 可靠事件通知模式的注意事项

可靠事件模式需要注意的有两点,1. 事件的正确发送; 2. 事件的重复消费。
通过异步消息服务可以确保事件的正确发送,然而事件是有可能重复发送的,那么就需要消费端保证同一条事件不会重复被消费,简而言之就是保证事件消费的
幂等性

如果事件本身是具备幂等性的状态型事件,如订单状态的通知(已下单、已支付、已发货等),则需要判断事件的顺序。一般通过时间戳来判断,既消费过了新的消息后,当接受到老的消息直接丢弃不予消费。如果无法提供全局时间戳,则应考虑使用全局统一的序列号。

对于不具备幂等性的事件,一般是动作行为事件,如扣款100,存款200,则应该将事件id及事件结果持久化,在消费事件前查询事件id,若已经消费则直接返回执行结果;若是新消息,则执行,并存储执行结果。

3.2 最大努力通知模式

相比可靠事件通知模式,最大努力通知模式就容易理解多了。最大努力通知型的特点是,业务服务在提交事务后,进行有限次数(设置最大次数限制)的消息发送,比如发送三次消息,若三次消息发送都失败,则不予继续发送。所以有可能导致消息的丢失。同时,主业务方需要提供查询接口给从业务服务,用来恢复丢失消息。最大努力通知型对于时效性保证比较差(既可能会出现较长时间的软状态),所以对于数据一致性的时效性要求比较高的系统无法使用。这种模式通常使用在不同业务平台服务或者对于第三方业务服务的通知,如银行通知、商户通知等,这里不再展开。

3.3 业务补偿模式

接下来介绍两种补偿模式,补偿模式比起事件通知模式最大的不同是,补偿模式的上游服务依赖于下游服务的运行结果,而事件通知模式上游服务不依赖于下游服务的运行结果。首先介绍业务补偿模式,业务补偿模式是一种纯补偿模式,其设计理念为,业务在调用的时候正常提交,当一个服务失败的时候,所有其依赖的上游服务都进行业务补偿操作。举个例子,小明从杭州出发,去往美国纽约出差,现在他需要定从杭州去往上海的火车票,以及从上海飞往纽约的飞机票。如果小明成功购买了火车票之后发现那天的飞机票已经售空了,那么与其在上海再多待一天,小明还不如取消去上海的火车票,选择飞往北京再转机纽约,所以小明就取消了去上海的火车票。这个例子中购买杭州到上海的火车票是服务a,购买上海到纽约的飞机票是服务b,业务补偿模式就是在服务b失败的时候,对服务a进行补偿操作,在例子中就是取消杭州到上海的火车票。

补偿模式要求每个服务都提供补偿借口,且这种补偿一般来说是不完全补偿,既即使进行了补偿操作,那条取消的火车票记录还是一直存在数据库中可以被追踪(一般是有相信的状态字段“已取消”作为标记),毕竟已经提交的线上数据一般是不能进行物理删除的。

业务补偿模式最大的缺点是软状态的时间比较长,既数据一致性的时效性很低,多个服务常常可能处于数据不一致的情况。

3.4 TCC/Try Confirm Cancel模式

TCC模式是一种优化了的业务补偿模式,它可以做到完全补偿,既进行补偿后不留下补偿的纪录,就好像什么事情都没有发生过一样。同时,TCC的软状态时间很短,原因是因为TCC是一种两阶段型模式(已经忘了两阶段概念的可以回顾一下1.2.1),只有在所有的服务的第一阶段(try)都成功的时候才进行第二阶段确认(Confirm)操作,否则进行补偿(Cancel)操作,而在try阶段是不会进行真正的业务处理的。

10.png

TCC模式


TCC模式的具体流程为两个阶段:

  1. Try,业务服务完成所有的业务检查,预留必需的业务资源
  2. 如果Try在所有服务中都成功,那么执行Confirm操作,Confirm操作不做任何的业务检查(因为try中已经做过),只是用Try阶段预留的业务资源进行业务处理;否则进行Cancel操作,Cancel操作释放Try阶段预留的业务资源。

这么说可能比较模糊,下面我举一个具体的例子,小明在线从招商银行转账100元到广发银行。这个操作可看作两个服务,服务a从小明的招行账户转出100元,服务b从小明的广发银行帐户汇入100元。

服务a(小明从招行转出100元):

try: update cmb_account set balance=balance-100, freeze=freeze+100 where acc_id=1 and balance>100;

confirm: update cmb_account set freeze=freeze-100 where acc_id=1;

cancel: update cmb_account set balance=balance+100, freeze=freeze-100 where acc_id=1;

服务b(小明往广发银行汇入100元):

try: update cgb_account set freeze=freeze+100 where acc_id=1;

confirm: update cgb_account set balance=balance+100, freeze=freeze-100 where acc_id=1;

cancel: update cgb_account set freeze=freeze-100 where acc_id=1;

具体说明:
a的try阶段,服务做了两件事,1:业务检查,这里是检查小明的帐户里的钱是否多余100元;2:预留资源,将100元从余额中划入冻结资金。

a的confirm阶段,这里不再进行业务检查,因为try阶段已经做过了,同时由于转账已经成功,将冻结资金扣除。

Dans la phase d'annulation de a, les ressources réservées sont libérées et les fonds sont gelés pour 100 yuans et rétablis dans le solde.

L'étape d'essai de b est effectuée, les ressources sont réservées et 100 yuans sont gelés.

Dans la phase de confirmation de b, utilisez les ressources réservées lors de la phase d'essai pour transférer 100 yuans de fonds gelés sur le solde.

Dans la phase d'annulation de b, les ressources réservées dans la phase d'essai sont libérées et 100 yuans sont soustraits des fonds gelés.

Comme on peut le voir dans l'exemple simple ci-dessus, le modèle TCC est plus compliqué que le modèle de rémunération d'entreprise pure, de sorte que chaque service doit implémenter deux interfaces, Cofirm et Cancel.

3.5 Résumé

Le tableau suivant compare ces quatre modes couramment utilisés:

Types de Nom Cohérence des données en temps réel Coûts de développement Si le service en amont dépend du résultat du service en aval
Type de notification effort maximal faible faible ne dépend pas de
Type de notification Événement fiable haute haute ne dépend pas de
Type de compensation Compensation d'entreprise faible faible compter
Type de compensation TCC haute haute compter

Enfin

Merci à tous d'avoir vu ici, l'article a des lacunes, et vous êtes invités à le signaler; si vous pensez qu'il est bien écrit, veuillez me donner un coup de pouce.

Bienvenue également à tous pour faire attention à mon compte officiel : programmeur Maidong, mettez à jour les informations de l'industrie tous les jours!

Je suppose que tu aimes

Origine blog.51cto.com/14849432/2555128
conseillé
Classement