[Business Function 101] Microservice-springcloud-Distributed Transaction-Local Transaction ACID-Isolation Level-Propagation Attribute-CAP Theorem-BASE Theorem-Distributed Transaction Solution 2PC/TCC/seata-RocketMQ

1. Why do you need to use distributed transactions?

  In a distributed environment, a business may involve calls between multiple modules. In order to ensure the atomicity of operations, distributed transactions are the best solution.

image.png

2. Local affairs

  Before introducing distributed transactions in the system, it is still necessary for us to review local transactions. Transactions that take effect in a service are called local transactions.

1. Characteristics of transactions

  The concept of transaction: A transaction is a logical group of operations. The logical units that make up this group of operations either succeed together or fail together.

Four characteristics of transactions (ACID):

  1. Atomicity: The original meaning of "atomic" is " indivisible ". The atomicity of a transaction means that multiple operations involved in a transaction are logically indispensable. The atomicity of a transaction requires that all operations in the transaction are either executed or none are executed.
  2. Consistency: 一致refers to the consistency of data, specifically: all data is in a consistent state that satisfies business rules. The principle of consistency requires that no matter how many operations are involved in a transaction, it must be ensured that the data is correct before the transaction is executed , and the data is still correct after the transaction is executed . If one or several operations fail during the execution of a transaction, all other operations must be undone to restore the data to the state before the transaction was executed, which is rollback.
  3. Isolation: During the actual running of the application, transactions are often executed concurrently, so it is very likely that many transactions process the same data at the same time, so each transaction should be isolated from other transactions to prevent data corruption . The principle of isolation requires that multiple transactions do not interfere with each other during concurrent execution .
  4. Durability: The durability principle requires that after a transaction is completed, the modifications to the data are permanently saved and will not be affected by various system errors or other unexpected situations. Normally, data modifications made by a transaction should be written to persistent storage .

2. Transaction isolation level

Transaction concurrency causes some read problems:

  • Dirty read A transaction can read uncommitted data of another transaction
  • Non-repeatable reading. One transaction can read data submitted by another transaction. Single records do not match before and after.
  • Virtual reading (phantom reading): One transaction can read the submitted data of another transaction. The data read before and after is a little more or a little less.

Concurrent writing: use mysql's default lock mechanism (exclusive lock)

Solving read problems: setting transaction isolation level

  • read uncommitted(0)
  • read committed(2)
  • repeatable read(4)
  • Serializable(8)

The higher the isolation level, the lower the performance.

image.png

Under normal circumstances: dirty reads are not allowed, but non-repeatable reads and phantom reads can be appropriately allowed.

3. Transmission properties of transactions

7 transaction propagation behaviors in Spring:

transaction behavior illustrate
PROPAGATION_REQUIRED Supports current transactions, assuming there are no current transactions. just create a new transaction
PROPAGATION_SUPPORTS Supports current transactions. If there is no current transaction, it will run in non-transactional mode.
PROPAGATION_MANDATORY Supports current transactions. If there is no current transaction, an exception will be thrown.
PROPAGATION_REQUIRES_NEW Create a new transaction, assuming that a transaction currently exists. suspend the current transaction
PROPAGATION_NOT_SUPPORTED Run the operation in non-transactional mode. Assuming that a transaction currently exists, suspend the current transaction
PROPAGATION_NEVER Runs in non-transactional mode, assuming there is a current transaction, throws an exception
PROPAGATION_NESTED If a transaction currently exists, it is executed within a nested transaction. If there is no current transaction, perform similar operations to PROPAGATION_REQUIRED.

for example

ServiceA

ServiceA {
    
       
     void methodA() {
    
    
         ServiceB.methodB();
     }
}

ServiceB

ServiceB {
    
     
     void methodB() {
    
    
     }  
}

