复习

  1. 服务发现,获取到提供方的服务地址

(1)nacos

(2)原理

  • 服务注册:提供方,启动时向服务注册表注册自己的实例信息
  • 拉取列表:调用方,从服务注册表获取服务列表
  • 健康监测:服务注册表监测服务提供方的健康状态
  • 变更通知:注册表通知调用方,服务实例变化,重新拉取服务列表
  1. 负载均衡,调用流量均衡化的问题,客户端式的负载均衡,默认策略轮询

(1)经典负载均衡策略

  • 轮询
  • 加权轮询
  • 随机
  • 来源IP 哈希 -> 粘性会话
  • ...
  1. 服务调用 openfeign 声明式服务调用客户端
    (1)feignClient定义在调用方

    (2)feignClient定义了要调用的接口(包含入参和出参)

    (3)@FeignClient注解的参数

    • name 服务名
    • contextId spring bean的id,区分同一个服务的不同客户端
    • path api的针对整个client的路径,对应到提供方的RestController的@RequestMapping

    (4)@EnableFeignClients

    (5)Feign实现原理

    • 动态代理+反射
    • 动态代理,动态创建一个实现类,实现了接口
    • 通过反射获取到一些接口的配置数据
    • 实现类的方法中,使用这些数据,实现服务发现+负载均衡+远程调用功能
    • feign组件:requestBean → contract → MethodHandler → Encoder → Interceptor → logger → Http Client

分布式事务

问题:服务拆分,每个服务有自己独立的数据库

image-WzlM.png

概念

  • 分布式事务 (全局事务) vs 本地事务(分支事务)

  • 分布式事务的参与角色

    • TC 事务协调者,管理全局事务
    • TM 事务发起者,发起全局事务
    • RM 事务参与者,参与全局事务
  • 两阶段提交 2pc

    • 一阶段,准备阶段,TC通知所有RM准备提交
    • 二阶段,提交阶段
  • TC统计准备结果,如果全部准备成功,则通知所有RM提交

  • 如果存在失败,则通知所有RM回滚

    解决方案

  • XA协议

    1. 第一阶段:准备阶段(Prepare Phase)
    • TM向所有参与的RM发送 prepare请求。
    • 各RM执行本地事务,记录 undoredo日志,但不提交,资源被锁定。
    • 各RM向TM返回“成功”或“失败”的投票结果。
    1. 第二阶段:提交/回滚阶段(Commit/Rollback Phase)
    • 如果TM收到所有RM的“成功”回复,则向所有RM发送 commit请求,完成提交并释放锁。
    • 如果任一RM回复“失败”或超时,TM则向所有RM发送 rollback请求,利用 undo日志回滚并释放锁。
    • 问题:
      • 事务挂起,资源空耗
      • 性能低
      • 重试+幂等
        • A -> B A成功后,如果B失败,则重试,直到B成功(人工介入)
        • 幂等性判断,通过订单号实现,redis/mysql存储订单号,进行订单号判断
        • 优点:实现成本低;
        • 缺点:性能消耗;不够自动;
  • TCC: Try-Confirm-Cancel 尝试-确认-取消

    • 将一个业务接口拆分成三个部分来执行,分别对应TCC
    • 一阶段: TC调用服务的Try接口,预备执行业务所需要的资源,确保执行成功。 举例:账务转账-try,冻结资金
  • update balance set available_balance-额度 , frozen_balance+额度;

  • insert into freeze_log(xxx);

  • commit;

  • 二阶段:

  • 如果所有的RM,try操作都执行成功,TC调用所有服务的confirm接口。举例:账务转账-confirm

    • update balance set frozen_balance-额度 where user_id=转出方。
    • update balance set avaliable_balance +额度 where user_id=转入方。
    • insert into freeze_log(xxx) 解冻记录
    • insert into transfer_record(xxxx) 转账记录
    • commit;
  • 如果存在Try失败的情况,TC调用所有服务的Cancel接口。举例:账务转账-cancel

    • update balance set available_balance+额度 , frozen_balance-额度;
    • insert into freeze_log(xxx);
    • commit;
  • 优点:

    • 没有资源空耗,性能较高
    • 实现逆向补偿,完成分布式事务的回滚
    • 没有引入额外的技术
    • 基本逻辑是由人为编码完成,灵活性高
  • 缺点

    • 一拆三,需要加入更多的代码,工作量大
    • 需要进行业务重构,重新设计,加入中间环节
  • AT模式

    • 每个数据库需要加入一张undo_log表
    • 一阶段:seata代理数据源,拦截sql执行,执行业务,获取sql执行前后的数据镜像,转换为sql插入到undo_log表,加入当前事务执行
    • 二阶段:
  • 提交:删除undo_log记录

  • 回滚:提取undo_log表中前后数据镜像,计算得出逆向补偿sql,执行sql进行补偿

  • 优点:

  • 没有资源空耗,性能较高

  • 实现逆向补偿,完成分布式事务的回滚

  • 无须编码,代码量极低

  • 缺点

  • 每个数据库要建表

  • 复杂场景容易出错,适配简单场景

    image-YVJV.png

  • saga

