☰
Current Page
Main Menu
Home
Home
Editing
应用市场安装app异常排查
Edit
Preview
h1
h2
h3
default
Set your preferred keybinding
default
vim
emacs
markdown
Set this page's format to
AsciiDoc
Creole
Markdown
MediaWiki
Org-mode
Plain Text
RDoc
Textile
Rendering unavailable for
BibTeX
Pod
reStructuredText
Help 1
Help 1
Help 1
Help 2
Help 3
Help 4
Help 5
Help 6
Help 7
Help 8
Autosaved text is available. Click the button to restore it.
Restore Text
### 应用市场安装app异常排查 ##### 1,修改表结构,导致死锁 [[http://iidp.chinasie.com:9999/iidpminio/issure/deadlock.png]] 由上图死锁导致整个流程卡住,安装app的流程无法继续,导致应用市场安装app异常。 下面的是详细的业务分析 [[http://iidp.chinasie.com:9999/iidpminio/issure/lock-1.png]] [[http://iidp.chinasie.com:9999/iidpminio/issure/lock-2.png]] [[http://iidp.chinasie.com:9999/iidpminio/issure/lock-4.jpg]] 可以发现数据库存在很多死锁 [[http://iidp.chinasie.com:9999/iidpminio/issure/lock-3.png]] 进一步可以查看相关代码 [[http://iidp.chinasie.com:9999/iidpminio/issure/lock-5.jpg]] 上图显示如果锁表以后,程序一直卡在 preepare write final meta to redis 以及 end write final meta to redis 结合代码发现恰好卡在修改表结构这里。 ```java // 加载配置 AppLifecycleFacade.initProcess(meta, MetaConstant.BUSSINESS, allInstallAppList); AppLifecycleFacade.initShard(); // 创建表结构. 修改表结构,可能会导致锁表。 AppLifecycleFacade.prepareAutoInitModels(appGroupContainer, allInstallAppList); // 根据全量模型生成业务种子数据,前端目录要与下载一致 SeedUtils.loadSeedData(meta, allInstallAppList, appGroupContainer,MetaConstant.BASE_FRONTEND); // 加载文件种子数据 AppLifecycleFacade.loadFileSeed(meta, allInstallAppList); ``` 除了锁表或者死锁等原因以外,可能还存在获取数据conn阻塞,导致安装流程卡死。执行 `jstack -l 1` 结果如下: ``` "http-nio-8060-exec-3" #131 daemon prio=5 os_prio=0 tid=0x00007f15d4009800 nid=0x8a waiting on condition [0x00007f161dbe2000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000005d1424370> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146) at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33) at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) -- at com.alibaba.druid.filter.stat.StatFilter.dataSource_getConnection(StatFilter.java:726) at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:5055) at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1407) at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1399) at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:100) at com.sie.snest.engine.db.relationdb.RelationDBAccessor.<init>(RelationDBAccessor.java:183) at com.sie.snest.engine.a.a.getNewAccessorFromDataSource(AbstractDataSourceStrategy.java:125) at com.sie.snest.engine.a.a.getDbAccessor(AbstractDataSourceStrategy.java:108) at com.sie.snest.engine.a.f.getRelationDBAccessor(DbDataSourceStrategy.java:18) at com.sie.snest.engine.data.access.BussModelDataAccess.getRelationDBAccessor(BussModelDataAccess.java:125) at com.sie.snest.engine.container.Meta.getRelationDBAccessor(Meta.java:135) at com.sie.snest.engine.model.loaders.SeedLoader.a(SeedLoader.java:2036) at com.sie.snest.engine.model.loaders.SeedLoader$$Lambda$1535/1824031629.accept(Unknown Source) at java.util.HashMap.forEach(HashMap.java:1289) at com.sie.snest.engine.model.loaders.SeedLoader.update(SeedLoader.java:308) at com.sie.snest.engine.model.loaders.AppDataInfo.updateSeedData(AppDataInfo.java:921) at com.sie.iidp.appstore.utils.SeedUtils.loadSeedData(SeedUtils.java:68) at com.sie.iidp.appstore.handler.service.FinalModelHandler.doComputeMeta(FinalModelHandler.java:141) at com.sie.iidp.appstore.handler.service.FinalModelHandler.handle(FinalModelHandler.java:60) at com.sie.iidp.appstore.handler.InstallStateMachine.execute(InstallStateMachine.java:26) at com.sie.iidp.appstore.appstore.DistributedAppLoaderV2.updateApps(DistributedAppLoaderV2.java:405) at com.sie.iidp.appstore.meta.MetaApp.updateApp(MetaApp.java:749) at com.sie.iidp.appstore.meta.MetaApp$$EnhancerByCGLIB$$9b7d6ddd.CGLIB$updateApp$6(<generated>) at com.sie.iidp.appstore.meta.MetaApp$$EnhancerByCGLIB$$9b7d6ddd$$FastClassByCGLIB$$f7fc717f.invoke(<generated>) at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) at com.sie.snest.engine.model.ModelInterceptor.intercept(ModelInterceptor.java:84) at com.sie.iidp.appstore.meta.MetaApp$$EnhancerByCGLIB$$9b7d6ddd.updateApp(<generated>) -- ``` 可以发现整个线程卡死在 `com.alibaba.druid.filter.stat.StatFilter.dataSource_getConnection(StatFilter.java:726)` ,一直等待连接。 ##### 2,调用distribute安装接口异常 [[http://iidp.chinasie.com:9999/iidpminio/issure/distributed-install.png]] 调用distribute接口异常,导致应用市场安装app失败,但是应用市场已经对数据库表、redis元模型数据更新,并没有回滚,导致数据不一致。 [[http://iidp.chinasie.com:9999/iidpminio/issure/service-conflict.png]] 如上图可知,当更新或者删除`deployment`和`service`的时候,会产生`Conflict`,意味着存在并发竞争的情况,这也是很常见的情况,由于etcdf采用mvcc机制来控制并发,常用的解决方式就是重试,详情可以参考官方文档:[etcd官网](https://etcd.io/) ##### 3,应用市场安装app异常 与场景2类似,应用市场本身的安装app接口异常,也没有回滚数据,导致数据不一致。 实际测试场景中出现的概率可能不是很高,但是肯定也存在这种异常情况,因为任何外部网络io请求、第三方中间件依赖等,都会出现异常的情况,业务必须考虑这种异常情况的存在性,并做必要的处理。 ### 解决方案 #### 1,业务制定回滚流程 应用市场安装app异常后,业务制定回滚流程,回滚数据库表、redis元模型数据等。 数据库在死锁后可以使死锁事务全部回滚,比如mysql可以配置innodb_rollback_on_timeout参数。 一个成熟的数据库产品肯定是满足事务的ACID的. #### 2,手动卸载并重新安装app 如果应用市场安装app异常,可以手动卸载已经安装的app,并重新安装。 distribute暴露的安装和卸载接口是幂等的,可以多次调用。 对于死锁(数据库锁表、分布式死锁等)情况,需要业务制定死锁处理流程,比如手动kill死锁的sql语句,或者重启应用市场服务等。 #### 3,异步处理,最终一致性 大概就是参考k8s如何启动一个pod的流程,具体还没想好怎么做 ૮(˶ᵔ ᵕ ᵔ˶)ა #### 4,解耦种子数据和业务数据 详情参考米久业给出的最新解决方案 任何一个系统都无法保证做一件事情一定成功,这是不可能的,但是任何一个系统必须保证做一件事情结果的一致性,要么成功,要么失败,这是确定性的。 我们常说的tcp协议是面向连接的可靠传输协议,但是tcp从来不保证一定传输成功,tcp只保证数据传输了就一定成功了,同时传输失败了就一定失败了,没有中间状态。 类似地,对于distributed的安装、卸载和更新接口,它底层是etcd,etcd也不保证一定成功,但是保证数据的地表最强的一致性,要么成功,要么失败。
Uploading file...
Sidebar
[[_TOC_]]
Edit message:
Cancel