1.PROPAGATION_REQUIRED

  If the transaction that is currently running is not in another transaction, then start a new transaction. For example, the transaction level definition of ServiceB.methodB is PROPAGATION_REQUIRED, so when ServiceA.methodA is executed, ServiceA.methodA has already started a transaction. At this time, ServiceB.methodB is called, and ServiceB.methodB sees that it has been executed within the transaction of ServiceA.methodA. There will be no new affairs. And if ServiceA.methodA finds that it is not in a transaction when it is executed, it will assign a transaction to itself. In this way, an exception occurs anywhere in ServiceA.methodA or ServiceB.methodB. Transactions will be rolled back. Even if the transaction of ServiceB.methodB has been committed, ServiceA.methodA will be rolled back if it fails next, and ServiceB.methodB will also be rolled back.
Insert image description here

2.PROPAGATION_SUPPORTS

  Assuming you are currently in a transaction. That is, it is executed in the form of a transaction. Assuming that it is not currently in a transaction, it will be executed in a non-transactional manner.

3.PROPAGATION_MANDATORY

  Must be executed within a transaction. In other words, it can only be called by a parent transaction. Otherwise, he will throw an exception

4.PROPAGATION_REQUIRES_NEW

  This is a bit more convoluted. For example, we design the transaction level of ServiceA.methodA to be PROPAGATION_REQUIRED, and the transaction level of ServiceB.methodB to be PROPAGATION_REQUIRES_NEW. Then when running to ServiceB.methodB, the transaction where ServiceA.methodA is located will be suspended. ServiceB.methodB will start a new transaction. Wait for the transaction of ServiceB.methodB to complete before continuing to run.
The difference between the transaction and PROPAGATION_REQUIRED is the degree of rollback of the transaction. Since ServiceB.methodB is a new transaction, there are two different transactions. Assume that ServiceB.methodB has been submitted, then ServiceA.methodA fails and rolls back. ServiceB.methodB will not be rolled back. Assume that ServiceB.methodB fails and rolls back, and assuming that the exception it throws is caught by ServiceA.methodA, the ServiceA.methodA transaction may still be submitted.
Insert image description here

5.PROPAGATION_NOT_SUPPORTED

  Transactions are not currently supported. For example, the transaction level of ServiceA.methodA is PROPAGATION_REQUIRED. The transaction level of ServiceB.methodB is PROPAGATION_NOT_SUPPORTED, then when ServiceB.methodB is executed. ServiceA.methodA's transaction is pending. And he finishes execution in a non-transactional state, and then continues the transaction of ServiceA.methodA.

6.PROPAGATION_NEVER

  Cannot be executed within a transaction.
If the transaction level of ServiceA.methodA is PROPAGATION_REQUIRED. The transaction level of ServiceB.methodB is PROPAGATION_NEVER, then ServiceB.methodB will throw an exception.

7.PROPAGATION_NESTED

  If a transaction currently exists, it is executed within a nested transaction. If there is no current transaction, perform similar operations to PROPAGATION_REQUIRED.

@Transactional(propagation=Propagation.REQUIRED)
If there is a transaction, then join the transaction, if not, create a new one (by default)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
The container does not open the transaction for this method
@Transactional(propagation=Propagation.REQUIRES_NEW )
Regardless of whether a transaction exists, a new transaction is created. The original one is suspended. After the new one is executed, the old transaction continues to be executed.
@Transactional(propagation=Propagation.MANDATORY)
must be executed in an existing transaction, otherwise it will be thrown Exception
@Transactional(propagation=Propagation.NEVER)
must be executed in a transaction that does not, otherwise an exception is thrown (as opposed to Propagation.MANDATORY)
@Transactional(propagation=Propagation.SUPPORTS)
If other beans call this method, in other beans If a transaction is declared, use a transaction. If other beans do not declare a transaction, then do not use a transaction.

4. SpringBoot transaction proxy object

  In SpringBoot, if multiple transaction methods in an object call each other, transaction propagation will fail. The main reason is that the current object directly calls the method of its own object, bypassing the processing of the proxy object, resulting in the failure of transaction propagation. Then the corresponding solution is spring-boot-stater-aopto explicitly obtain the proxy object to call

Introduce relevant dependencies

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.4.12</version>
        </dependency>

Add aspectj annotations

image.png

