首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >解释器模式实战:实现自定义的告警规则功能

解释器模式实战:实现自定义的告警规则功能

作者头像
somenzz
发布于 2021-07-01 08:40:51
发布于 2021-07-01 08:40:51
72100
代码可运行
举报
文章被收录于专栏:Python七号Python七号
运行总次数:0
代码可运行

大家好,我是征哥,今天分享一种设计模式,解释器模式。

先来看一个需求:

在告警系统中,有很多规则的配置,如果配置的规则被触发,监控系统就通过短信、微信、邮件等方式发送告警给开发者。比如,每分钟 API 总出错数超过 100 或者每分钟 API 总调用数超过 10000 就触发告警。配置的规则如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
api_error_per_minute > 9 || api_count_per_minute > 10000

在监控系统中,告警模块只判断是否触发告警。至于每分钟 API 接口出错数、每分钟接口调用数等统计数据的计算,是由其他模块来负责的。其他模块将统计数据放到一个 dict 中,数据的格式如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
apiStat = {}
apiStat["api_error_per_minute"] = 10
apiStat["api_count_per_minute"] = 987

接下来,编写程序,输入是一个字典,代表统计数据 apiStat,和一个字符串,代表告警规则 "api_error_per_minute > 9 || api_count_per_minute > 10000",输出:True 或 False,True 表述满足告警规则,False 表示不满足。

为了简化代码实现,我们假设自定义的告警规则只包含“||、&&、>、<、==”这五个运算符,其中,“>、<、==”运算符的优先级高于“||、&&”运算符,“&&”运算符优先级高于“||”。在表达式中,任意元素之间需要通过空格来分隔。

除此之外,用户可以自定义要监控的 key,比如前面的 api_error_per_minute、api_count_per_minute。

那么如何写代码实现呢?

更具体一点,请将以下 pass 语句替换成可以执行的代码,其中 rule 字符串是可以自由变化的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class AlertRuleInterpreter:
    def __init__(self, ruleExpression: str):
        pass

    def interpret(self, stats: dict) -> bool:
        pass


if __name__ == "__main__":
    rule = "key1 > 100 && key2 < 30 || key3 < 100 || key4 == 88"
    interpreter = AlertRuleInterpreter(rule)
    stats = {}
    stats["key1"] = 101
    stats["key3"] = 121
    stats["key4"] = 88
    alert = interpreter.interpret(stats)
    print(alert)

你可以先暂停,思考下怎么写,然后和我这里对比一下:

基础版本

思路:先把字符串按照 || 分割成子串,每一个字串内部的逻辑关系就是 &&,再将字串按照 && 分割成子子串,每一个子子串的内部逻辑关系就三种:>、<、==,这是不是很像一个树?

编码实现每一个字串、子子串对应处理逻辑 Expression 类,这里为统一格式,用到了抽象基类,每一种 Expression 类必须包含 interpret 方法。具体代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from abc import ABCMeta
from numbers import Real
import re

class Expression(metaclass=ABCMeta):
    def interpret(self, stats: dict) -> bool:
        pass


class GreaterExpression(Expression):
    def __init__(self, express: str = None, key: str = None, value: Real = None):
        if express:
            elements = re.split(r"\s+", express)
            if len(elements) == 3 and elements[1] == ">":
                self.key = elements[0]
                self.value = float(elements[2])
            else:
                raise Exception("Invalid GreaterExpression")
        elif key and value:
            self.key = key
            self.value = value
        else:
            raise Exception("GreaterExpression init error")

    def interpret(self, stats: dict) -> bool:
        if self.key in stats:
            return stats[self.key] > self.value
        return False


class LessExpression(Expression):
    def __init__(self, express: str = None, key: str = None, value: Real = None):
        if express:
            elements = re.split(r"\s+", express)
            if len(elements) == 3 and elements[1] == "<":
                self.key = elements[0]
                self.value = float(elements[2])
            else:
                raise Exception("Invalid LessExpression")
        elif key and value:
            self.key = key
            self.value = value
        else:
            raise Exception("LessExpression init error")

    def interpret(self, stats: dict) -> bool:
        if self.key in stats:
            return stats[self.key] < self.value
        return False