Saga 模式是 SEATA 提供的长事务解决方案,在 Saga 模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

image-oQSc.png

AT案例

  1. 搭建seata-server

    1. 下载
    2. 解压
    3. 修改配置
  2. 修改内存

  3. 修改application.yml

seata:
  config:
    # support: nacos, consul, apollo, zk, etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace:
      group: DEFAULT_GROUP
      username: nacos
      password: nacos
      context-path:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key:
      #secret-key:
      data-id: seataServer.properties
  registry:
    # support: nacos, eureka, redis, zk, consul, etcd3, sofa
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: DEFAULT_GROUP
      namespace:
      cluster: default
      username: nacos
      password: nacos
      context-path:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key:
      #secret-key:

在nacos配置seata属性

image-NvVQ.png

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

#For details about configuration items, see https://seata.apache.org/zh-cn/docs/user/configurations
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none

#Transaction routing rules configuration, only for the client
service.vgroupMapping.default_tx_group=default
#If you use a registry, you can ignore it
service.default.grouplist=127.0.0.1:8091
service.disableGlobalTransaction=false

client.metadataMaxAgeMs=30000
#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=true
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.rm.sagaJsonParser=fastjson
client.rm.tccActionInterceptorOrder=-2147482648
client.rm.sqlParserType=druid
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
client.tm.interceptorOrder=-2147482648
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h
# You can choose from the following options: fastjson, jackson, gson
tcc.contextJsonParserType=fastjson

#Log rule configuration, for client and server
log.exceptionRate=100

#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
store.mode=file
store.lock.mode=file
store.session.mode=file
#Used for password encryption
store.publicKey=

#If `store.mode,store.lock.mode,store.session.mode` are not equal to `file`, you can remove the configuration block.
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100

#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=username
store.db.password=password
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.distributedLockTable=distributed_lock
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000

#These configurations are required if the `store mode` is `redis`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `redis`, you can remove the configuration block.
store.redis.mode=single
store.redis.type=pipeline
store.redis.single.host=127.0.0.1
store.redis.single.port=6379
store.redis.sentinel.masterName=
store.redis.sentinel.sentinelHosts=
store.redis.sentinel.sentinelPassword=
store.redis.maxConn=10
store.redis.minConn=1
store.redis.maxTotal=100
store.redis.database=0
store.redis.password=
store.redis.queryLimit=100

#Transaction rule configuration, only for the server
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.distributedLockExpireTime=10000
server.session.branchAsyncQueueSize=5000
server.session.enableBranchAsyncRemove=false
server.enableParallelRequestHandle=true
server.enableParallelHandleBranch=false
server.applicationDataLimit=64000
server.applicationDataLimitCheck=false