AopContextThen get the current proxy object where you need to call

    /**
     * 在service中调用自身的其他事务方法的时候,事务的传播行为会失效
     *   因为会绕过代理对象的处理
     *
     */
    @Transactional // 事务A
    public void a(){
    
    
        OrderServiceImpl o = (OrderServiceImpl) AopContext.currentProxy();
        o.b(); // 事务A
        o.c(); // 事务C
        int a = 10/0;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void b(){
    
    

    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void c(){
    
    

    }

3. Distributed transactions

1. Distributed transaction basics

CAP theorem

CAP principle of distributed storage system (three indicators of distributed system):

  1. Consistency : Whether all data backups in a distributed system have the same value at the same time .

    For data whose data is distributed on different nodes, if the data is updated on a certain node, if the latest data can be read on other nodes, then it is called strong consistency. Obtained, that is distributed inconsistency.

  2. A availability: After a part of the nodes in the cluster fails, whether the entire cluster can still respond to client read and write requests. (requires data to be backed up)

  3. Partition tolerance: Most distributed systems are spread across multiple subnetworks. Each subnetwork is called a partition. Partition fault tolerance means that communication between zones may fail.

  The CAP theory means that in a distributed storage system, at most the above two points can only be achieved. And since current network hardware will definitely have problems such as delayed packet loss, partition tolerance is something we cannot avoid . So we can only make a trade-off between consistency and availability. No system can guarantee these three points at the same time. Either choose CP or AP.

image.png

BASE theorem

  BASE is the result of the trade-off between consistency and availability in CAP. It comes from the conclusion of the distributed practice of large-scale Internet systems. It is gradually evolved based on the CAP theorem. Its core idea is that even if strong consistency cannot be achieved (Strong consistency), but each application can adopt appropriate methods according to its own business characteristics to make the system achieve eventual consistency (Eventual consistency). Next, let’s look at the three elements in BASE:

  1. Basically Available _

    Basic availability means that when a failure occurs in a distributed system, partial availability is allowed to be lost, that is, core availability is guaranteed.
    During e-commerce promotions, in order to cope with the surge in traffic, some users may be directed to the downgrade page, and the service layer may only provide downgrade services. This is a reflection of the loss of some availability.

  2. Soft state

    Soft state refers to allowing the system to have intermediate states that do not affect the overall availability of the system. In distributed storage, a piece of data generally has at least three copies. The delay that allows synchronization of copies between different nodes is the embodiment of soft state. Asynchronous replication of mysql replication is also a manifestation.

  3. Eventually consistent

    Eventual consistency means that all data copies in the system can eventually reach a consistent state after a certain period of time. Weak consistency is the opposite of strong consistency. Eventual consistency is a special case of weak consistency.

The BASE model is the opposite of the traditional ACID model. Unlike ACID, BASE emphasizes sacrificing high consistency to obtain availability. Data is allowed to be inconsistent for a period of time, as long as the final consistency is guaranteed .

2.Solutions for distributed transactions

https://www.processon.com/view/link/62a1ddce0791293ad1a552c0

Distributed transaction is a technical difficulty   in enterprise integration , and it is also something involved in every distributed system architecture, especially in microservice architecture, it can almost be said to be unavoidable .

The mainstream solutions are as follows:

  1. Two-phase commit (2PC) based on XA protocol
  2. Flexible transaction-TCC transaction
  3. Flexible transactions-eventual consistency

2.1 Two-Phase Commit (2PC)

  2PC is the two-phase commit protocol, which divides the entire transaction process into two phases, the prepare phase and the commit phase, 2 refers to the two phases, P refers to the prepare phase, and C refers to the commit phase .

image.png

The first stage: the transaction coordinator requires each database involved in the transaction to precommit this operation and reflect whether it can be submitted.

Phase 2: The transaction coordinator asks each database to submit data.

Among them, if any database vetoes the submission, all databases will be required to roll back their part of the transaction.

At present, all mainstream databases support 2PC【2 Phase Commit】

XA is a two-phase commit protocol, also known as XA Transactions.

MySQL is supported starting from version 5.5, SQL Server 2005 is supported, and Oracle 7 is supported.

  In general, the XA protocol is relatively simple, and once a commercial database implements the XA protocol, the cost of using distributed transactions is also relatively low. However, XA also has a fatal shortcoming, that is, the performance is not ideal, especially in the transaction order link, which often has a high amount of concurrency, and XA cannot meet high concurrency scenarios.

  1. Two-phase commit involves multiple network communications between nodes, and the communication time is too long!
  2. The transaction time has become relatively longer, and the time of locked resources has also become longer, causing the resource waiting time to also increase a lot.
  3. Currently, XA is ideally supported by commercial databases, but not ideally supported by mysql databases. The XA implementation of mysql does not record the prepare stage log, and the master-standby switch will cause data inconsistencies between the master and standby databases. Many nosql also do not support XA, which makes the application scenarios of XA very narrow.

2.2 TCC Compensation Transaction

TCC is a programmatic distributed transaction solution.

TCC is actually the compensation mechanism adopted. Its core idea is: for each operation, a corresponding confirmation and compensation (cancellation) operation must be registered. The TCC model requires the slave service to provide three interfaces: Try, Confirm, and Cancel.

  • Try: Mainly to detect the business system and reserve resources
  • Confirm: The business is actually executed without any business checks; only the business resources reserved in the Try phase are used; the Confirm operation satisfies idempotence.
  • Cancel: Release the business resources reserved in the Try phase; the Cancel operation satisfies idempotence.

The entire TCC business is divided into two stages:

image.png

The first stage: The main business service calls the try operations of all slave businesses respectively, and registers all slave business services in the activity manager. When all try operations from the business service are successfully called or a try operation from a certain business service fails, the second phase is entered.

Phase 2: The activity manager performs the confirm or cancel operation based on the execution results of the first phase. If all try operations in the first phase are successful, the activity manager calls the confirm operations of all slave business activities. Otherwise, the cancel operation of all slave business services is called.

For example, if Bob wants to transfer 100 yuan to Smith, the idea is probably:

We have a native method which in turn calls

  1. First, in the Try stage, it is necessary to check whether Bob has enough money, lock the 100 yuan, and freeze Smith's account.
  2. In the Confirm stage, the remotely called transfer operation is performed, and the transfer is successfully unfrozen.
  3. If the execution of step 2 is successful, then the transfer is successful. If the execution of step 2 fails, the unfreezing method (Cancel) corresponding to the remote freezing interface is called.

shortcoming:

  • The idempotency of Canfirm and Cancel is difficult to guarantee.
  • This method has many disadvantages, and it is usually not recommended in complex scenarios , unless it is a very simple scenario, where it is very easy to provide rollback Cancel, and there are very few dependent services.
  • This implementation method will result in a large amount of code and high coupling . And it is very limited, because there are many businesses that cannot be easily rolled back. If there are many serial services, the cost of rollback is too high.

Many large companies actually develop their own TCC distributed transaction framework and use it exclusively within the company. Domestic open source: ByteTCC, TCC-transaction, Himly.

2.3 Message transaction + eventual consistency

  The two-phase commit based on message middleware is often used in high-concurrency scenarios to split a distributed transaction into a message transaction (local operation of system A + message sending) + local operation of system B, where the operation of system B is determined by the message Driver, as long as the message transaction is successful, then the operation of A must be successful, and the message must be sent out. At this time, B will receive the message to perform the local operation. If the local operation fails, the message will be re-submitted until the operation of B succeeds, which is disguised The distributed transaction between A and B is realized effectively.

image.png

Although the above scheme can complete the operations of A and B, A and B are not strictly consistent, but eventually consistent. We sacrifice consistency here in exchange for a substantial improvement in performance. Of course, this way of playing is also risky. If B has been unsuccessfully executed, the consistency will be destroyed. Whether to play or not depends on how much risk the business can take.

Suitable for high concurrency and eventual consistency

Low concurrency is basically the same: two-phase commit

High concurrency and strong consistency: no solution

3. set

Distributed transaction solution seata

Official website: https://seata.io/zh-cn/docs/overview/what-is-seata.html

GitHub: https://github.com/seta/seta

3.1 Installation of seata service

  We can first import seata's dependencies and download the corresponding seata installation files based on the dependent versions.

image.png

https://github.com/seata/seata/releases

After downloading, unzip it and enter the conf folder, and then update the configuration center and registration center information through the registration.conf file.

image.png

Then enter the bin directory to start the service through the seata-server.bat file

image.png

Then enter the nacos registration center to see the corresponding service, which means OK

image.png

3.2 Project integration Seata

  Next, let’s take a look at how to integrate Seata in the mall project. The first is the file.conf file.

Network transmission configuration:

transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #enable heartbeat
  heartbeat = true
  #thread factory for netty
  thread-factory {
    boss-thread-prefix = "NettyBoss"
    worker-thread-prefix = "NettyServerNIOWorker"
    server-executor-thread-prefix = "NettyServerBizHandler"
    share-boss-worker = false
    client-selector-thread-prefix = "NettyClientSelector"
    client-selector-thread-size = 1
    client-worker-thread-prefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    boss-thread-size = 1
    #auto default pin or 8
    worker-thread-size = 8
  }
}

Transaction log storage configuration: This part of the configuration is only used in seata-server. If you choose db, please use it with seata.sql

## transaction log store, only used in seata-server
store {
  ## store mode: file、db
  mode = "file"

  ## file store property
  file {
    ## store location dir
    dir = "sessionStore"
  }

  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata"
    user = "mysql"
    password = "mysql"
  }
}

