您好,登錄后才能下訂單哦!
提高微服務(wù)可用性的中間件CoralCache,針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。
當(dāng)數(shù)據(jù)庫出問題時(shí)能降級(jí)從本地緩存的數(shù)據(jù)中查詢數(shù)據(jù), CoralCache就是這樣一個(gè)提高微服務(wù)可用性的中間件。
有些場(chǎng)景下,微服務(wù)依賴數(shù)據(jù)庫中一些配置項(xiàng)或者數(shù)量很少的數(shù)據(jù),但當(dāng)數(shù)據(jù)庫本身有問題時(shí)候,即使數(shù)據(jù)量很少,這個(gè)服務(wù)是不能正常工作;因此需要考慮一種能支持全量+極少變更的全局?jǐn)?shù)據(jù)的場(chǎng)景,當(dāng)數(shù)據(jù)庫出問題時(shí)能降級(jí)從本地緩存的數(shù)據(jù)中查詢數(shù)據(jù),CoralCache就是這樣一個(gè)提高微服務(wù)可用性的中間件。
CoralCache中間件架構(gòu)如下圖所示,通過@EnableLocal注解開啟功能,應(yīng)用啟動(dòng)后將配置的表數(shù)據(jù)一次性加載到內(nèi)存中,內(nèi)存中的數(shù)據(jù)邏輯結(jié)構(gòu)和數(shù)據(jù)庫中的邏輯結(jié)構(gòu)一樣。
圖1. 架構(gòu)圖
內(nèi)存查詢引擎的原理是數(shù)據(jù)庫查詢降級(jí)發(fā)生后,Intercepter將攔截到的原始SQL傳入查詢引擎中,查詢引擎解析SQL后得到表名、列名、where條件表達(dá)式,遍歷InnerDB中對(duì)應(yīng)表的數(shù)據(jù)行,并通過表達(dá)式計(jì)算引擎計(jì)算結(jié)果,計(jì)算結(jié)果為真則添加到結(jié)果集中最后返回給調(diào)用方。
計(jì)算引擎結(jié)構(gòu)如下圖所示,將where條件表達(dá)式轉(zhuǎn)為后綴表達(dá)式后依次遍歷后綴表達(dá)式,遇到操作數(shù)直接入棧,遇到操作符則根據(jù)操作符需要的操作數(shù)個(gè)數(shù)彈棧。
圖2. 表達(dá)式計(jì)算引擎結(jié)構(gòu)
然后根據(jù)操作符和彈出的操作數(shù)進(jìn)行計(jì)算,不同操作符對(duì)應(yīng)不同的計(jì)算方法,并將計(jì)算后的結(jié)果重新作為操作數(shù)入棧執(zhí)到遍歷完成,核心計(jì)算流程代碼如下所示:
public Object calc(Expression where, InnerTable table, InnerRow row) { try { postTraversal(where); } catch (Exception e) { log.warn("calc error: {}", e.getMessage()); return false; } for (ExprObj obj : exprList) { switch (obj.exprType()) { case ITEM: stack.push(obj); break; case BINARY_OP: { ExprObj result = calcBinaryOperation(((ExprOperation) obj).getOperationType(), table, row); stack.push(result); break; } case UNARY_OP: { ExprObj result = calcSingleOperation(((ExprOperation) obj).getOperationType(), table, row); stack.push(result); break; } case FUNCTION_OP: { ExprObj result = calcFunctionOperation(((ExprOperation) obj).getOperationType(), table, row); stack.push(result); break; } default: break; } } return stack.pop(); }
邏輯常見運(yùn)算符為<、<=、>、>=、=等,它們的共性都是需要2個(gè)操作數(shù)并且返回值是布爾類型。
public ExprItem logicalCalculus(InnerTable table, InnerRow row, LogicalOperation logicalOperation) { ExprObj second = stack.pop(); ExprObj first = stack.pop(); ExprItem result = new ExprItem(); result.setItemType(ItemType.T_CONST_OBJ); Obj firstObj = getObj((ExprItem) first, table, row); Obj secondObj = getObj((ExprItem) second, table, row); boolean value = logicalOperation.apply(firstObj, secondObj); result.setValue(new Obj(value, ObjType.BOOL)); return result; }
例子,以"="的實(shí)現(xiàn)來展示:
private ExprObj calcBinaryOperation(OperationType type, InnerTable table, InnerRow row) { ExprObj result = null; switch (type) { case T_OP_EQ: result = logicalCalculus(table, row, (a, b) -> ObjUtil.eq(a, b)); // 等于符號(hào)的實(shí)現(xiàn) break; ... default: break; } return result; } public class ObjUtil { private static ObjType resultType(ObjType first, ObjType second) { return ObjType.RESULT_TYPE[first.ordinal()][second.ordinal()]; } public static boolean eq(Obj first, Obj second) { ObjType type = resultType(first.getType(), second.getType()); switch (type) { case LONG: { long firstValue = first.getValueAsLong(); long secondValue = second.getValueAsLong(); return firstValue == secondValue; } case DOUBLE: { double firstValue = first.getValueAsDouble(); double secondValue = second.getValueAsDouble(); return Double.compare(firstValue, secondValue) == 0; } case TIMESTAMP: { java.util.Date firstValue = first.getValueAsDate(); java.util.Date secondValue = first.getValueAsDate(); return firstValue.compareTo(secondValue) == 0; } ... default: break; } throw new UnsupportedOperationException(first.getType() + " and " + second.getType() + " not support '=' operation."); } }
數(shù)學(xué)運(yùn)算和邏輯運(yùn)算的流程都一樣,只不過運(yùn)算后的結(jié)果為數(shù)字類型。
除了上面說的邏輯運(yùn)算和數(shù)學(xué)運(yùn)算外,還支持進(jìn)行模糊匹配的特殊操作符LIKE。
常見用法如下
LIKE "%HUAWEI" 匹配以HUAWEI結(jié)尾的字符串
LIKE "HUAWEI%" 匹配以HUAWEI開頭的字符串
LIKE "A_B" 匹配以"A"起頭且以"Z"為結(jié)尾的字串
LIKE "A?B" 同上
LIKE "%[0-9]%" 匹配含有數(shù)字的字符串
LIKE "%[a-z]%" 匹配含有小寫字母字符串
LIKE "%[!0-9]%"匹配不含數(shù)字的字符串
?和_都表示單個(gè)字符
JAVA中實(shí)現(xiàn)LIKE的方案:將LIKE的模式轉(zhuǎn)為JAVA中的正則表達(dá)式。
expr := wild-card + expr | wild-char + expr | escape + expr | string + expr | "" wild-card := % wild-char := _ escape := [%|_] string := [^%_]+ (One or > more characters that are not wild-card or wild-char)
public abstract class Token { private final String value; public Token(String value) { this.value = value; } public abstract String convert(); public String getValue() { return value; } } public class ConstantToken extends Token { public ConstantToken(String value) { super(value); } @Override public String convert() { return getValue(); } } public class EscapeToken extends Token { public EscapeToken(String value) { super(value); } @Override public String convert() { return getValue(); } } public class StringToken extends Token { public StringToken(String value) { super(value); } @Override public String convert() { return Pattern.quote(getValue()); } } public class WildcardToken extends Token { public WildcardToken(String value) { super(value); } @Override public String convert() { return ".*"; } } public class WildcharToken extends Token { public WildcharToken(String value) { super(value); } @Override public String convert() { return "."; } }
public class Tokenizer { private Collection<Tuple> patterns = new LinkedList<>(); public <T extends Token> Tokenizer add(String regex, Function<String, Token> creator) { this.patterns.add(new Tuple<Pattern, Function<String, Token>>(Pattern.compile(regex), creator)); return this; } public Collection<Token> tokenize(String clause) throws RuntimeException { Collection<Token> tokens = new ArrayList<>(); String copy = String.copyValueOf(clause.toCharArray()); int position = 0; while (!copy.equals("")) { boolean found = false; for (Tuple tuple : this.patterns) { Pattern pattern = (Pattern) tuple.getFirst(); Matcher m = pattern.matcher(copy); if (m.find()) { found = true; String token = m.group(1); Function<String, Token> fn = (Function<String, Token>) tuple.getSecond(); tokens.add(fn.apply(token)); copy = m.replaceFirst(""); position += token.length(); break; } } if (!found) { throw new RuntimeException("Unexpected sequence found in input string, at " + position); } } return tokens; } }
public class LikeTranspiler { private static Tokenizer TOKENIZER = new Tokenizer() .add("^(\\[[^]]*])", ConstantToken::new) .add("^(%)", WildcardToken::new) .add("^(_)", WildcharToken::new) .add("^([^\\[\\]%_]+)", StringToken::new); public static String toRegEx(String pattern) throws ParseException { StringBuilder sb = new StringBuilder().append("^"); for (Token token : TOKENIZER.tokenize(pattern)) { sb.append(token.convert()); } return sb.append("$").toString(); } }
直接調(diào)用LikeTranspiler的toRegEx方法將LIKE語法轉(zhuǎn)為JAVA中的正則表達(dá)式。
private ExprObj calcBinaryOperation(OperationType type, InnerTable table, InnerRow row) { ExprObj result = null; switch (type) { . . . case T_OP_LIKE: result = logicalCalculus(table, row, (a, b) -> ObjUtil.like(a, b)); break; . . . } return result; } public static boolean like(Obj first, Obj second) { Assert.state(first.getType() == ObjType.STRING, OperationType.T_OP_LIKE + " only support STRING."); Assert.state(second.getType() == ObjType.STRING, OperationType.T_OP_LIKE + " only support STRING."); String firstValue = (String) first.getRelValue(); String secondValue = (String) second.getRelValue(); String regEx = LikeTranspiler.toRegEx(secondValue); return Pattern.compile(regEx).matcher(firstValue).matches(); }
通過創(chuàng)建詞法分析器并使用此方法進(jìn)行轉(zhuǎn)換,我們可以防止LIKE像這樣的子句被轉(zhuǎn)換為正則表達(dá)式%abc[%]%,該子句應(yīng)將其中的任何子字符串與其中的子字符串匹配,該子句將與子字符串或匹配任何字符串。abc%.abc[.].abc.abc。
不同數(shù)據(jù)類型在進(jìn)行計(jì)算時(shí)需要轉(zhuǎn)型,具體的轉(zhuǎn)化入下二維數(shù)組中。
// 不同類型計(jì)算后的類型 ObjType[][] RESULT_TYPE = { //UNKNOWN BYTE SHORT INT LONG FLOAT DOUBLE DECIMAL BOOL DATE TIME TIMESTAMP STRING NULL { UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN },// UNKNOWN { UNKNOWN, LONG, LONG, LONG, LONG, DOUBLE, DOUBLE, DECIMAL, BOOL, UNKNOWN, UNKNOWN, UNKNOWN, LONG, UNKNOWN },// BYTE { UNKNOWN, LONG, LONG, LONG, LONG, DOUBLE, DOUBLE, DECIMAL, BOOL, UNKNOWN, UNKNOWN, UNKNOWN, LONG, UNKNOWN },// SHORT { UNKNOWN, LONG, LONG, LONG, LONG, DOUBLE, DOUBLE, DECIMAL, BOOL, UNKNOWN, UNKNOWN, UNKNOWN, LONG, UNKNOWN },// INT { UNKNOWN, LONG, LONG, LONG, LONG, DOUBLE, DOUBLE, DECIMAL, BOOL, UNKNOWN, UNKNOWN, UNKNOWN, LONG, UNKNOWN },// LONG { UNKNOWN, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DECIMAL, BOOL, UNKNOWN, UNKNOWN, UNKNOWN, DOUBLE, UNKNOWN },// FLOAT { UNKNOWN, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DECIMAL, BOOL, UNKNOWN, UNKNOWN, UNKNOWN, DOUBLE, UNKNOWN },// DOUBLE { UNKNOWN, DECIMAL, DECIMAL, DECIMAL, DECIMAL, DECIMAL, DECIMAL, DECIMAL, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, DECIMAL, UNKNOWN },// DECIMAL { UNKNOWN, BOOL, BOOL, BOOL, BOOL, BOOL, BOOL, BOOL, BOOL, UNKNOWN, UNKNOWN, UNKNOWN, BOOL, UNKNOWN },// BOOL { UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, TIMESTAMP, TIMESTAMP, TIMESTAMP, TIMESTAMP, UNKNOWN },// DATE { UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, TIMESTAMP, TIMESTAMP, TIMESTAMP, TIMESTAMP, UNKNOWN },// TIME { UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, TIMESTAMP, TIMESTAMP, TIMESTAMP, TIMESTAMP, UNKNOWN },// TIMESTAMP { UNKNOWN, LONG, LONG, LONG, LONG, DOUBLE, DOUBLE, DECIMAL, BOOL, TIMESTAMP, TIMESTAMP, TIMESTAMP, STRING, UNKNOWN },// STRING { UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN },// NULL };
點(diǎn)擊關(guān)注,第一時(shí)間了解華為云新鮮技術(shù)~
關(guān)于提高微服務(wù)可用性的中間件CoralCache問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。