卡飞资源网

专业编程技术资源共享平台

工作笔记用Java的cmd程序生成curl脚本批量处理数据

情况是这样的,线上系统出问题了。有一个弱密码的判断功能,我做了缓存,结果用户修改了密码后,没有及时修改缓存,导致重复报弱密码,要求修改密码后才能执行下一步操作。

卡BUG了。

怎么办呢?首先,因为系统性能问题,我做了redis缓存,缓存时长是可配置的,但配置了10天;原则上,如果旅客修改了密码,会触发事件清空缓存的,但修改密码的接口比较多,代码没有覆盖到;现在唯一能抢救的,就是先把缓存时长配置到最低(几乎无缓存),然后想法清理掉原先的缓存值。

生产服务器是隔离的,我要去查看和操作redis,有点麻烦。幸好有一个运维接口,获得了redis所有的key值。但是,取出来的key值很多,有27M的大小,我需要通过匹配关系,取出需要删除的key值。于是写了一个cmd控制台小程序,把需要的值批量取出来,然后在生成curl脚本,回调服务器清空相关的缓存。

操作步骤如下:

1、通过运维接口,获得应用下redis的所有key值(但有27M大小,需要筛选)

2、临时编写一个cmd控制台小程序,筛选出要清理的key值,并批量生成curl脚本

3、在服务器,通过curl脚本调用接口的方式,清空对应的redis缓存。

相关代码:

一、获得应用下redis的所有key值

定义操作的枚举



import com.alibaba.fastjson.JSONObject;
import com.hna.hicloud.uni.user.common.util.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.ArrayList;
import java.util.List;


@AllArgsConstructor
@Getter
public enum RedisDevManageActionEnum {
    GET_VALUE("get-value", "通过key获得值"),
    GET_KEY("get-key", "获得keys"),
    DELETE_ITEM("delete-item","删除"),
    DELETE_BATCH("delete-batch","批量删除"),
    UNKNOWN("unknown", "未知"),
    ;

    private final String code;
    private final String desc;


    public static RedisDevManageActionEnum getItem(String pKey, RedisDevManageActionEnum defaultValue) {
        if (pKey == null || pKey.isEmpty()) {
            return defaultValue;
        }
        for (RedisDevManageActionEnum item : RedisDevManageActionEnum.values()) {
            if (pKey.equals(item.getCode())) {
                return item;
            }
        }
        return defaultValue;
    }


    protected static String toJson() {
        List<JSONObject> dataList = new ArrayList<>();
        JSONObject item = null;
        for (RedisDevManageActionEnum em : RedisDevManageActionEnum.values()) {

            item = new JSONObject();
            dataList.add(item);
            item.put("id", em.getCode());
            item.put("name", em.getDesc());
        }

        return JsonUtil.objToJsonStr(dataList);
    }

    public static void main(String[] args) {

        String str = toJson();
        System.out.println(str);

    }
}


定义redis操作的相关方法

@Override
public RestResponse redisDevManage(String appId, RedisDevManageReqDTO req) {

    String key = null;
    String data = null;
    RedisDevManageActionEnum actionEnum = RedisDevManageActionEnum.getItem(req.getAction(), RedisDevManageActionEnum.UNKNOWN);
    switch (actionEnum) {
        case GET_KEY:

            // 例如,匹配所有以 “user:” 开头的键。user:*
            // "*product*"
            // 比如匹配以 “category-” 开头,后面跟着任意一个字符,再接着是 “-item” 结尾的键。category-?-item
            // “*”:代表任意数量的任意字符。
            // “?”:代表单个任意字符。
            key = req.getKeyName();
            if (StrUniUtil.strIsTrimEmpty(key)) {
                key = "*";
            }
            return RestResponse.ok(redisTemplate.keys(key));
        case GET_VALUE:
            key = req.getKeyName();
            data = redisTemplate.opsForValue().get(key);
            if (StrUniUtil.strIsNotEmpty(data)) {
                return RestResponse.ok(data);
            } else {
                return RestResponse.ok();
            }

        case DELETE_ITEM:
            key = req.getKeyName();
            CheckUtil.assertStrTrimNotEmpty(key, ErrorCode.ILLEGAL_ARGUMENT, "keyName不能为空");
            return RestResponse.ok(redisTemplate.delete(key));
        case DELETE_BATCH:
            List<String> keyArray = StrUniUtil.strSplitRnToArray(req.getKeyName(), true, true, false, "");
            boolean deleteItemResult = false;
            JSONObject batchDeleteResult = new JSONObject();
            for (String keyEach : keyArray) {
                key = keyEach;
                deleteItemResult = redisTemplate.delete(key);
                batchDeleteResult.put(key, deleteItemResult);
            }
            return RestResponse.ok(batchDeleteResult);
        case UNKNOWN:
        default:
            return RestResponse.ok(actionEnum);
    }
}