* The information configuration of the current microservice registered in the seata server:

service {
  # 事务分组,默认:${spring.applicaiton.name}-fescar-service-group,可以随便写
  vgroup_mapping.${spring.application.name}-fescar-service-group = "default"
  # 仅支持单节点,不要配置多地址,这里的default要和事务分组的值一致
  default.grouplist = "127.0.0.1:8091" #seata-server服务器地址,默认是8091
  # 降级,当前不支持
  enableDegrade = false
  # 禁用全局事务
  disableGlobalTransaction = false
}

Mechanism of client related work

client {
  rm {
    async.commit.buffer.limit = 10000
    lock {
      retry.internal = 10
      retry.times = 30
      retry.policy.branch-rollback-on-conflict = true
    }
    report.retry.count = 5
    table.meta.check.enable = false
    report.success.enable = true
  }
  tm {
    commit.retry.count = 5
    rollback.retry.count = 5
  }
  undo {
    data.validation = true
    log.serialization = "jackson"
    log.table = "undo_log"
  }
  log {
    exceptionRate = 100
  }
  support {
    # auto proxy the DataSource bean
    spring.datasource.autoproxy = false
  }
}

First we need to copy the two configuration files registry.conf and file.conf to the property file directory of the corresponding project.

image.png

Then we define the tx-service-group information in the properties file

