一个缓存容灾写的样例

背景

有时我们能够使用缓存进行容灾的处理。场景例如以下:我们当前有一个专门提供各种数据的应用DataCore,该应用开放多个RFC方法供其它应用使用。

     我们平时在读写数据时,会在Cache备份一份(为平时DataCore提高响应速度、减少DB、CPU压力所用),当DB挂掉的时候。Cache还能够用来容灾。使用缓存容灾的优点是:性能足够好,坏处是缓存可比数据库成本高多了。

让我们想象得更猛烈些,当DataCore整个挂掉的时候,A、B、C、D方怎么才干安然的执行下去?

我们能够在A、B、C、D端上提供DataCore的缓存容灾服务。这样。即使在DataCore整个挂掉的情况下,其它应用也不会受影响。

要考虑的几个问题

  1. 容灾读的部分不必说。对象原本在存入缓存时就依据类型分了区域,读的时候直接在对应的区域取出就可以。

    容灾写的话,针对同一类型对象的写操作,怎样将这些对象集合在一块,待DataCore恢复后,再将容灾写过的对象覆盖到DB。
  2. 缓存的写操作必须是线程安全的

具体设计

缓存容灾写的一种可能策略是:针对每种类型的对象在缓存中开辟一大段储存空间(数组方式或者数组链接结合方式),然后把每一个容灾写的对象塞进这段空间内,在要覆写回DB时,直接从头到尾在缓存里把对象取出来就可以。示意图例如以下:

上面这样的设计缺点是须要一大片的连续的储存空间,对于缓存来说,这是要命的。

缓存的底层储存机制就是基于分散的hash。

上面设计的一种改进方案是。我们仅仅在连续空间中储存UserDO的唯一标示符。比方id或者key什么的。

这样我们的就不须要那么大的连续空间了。示意图例如以下:

更进一步,我们能够把UserDO的id也分散储存。能够利用一个DisasterIndexDO储存每个类型的容灾写的信息,利用beginIndex以及currentIndex字段为全部容灾写对象打上一个序号。在缓存中储存该序号与对象id的相应关系,然后我们就能够通过序号检索出id,再通过id检索出对象。

示意图例如以下:

在多个调用方在对某一类型的对象进行容灾写操作时,仅仅须要对DisasterIndexDO进行安全的并发訪问就可以,抢占currentIndex,然后再进行缓存的写操作。这样。我们的容灾写就实现了。

范例实现

//index对象
public class DisasterIndexDO implements Serializable {

private static final long serialVersionUID = -8688243351154917184L;
     public int namespace;
     public int beginIndex;
     public int currentIndex;
     public long expireLockTime;
     public static final long DEFAULT_EXPIRE_TIME = 50; // 当序列被锁时间超时,防止死锁

     public DisasterIndexDO(int namespace, int bIndex, int cIndex, long expireLockTime) {
        this.namespace = namespace;
        this.beginIndex = bIndex;
        this.currentIndex = cIndex;
        this.expireLockTime = expireLockTime;
     }
}
//容灾实现类
public class DisasterCacheHandler {
     RemoteCache remoteCache;

     private  final int DS_CACHE_NAMESPACE = 67;
     private  final int DS_WRITE_REPETECOUNT = 3;
     private  final int DISASTER_INDEX = 250;
     private  final String DISASTER_KEYS = "disaster_keys";
//容灾读
    public Object dsGetRemoteData(int namespace, String key){
         return remoteCache.get(DS_CACHE_NAMESPACE, namespace+key);
    }

    //本地同步的namespace
     private Map<Integer, Object> synNamespace = new
           HashMap<Integer, Object>();
     //容灾写
     protected boolean dsWriteRemoteData(int namespace,
          String key, Serializable value) {

          //先把数据写入缓存
          remoteCache.put(this.DS_CACHE_NAMESPACE, namespace+key, value);
          //本地同步的NameSpace
          if (!this.synNamespace.containsKey(Integer.valueOf(namespace))) {
                this.synNamespace.put(Integer.valueOf(namespace), namespace);
          }
          // update the Namespace Index and namespace disaster key queue
          synchronized (this.synNamespace.get(namespace)){
               int count = this.DS_WRITE_REPETECOUNT;
               DisasterIndexDO index = null;
               do {
                    count--;
                     // try to lock Namespace Index
                     int rc = remoteCache.lock(this.DISASTER_INDEX,
                         String.valueOf(namespace));
                    // Namespace Index not exist
                    if ( rc == 2 ) {
                         // Initialize Namespace Index
                         index = new DisasterIndexDO(namespace, 1, 1,
                              System.currentTimeMillis());
                         remoteCache.put(this.DISASTER_INDEX,
                              String.valueOf(namespace), index);
                 // for each namespace should handle disaster, keep the only index object
                         remoteCache.put(namespace,
                              this.DISASTER_KEYS + index.currentIndex, key);
                         return true;
                    } else if (rc == 0) { // lock failure
                         index = (DisasterIndexDO)remoteCache.
                                   get(this.DISASTER_INDEX, String.valueOf(namespace));
                    // 假设鎖已經超時。則解开,避免在訪问缓存时死住的情况
                         if (System.currentTimeMillis() - index.expireLockTime >
                                   DisasterIndexDO.DEFAULT_EXPIRE_TIME) {
                                        remoteCache.unLock(this.DISASTER_INDEX,
                                             String.valueOf(namespace));
                                   continue;
                          }
                         continue;
                   } else if (rc == 1) { // lock success
                         try {
                              index = (DisasterIndexDO)remoteCache.get(this.DISASTER_INDEX,
                                        String.valueOf(namespace));
                              // update locked Namespace Index
                              int curIdx = index.currentIndex + 1;
                              remoteCache.delete(this.DISASTER_INDEX,
                                        String.valueOf(namespace));
                              remoteCache.put(this.DISASTER_INDEX, String.valueOf(namespace),
                                   new DisasterIndexDO(namespace, index.beginIndex, curIdx,
                                        System.currentTimeMillis()));
                              // keep key of this Namespace with current index
                              remoteCache.put(namespace, this.DISASTER_KEYS + curIdx, key);
                              return true;
                         }  catch (Throwable e ) {
                         } finally {
                              // unlock and handle unlock failure
                              remoteCache.unLock(this.DISASTER_INDEX,
                                   String.valueOf(namespace));
                         }
                   }
             } while (count >= 0);
            if (count <= 0) {
                 return false;
            }
        }
    }
}
时间: 2024-10-11 07:51:09

