dubbo 注册层源码解析 注册中心分布式锁失效

每次一上线就发现有时dubbo调用接口超时会报如下的错误

2019-07-23 12:52:50,932 DEBUG [ZkEventThread.java:69] : Delivering event #3600 ZkEvent[Children of /dubbo/com.zoe.base.healthservice.api.mservice.indexmanager.RecIndexEddApi/providers changed sent to com.alibaba.dubbo.remoting.zookeeper.zkclient.ZkclientZookeeperClient$2@1ced40b]
2019-07-23 12:52:50,934 DEBUG [ClientCnxn.java:818] : Reading reply sessionid:0x100010739720373, packet:: clientPath:null serverPath:null finished:false header:: 10372,3  replyHeader:: 10372,42375930,0  request:: '/dubbo/com.zoe.base.healthservice.api.mservice.indexmanager.RecIndexEddApi/providers,T  response:: s{22154898,22154898,1551930057480,1551930057480,0,1674,0,0,0,2,42375930} 
2019-07-23 12:52:50,936  WARN [AbstractRegistry.java:221] :  [DUBBO] Failed to save registry store file, cause: Can not lock the registry cache file C:\Users\Du Wenqing\.dubbo\dubbo-registry-172.16.34.101.cache, ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties, dubbo version: 2.5.3, current host: 172.16.36.42
java.io.IOException: Can not lock the registry cache file C:\Users\Du Wenqing\.dubbo\dubbo-registry-172.16.34.101.cache, ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties
    at com.alibaba.dubbo.registry.support.AbstractRegistry.doSaveProperties(AbstractRegistry.java:193)
    at com.alibaba.dubbo.registry.support.AbstractRegistry$SaveProperties.run(AbstractRegistry.java:150)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

跟踪源码发现,在注册中心保存配置的时候

public void doSaveProperties(long version) {
        if (version >= this.lastCacheChanged.get()) {
            if (this.file != null) {
                Properties newProperties = new Properties();
                FileInputStream in = null;

                try {
                    if (this.file.exists()) {
                        in = new FileInputStream(this.file);
                        newProperties.load(in);
                    }
                } catch (Throwable var69) {
                    this.logger.warn("Failed to load registry store file, cause: " + var69.getMessage(), var69);
                } finally {
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException var64) {
                            this.logger.warn(var64.getMessage(), var64);
                        }
                    }

                }

                try {
                    newProperties.putAll(this.properties);
                    File lockfile = new File(this.file.getAbsolutePath() + ".lock");
                    if (!lockfile.exists()) {
                        lockfile.createNewFile();
                    }

                    RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");

                    try {
                        FileChannel channel = raf.getChannel();

                        try {
                            FileLock lock = channel.tryLock();
                            if (lock == null) {
                                throw new IOException("Can not lock the registry cache file " + this.file.getAbsolutePath() + ", ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties");
                            }

                            try {
                                if (!this.file.exists()) {
                                    this.file.createNewFile();
                                }

                                FileOutputStream outputFile = new FileOutputStream(this.file);

                                try {
                                    newProperties.store(outputFile, "Dubbo Registry Cache");
                                } finally {
                                    outputFile.close();
                                }
                            } finally {
                                lock.release();
                            }
                        } finally {
                            channel.close();
                        }
                    } finally {
                        raf.close();
                    }
                } catch (Throwable var71) {
                    if (version < this.lastCacheChanged.get()) {
                        return;
                    }

                    this.registryCacheExecutor.execute(new AbstractRegistry.SaveProperties(this.lastCacheChanged.incrementAndGet()));
                    this.logger.warn("Failed to save registry store file, cause: " + var71.getMessage(), var71);
                }

            }
        }
    }

因为dubbo底层是用netty进行数据通信协议传输的,根据多路复用的思想,对于每一个线程都会为其创建一个channel进行读写。而在分布式系统中,如果有来自不同系统的线程同时对缓存文件进行读写时,就会造成死锁

所以在FileChannel中提供了一个自旋操作tryLock

public FileLock tryLock(long var1, long var3, boolean var5) throws IOException {
        this.ensureOpen();
        if (var5 && !this.readable) {
            throw new NonReadableChannelException();
        } else if (!var5 && !this.writable) {
            throw new NonWritableChannelException();
        } else {
            FileLockImpl var6 = new FileLockImpl(this, var1, var3, var5);
            FileLockTable var7 = this.fileLockTable();
            var7.add(var6);
            int var9 = this.threads.add();

            FileLockImpl var11;
            try {
                int var8;
                try {
                    this.ensureOpen();
                    var8 = this.nd.lock(this.fd, false, var1, var3, var5);
                } catch (IOException var15) {
                    var7.remove(var6);
                    throw var15;
                }

                FileLockImpl var10;
                if (var8 == -1) {
                    var7.remove(var6);
                    var10 = null;
                    return var10;
                }

                if (var8 != 1) {
                    var10 = var6;
                    return var10;
                }

                assert var5;

                var10 = new FileLockImpl(this, var1, var3, false);
                var7.replace(var6, var10);
                var11 = var10;
            } finally {
                this.threads.remove(var9);
            }

            return var11;
        }
    }

分为以下几个步骤

  • 定义了来自不同Channel的FileLock,并把FileLock的信息用FileLockImpl进行封装,交给FileLockTable进行记录。
  • 记录当前FileLock使用的线程数,加入线程队列
  • 根据获得锁的线程,把文件描述符指定给对应的锁
  • 如果锁对象不存在,就从线程队列中把该线程移除

这个时候如果consumer调用provider,首先会判断是否存在锁对象,如果不在的话,也就不能将文件描述符指定给相应的所对象,进而调用本地缓存,debug如下所示

index2.png
index4.png
index5.png

由于竞争文件锁导致的,那么让服务模块各自缓存自己的cache文件就可以避免这样的问题了。

具体做法是:在provider的xml配置文件中加入

<dubbo:registry id="zkcenter" protocol="zookeeper" address="${dubbo.zk_address}" file="${catalina.home}/dubbo-registry/dubbo-registry.properties"/>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,408评论 4 371
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,690评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,036评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,726评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,123评论 3 296
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,037评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,178评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,964评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,703评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,863评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,333评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,658评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,374评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,195评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,988评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,167评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,970评论 2 279