class EqualExpression(Expression):
    def __init__(self, express: str = None, key: str = None, value: Real = None):
        if express:
            elements = re.split(r"\s+", express)
            if len(elements) == 3 and elements[1] == "==":
                self.key = elements[0]
                self.value = float(elements[2])
            else:
                raise Exception("Invalid EqualExpression")
        elif key and value:
            self.key = key
            self.value = value
        else:
            raise Exception("EqualExpression init error")

    def interpret(self, stats: dict) -> bool:
        if self.key in stats:
            return stats[self.key] == self.value
        return False


class AndExpression(Expression):
    def __init__(self, express: str):
        self.express_list = []
        strExpressions = re.split(r"\s+&&\s+", express)
        for express in strExpressions:
            if ">" in express:
                self.express_list.append(GreaterExpression(express))
            elif "<" in express:
                self.express_list.append(LessExpression(express))
            elif "==" in express:
                self.express_list.append(EqualExpression(express))
            elif "True" == express or "False" == express:
                self.express_list.append(BoolExpression(express))

    def interpret(self, stats: dict) -> bool:
        for expression in self.express_list:
            if expression.interpret(stats) == False:
                return False
        return True


class OrExpression(Expression):
    def __init__(self, express: str):
        self.express_list = []
        strExpressions = re.split(r"\s+\|\|\s+", express)
        for express in strExpressions:
            self.express_list.append(AndExpression(express))

    def interpret(self, stats: dict) -> bool:
        for expression in self.express_list:
            if expression.interpret(stats) == True:
                return True
        return False


class AlertRuleInterpreter:
    def __init__(self, ruleExpression: str):
        self.expression = OrExpression(ruleExpression)

    def interpret(self, stats: dict) -> bool:
        return self.expression.interpret(stats)


if __name__ == "__main__":
    rule = "key1 > 100 && key2 < 30 || key3 < 100 || key4 == 88"

    interpreter = AlertRuleInterpreter(rule)
    stats = {}
    stats["key1"] = 120
    stats["key3"] = 121
    stats["key4"] = 88
    alert = interpreter.interpret(stats)
    print(alert)

这样的设计代码的模式,就叫做解释器模式,英文翻译是 Interpreter Design Pattern。在 GoF 的《设计模式》一书中,它是这样定义的:

Interpreter pattern is used to defines a grammatical representation for a language and provides an interpreter to deal with this grammar.

解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。它属于行为型模式。这种模式被用在 SQL 解析、符号处理引擎等。这里的语言并不是我们说的中文和英文,而是任意一个信息的载体,比如本文中的告警规则。

加点难度

现在,我们给他增加点难度,比如说支持小括号,小括号内的表达式会作为一个整体,与 || 或 && 平级,小括号内还嵌套小括号。

与基础版本相比,只需要增加一个 ComplexAlertRuleInterpreter 类,利用栈来先计算括号内的值,要么是 True,要么是 False,然后写回表达式,已达到去除括号的目的,最后在用基础版本的套路来实现即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ComplexAlertRuleInterpreter:
    def __init__(self, ruleExpression: str):
        self.expression = ruleExpression

    def interpret(self, stats: dict) -> bool:

        stack = deque()
        for express in re.split(r"\s+", self.expression):
            if express == "(":
                stack.append(express)
                continue
            elif express == ")":
                # 取出括号内容,并计算
                tmp_express = deque()
                while len(stack) > 0:
                    tmp = stack.pop()
                    if tmp == "(":
                        break
                    else:
                        tmp_express.appendleft(tmp)
                # 计算结果
                result = AlertRuleInterpreter(" ".join(tmp_express)).interpret(stats)
                stack.append(str(result))
                continue

            else:
                stack.append(express)

        return AlertRuleInterpreter(" ".join(stack)).interpret(stats)

括号去除后,表达式多了'True' 或者 'False' 这样的文本,因此就需要一个 BoolExpression 来处理 'True' 或者 'False' :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class BoolExpression(Expression):
    def __init__(self, express: str):
        self.express = express

    def interpret(self, stats: dict) -> bool:
        if self.express == "True":
            return True
        return False

