REDIS客戶端封裝意淫

先拋出來一個(gè)問題其垄?


redis 本身有客戶端扔役,為什么要對(duì)redis客戶端進(jìn)行二次封裝权纤?

大概在11年時(shí)侯社证,第一次接觸redis,那時(shí)侯研究過redis的各種數(shù)據(jù)結(jié)構(gòu)逼龟,直接拿redis的客戶端jedis直接用。公司安排人要對(duì)jedis進(jìn)行封裝追葡,當(dāng)時(shí)就很不理解腺律,為什么非要封裝一次才可以?

寫框架之后


后來自己寫框架宜肉,意識(shí)到一些東西是需要封裝的匀钧,比如連接的打開和釋放,比如一些危險(xiǎn)的方法谬返,比如keys * 比如flushdb 等

后來形成了這樣的代碼結(jié)構(gòu)


T execute(Executor executor, KEY key)throws CacheConnectionException {

ShardedJedis jedis =null;

try {

Long startTime = System.currentTimeMillis();

jedis =this.pool.getResource();.//連接的獲取

T result = executor.execute(jedis);

this.pool.returnResource(jedis);//連接的釋放

Long endTime = System.currentTimeMillis();

if(this.cacheMonitor!=null) {

this.cacheMonitor.monitor(startTime, endTime, key);

}

return result;

}catch (JedisConnectionException e) {

this.pool.returnBrokenResource(jedis);//出錯(cuò)時(shí)連接釋放

logger.error(this.getInfo() + SYMBOL.COLON + e.getMessage());

throw new CacheConnectionException(e.getMessage());

}

}

業(yè)務(wù)層會(huì)出現(xiàn)這樣的代碼


@Override

public Long addToSet(final KEY key,final Object value)throws CacheConnectionException {

//拋出connection exception異常提示業(yè)務(wù)方捕獲處理

return redisPool.execute(new Executor() {

@Override

        public Long execute(ShardedJedis jedis) {

return jedis.sadd(key.key(),value.toString());

}

},key);

}

對(duì)連接的打開和釋放進(jìn)行了封裝榴捡,避免業(yè)務(wù)端忘關(guān)鏈接而導(dǎo)致連接超時(shí)。拋出聲明式異常朱浴,提示業(yè)務(wù)端處理鏈接斷開的場(chǎng)景。

從業(yè)務(wù)使用上來講基本不會(huì)有什么問題达椰。

但這種結(jié)構(gòu)存在幾個(gè)問題


  • 業(yè)務(wù)端存在jedis代碼翰蠢,如果想換jedis客戶端,成本很大

  • 如果業(yè)務(wù)端想換另一種no sql 成本一樣很大啰劲。

  • 監(jiān)控梁沧,當(dāng)業(yè)務(wù)足夠復(fù)雜時(shí),對(duì)key的監(jiān)控蝇裤,問題排查和熱點(diǎn)隔離成為痛點(diǎn)廷支,如果讓業(yè)務(wù)端對(duì)每一個(gè) redis的請(qǐng)求點(diǎn)都加代碼監(jiān)控的話频鉴,這個(gè)成本依然很大。

一般公司業(yè)務(wù)都是從單體應(yīng)用到分布式應(yīng)用,如果某臺(tái)機(jī)器報(bào)超時(shí)恋拍,對(duì)業(yè)務(wù)的key的監(jiān)控就比較困難,比如

KEY為USER:1 USER:2 ...USER.N 進(jìn)行監(jiān)控垛孔,如何識(shí)別這是一組KEY?

REDIS分布式架構(gòu)演進(jìn)


image

所以對(duì)KEY的規(guī)范和統(tǒng)一監(jiān)控就成為痛點(diǎn)

意淫部分


  • KEY的規(guī)范

模塊.業(yè)務(wù)類型:key id

redis KEY的定義
image
REDIS  相關(guān)類圖
image
REDIS 操作接口定義

package com.sparrow.cache;

import com.sparrow.constant.cache.KEY;

import com.sparrow.exception.CacheConnectionException;

import com.sparrow.support.Entity;

import java.util.List;

import java.util.Map;

/**

* @author harry

* @date 2018/1/18

*/