image.png

Then add the service attribute in file.conf, and then associate the tx-service-group information just set. Note that after version 1.1, the properties were updated to camel case naming:

image.png
Complete file.conf

service {
    
    
  # 事务分组,默认:${spring.applicaiton.name}-fescar-service-group,可以随便写
  vgroupMapping.mall-order-group = "default"
  # 仅支持单节点,不要配置多地址,这里的default要和事务分组的值一致
  default.grouplist = "127.0.0.1:8091" #seata-server服务器地址,默认是8091
  # 降级,当前不支持
  enableDegrade = false
  # 禁用全局事务
  disableGlobalTransaction = false
}
## transaction log store, only used in seata-server
store {
    
    
  ## store mode: file、db、redis
  mode = "file"

  ## file store property
  file {
    
    
    ## store location dir
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    maxBranchSessionSize = 16384
    # globe session size , if exceeded throws exceptions
    maxGlobalSessionSize = 512
    # file buffer size , if exceeded allocate new buffer
    fileWriteBufferCacheSize = 16384
    # when recover batch read size
    sessionReloadReadSize = 100
    # async, sync
    flushDiskMode = async
  }

  ## database store property
  db {
    
    
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata"
    user = "mysql"
    password = "mysql"
    minConn = 5
    maxConn = 30
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }

  ## redis store property
  redis {
    
    
    host = "127.0.0.1"
    port = "6379"
    password = ""
    database = "0"
    minConn = 1
    maxConn = 10
    queryLimit = 100
  }

}

Complete registry.conf

