当前位置: 首页 > news >正文

网站代理建设wordpress 模板吧

网站代理建设,wordpress 模板吧,cc域名做网站怎么样,珠海网站建设知识1 前言 在Seata 中提供了一个全局锁注解GlobalLock#xff0c;字面意思是全局锁#xff0c;搜索相关文档#xff0c;发现资料很少#xff0c;所以分析下它的应用场景和基本原理#xff0c;首先看下源码中对该注解的说明#xff1a; // 声明事务仅在单个本地RM中执行 //…1 前言 在Seata 中提供了一个全局锁注解GlobalLock字面意思是全局锁搜索相关文档发现资料很少所以分析下它的应用场景和基本原理首先看下源码中对该注解的说明 // 声明事务仅在单个本地RM中执行 // 但事务需要确保要更新或选择更新的记录不在全局事务中 // 在上述情况下使用此注解而不是GlobalTransaction将有助于提高性能。 // see io.seata.spring.annotation.GlobalTransactionScanner#wrapIfNecessary(Object, String, Object)用于TM、GlobalLock和TCC模式的扫描器 // see io.seata.spring.annotation.GlobalTransactionalInterceptor#handleGlobalLock(MethodInvocation)GlobalLock的拦截器 // see io.seata.spring.annotation.datasource.SeataAutoDataSourceProxyAdvice#invoke(MethodInvocation) GlobalLockLogic和AT/XA模式的拦截器 Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD,ElementType.TYPE}) Inherited public interface GlobalLock {/*** 自定义全局锁重试间隔单位毫秒* 您可以使用它覆盖“client.rm.lock.retryInterval”的全局配置默认10* 注意0或负数将不起作用这意味着返回到全局配置*/int lockRetryInternal() default 0;/*** 自定义全局锁重试次数* 您可以使用它覆盖“client.rm.lock.retryTimes”的全局配置默认30* 注负数无效这意味着返回全局配置*/int lockRetryTimes() default -1; } 源码注释大概意思对于某条数据进行更新操作如果全局事务正在进行当某个本地事务需要更新该数据时需要使用GlobalLock确保其不会对全局事务正在操作的数据进行修改。 2 问题场景 我们参考下图搭建一个测试案例 2.1 编写代码 首先编写一个全局事务调用订单服务下订单扣除余额-1。 GlobalTransactional(rollbackFor Throwable.class, timeoutMills 300000)public void test() throws InterruptedException {log.info(Assign Service Begin ... xid: RootContext.getXID() \n);//1.创建账户 扣款AccountTbl accountTbl accountTblMapper.selectById(11111111);AccountTbl accountTbl1 accountTbl.setMoney(accountTbl.getMoney() - 1);accountTblMapper.updateById(accountTbl1);//2.创建订单orderClint.insert(accountTbl.getUserId() , iphone11, 1 );// 休眠5秒TimeUnit.SECONDS.sleep(5);int i 5 / 0;//模拟异常} 在编写一个本地Transactional事务直接扣除余额-1。 GetMapping(/GlobalLock)Transactionalpublic Object GlobalLock() {AccountTbl accountTbl accountTblMapper.selectById(11111111);AccountTbl accountTbl1 accountTbl.setMoney(accountTbl.getMoney()-1);accountTblMapper.updateById(accountTbl1);return 成功执行;} 2.2 测试 数据库修改余额为100 元然后测试全局事务接口发现异常时能正常全局回滚。 在执行全局事务的过程中调用GlobalLock接口修改数据因为全局事务接口中休眠了5秒所以需要在访问全局接口打印全局事务日志后快速访问GlobalLock接口。 这个时候会发现全局事务第二阶段回滚失败并一直在重试 原因分析 因为在全局事务执行的过程中一阶段会直接提交本地事务其他本地事务可直接修改该数据所以会导致全局事务二阶段回滚时发现数据被修改过认为数据已经脏了回滚失败。 2.3 解决方案 手动处理锁表然后直接将数据修改为正常状态但是这种比较麻烦需要梳理脏数据的原因也影响业务实际运行 提前预防使用GlobalLock在执行本地事务时去获取该数据的全局锁如果获取不到说明该数据正在被全局事务执行可以进行重试获取。 在本地修改事务上加上GlobalLock配置重试间隔为100ms次数为100次说明在10S内会不断重试获取全局锁如果该记录在全局事务中则会失败 GlobalLock(lockRetryInternal 100, lockRetryTimes 100)GetMapping(/GlobalLock)Transactionalpublic Object GlobalLock() {AccountTbl accountTbl accountTblMapper.selectById(11111111);AccountTbl accountTbl1 accountTbl.setMoney(accountTbl.getMoney() - 1);accountTblMapper.updateById(accountTbl1);return 成功执行;} 2.4 注意事项 在使用GlobalLock注解的时候我们需要更新之前在查询方法中添加排它锁比如根据ID 查询时需要如下SQL 书写 select idselectById parameterTypeinteger resultTypecom.hnmqet.demo01.entity.AccountTblSELECT id,user_id,money FROM account_tbl WHERE id#{id} FOR UPDATE/select 这是因为只有添加了 FOR UPDATESeata 才会进行创建重试的执行器这样事务失败时会释放本地锁等待一定时间再重试。如果不添加则会一直占有本地锁全局事务回滚需要本地锁则全局事务就只能等GlobalLock事务超时失败才能拿到本地锁释放全局锁造成GlobalLock永远获取不到全局锁。 3 源码分析 3.1 进入拦截器 之前分析过GlobalTransactionScanner全局事务扫描器会扫描GlobalLock、GlobalTransactional注解标识的方法并为其添加GlobalTransactionalInterceptor全局事务拦截器。 所以GlobalLock标注的方法执行时会进入到GlobalTransactionalInterceptor的invoke方法获取GlobalLock注解然后进入到handleGlobalLock方法处理。 handleGlobalLock方法会创建一个GlobalLockExecutor匿名内部类然后调用GlobalLockTemplate 的execute方法 Object handleGlobalLock(final MethodInvocation methodInvocation, final GlobalLock globalLockAnno) throws Throwable {return this.globalLockTemplate.execute(new GlobalLockExecutor() {public Object execute() throws Throwable {return methodInvocation.proceed();}public GlobalLockConfig getGlobalLockConfig() {// 获取GlobalLock 注解上的配置GlobalLockConfig config new GlobalLockConfig();config.setLockRetryInternal(globalLockAnno.lockRetryInternal());config.setLockRetryTimes(globalLockAnno.lockRetryTimes());return config;}});} GlobalLockTemplate模板类只有一个方法处理逻辑也很简单就是将注解配置塞入线程中结束后清理 public Object execute(GlobalLockExecutor executor) throws Throwable {boolean alreadyInGlobalLock RootContext.requireGlobalLock();if (!alreadyInGlobalLock) {RootContext.bindGlobalLockFlag();}// 将注解配置塞入ThreadLocal中GlobalLockConfig myConfig executor.getGlobalLockConfig();GlobalLockConfig previousConfig GlobalLockConfigHolder.setAndReturnPrevious(myConfig);try {// 调用内部类的执行方法执行业务逻辑return executor.execute();} finally {//仅当这是根调用者时解除绑定。//否则外部调用方将丢失全局锁标志if (!alreadyInGlobalLock) {RootContext.unbindGlobalLockFlag();}//如果前面的配置不是空的我们需要将其设置回原来的配置//这样外部逻辑仍然可以使用它们的配置if (previousConfig ! null) {GlobalLockConfigHolder.setAndReturnPrevious(previousConfig);} else {GlobalLockConfigHolder.remove();}}} 3.2 进入数据源代理 在执行业务逻辑时因为配置了数据源代理SQL 操作都会进入到代理数据源中大概流程为PreparedStatementProxy.executeExecuteTemplate.executeExecutor.executor。 因为我们根据ID 查询数据时加了 FOR UPDATE排它锁所以执行器为SelectForUpdateExecutor在这个执行方法中就会进行全局锁的获取这个时候会遇到以下几种情况 获取到全局锁则正常执行因为加了排它锁其他事务都会被隔离得等待当前事务执行完成 被全局事务占有全局锁和排它锁则会等待全局一阶段事务提交释放本地锁GlobalLock获取到本地锁后等待全局事务提交释放全局锁后再执行 如果全局失败回滚时需要排它锁这个时候GlobalLock因为没有获取到全局锁抛出异常会在异常中进行事务回滚休眠一定时间这个时候会让出排它锁全局获取到排它锁后再进行全局回滚成功释放全局锁GlobalLock在重试过程中获取到全局锁则成功执行做到了很好的事务隔离性。 Overridepublic T doExecute(Object... args) throws Throwable {// 1. 获取数据库连接Connection conn statementProxy.getConnection();// 2. 获取数据库元数据DatabaseMetaData dbmd conn.getMetaData();T rs;Savepoint sp null;boolean originalAutoCommit conn.getAutoCommit();try {if (originalAutoCommit) {/** 为了在全局锁检查期间保持本地数据库锁* 如果原始自动提交为true则首先将自动提交值设置为false*/conn.setAutoCommit(false);} else if (dbmd.supportsSavepoints()) {/** 为了在全局锁冲突时释放本地数据库锁* 如果原始自动提交为false则创建一个保存点然后使用此处的保存点释放db* 如有必要在全局锁定检查期间锁定*/sp conn.setSavepoint();} else {throw new SQLException(not support savepoint. please check your db version);}// 3. 创建一个锁重试控制器LockRetryController lockRetryController new LockRetryController();ArrayListListObject paramAppenderList new ArrayList();// 4. SELECT id FROM account_tbl WHERE id ? FOR UPDATE// String selectPKSQL buildSelectSQL(paramAppenderList);while (true) {try {// #870// rs statementCallback.execute(statementProxy.getTargetStatement(), args);// 尝试获取选定行的全局锁// 获取主键列及值TableRecords selectPKRows buildTableRecords(getTableMeta(), selectPKSQL, paramAppenderList);// 构建全局锁Key :account_tbl:11111111String lockKeys buildLockKey(selectPKRows);if (StringUtils.isNullOrEmpty(lockKeys)) {break;}if (RootContext.inGlobalTransaction() || RootContext.requireGlobalLock()) {// 在GlobalTransactional或GlobalLock下做同样的事情// 这里只检查全局锁statementProxy.getConnectionProxy().checkLock(lockKeys);} else {throw new RuntimeException(Unknown situation!);}break;} catch (LockConflictException lce) {// 如锁被占用会抛出锁冲突异常 LockConflictException// 直接回滚释放本地锁if (sp ! null) {conn.rollback(sp);} else {conn.rollback();}// 触发重试线程睡眠设置的时间超过重试此时则会抛出LockWaitTimeoutException 异常lockRetryController.sleep(lce);}}} finally {if (sp ! null) {try {if (!JdbcConstants.ORACLE.equalsIgnoreCase(getDbType())) {conn.releaseSavepoint(sp);}} catch (SQLException e) {LOGGER.error({} release save point error., getDbType(), e);}}if (originalAutoCommit) {conn.setAutoCommit(true);}}return rs;} 3.3 更新数据 在通过 FOR UPDATE 查询到数据后再更新当前数据因为查询和修改在一个Transactional方法里所以他们是一个事务在查询的时候添加了排它锁并且获取到了全局锁才会执行到更新方法。 FOR UPDATE 获取到全局锁后进入到业务的更新操作这里和一阶段执行本地事务完全一致之前分析过就不赘述了。
http://www.ho-use.cn/article/10816814.html

