一日一技:如何实现一个轻量插件系统

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介: 一日一技:如何实现一个轻量插件系统

摄影:产品经理非常贵的牛肉就这么一丁点

假设我们实现了一个程序,它从 Redis 读取数据,然后写入 MongoDB。一开始程序是这样的:


def read_from_redis():    ...
def write_to_mongodb(doc):    ...
def parse():    for doc in self.read_from_redis():        self.write_to_mongodb(doc)

for doc in self.read_from_redis()的循环中,每次循环返回的是一个字典,这个字典包含很多项,例如agedate等等。我们需要设计一些逻辑对这个数据进行处理或者过滤。

但这些逻辑是逐渐增加,一开始只有一个需求,就是如果发现docage字段中,如果age不是数字且不能转换为数字,那么需要把它改成N/A

后来又增加了一个新的需求,如果doc里面的date字段对应的日期小于2020-05-01,那么这条数据直接丢弃。

接下来还要新增很多其他的需求。为了避免反复修改代码,我们可以实现一个轻量级的插件系统。

我们先实现调用这个插件系统的部分:


plugins = {}
def read_from_redis():    datas = [        {'age': 34, 'name': 'xxx', 'date': '2020-05-10'},        {'age': 12, 'name': 'yyy', 'date': '2020-04-03'},        {'age': '23', 'name': 'zzz', 'date': '2020-05-12'},        {'age': 'aa', 'name': 'abc', 'date': '2020-05-10'},        {'age': 89, 'name': 'def', 'date': '2020-02-10'},        {'age': '', 'name': 'xyz', 'date': '2020-05-10'},        {'age': 'xy', 'name': 'xxx', 'date': '2020-05-10'},        {'age': 'mp', 'name': 'xxx', 'date': '2020-05-10'},        {'age': 34, 'name': 'xxx', 'date': '2019-02-10'},    ]    for data in datas:        yield data
def write_to_mongodb(doc):    print(f'正在把数据:{doc} 写入到 MongoDB 中')
def parse():    for doc in read_from_redis():        for name, plugin in plugins.items():            print(f'正在运行插件:{name}')            doc = plugin(doc)            if not doc:                print(f'数据: {doc},被插件:{name}过滤。')            continue        write_to_mongodb(doc)
if __name__ == '__main__':    parse()

看到这里,你会不会觉得很奇怪,这里的plugins不是一个空字典吗?那你下面的 for 循环怎么能够执行呢?

不慌,我们现在使用装饰器把插件注册plugins中:

def register(plugin):    plugins[plugin.__name__] = plugin    return plugin
@registerdef transfer_age_to_int(doc):    try:        doc['age'] = int(doc['age'])    except ValueError:        doc['age'] = 'N/A'    return doc
@registerdef filter_date(doc):    date = doc['doc']    if date < '2020-05-01':        return None    return doc

这个装饰器,它的作用就是把它装饰的函数存到plugins字典中。所以当我们使用装饰器装饰一个 plugin 函数的时候,它就已经被自动注册到plugins字典中了。不需要我们再手动存放一次。

下面我们来实际运行一下:

可以看到,不是数字且不能被转换为数字的age字段的值被改成了N/Adate小于2020-05-01的数据就直接丢弃了。


目录
相关文章
|
存储 安全 Java
SpringSecurity6从入门到实战之初始用户如何存储到内存
Spring Security 在 SpringBoot 应用中默认使用 `UserDetailsServiceAutoConfiguration` 类将用户信息存储到内存中。当classpath有`AuthenticationManager`、存在`ObjectPostProcessor`实例且无特定安全bean时,此配置生效。`inMemoryUserDetailsManager()`方法创建内存用户,通过`UserDetails`对象填充`InMemoryUserDetailsManager`的内部map。若要持久化到数据库,需自定义`UserDetailsService`接口实
|
开发框架 Java .NET
基于SpringBoot+Bootstrap【爱码个人博客系统】附源码
基于SpringBoot+Bootstrap【爱码个人博客系统】附源码
399 1
|
消息中间件 缓存 并行计算
每秒钟承载600万订单级别的无锁并行计算框架 Disruptor学习
每秒钟承载600万订单级别的无锁并行计算框架 Disruptor学习
161 0
|
前端开发
基于jeecgboot的大屏设计器开发——数据集设计(一)
基于jeecgboot的大屏设计器开发——数据集设计(一)
249 3
|
JSON 数据格式 C++
C++ Json和结构体或类的转换
C++ Json和结构体或类的转换
616 0
|
JavaScript 前端开发 安全
三个经典的TypeScript易混淆点
前言 本文会讲什么:主要讲解TypeScript在开发过程中的易混淆点,当然也同样是面试官常考的几个题目 本文不会讲什么:本文并不是又大又全的TypeScript学习教程,不会讲那些基础知识、简单概念等,比如JS的内置类型这类。所以如果你是新手玩家,最好先去做一下新手任务出了新手村再这里
274 0
|
缓存 JavaScript 前端开发
最新Vue常见基础面试题(看这一篇就够了)(一)
最新Vue常见基础面试题(看这一篇就够了)
180 0
|
Oracle MySQL 关系型数据库
如何将不同类型数据导入Elaticsearch中?
Elaticsearch的原理明白了以后,手头有很多不同类型的数据,如何将这些数据导入到Elasticsearch中呢?接下来,本文将逐个介绍。
1021 0
|
11天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1239 5
|
10天前
|
机器学习/深度学习 人工智能 前端开发
通义DeepResearch全面开源!同步分享可落地的高阶Agent构建方法论
通义研究团队开源发布通义 DeepResearch —— 首个在性能上可与 OpenAI DeepResearch 相媲美、并在多项权威基准测试中取得领先表现的全开源 Web Agent。
1219 87