自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

架構(gòu)治理調(diào)研:規(guī)則、表達(dá)式還有語(yǔ)言

開發(fā) 開發(fā)工具 架構(gòu)
為了在 ArchGuard 中完善分布式規(guī)范的能力,便分析了幾個(gè)現(xiàn)有的工具。

我們談?wù)摰搅?“分布式” 場(chǎng)景下,對(duì)于架構(gòu)治理和規(guī)范治理的一系列問題。我們提及了一系列的工具,如 API Linter 工具 Spectral,數(shù)據(jù)庫(kù) Linter 工具 SQLFluff。而為了在 ArchGuard 中完善分布式規(guī)范的能力,便分析了幾個(gè)現(xiàn)有的工具。

對(duì)于我們來說,構(gòu)建一個(gè)類似的工具,需要考慮的一些因素有:

  • 插件化。開發(fā)人員可以根據(jù)已有的守護(hù)規(guī)則,開發(fā)一些新的架構(gòu)守護(hù)規(guī)則,如針對(duì)于 API 的,針對(duì)于數(shù)據(jù)庫(kù)調(diào)用鏈路的。
  • 可測(cè)試性。如果采用的是完全 DSL 或者 半 DSL,那么如何讓后續(xù)的
  • 語(yǔ)言無關(guān)。如何不綁定于語(yǔ)言的語(yǔ)法樹,而實(shí)現(xiàn)對(duì)于多種語(yǔ)言的支持。

出于這個(gè)目的,只好拿起現(xiàn)有的代碼進(jìn)行一番分析,主要有四個(gè)工具,適用于 Kotlin 語(yǔ)言的 KtLint、適用于 OpenAPI 的 Spectral、適用于多數(shù)據(jù)庫(kù)的 SQLFluff,以及被諸如 MyBatis 采用的表達(dá)式語(yǔ)言 Ognl。

Kotlin 代碼的治理:KtLint

KtLint 與一般的 Lint 工具稍有區(qū)別的是,它自帶了一個(gè)自動(dòng)格式化的功能。KtLint 整體的邏輯還是比較簡(jiǎn)單的,基于單個(gè)文件進(jìn)行 AST 生成,隨后針對(duì)于 AST 進(jìn)行規(guī)則匹配。Ktlint 圍繞于 Rule、Rulesets、RulesetsProvider 構(gòu)建了規(guī)則的層級(jí)關(guān)系,同時(shí)用 Vistor (即 VisitorProvider)模式圍繞 AST 進(jìn)行分析,如下是 KtLint 的抽象 Rule:

/**
* This method is going to be executed for each node in AST (in DFS fashion).
*
* @param node AST node
* @param autoCorrect indicates whether rule should attempt auto-correction
* @param emit a way for rule to notify about a violation (lint error)
*/
abstract fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
)

如注釋中所說的,三個(gè)參數(shù)代表了各自的用途。這里的 ASTNode 是來源于 Kotlin 的 AST 樹( kotlin-compiler-embeddable 包)。模式上也是獲取配置,然后運(yùn)行檢測(cè)規(guī)則:

val ruleSets = ruleSetProviders.map { it.value.get() }
val visitorProvider = VisitorProvider(ruleSets, debug)

其中對(duì)應(yīng)的 visit:

visitorProvider
.visitor(
params.ruleSets,
preparedCode.rootNode,
concurrent = false
).invoke { node, rule, fqRuleId -> }

在 VistorProvider 中會(huì)過濾對(duì)應(yīng)的規(guī)則:

val enabledRuleReferences =
ruleReferences
.filter { ruleReference -> isNotDisabled(rootNode, ruleReference.toQualifiedRuleId()) }
val enabledQualifiedRuleIds = enabledRuleReferences.map { it.toQualifiedRuleId() }
val enabledRules = ruleSets
.flatMap { ruleSet ->
ruleSet
.rules
.filter { rule -> toQualifiedRuleId(ruleSet.id, rule.id) in enabledQualifiedRuleIds }
.filter { rule -> isNotDisabled(rootNode, toQualifiedRuleId(ruleSet.id, rule.id)) }
.map { rule -> "${ruleSet.id}:${rule.id}" to rule }
}.toMap()
....

然后,再去并行或者串行地運(yùn)行 Rule 里的 visit。

而對(duì)于規(guī)則的方式是通過 ServicesLoader 進(jìn)行的插件化方式:

private fun getRuleSetProvidersByUrl(
url: URL?,
debug: Boolean
): Pair<URL?, List<RuleSetProvider>> {
if (url != null && debug) {
logger.debug { "JAR ruleset provided with path \"${url.path}\"" }
}
val ruleSetProviders = ServiceLoader.load(
RuleSetProvider::class.java,
URLClassLoader(listOfNotNull(url).toTypedArray())
).toList()
return url to ruleSetProviders.toList()
}

如果粒度更大的情況下,采用 Java 9 的模塊是不是會(huì)更加方便?