public interface CacheClient {

Map hashGetAll(KEYkey)throws CacheConnectionException;

Map hashGetAll(KEYkey,Class keyClazz, Class dataClazz)throws CacheConnectionException;

Long getHashSize(KEYkey)throws CacheConnectionException;

Long getSetSize(KEYkey)throws CacheConnectionException;

Long removeFromOrderSet(KEYkey, Long from, Long to)throws CacheConnectionException;

Double getScore(KEYkey, Object value)throws CacheConnectionException;

Long getIndexOfOrderSet(KEYkey, Object value)throws CacheConnectionException;

Map getAllWithScore(KEYkey, Class clazz)throws CacheConnectionException;

Long getListSize(KEYkey)throws CacheConnectionException;

String hashGet(KEYkey, String hashKey)throws CacheConnectionException;

T hashGet(KEYkey, String hashKey, Class clazz)throws CacheConnectionException;

Long hashSet(KEYkey, String hashKey, Object value)throws CacheConnectionException;

//order set

    Long getOrderSetSize(KEYkey)throws CacheConnectionException;

Long addToSet(KEYkey, Object value)throws CacheConnectionException;

Long addToSet(KEYkey, String... value)throws CacheConnectionException;

Integer addToSet(KEYkey, Iterable values)throws CacheConnectionException;

Long addToList(KEYkey, Object value)throws CacheConnectionException;

Long removeFromList(KEYkey, Object value)throws CacheConnectionException;

Long removeFromSet(KEYkey, Object value)throws CacheConnectionException;

Long addToOrderSet(KEYkey, Object value, Long score)throws CacheConnectionException;

Long removeFromOrderSet(KEYkey, Object value)throws CacheConnectionException;

Boolean existInSet(KEYkey, Object value)throws CacheConnectionException;

Long addToList(KEYkey, String... value)throws CacheConnectionException;

Integer addToList(KEYkey, Iterable values)throws CacheConnectionException;

Long expire(KEYkey, Integer expire)throws CacheConnectionException;

Long delete(KEYkey)throws CacheConnectionException;

Long expireAt(KEYkey, Long expire)throws CacheConnectionException;

String setExpire(KEYkey, Integer seconds, Object value)throws CacheConnectionException;

String set(KEYkey, Entity value)throws CacheConnectionException;

String set(KEYkey, Object value)throws CacheConnectionException;

String get(KEYkey)throws CacheConnectionException;

T get(KEYkey, Class clazz)throws CacheConnectionException;

List getAllOfList(KEYkey)throws CacheConnectionException;

List getAllOfList(KEYkey, Class clazz)throws CacheConnectionException;

Long setIfNotExist(KEYkey, Object value)throws CacheConnectionException;

Long append(KEYkey, Object value)throws CacheConnectionException;

Long decrease(KEYkey)throws CacheConnectionException;

Long decrease(KEYkey, Long count)throws CacheConnectionException;

Long increase(KEYkey, Long count)throws CacheConnectionException;

Long increase(KEYkey)throws CacheConnectionException;

boolean bit(KEYkey, Integer offset)throws CacheConnectionException;

}

業(yè)務(wù)方必須統(tǒng)一按KEY類型讀寫redis,類型保護(hù)施敢,保證KEY的規(guī)范一致周荐。

KEY的定義

/*

* 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.

*/

package com.sparrow.constant.cache;

import com.sparrow.constant.magic.SYMBOL;

import com.sparrow.core.Pair;

import com.sparrow.support.ModuleSupport;

import com.sparrow.utility.StringUtility;

import java.util.Arrays;

/**

* Created by harry on 2018/1/8.

*/

public class KEY {

private Stringbusiness;

private ObjectbusinessId;

private Stringmodule;

private KEY(){}

private KEY(Builder builder) {

this.business = builder.business.getKey();

this.module=builder.business.getModule();

if (builder.businessId !=null) {

this.businessId = StringUtility.join(Arrays.asList(builder.businessId), SYMBOL.DOT);

}

}

public static KEY parse(String key){

if(StringUtility.isNullOrEmpty(key)){

return null;

}

KEY k=new KEY();

Pair businessWithId=Pair.split(key,SYMBOL.COLON);

k.businessId=businessWithId.getSecond();

String[] businessArray=businessWithId.getFirst().split("\\.");

k.module=businessArray[0];

k.business=businessWithId.getFirst();

return k;

}

public String key() {

if (StringUtility.isNullOrEmpty(this.businessId)) {

return this.business;

}

return this.business + SYMBOL.COLON +this.businessId;

}

public String getBusiness() {

return business;

}

public String getModule() {

return module;

}

public static class Business {

private Stringmodule;

private Stringkey;

public Business(ModuleSupport module, String... business) {

this.module = module.name();

this.key =this.module;

if (business !=null && business.length >0) {

this.key += SYMBOL.DOT + StringUtility.join(Arrays.asList(business), SYMBOL.DOT);

}

}

public String getKey() {

return key;

}

public String getModule() {

return module;

}

}

public static class Builder {

private Businessbusiness;

private Object[]businessId;

public Builder business(Business business) {

this.business = business;

return this;

}

public Builder businessId(Object... businessId) {

this.businessId = businessId;

return this;

}

public KEY build() {

return new KEY(this);

}

}

}

