乐趣区

关于后端:sensitivewordadmin-v130-发布-如何支持敏感词控台分布式部署

拓展浏览

sensitive-word-admin v1.3.0 公布 如何反对分布式部署?

sensitive-word-admin 敏感词控台 v1.2.0 版本开源

sensitive-word 基于 DFA 算法实现的高性能敏感词工具介绍

更多技术交换

业务背景

如果咱们的敏感词部署之后,不会变动,那么其实不必思考这个问题。

然而理论业务,敏感词总是随着工夫一直变动的,所以咱们须要反对敏感词的动静批改。

整体设计

pull vs push

以数据库存储自定义场景为例,如果页面批改了敏感词信息,那么如何告诉到部署的多台敏感词客户端呢?

个别告诉形式有两大类:

1)push 推送形式

批改时同时告诉敏感词产生了变动,每个敏感词客户端接管到告诉后,从新初始化敏感词信息。

长处是实时性比拟高,毛病是须要引入额定的告诉机制,须要告诉的服务比拟多时,也比拟麻烦。

2)pull 拉取形式

批改后,间接落库数据库,每一个敏感词客户端本人定时拉取变更的信息。

这种形式有点是非常简单,毛病是存在肯定的提早性。

思考到咱们的场景能够容许分钟级的提早,所以这里先实现定时拉取形式。

如何晓得敏感词是否产生了变动?

定时拉取的形式比较简单,然而每一次拉取的话,如何晓得是否须要从新初始化呢?

尽管每次的初始化的耗时还好,然而思考到变更不是很频繁,所以有没有方法定时拉取时晓得有没有变动呢?

回顾一下上一篇文章,咱们设计的 word 表

create table word
(
    id int unsigned auto_increment comment '利用自增主键' primary key,
    word varchar(128) not null comment '单词',
    type varchar(8) not null comment '类型',
    status char(1) not null default 'S' comment '状态',
    remark varchar(64) not null comment '配置形容' default '',
    operator_id varchar(64) not null default 'system' comment '操作员名称',
    create_time timestamp default CURRENT_TIMESTAMP not null comment '创立工夫戳',
    update_time timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新工夫戳'
) comment '敏感词表' ENGINE=Innodb default charset=UTF8 auto_increment=1;
create unique index uk_word on word (word) comment '惟一索引';

依据更新工夫能够吗?

如果咱们所有的数据都不执行物理删除,那么间接依据 word 表的 update_time 即可判断。

然而如果一个数据真的被删除了,那么这种形式就不行了。

delete 的数据怎么办?

如果咱们冀望执行物理删除的话,那只有增加对应的日志表。

咱们能够通过日志表的 update_time 来解决。

操作日志表

v1.2.0 的表设计

回顾一下 v1.2.0 表设计,如下:

create table word_log
(
    id int unsigned auto_increment comment '利用自增主键' primary key,
    batch_id varchar(128) not null comment '批次号',
    word varchar(128) not null comment '单词',
    type varchar(8) not null comment '类型',
    status char(1) not null default 'S' comment '单词状态。S: 启用;F: 禁用',
    remark varchar(64) not null comment '配置形容' default '',
    operator_id varchar(64) not null default 'system' comment '操作员名称',
    create_time timestamp default CURRENT_TIMESTAMP not null comment '创立工夫戳',
    update_time timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新工夫戳'
) comment '敏感词操作日志表' ENGINE=Innodb default charset=UTF8 auto_increment=1;
create index ix_word on word_log (word) comment '单词一般索引';
create index ix_batch_id on word_log (batch_id) comment '批次号一般索引';

枚举:

insert into lc_enum_mapping (table_name, column_name, `key`, label)  values ('word_log', 'status', 'S', '失常');
insert into lc_enum_mapping (table_name, column_name, `key`, label)  values ('word_log', 'status', 'F', '生效');

insert into lc_enum_mapping (table_name, column_name, `key`, label)  values ('word_log', 'type', 'ALLOW', '容许');
insert into lc_enum_mapping (table_name, column_name, `key`, label)  values ('word_log', 'type', 'DENY', '禁止');

表结构调整

咱们对原来的表做一点调整。

调整后的建表语句

思考到后续 sensitive-word 可能做准确的单个单词变动解决,咱们最好能够晓得每一次词内容的具体变动。

word 敏感词主题
word_before 变更前的单词
word_after 变更后的单词

调整后的建表语句:

drop table word_log;