获得的数据样例如下:

{
    "code":0,
    "data":[
        "app::1179208949",
        "channelAppInfo::user",
        "user:center:userid:CZ:99:20250614214935",
        "uc:HU::query:member:pwd:status:123456435",
        "user:center:userid:MU:99:20250614215103",
        "spring:session:expirations:1750210140000",
        "uc:utils:country:list::79b5e4e45df93f47fu",
        "spring:session:expirations:1750321200000",
        "app::wx123",
        "spring:session:sessions:9af1a112-a192-410d-8bf0-4451ab3496f3",
        "channelAppInfo::user111",
        "spring:session:expirations:1750058160000",
        "spring:session:sessions:3544cc02-37dc-449e-8dd6-867ee1f14630",
        "sysConfig::KAFKA_IS_PUSH",
        "uni-risk-sit:uni-risk:email_blacklist",
        "spring:session:sessions:d7d74fe3-702c-4d2d-ada6-3bd683b33f7f",
        "app::web2111" ],
    "requestId":"j0vchk6zedAhuT0614215155P99",
    "traceId":""
}

上面只是简单样例,为了能提取
uc:HU::query:member:pwd:status:为前缀的key值,并生成在sh下能执行的curl脚本,编写cmd代码如下:


import cn.hutool.core.io.FileUtil;

public class ToolCommonOutFileTwo {

    protected static void doOne() {
        String fileName = "C:\\temps\\1.log";
        String outFileName = "C:\\temps\\cidItem.log";
        String outCurlFileName = "C:\\temps\\curl.sh";
        String text = FileUtil.readUtf8String(fileName);
        int idx = -1;
        String cid = null;
        int addNum = 0;

        StringBuilder cidSb = new StringBuilder();
        String curlText = "";
        int curlIdx = 0;
        while (true) {
            idx = text.indexOf("\"uc:HU::query:member:pwd:status:");
            if (idx == -1) {
                break;
            }

            text = text.substring(idx + ("\"uc:HU::query:member:pwd:status:").length());
            idx = text.indexOf("\",");
            if (idx == -1) {
                break;
            }
            cid = text.substring(0, idx);
            ++addNum;
            cidSb.append(cid + '\n');

            if (addNum >= 1000) {
                FileUtil.appendUtf8String(cidSb.toString(), outFileName);
                curlText = "curl -H'Content-Type:application/json' -H'ip:127.0.0.1' -X POST --data '{\"input\":\"" + cidSb.toString().replace("" + '\n', "\\n") + "\",\"action\":\"batch-delete\"}' http://my.app.com/ctrl/clearRedis";
                ++curlIdx;
                FileUtil.appendUtf8String(curlText + '\n' + '\n', outCurlFileName);
                FileUtil.appendUtf8String("echo " + curlIdx + '\n' + '\n', outCurlFileName);
                cidSb = new StringBuilder();
                addNum = 0;

                System.out.println(curlIdx);
            }

        }

        if (addNum > 0) {
            FileUtil.appendUtf8String(cidSb.toString(), outFileName);
            curlText = "curl -H'Content-Type:application/json' -H'ip:127.0.0.1' -X POST --data '{\"input\":\"" + cidSb.toString().replace("" + '\n', "\\n") + "\",\"action\":\"batch-delete\"}' http://my.app.com/ctrl/clearRedis";
            ++curlIdx;
            FileUtil.appendUtf8String(curlText + '\n' + '\n', outCurlFileName);
            FileUtil.appendUtf8String("echo " + curlIdx + '\n' + '\n', outCurlFileName);
            cidSb = new StringBuilder();
            addNum = 0;

            System.out.println(curlIdx);
        }

        System.out.println("end");
    }

    public static void main(String[] args) {
        doOne();
    }
}

几点:

1、临时工具,代码里面固定写好参数

2、如果每条记录都写文件流,执行效率慢,改成1000条一批次写入

3、echo输出,在sh执行的时候,可以知道执行到什么位置

最后,输出效果如下:



生成的curl脚本执行效果:



总结:

1、通过接口获得redis的key值,应该有更好的方法,可以表达式获得指定前缀的,但之前没有研究,因而这次需要取出来的值做二次处理

2、cmd的程序,因为有27M的文件,内容比较多,只能考虑输出到文件,在二次处理。但如果逐条输出,性能比较慢,所以用1000条一批次的方案。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言