一个缓存容灾写的样例的相关文章

一个缓存容灾写的例子

背景 有时我们可以使用缓存进行容灾的处理.场景如下:我们当前有一个专门提供各种数据的应用DataCore,该应用开放多个RFC方法供其他应用使用.      我们平时在读写数据时,会在Cache备份一份(为平时DataCore提高响应速度.降低DB.CPU压力所用),当DB挂掉的时候,Cache还可以用来容灾.使用缓存容灾的好处是:性能足够好,坏处是缓存可比数据库成本高多了. 让我们想象得更猛烈些,当DataCore整个挂掉的时候,A.B.C.D方怎么才能安然的运行下去? 我们可以在A.B.C.

Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动样例)

在Linux,网络分为两个层,各自是网络堆栈协议支持层,以及接收和发送网络协议的设备驱动程序层. 网络堆栈是硬件中独立出来的部分.主要用来支持TCP/IP等多种协议,网络设备驱动层是连接网络堆栈协议层和网络硬件的中间层. 网络设备驱动程序的主要功能是: (1)模块载入或内核启动相关的初始化处理 (2)清除模块时的处理 (3)网络设备的检索和探測 (4)网络设备的初始化和注冊 (5)打开或关闭网络设备 (6)发送网络数据 (7)接收网络数据 (8)中断处理(在发送完数据时.硬件向内核产生一个中断.

VB.net数据库编程(03):一个SQLserver连接查询的简单样例

这个样例,因为在ADO.net入门已经专门学了,再次进行复习 一下. 主要掌握连接字串的情况. 过程就是: 1.引用System.Data.SqlClient.而Access中引用 的是System.Data.OleDB.所以是有差别的 2.相关连接.适配器.数据集. 3.DataGridView绑定显示. 连接字串例如以下: Data Source                     IP地址或计算名(数据库所在位置的),假设是本地计算机能够用(local)或直接用.来取代,或者本地IP:

hdu1011(树形背包)(提供一个特殊样例)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011 Starship Troopers Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 23806    Accepted Submission(s): 6342 Problem Description You, the leader o

从零学scala(七)集合、模式匹配和样例类

一:集合 主要的集合特质 scala集合中重要的特质: Trait(Iterable) Trait(Seq) Trait(Set) Trait(Map) Trait(IndexedSeq) Trait(SoredSet) Trait(SoredMap) Seq是一个有先后次序的值的序列,比如数组和列表.IndexSeq允许我们通过下表快速访问元素,ArrayBuffer是带下标的,但是链表不是. Set是一个没有先后次序的值的序列,SortedSet中,元素以排过序的顺序被访问. Map是一组(

ADF Faces导出Excel文件【附样例工程】

本文提供一个基于ADF Face组件开发样例工程,工程的实现过程分为3个部分以应对Excel导出开发中常见的处理. 1.空模版文件下载:将Excel文件视为普通文件提供下载操作. 2.数据文件输出,将数据内容输出为Excel文件,目标文件尽在服务端内存中存在,这种方式需要对Excel文件的内容处理,需要引入响应的类库. 3.模版文件填充数据后下载,基于服务端的物理文件为模板,将业务数据填入约定位置后提供下载,在实现方面需要为工作簿对象指定源文件输入流,并完成后续内容处理. 实现的基本思路,由AD

ADF Faces 表格应用基础案例二:动态字段+事件处理【附样例工程】

本文提供一个基于ADF Face组件开发样例工程,实现表格开发中常见的处理: 1.Map对象+Bean对象填充表格的数据行. 2.使用静态列.动态列.嵌套列的实现方法. 3.介绍表格中表单组件的使用方法. 4.介绍表格单行选中事件的处理过程. 本文是基于"ADF Faces 表格应用基础案例一:应用List<Class>填充文本表格"编写的,会省去许多细节部分的介绍. 实现的基本思路: 将样例工程的创建过程分为几个小的阶段,每个阶段实现了不同的目标. 第一阶段: 表格数据:

使用SALT-API进入集成开发的简单样例

测试的时候,可以CURL -K,但真正作集成的时候,却是不可以的. 必须,不可以让TOKEN满天飞吧. 现在进入这个阶段了.写个样例先: import salt import salt.auth import salt.log import saltapi opts = salt.client.LocalClient().opts auth = salt.auth.LoadAuth(opts) lowstate = {'username':'XXX','password':'XXX','eaut

使用Dropzone上传图片及回显演示样例

一.图片上传所涉及到的问题 1.HTML页面中引入这么一段代码 <div class="row"> <div class="col-md-12"> <form dropzone2 class="dropzone" enctype="multipart/form-data" method="post"></form> </div> </div&