create table word_log
(
    id int unsigned auto_increment comment '利用自增主键' primary key,
    batch_id varchar(128) not null comment '批次号',
    word varchar(128) not null comment '单词',
    word_before varchar(128) null comment '变更前单词',
    word_after varchar(128) null comment '变更后单词',
    type varchar(8) not null comment '类型',
    status char(1) not null default 'S' comment '单词状态',
    remark varchar(64) not null comment '配置形容' default '',
    operator_type varchar(16) not null default ''comment' 操作类别 ',
    operator_id varchar(64) not null default 'system' comment '操作员名称',
    create_time timestamp default CURRENT_TIMESTAMP not null comment '创立工夫戳',
    update_time timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新工夫戳'
) comment '敏感词操作日志表' ENGINE=Innodb default charset=UTF8 auto_increment=1;
create index ix_word on word_log (word) comment '单词一般索引';
create index ix_batch_id on word_log (batch_id) comment '批次号一般索引';
create index ix_update_time on word_log (update_time) comment '更新工夫一般索引';

增加操作类别 (operator_type):

insert into lc_enum_mapping (table_name, column_name, `key`, label)  values ('word_log', 'operator_type', 'CREATE', '新增');
insert into lc_enum_mapping (table_name, column_name, `key`, label)  values ('word_log', 'operator_type', 'DELETE', '删除');
insert into lc_enum_mapping (table_name, column_name, `key`, label)  values ('word_log', 'operator_type', 'UPDATE', '更新');

例子

1)新增

新增 ‘ 敏感 ’

word 敏感
word_before null
word_after 敏感 

2)批改

批改 ‘ 敏感 ’,到 ‘ 敏感批改 ’

word 敏感
word_before 敏感
word_after 敏感批改 

3) 删除

删除 ‘ 敏感批改 ’

word 敏感批改
word_before 敏感批改
word_after null

刷新外围逻辑

咱们启动一个定时工作,判断存在更新时,则从新初始化对应的敏感词信息。

package com.github.houbb.sensitive.word.admin.web.config;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.github.houbb.heaven.util.util.DateUtil;
import com.github.houbb.sensitive.word.admin.dal.entity.WordLog;
import com.github.houbb.sensitive.word.admin.service.service.WordLogService;
import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
import groovy.util.logging.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 分布式部署的更新问题:*
 * 模式 1:push
 * 实时性好,然而须要感知零碎的存在。*
 * 模式 2:pull
 * 存在提早,然而无状态,简略。*
 * 这里采纳模式 2
 *
 * @since 1.2.0
 */
@Component
@Slf4j
public class MySensitiveWordScheduleRefresh {private static final Logger logger = LoggerFactory.getLogger(MySensitiveWordScheduleRefresh.class);

    @Autowired
    private SensitiveWordBs sensitiveWordBs;

    @Autowired
    private WordLogService wordLogService;

    /**
     * 刷新工夫距离
     * @since 1.3.0
     */
    @Value("${sensitive-word.refresh-interval-seconds}")
    private int refreshIntervalSeconds;

    @PostConstruct
    public void init() {logger.info("MySensitiveWordScheduleRefresh init with refreshIntervalSeconds={}", refreshIntervalSeconds);

        // 单线程定时调度。// TODO: 调整对应的 word_log 实现
        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {logger.info("MySensitiveWordScheduleRefresh start");

                    refresh();

                    logger.info("MySensitiveWordScheduleRefresh end");
                } catch (Exception e) {logger.error("MySensitiveWordScheduleRefresh meet ex", e);
                }
            }
        }, refreshIntervalSeconds, refreshIntervalSeconds, TimeUnit.SECONDS);
    }

    /**
     * 更新词库
     *
     * 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的办法。* 如果须要失效,则调用这个办法。*
     * 阐明:从新初始化不影响旧的办法应用。初始化实现后,会以新的为准。*/
    private void refresh() {
        // 缩短 10S,防止脱漏
        int timeDiffer = refreshIntervalSeconds + 10;
        // 判断以后一段时间内是否存在变动?Date date = DateUtil.addSecond(new Date(), -timeDiffer);

        Wrapper<WordLog> wordLogWrapper = new EntityWrapper<>();
        wordLogWrapper.gt("update_time", date);
        int count = wordLogService.selectCount(wordLogWrapper);
        if(count <= 0) {logger.info("MySensitiveWordScheduleRefresh 没有新增的变动信息,疏忽更新。");
            return;
        }

        // 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的办法,而后调用这个办法。// 后续能够优化为针对变动的初始化。sensitiveWordBs.init();}
    
}

sensitive-word.refresh-interval-seconds 属性指定了刷新的距离,可配置。

小结

分布式环境下还是尽可能的谋求架构的简洁性,这里只是一种实现的形式,也能够本人实现基于 push 的模式。

开源代码

sensitive-word-admin v1.3.0

参考资料

https://github.com/houbb/sensitive-word-admin

本文由博客一文多发平台 OpenWrite 公布!

退出移动版