基于 API 數(shù)據(jù)的 Spectral

與 Ktlint 不同的是 Spectral 是一個(gè)針對(duì)于 JSON/YAML Lint 的工具,特別是針對(duì)于 OpenAPI 文檔(就是 swagger 的 yaml/json 文件)。與 Ktlint 相比,Spectral 最有趣的地方是,它提供了一個(gè) JSON Path(類似于 XPath)的功能,可以針對(duì)于對(duì)象中的特定部分,進(jìn)采用特定的規(guī)則。如下是 Spectral 的示例:

'oas3-valid-schema-example': {
description: 'Examples must be valid against their defined schema.',
message: '{{error}}',
severity: 0,
formats: [oas3],
recommended: true,
type: 'validation',
given: [
"$.components.schemas..[?(@property !== 'properties' && @ && (@ && @.example !== void 0 || @.default !== void 0) && (@.enum || @.type || @.format || @.$ref || @.properties || @.items))]",
"$..content..[?(@property !== 'properties' && @ && (@ && @.example !== void 0 || @.default !== void 0) && (@.enum || @.type || @.format || @.$ref || @.properties || @.items))]",
"$..headers..[?(@property !== 'properties' && @ && (@ && @.example !== void 0 || @.default !== void 0) && (@.enum || @.type || @.format || @.$ref || @.properties || @.items))]",
"$..parameters..[?(@property !== 'properties' && @ && (@ && @.example !== void 0 || @.default !== void 0) && (@.enum || @.type || @.format || @.$ref || @.properties || @.items))]",
],
then: {
function: oasExample,
functionOptions: {
schemaField: '$',
oasVersion: 3,
type: 'schema',
},
},
}

上面對(duì)象中的 given 即是針對(duì)于對(duì)象中的相關(guān)屬性作為條件,執(zhí)行后面的 then 函數(shù),詳細(xì)可以見官方的文檔:《 Custom Rulesets 》。順帶一提:Spectral 采用的是 nimma 作為 JSON Path 表達(dá)式。

Spectral 的模型

與 Ktlint 相比,由于 Spectral 是與 OpenAPI/Async API 進(jìn)行了相關(guān)的綁定,加上特定的規(guī)則表達(dá)式,所以其數(shù)據(jù)模型稍微復(fù)雜一些。其數(shù)據(jù)模型包含了:描述,消息級(jí)別,given - then,上下文。如下所示:

  • recommended。是否是推薦配置。
  • enabled。是否允許
  • description。規(guī)則描述
  • message。錯(cuò)誤信息
  • documentationUrl。文檔地址。
  • severity。嚴(yán)重程度,`error`, `warn`, `info`, or `hint`。
  • formats。格式化標(biāo)準(zhǔn),如 OpenAPI 2.0、OpenAPI 3.0 等。
  • resolved。是否已解決。
  • given。類似于 CSS 中的選擇器,使用類似于 XPath 的 JsonPath, JSONPath
  • then。
  • field,字段
  • function,函數(shù),模式
  • functionOptions

此外,它還有一個(gè)簡(jiǎn)單的類型系統(tǒng),以及對(duì)應(yīng)的表達(dá)式判斷。如下:

  • CASES。flat、camel、pascal、kebab、cobol、snake、macro
  • 長(zhǎng)度:最大值、最小值。
  • 數(shù)字
  • Boolean 判斷。
  • 類型系統(tǒng)。枚舉

總的來說,Spectral 在實(shí)現(xiàn)上比較靈活有趣。

SQLFluff

與 Ktlint 和 Spectral 這種基于已有的數(shù)據(jù)模型的應(yīng)用來說,SQLFluff 顯得更有挑戰(zhàn)性 —— 它是基于多種不同的數(shù)據(jù)庫(kù)方言來構(gòu)建規(guī)則的。SQLFluff 是直接基于源碼來進(jìn)行分析的,將不同的數(shù)據(jù)庫(kù)方言轉(zhuǎn)換為基本元素(分詞)。隨后,基于分詞的類型 + 規(guī)則 ,來對(duì)它們進(jìn)行處理。簡(jiǎn)單來說,就是更抽象的分詞上下文,構(gòu)建對(duì)應(yīng)的規(guī)則上下文。如下是

  • segement。位于其核心的是 BaseSegment,它定義了 Lexing、Parsing 和 Linting 三個(gè)基本的元素,產(chǎn)生諸如: groupby_clause  orderby_clause 、 select_clause 等分詞。
  • parent_stack。
  • siblings_pre。
  • siblings_post。
  • raw_stack。
  • memory。
  • dialect。作為語(yǔ)法運(yùn)行時(shí)解析的基礎(chǔ)。
  • path。路徑。
  • templated_file。模板文件。

示例:

{
"file": {
"statement": {
"select_statement": {
"select_clause": {
"keyword": "SELECT",
"whitespace": " ",
"select_clause_element": {
"column_reference": {
"identifier": "foo"
}
}
},
"whitespace": " ",
"from_clause": {
"keyword": "FROM",
"whitespace": " ",
"from_expression": {
"from_expression_element": {
"table_expression": {
"table_reference": {
"identifier": "bar"
}
}
}
}
}
}
},
"statement_terminator": ";",
"newline": "\n"
}
}

隨后的規(guī)則,便是在對(duì)這些規(guī)則進(jìn)行 eval ,如下示例:

class Rule_L021(BaseRule):
def _eval(self, context: RuleContext) -> Optional[LintResult]:
"""Ambiguous use of DISTINCT in select statement with GROUP BY."""
segment = context.functional.segment
if (
segment.all(sp.is_type("select_statement"))
# Do we have a group by clause
and segment.children(sp.is_type("groupby_clause"))
):
# Do we have the "DISTINCT" keyword in the select clause
distinct = (
segment.children(sp.is_type("select_clause"))
.children(sp.is_type("select_clause_modifier"))
.children(sp.is_type("keyword"))
.select(sp.is_name("distinct"))
)
if distinct:
return LintResult(anchor=distinct[0])
return None

在這里所有的規(guī)則判斷都是基于這種抽象的語(yǔ)法樹。從某種意義上來說,構(gòu)建了一個(gè)統(tǒng)一的抽象。本來想進(jìn)一步分析,但是發(fā)現(xiàn)各種 SQL dialect 里是各種正則表達(dá)式,我就選擇了臨時(shí)性撤退。

表達(dá)式語(yǔ)言:OGNL

起初,我是在實(shí)現(xiàn) ArchGuard Scanner 對(duì)于 MyBatis 的 SQL 生成支持時(shí),看到了 XML 中嵌套的 OGNL 表達(dá)式,發(fā)現(xiàn)了 OGNL。從實(shí)現(xiàn)上來說,它比我之前設(shè)想的 TreeSitter 中的 S 表達(dá)式,在與數(shù)據(jù)結(jié)合的完善度上更高。同樣,也可以用于這里的規(guī)則判斷,可以用表達(dá)式來對(duì)數(shù)據(jù)進(jìn)行匹配。

對(duì)象導(dǎo)航圖語(yǔ)言(Object Graph Navigation Language),簡(jiǎn)稱 OGNL,是應(yīng)用于 Java 中的一個(gè)開源的表達(dá)式語(yǔ)言(Expression Language),用于獲取和設(shè)置 Java 對(duì)象的屬性,以及其他附加功能,例如列表投影(projection)和選擇以及 lambda 表達(dá)式。您可以使用相同的表達(dá)式來獲取和設(shè)置屬性的值。Ognl 類包含了評(píng)估 OGNL 表達(dá)式快捷方式。它可以分兩個(gè)階段執(zhí)行此操作,將表達(dá)式解析為內(nèi)部形式,然后使用該內(nèi)部形式設(shè)置或獲取屬性的值;或者可以在一個(gè)階段完成,并直接使用表達(dá)式的字符串形式獲取或設(shè)置屬性。

Ognl.getValue("name='jerry'", oc, oc.getRoot());
String name2 = (String) Ognl.getValue("#user1.name='jack',#user1.name", oc, oc.getRoot());

本來想模仿 OGNL 編寫一個(gè)表達(dá)式語(yǔ)言,但是發(fā)現(xiàn)使用的是 Jacc,也沒有 Antlr 實(shí)現(xiàn)。所以,在尋找一種更合理的方式。

結(jié)論

作為相關(guān)工具的分析,這里先開個(gè)頭。

責(zé)任編輯:張燕妮 來源: Phodal全棧工程師
相關(guān)推薦

2011-04-22 11:07:24

groovyJava

2010-08-09 14:43:25

Flex正則表達(dá)式

2010-08-09 14:14:13

Flex正則表達(dá)式

2010-08-09 14:30:39

Flex正則表達(dá)式

2010-08-13 15:31:11

Flex正則表達(dá)式

2014-01-05 17:41:09

PostgreSQL表達(dá)式

2023-07-31 07:43:07

Java語(yǔ)言表達(dá)式變量

2009-04-09 09:19:25

C#規(guī)則表達(dá)式.NET

2010-08-13 15:23:10

Flex正則表達(dá)式

2013-09-27 09:43:44

Linux Shell腳本數(shù)學(xué)表達(dá)式

2009-08-14 16:50:59

C#正則表達(dá)式語(yǔ)法

2024-03-25 13:46:12

C#Lambda編程

2018-09-27 15:25:08

正則表達(dá)式前端

2020-09-04 09:16:04

Python正則表達(dá)式虛擬機(jī)

2009-09-09 17:45:07

Linq表達(dá)式

2009-09-11 09:48:27

Linq Lambda

2009-09-17 14:21:19

LINQ表達(dá)式

2009-09-11 12:32:33

LINQ表達(dá)式

2009-07-03 18:31:04

JSP表達(dá)式

2009-09-09 13:01:33

LINQ Lambda
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)