最后修改 AndExpression 的初始化部分,增加对 'True' 或者 'False' 表达式的处理:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class AndExpression(Expression):
    def __init__(self, express: str):
        self.express_list = []
        strExpressions = re.split(r"\s+&&\s+", express)
        for express in strExpressions:
            if ">" in express:
                self.express_list.append(GreaterExpression(express))
            elif "<" in express:
                self.express_list.append(LessExpression(express))
            elif "==" in express:
                self.express_list.append(EqualExpression(express))
            elif "True" == express or "False" == express:
                self.express_list.append(BoolExpression(express))

    def interpret(self, stats: dict) -> bool:
        for expression in self.express_list:
            if expression.interpret(stats) == False:
                return False
        return True

最后, rule 可以增加任意多的括号,main 函数如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if __name__ == "__main__":
    # rule = "key1 > 100 && key2 < 30"
    rule = "key1 > 101 && ( ( key2 < 30 || ( key3 < 100 || key4 == 88 ) ) || False ) && True"

    interpreter = ComplexAlertRuleInterpreter(rule)
    stats = {}
    stats["key1"] = 120
    stats["key3"] = 121
    stats["key4"] = 88
    alert = interpreter.interpret(stats)
    print(alert)

完整代码

https://github.com/somenzz/geekbang/blob/master/design-pattern/72-interpreter-demo.py

解释器模式应用场景:

简单来说,程序需要对字符串进行解释的,就像编程语言对代码的解释一样,这种情况下,就需要用到解释器模式。比如说:

  • 需要解释的字符串可以表示为一个抽象的语法树
  • 一个重复出现的问题可以用一种简单的语言来表达
  • 现在比较流行的规则引擎系统

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python七号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
设计模式学习笔记(二十二)解释器模式及其实现
解释器模式(Interpreter Design Pattern)指给定一个“语言”,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。这里所指的“语言”是指使用规定格式和语法的代码。
归思君
2023/10/16
3420
设计模式学习笔记(二十二)解释器模式及其实现
行为型-Interpreter
命令模式的原理解读命令模式的英文翻译是 Command Design Pattern。在 GoF 的《设计模式》一书中,它是这么定义的:
acc8226
2022/05/17
3260
Python自定义对象转json、jso
二、Json 转自定义对象:暂时查不到 Json 转 Python 对象的傻瓜办法,这里贴一个网友的来自:
py3study
2020/01/06
4.1K0
解释器模式--相亲的公式
小美的要求高着呢:起码要有房吧?年收入不能低于20万吧?身高最低也要175cm吧?年龄也不能太大,不能超过30岁吧?学历么也要本科起步吧?
zhanyd
2022/05/16
3220
解释器模式--相亲的公式
MCP与企业数据集成:ERP、CRM、数据仓库的统一接入
🎵 在编程的交响乐中,我既是指挥家也是演奏者。让我们一起,在技术的音乐厅里,奏响属于程序员的华美乐章。
摘星.
2025/07/23
960
MCP与企业数据集成:ERP、CRM、数据仓库的统一接入
Java描述设计模式(14):解释器模式
一、解释器模式 1、基础概念 解释器模式是对象的行为模式。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的表达式。 2、模式图
知了一笑
2019/10/18
5440
详解设计模式:解释器模式
解释器模式(interpreter pattern),是在 GoF 23 种设计模式中定义了的行为型模式。
栗筝i
2022/12/09
3370
详解设计模式:解释器模式
解释器模式
一、简介 1、解释器模式给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 2、类成员 (1)AbstractExpression(抽象表达式):声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。 (2)TerminalExpression(终结符表达式):实现与文法中的终结符相关联的解释操作。实现抽象表达式中所要求的接口,主要是一个interpreter()方法。文法中每一个终结符都有一个具体终结表达式与之相对应。 (3)NonterminalE
用户1215536
2018/02/05
1K0
解释器模式
设计模式 | 行为型 | 解释器模式
解释器模式代码实现的核心思想就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。
被水淹没
2023/02/25
4140
设计模式 | 行为型 | 解释器模式
ScrapyRedis源码解析
本文介绍了分布式爬虫的原理、架构、实现,并基于 Scrapy-Redis 实现分布式爬虫。通过案例展示了如何使用分布式爬虫提高爬取效率,降低服务器压力。同时介绍了基于 Redis 的分布式锁,以及使用分布式爬虫在爬取过程中实现增量更新。
崔庆才
2017/10/31
1.8K1
[MYSQL] REDUNDANT行格式的数据解析
mysql的行格式有4种,REDUNDANT,COMPACT,DYNAMIC和COMPRESSED. 最常用的就是DYNAMIC, 也是mysql默认的行格式(很早只有REDUNDANT). 该行格式虽然复杂一点, 但是支持的索引前缀可达3072字节.(REDUNDANT只支持到768字节).
大大刺猬
2024/12/06
3080
[MYSQL] REDUNDANT行格式的数据解析
python 中json的实现
490 lines (416 sloc)  16.611 kb # -*- coding: utf8 -*- import sys import math #---------------------------------------- debug_flag = False # for debug info ouput disp_flag = False # for disp info ouput # debug info output controller def deb
bear_fish
2018/09/20
1.3K0
python 基础语法笔记
记录python的一些基础语法,用于查阅 列表和元组 列表和元组都是有序的,可以存储任意数据类型的集合 列表是动态的,长度可变,存储空间和性能略逊与元组 元组是静态的,长度大小固定,不能增加修改 创建一个列表使用 empty_list = [] 相比于 list() 更好,因为 [] 底层走的c,而 list() 是函数,更加贵 l = [1, 2, 3, 4] tup = (1, 2, 3, 4) list((1, 2, 3)) [1, 2, 3] l.count(3) l.index(7) l.re
LinkinStar
2022/09/01
3290
行为型模式:解释器模式
姓名 :解释器模式 英文名 :Interpreter Pattern 价值观 :不懂解释到你懂 个人介绍 : Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. 给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。 (来自《设计模式之禅》)
LieBrother
2019/04/23
3820
根据指定键对自定义 JSON 输出
接下来,我们将用 Python 演示这些操作,结合 json 模块解析和处理 JSON 数据。
华科云商小徐
2024/10/14
2470
python常用函数总结
print()会依次打印每个字符串,遇到逗号“,”会输出空格,输出的内容是这样的:
全栈程序员站长
2022/09/07
1.9K0
python常用函数总结
企业级沃尔玛数据采集方案:从反爬虫策略到分布式架构深度实践
导语: 随着零售数字化的深入,高效获取以沃尔玛为代表的电商平台数据,已成为企业进行市场分析和商业决策的关键一环。然而,其复杂且持续进化的反爬虫体系为数据采集带来了巨大的技术挑战。本文旨在提供一个企业级的 沃尔玛爬虫工具 构建思路,深入探讨从底层的 沃尔玛反爬虫绕过 技术,到上层的分布式、高可用系统架构设计,并结合具体代码实践,为开发者构建稳定、可扩展的 沃尔玛数据采集 系统提供一份详尽的技术蓝图。(本文约 38000 字,建议先码后看)
Amazon 爬虫 API
2025/07/17
870
企业级沃尔玛数据采集方案:从反爬虫策略到分布式架构深度实践
Python3基础
强类型编程语言在使用变量之前需要显示定义,然后才能使用。例如C语言,以下为C语言的一段变量定义代码:
py3study
2020/01/02
4650
【设计模式】—— 解释器模式Interpret
  模式意图   自定义某种语言后,给定一种文法标准,定义解释器,进行解析。   做过搜索的朋友们可能更了解一些,平时我们搜索所需要的词库,通常就需要用这种方式来实现。   应用场景   1 有复杂的
用户1154259
2018/01/18
6100
【设计模式】—— 解释器模式Interpret
解释器(Interpreter)模式
以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一颗解析树,用来检验一个文本是否满足解析树定义的规则。
MickyInvQ
2022/05/06
5140
解释器(Interpreter)模式
相关推荐
设计模式学习笔记(二十二)解释器模式及其实现
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档