業(yè)務(wù)方可通過實(shí)現(xiàn)CacheMonitor 接口,對(duì)KEY進(jìn)行統(tǒng)一監(jiān)控

/**

* Created by harry on 2018/1/25.

*/

public class SparrowCacheMonitorimplements CacheMonitor{

@Override

    public void monitor(Long startTime, Long endTime, KEY key) {

可以對(duì)module 或business維護(hù)對(duì)KEY進(jìn)行監(jiān)控

System.out.println("module-"+key.getModule()+" business.type-"+key.getBusiness()+" key-"+key.key()+" start.time-"+startTime+" end.time-"+endTime);

}

}

DEMO實(shí)例

/**

* @author by harry

*/

public class RedisTest {

public static void main(String[] args)throws CacheConnectionException {

Container container =new SparrowContainerImpl();

//定義模塊僵娃,一個(gè)業(yè)務(wù)會(huì)存在多個(gè)模塊

        ModuleSupport OD=new ModuleSupport() {

@Override

            public String code() {

return "01";

}

@Override

            public String name() {

return "OD";

}

};

//相同模塊下會(huì)存在多個(gè)業(yè)務(wù)

        KEY.Business od=new KEY.Business(OD,"POOL");

container.init();

CacheClient client = container.getBean("cacheClient");

//相同業(yè)務(wù)下存在多個(gè)KEY

        KEY key =new KEY.Builder().business(od).businessId("BJS","CHI","HU").build();

client.set(key,"test");

KEY k2=KEY.parse("OD.POOL:BJS.CHI.HU");

System.out.println("key:"+k2.key()+",module:"+k2.getModule()+" business:"+k2.getBusiness());

}

}

運(yùn)行結(jié)果

容器初始化...

module-OD business.type-OD.POOL key-OD.POOL:BJS.CHI.HU start.time-1516877958682 end.time-1516877958714

key:OD.POOL:BJS.CHI.HU,module:OD business:OD.POOL

源碼下載


https://github.com/sparrowzoo/sparrow-shell

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末概作,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子默怨,更是在濱河造成了極大的恐慌讯榕,老刑警劉巖训唱,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件说订,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡媒鼓,警方通過查閱死者的電腦和手機(jī)垃僚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門集绰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谆棺,你說我怎么就攤上這事栽燕。” “怎么了改淑?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵碍岔,是天一觀的道長。 經(jīng)常有香客問我朵夏,道長蔼啦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任仰猖,我火速辦了婚禮捏肢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饥侵。我一直安慰自己鸵赫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布躏升。 她就那樣靜靜地躺著辩棒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上一睁,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天钻弄,我揣著相機(jī)與錄音,去河邊找鬼者吁。 笑死窘俺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的砚偶。 我是一名探鬼主播批销,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼染坯!你這毒婦竟也來了均芽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤单鹿,失蹤者是張志新(化名)和其女友劉穎掀宋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仲锄,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡劲妙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了儒喊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镣奋。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖怀愧,靈堂內(nèi)的尸體忽然破棺而出侨颈,到底是詐尸還是另有隱情,我是刑警寧澤芯义,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布哈垢,位于F島的核電站,受9級(jí)特大地震影響扛拨,放射性物質(zhì)發(fā)生泄漏耘分。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一绑警、第九天 我趴在偏房一處隱蔽的房頂上張望求泰。 院中可真熱鬧,春花似錦计盒、人聲如沸拜秧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春暖庄,著一層夾襖步出監(jiān)牢的瞬間聊替,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工培廓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惹悄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓肩钠,卻偏偏與公主長得像泣港,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子价匠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容