提高微服務可用性的中間件CoralCache,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
目前成都創新互聯公司已為上千余家的企業提供了網站建設、域名、虛擬空間、網站改版維護、企業網站設計、昆明網站維護等服務,公司將堅持客戶導向、應用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協力一起成長,共同發展。
當數據庫出問題時能降級從本地緩存的數據中查詢數據, CoralCache就是這樣一個提高微服務可用性的中間件。
有些場景下,微服務依賴數據庫中一些配置項或者數量很少的數據,但當數據庫本身有問題時候,即使數據量很少,這個服務是不能正常工作;因此需要考慮一種能支持全量+極少變更的全局數據的場景,當數據庫出問題時能降級從本地緩存的數據中查詢數據,CoralCache就是這樣一個提高微服務可用性的中間件。
CoralCache中間件架構如下圖所示,通過@EnableLocal注解開啟功能,應用啟動后將配置的表數據一次性加載到內存中,內存中的數據邏輯結構和數據庫中的邏輯結構一樣。

圖1. 架構圖
內存查詢引擎的原理是數據庫查詢降級發生后,Intercepter將攔截到的原始SQL傳入查詢引擎中,查詢引擎解析SQL后得到表名、列名、where條件表達式,遍歷InnerDB中對應表的數據行,并通過表達式計算引擎計算結果,計算結果為真則添加到結果集中最后返回給調用方。
計算引擎結構如下圖所示,將where條件表達式轉為后綴表達式后依次遍歷后綴表達式,遇到操作數直接入棧,遇到操作符則根據操作符需要的操作數個數彈棧。

圖2. 表達式計算引擎結構
然后根據操作符和彈出的操作數進行計算,不同操作符對應不同的計算方法,并將計算后的結果重新作為操作數入棧執到遍歷完成,核心計算流程代碼如下所示:
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();
}邏輯常見運算符為<、<=、>、>=、=等,它們的共性都是需要2個操作數并且返回值是布爾類型。
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;
}例子,以"="的實現來展示:
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)); // 等于符號的實現
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.");
}
}數學運算和邏輯運算的流程都一樣,只不過運算后的結果為數字類型。
除了上面說的邏輯運算和數學運算外,還支持進行模糊匹配的特殊操作符LIKE。
常見用法如下
LIKE "%HUAWEI" 匹配以HUAWEI結尾的字符串
LIKE "HUAWEI%" 匹配以HUAWEI開頭的字符串
LIKE "A_B" 匹配以"A"起頭且以"Z"為結尾的字串
LIKE "A?B" 同上
LIKE "%[0-9]%" 匹配含有數字的字符串
LIKE "%[a-z]%" 匹配含有小寫字母字符串
LIKE "%[!0-9]%"匹配不含數字的字符串
?和_都表示單個字符
JAVA中實現LIKE的方案:將LIKE的模式轉為JAVA中的正則表達式。
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();
}
}直接調用LikeTranspiler的toRegEx方法將LIKE語法轉為JAVA中的正則表達式。
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();
}通過創建詞法分析器并使用此方法進行轉換,我們可以防止LIKE像這樣的子句被轉換為正則表達式%abc[%]%,該子句應將其中的任何子字符串與其中的子字符串匹配,該子句將與子字符串或匹配任何字符串。abc%.abc[.].abc.abc。
不同數據類型在進行計算時需要轉型,具體的轉化入下二維數組中。
// 不同類型計算后的類型
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
};點擊關注,第一時間了解華為云新鮮技術~
關于提高微服務可用性的中間件CoralCache問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注創新互聯行業資訊頻道了解更多相關知識。
文章標題:提高微服務可用性的中間件CoralCache
文章路徑:http://www.yijiale78.com/article2/gijhic.html
成都網站建設公司_創新互聯,為您提供網站內鏈、定制網站、做網站、微信公眾號、電子商務、虛擬主機
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