相关文章:

  • 天津 公司做网站网站制作河南
  • 重庆网站建设重庆零臻科技行赣州互联网公司
  • 中山网站建设文化报价网站开发使用技术第二版答案
  • 艾迪网络专业的网站建设公司网站建设流程有几个阶段
  • 企业网站 三网系统win7系统做asp网站
  • 石家庄网络推广建站网站推广工作如何做
  • 陕西做天然气公司网站谷歌浏览器网页版进入
  • 如何关闭网站 备案个人网站公司网站区别经营区别
  • 中英文切换网站开发广告学
  • 建站 网站程序如何购买网站虚拟主机
  • 网站源代码分列怎么做7c框架 网站建设
  • 旅游网站开发报告淮安做网站优化
  • 朝阳市建设厅查询网站企业文化墙设计公司
  • 用php做京东网站页面专业网站建设推广
  • 如何禁止某ip访问网站做网站游戏的网站有哪些
  • 怎么查看网站访问速度石家庄免费自助建站模板
  • 福建住房与城乡建设网站wordpress彩票类模板
  • 淘宝佣金推广网站建设中建八局第一建设有限公司电话
  • 网站策划与设计(百度推广可以自己开户吗
  • 泰安集团网站建设地点优化搜索引擎的方法
  • 电脑网站建设方案网站怎么用栏目做地区词
  • 交易网站备案p2p视频网站开发
  • 微信网站公司买了个网站源码后要怎么用
  • wordpress无法创建页面合肥seo按天扣费
  • 做网站订金为什么需要交那么多做积分商城网站
  • 帝国cms做漫画网站教程唐山路北网站建设
  • 衡水微信网站建设厦门网页设计代做
  • 网站开发需要证书吗优秀网页欣赏
  • 制作网站多少钱一个网页制作步骤流程
  • 如果网站设计时wordpress2018