registry {
    
    
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    
    
    application = "seata-server"
    serverAddr = "192.168.56.100:8848"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "default"
    username = ""
    password = ""
  }
  eureka {
    
    
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    
    
    serverAddr = "localhost:6379"
    db = 0
    password = ""
    cluster = "default"
    timeout = 0
  }
  zk {
    
    
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  consul {
    
    
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    
    
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    
    
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    
    
    name = "file.conf"
  }
}

config {
    
    
  # file、nacos 、apollo、zk、consul、etcd3
  type = "file"

  nacos {
    
    
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = ""
    password = ""
  }
  consul {
    
    
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    
    
    appId = "seata-server"
    apolloMeta = "http://192.168.1.204:8801"
    namespace = "application"
  }
  zk {
    
    
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  etcd3 {
    
    
    serverAddr = "http://localhost:2379"
  }
  file {
    
    
    name = "file.conf"
  }
}

3.3 Case demonstration

  In the operation of placing an order, in addition to the existing operations of generating orders and order items that have been locked in inventory, we also added a service that will adjust points. Here is an example of an arithmetic exception, [1/0], such a If the inventory is locked successfully, but the adjustment of membership points fails, the inventory locking operation will be rolled back due to the logic of distributed transaction management.
Seata distributed transaction management needs to be modified by @GlobalTransactional , so we need to add annotations to the method, so that after our custom arithmetic exception is thrown, the intermediate remote call service lock inventory operation will also be rolled back

    @GlobalTransactional
    @Transactional()
    @Override
    public OrderResponseVO submitOrder(OrderSubmitVO vo) throws NoStockExecption {
    
    ...}

image.png

image.png

4. Cancellation of order

  Situations when canceling an order:

  • If the order is not paid for more than 30 minutes after placing the order, the order closing operation needs to be triggered.
  • Payment failed, same need to close the order

Implementation method: scheduled tasks and message middleware. Scheduled tasks definitely have an impact on the performance of the system.

image.png

image.png

image.png

RocketMQ:https://github.com/apache/rocketmq/tree/master/docs/cn

image.png

RocketMQ: Docker installation

Install NameServer

docker pull rocketmqinc/rocketmq

Create storage directory

mkdir -p /mydata/docker/rocketmq/data/namesrv/logs /mydata/docker/rocketmq/data/namesrv/store

then install

docker run -d --restart=always --name rmqnamesrv --privileged=true -p 9876:9876  -v /docker/rocketmq/data/namesrv/logs:/root/logs -v /docker/rocketmq/data/namesrv/store:/root/store -e "MAX_POSSIBLE_HEAP=100000000" rocketmqinc/rocketmq sh mqnamesrv

Related parameter description

image.png

Install Broker

Border configuration: Create the broker.conf configuration file vi /mydata/rocketmq/conf/broker.conf with the following configuration:

# 所属集群名称,如果节点较多可以配置多个
brokerClusterName = DefaultCluster 
#broker名称,master和slave使用相同的名称,表明他们的主从关系 
brokerName = broker-a 
#0表示Master,大于0表示不同的
slave brokerId = 0 
#表示几点做消息删除动作,默认是凌晨4点 
deleteWhen = 04 
#在磁盘上保留消息的时长,单位是小时 
fileReservedTime = 48 
#有三个值:SYNC_MASTER,ASYNC_MASTER,SLAVE;同步和异步表示Master和Slave之间同步数据的机 制;
brokerRole = ASYNC_MASTER 
#刷盘策略,取值为:ASYNC_FLUSH,SYNC_FLUSH表示同步刷盘和异步刷盘;SYNC_FLUSH消息写入磁盘后 才返回成功状态,ASYNC_FLUSH不需要;
flushDiskType = ASYNC_FLUSH 
# 设置broker节点所在服务器的ip地址 
brokerIP1 = 192.168.100.130 
#剩余磁盘比例 
diskMaxUsedSpaceRatio=99

Install:

docker run -d --restart=always --name rmqbroker --link rmqnamesrv:namesrv -p 10911:10911 -p 10909:10909 --privileged=true -v /docker/rocketmq/data/broker/logs:/root/logs -v /docker/rocketmq/data/broker/store:/root/store -v /docker/rocketmq/conf/broker.conf:/opt/rocketmq-4.4.0/conf/broker.conf -e "NAMESRV_ADDR=namesrv:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq sh mqbroker -c /opt/rocketmq-4.4.0/conf/broker.conf

Related parameter description:

image.png

Install console :

Pull image

docker pull pangliang/rocketmq-console-ng

Console installation:

docker run -d --restart=always --name rmqadmin -e "JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.56.100:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8080:8080 pangliang/rocketmq-console-ng

Visit test: http://192.168.56.100:8080

image.png

5. Release inventory

  Situations where stock release is required:

  • Manually cancel the order after placing the order or pay the order overtime
  • The payment is successful to release the inventory and update the inventory

SpringBoot integrates RocketMQ

Need to add corresponding dependencies

<dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.9.1</version>
        </dependency>

Corresponding configuration information needs to be added

image.png

Demonstration example

Scenario: The front end clicks to submit the order, calls the submit order interface service, and returns the submission success page. In the service, the MQ message producer will be called to send the order number regularly, that is, delayed sending, specifying how many seconds and how many minutes later ( Here is the time according to the level, a total of 18 levels, you can see the MQ official website for details, the highest is 2h) will be delivered to the topic topic, and then listen to consumers to consume, then it can be completed: If half an hour is not in If the payment is made on the page, then the business logic of order cancellation and closing is required in the consumer service, and the corresponding message, that is, the order number, can be obtained from the topic topic. After obtaining the order number, then the related order business is carried out Logical operations

Then define the corresponding message producer

Parameters passed: orderSN order number
syncSend synchronous sending method parameters: topic element topic, encapsulate the order number in a message, timeout 5s, level 4 means post the information to the topic topic after 30s

@Component
public class OrderMsgProducer {
    
    

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    public  void sendOrderMessage(String orderSN){
    
    

        rocketMQTemplate.syncSend(OrderConstant.ROCKETMQ_ORDER_TOPIC, MessageBuilder.withPayload(orderSN).build(),5000,4);
    }
}

Then there is the consumer of delayed messages, which we have to handle through the corresponding listener.

@RocketMQMessageListener(topic = OrderConstant.ROCKETMQ_ORDER_TOPIC,consumerGroup = "${rocketmq.consumer.group}")
@Component
public class OrderMsgConsumer implements RocketMQListener<String> {
    
    

    @Override
    public void onMessage(String s) {
    
    
        System.out.println("收到的消息:" + s);
    }
}

Service interface

Make a call to send information:
orderMsgProducer.sendOrderMessage(orderCreateTO.getOrderEntity().getOrderSn());

      @Autowired
    OrderMsgProducer orderMsgProducer;

	@Transactional()
    @Override
    public OrderResponseVO submitOrder(OrderSubmitVO vo) throws NoStockExecption {
    
    
        // 需要返回响应的对象
        OrderResponseVO responseVO = new OrderResponseVO();

        // 获取当前登录的用户信息
        MemberVO memberVO = (MemberVO) AuthInterceptor.threadLocal.get();
        // 1.验证是否重复提交  保证Redis中的token 的查询和删除是一个原子性操作
        String key = OrderConstant.ORDER_TOKEN_PREFIX+":"+memberVO.getId();
        String script = "if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class)
                , Arrays.asList(key)
                , vo.getOrderToken());
        if(result == 0){
    
    
            // 表示验证失败 说明是重复提交
            responseVO.setCode(1);
            return responseVO;
        }

        // 2.创建订单和订单项信息
        OrderCreateTO orderCreateTO = createOrder(vo);
        responseVO.setOrderEntity(orderCreateTO.getOrderEntity());
        // 3.保存订单信息
        saveOrder(orderCreateTO);

        // 4.锁定库存信息
        // 订单号  SKU_ID  SKU_NAME 商品数量
        // 封装 WareSkuLockVO 对象
        lockWareSkuStock(responseVO, orderCreateTO);
        // 5.同步更新用户的会员积分
        // int i = 1 / 0;
        // 订单成功后需要给 消息中间件发送延迟30分钟的关单消息
        orderMsgProducer.sendOrderMessage(orderCreateTO.getOrderEntity().getOrderSn());
        return responseVO;
    }

Then there is the effect of execution

image.png

Guess you like

Origin blog.csdn.net/studyday1/article/details/132733708