server.raft.server-addr=127.0.0.1:7091,127.0.0.1:7092,127.0.0.1:7093
server.raft.snapshotInterval=600
server.raft.applyBatch=32
server.raft.maxAppendBufferSize=262144
server.raft.maxReplicatorInflightMsgs=256
server.raft.disruptorBufferSize=16384
server.raft.electionTimeoutMs=2000
server.raft.reporterEnabled=false
server.raft.reporterInitialDelay=60
server.raft.serialization=jackson
server.raft.compressor=none
server.raft.sync=true



#Metrics configuration, only for the server
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898

service.vgroupMapping.default-tx-group=default

启动

  1. 添加seata-client

    1. 建表

      CREATE TABLE IF NOT EXISTS `undo_log`
      (
        `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
        `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
        `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
        `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
        `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
        `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
        `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
        UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
      ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
      ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`);
      
  2. 加依赖

    <!--         加入seata -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>
    
  3. 修改配置

    seata:
        enabled: true
        application-id: ${spring.application.name}
        tx-service-group: default-tx-group
        config:
            type: nacos
            nacos:
                serverAddr: 127.0.0.1:8848
                dataId: "seataServer.properties"
                group: "DEFAULT_GROUP"
                username: 'nacos'
                password: 'nacos'
        registry:
            type: nacos
            nacos:
                application: seata-server
                server-addr: 127.0.0.1:8848
                group: "DEFAULT_GROUP"
                username: 'nacos'
                password: 'nacos'
    
  4. 加注解 @GlobalTransactional

  5. 抛异常,测试回滚

seata-at模式的执行流程

image-KrBN.png

  1. 请求到达TM,TM检查是否存在xid,若无,则向TC注册全局事务
  2. TC接收到请求,开启事务,返回xid
  3. TM将xid透传给后续的服务(RM)
  4. 后续服务,检查是否有xid,存在,则向TC申请加入全局事务
  5. TC维护全局事务的参与者列表
  6. TC协调所有参与者提交或回滚

配置管理

需求:统一配置管理平台

  1. 配置审计

  2. 公用配置

  3. 配置刷新

    image-ePwI.png

    基本示例

    1. 加依赖

      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
      </dependency>
      
  4. 新增配置(nacos)

    image-ddeB.png

    1. 应用配置移到nacos,保留spring.application.name

    2. 添加必要的配置

      spring:
          application:
              name: mainsite  #服务名
          cloud:
              nacos:
                  config:
                      server-addr: 127.0.0.1:8848
                      file-extension: yaml
          config:
              import:
                  # 使用 optional 前缀可以让服务在 Nacos 配置不存在时也能启动
                  # 这里导入了服务自身的配置,格式为:应用名-环境.文件扩展名
                  - optional:nacos:${spring.application.name}.${spring.cloud.nacos.config.file-extension}
      
      
  5. 重启

公共配置

spring:
    application:
        name: mainsite  #服务名
    cloud:
        nacos:
            config:
                server-addr: 127.0.0.1:8848
                file-extension: yaml
    config:
        import:
            # 使用 optional 前缀可以让服务在 Nacos 配置不存在时也能启动
            # 这里导入了服务自身的配置,格式为:应用名-环境.文件扩展名
            - optional:nacos:${spring.application.name}.${spring.cloud.nacos.config.file-extension}
            - optional:nacos:common.${spring.cloud.nacos.config.file-extension}
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  mvc:
    format:
      date-time: yyyy-MM-dd HH:mm:ss
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss

mybatis-plus:
  mapper-locations: classpath*:mappers/**/*.xml
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: default-tx-group
  config:
    type: nacos
    nacos:
      serverAddr: 127.0.0.1:8848
      dataId: "seataServer.properties"
      group: "DEFAULT_GROUP"
      username: 'nacos'
      password: 'nacos'
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: "DEFAULT_GROUP"
      username: 'nacos'
      password: 'nacos'
logging:
  level:
    com.woniuxy: debug

image-nClZ.png

配置刷新

image-iLkg.png