我的编程空间,编程开发者的网络收藏夹
学习永远不晚

利用AOP实现系统告警的方法详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

利用AOP实现系统告警的方法详解

一、业务背景

在开发的过程中会遇到各种各样的开发问题,服务器宕机、网络抖动、代码本身的bug等等。针对代码的bug,我们可以提前预支,通过发送告警信息来警示我们去干预,尽早处理。

二、告警的方式

1、钉钉告警

通过在企业钉钉群,添加群机器人的方式,通过机器人向群内发送报警信息。至于钉钉机器人怎么创建,发送消息的api等等,请参考官方文档

2、企业微信告警

同样的套路,企业微信也是,在企业微信群中,添加群机器人。通过机器人发送告警信息。具体请看官方文档

3、邮件告警

与上述不同的是,邮件是发送给个人的,当然也可以是批量发送,只实现了发送文本格式的方式,至于markdown格式,有待考察。邮件发送相对比较简单,这里就不展开赘述。

三、源码解析

1、Alarm自定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Alarm {

    
    String title() default "";

    
    MessageTye messageType() default MessageTye.TEXT;

    
    String templateId() default "";

    
    boolean successNotice() default false;
}

1.1、注解使用

@Alarm标记在方法上使用,被标记的方法发生异常,会根据配置,读取配置信息,发送异常堆栈信息。使用方法如下所示:

@Alarm(title = "某某业务告警", messageType = MessageTye.MARKDOWN, templateId = "errorTemp")

1.2、注解字段解析

title

告警消息标题:可以定义为业务信息,如导师身份计算

messageType

告警消息展示类型:目前支持text文本类型,markdown类型

templateId

消息模板id:与配置文件中配置的模板id一致

successNotice

正常情况是否也需要发送告警信息,默认值是fasle,表示不需要发送。当然,有些业务场景正常情况也需要发送,比如:支付出单通知等。

2、配置文件分析

2.1、钉钉配置文件

spring:
  alarm:
    dingtalk:
     # 开启钉钉发送告警
      enabled: true
     # 钉钉群机器人唯一的token
      token: xxxxxx
     # 安全设置:加签的密钥
      secret: xxxxxxx

2.2、企业微信配置文件

spring:
  alarm:
    wechat:
     # 开启企业微信告警
      enabled: true
     # 企业微信群机器人唯一key
      key: xxxxxdsf
     # 被@人的手机号
      to-user: 1314243

2.3、邮件配置文件

spring:
  alarm:    
    mail:
      enabled: true
      smtpHost: xxx@qq.com
      smtpPort: 22
      to: xxx@qq.com
      from: 132@qq.com
      username: wsrf
      password: xxx

2.4、自定义模板配置

spring:
  alarm:
    template:
      # 开启通过模板配置
      enabled: true
      # 配置模板来源为文件
      source: FILE
      # 配置模板数据
      templates:
        errorTemp:
          templateId: errorTemp
          templateName: 服务异常模板
          templateContent: 这里是配置模板的内容
  • spring:alarm:template:enabled,Boolean类型,表示开启告警消息使用模板发送。
  • spring:alarm:template:source,模板来源,枚举类:JDBC(数据库)、FILE(配置文件)、MEMORY(内存),目前只支持FILE,其他两种可自行扩展。
  • spring:alarm:template:templates,配置模板内容,是一个map,errorTemp是模板id,需要使用哪种模板,就在@Alarm中的templateId设置为对应配置文件中的templateId。

3、核心AOP分析

3.1、原理分析

3.2、自定义切面

@Aspect
@Slf4j
@RequiredArgsConstructor
public class AlarmAspect {
    private final AlarmTemplateProvider alarmTemplateProvider;

    private final static String ERROR_TEMPLATE = "\n\n<font color=\"#F37335\">异常信息:</font>\n" +
            "```java\n" +
            "#{[exception]}\n" +
            "```\n";

    private final static String TEXT_ERROR_TEMPLATE = "\n异常信息:\n" +
            "#{[exception]}";

    private final static String MARKDOWN_TITLE_TEMPLATE = "# 【#{[title]}】\n" +
            "\n请求状态:<font color=\"#{[stateColor]}\">#{[state]}</font>\n\n";

    private final static String TEXT_TITLE_TEMPLATE = "【#{[title]}】\n" +
            "请求状态:#{[state]}\n";

    @Pointcut("@annotation(alarm)")
    public void alarmPointcut(Alarm alarm) {

    }

    @Around(value = "alarmPointcut(alarm)", argNames = "joinPoint,alarm")
    public Object around(ProceedingJoinPoint joinPoint, Alarm alarm) throws Throwable {
        Object result = joinPoint.proceed();
        if (alarm.successNotice()) {
            String templateId = alarm.templateId();
            String fileTemplateContent = "";
            if (Objects.nonNull(alarmTemplateProvider)) {
                AlarmTemplate alarmTemplate = alarmTemplateProvider.loadingAlarmTemplate(templateId);
                fileTemplateContent = alarmTemplate.getTemplateContent();
            }
            String templateContent = "";
            MessageTye messageTye = alarm.messageType();
            if (messageTye.equals(MessageTye.TEXT)) {
                templateContent = TEXT_TITLE_TEMPLATE.concat(fileTemplateContent);
            } else if (messageTye.equals(MessageTye.MARKDOWN)) {
                templateContent = MARKDOWN_TITLE_TEMPLATE.concat(fileTemplateContent);
            }
            Map<String, Object> alarmParamMap = new HashMap<>();
            alarmParamMap.put("title", alarm.title());
            alarmParamMap.put("stateColor", "#45B649");
            alarmParamMap.put("state", "成功");
            sendAlarm(alarm, templateContent, alarmParamMap);
        }
        return result;
    }


    @AfterThrowing(pointcut = "alarmPointcut(alarm)", argNames = "joinPoint,alarm,e", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, Alarm alarm, Exception e) {
        log.info("请求接口发生异常 : [{}]", e.getMessage());
        String templateId = alarm.templateId();
        // 加载模板中配置的内容,若有
        String templateContent = "";
        String fileTemplateContent = "";
        if (Objects.nonNull(alarmTemplateProvider)) {
            AlarmTemplate alarmTemplate = alarmTemplateProvider.loadingAlarmTemplate(templateId);
            fileTemplateContent = alarmTemplate.getTemplateContent();
        }
        MessageTye messageTye = alarm.messageType();
        if (messageTye.equals(MessageTye.TEXT)) {
            templateContent = TEXT_TITLE_TEMPLATE.concat(fileTemplateContent).concat(TEXT_ERROR_TEMPLATE);
        } else if (messageTye.equals(MessageTye.MARKDOWN)) {
            templateContent = MARKDOWN_TITLE_TEMPLATE.concat(fileTemplateContent).concat(ERROR_TEMPLATE);
        }
        Map<String, Object> alarmParamMap = new HashMap<>();
        alarmParamMap.put("title", alarm.title());
        alarmParamMap.put("stateColor", "#FF4B2B");
        alarmParamMap.put("state", "失败");
        alarmParamMap.put("exception", ExceptionUtil.stacktraceToString(e));
        sendAlarm(alarm, templateContent, alarmParamMap);
    }

    private void sendAlarm(Alarm alarm, String templateContent, Map<String, Object> alarmParamMap) {
        ExpressionParser parser = new SpelExpressionParser();
        TemplateParserContext parserContext = new TemplateParserContext();
        String message = parser.parseExpression(templateContent, parserContext).getValue(alarmParamMap, String.class);
        MessageTye messageTye = alarm.messageType();
        NotifyMessage notifyMessage = new NotifyMessage();
        notifyMessage.setTitle(alarm.title());
        notifyMessage.setMessageTye(messageTye);
        notifyMessage.setMessage(message);
        AlarmFactoryExecute.execute(notifyMessage);
    }
}

4、模板提供器

4.1、AlarmTemplateProvider

定义一个抽象接口AlarmTemplateProvider,用于被具体的子类实现

public interface AlarmTemplateProvider {


    
    AlarmTemplate loadingAlarmTemplate(String templateId);
}

4.2、BaseAlarmTemplateProvider

抽象类BaseAlarmTemplateProvider实现该抽象接口

public abstract class BaseAlarmTemplateProvider implements AlarmTemplateProvider {

    @Override
    public AlarmTemplate loadingAlarmTemplate(String templateId) {
        if (StringUtils.isEmpty(templateId)) {
            throw new AlarmException(400, "告警模板配置id不能为空");
        }
        return getAlarmTemplate(templateId);
    }

    
    abstract AlarmTemplate getAlarmTemplate(String templateId);
}

4.3、YamlAlarmTemplateProvider

具体实现类YamlAlarmTemplateProvider,实现从配置文件中读取模板,该类在项目启动时,会被加载进spring的bean容器

@RequiredArgsConstructor
public class YamlAlarmTemplateProvider extends BaseAlarmTemplateProvider {

    private final TemplateConfig templateConfig;

    @Override
    AlarmTemplate getAlarmTemplate(String templateId) {
        Map<String, AlarmTemplate> configTemplates = templateConfig.getTemplates();
        AlarmTemplate alarmTemplate = configTemplates.get(templateId);
        if (ObjectUtils.isEmpty(alarmTemplate)) {
            throw new AlarmException(400, "未发现告警配置模板");
        }
        return alarmTemplate;
    }
}

4.4、MemoryAlarmTemplateProvider和JdbcAlarmTemplateProvider

抽象类BaseAlarmTemplateProvider还有其他两个子类,分别是MemoryAlarmTemplateProviderJdbcAlarmTemplateProvider。但是这两个子类暂时还未实现逻辑,后续可以自行扩展。

@RequiredArgsConstructor
public class MemoryAlarmTemplateProvider extends BaseAlarmTemplateProvider {

    private final Function<String, AlarmTemplate> function;
    @Override
    AlarmTemplate getAlarmTemplate(String templateId) {
        AlarmTemplate alarmTemplate = function.apply(templateId);
        if (ObjectUtils.isEmpty(alarmTemplate)) {
            throw new AlarmException(400, "未发现告警配置模板");
        }
        return alarmTemplate;
    }
}
@RequiredArgsConstructor
public class JdbcAlarmTemplateProvider extends BaseAlarmTemplateProvider {

    private final Function<String, AlarmTemplate> function;

    @Override
    AlarmTemplate getAlarmTemplate(String templateId) {
        AlarmTemplate alarmTemplate = function.apply(templateId);
        if (ObjectUtils.isEmpty(alarmTemplate)) {
            throw new AlarmException(400, "未发现告警配置模板");
        }
        return alarmTemplate;
    }
}

两个类中都有Function<String, AlarmTemplate>接口,为函数式接口,可以供外部自行去实现逻辑。

5、告警发送

5.1、AlarmFactoryExecute

该类内部保存了一个容器,主要用于缓存真正的发送类

public class AlarmFactoryExecute {

    private static List<AlarmWarnService> serviceList = new ArrayList<>();

    public AlarmFactoryExecute(List<AlarmWarnService> alarmLogWarnServices) {
        serviceList = alarmLogWarnServices;
    }

    public static void addAlarmLogWarnService(AlarmWarnService alarmLogWarnService) {
        serviceList.add(alarmLogWarnService);
    }

    public static List<AlarmWarnService> getServiceList() {
        return serviceList;
    }

    public static void execute(NotifyMessage notifyMessage) {
        for (AlarmWarnService alarmWarnService : getServiceList()) {
            alarmWarnService.send(notifyMessage);
        }
    }
}

5.2、AlarmWarnService

抽象接口,只提供一个发送的方法

public interface AlarmWarnService {

    
    void send(NotifyMessage notifyMessage);

}

5.3、BaseWarnService

与抽象的模板提供器AlarmTemplateProvider一样的套路,该接口有一个抽象的实现类BaseWarnService,该类对外暴露send方法,用于发送消息,内部用doSendMarkdown,doSendText方法实现具体的发送逻辑,当然具体发送逻辑还是得由其子类去实现。

@Slf4j
public abstract class BaseWarnService implements AlarmWarnService {

    @Override
    public void send(NotifyMessage notifyMessage) {
        if (notifyMessage.getMessageTye().equals(MessageTye.TEXT)) {
            CompletableFuture.runAsync(() -> {
                try {
                    doSendText(notifyMessage.getMessage());
                } catch (Exception e) {
                    log.error("send text warn message error", e);
                }
            });
        } else if (notifyMessage.getMessageTye().equals(MessageTye.MARKDOWN)) {
            CompletableFuture.runAsync(() -> {
                try {
                    doSendMarkdown(notifyMessage.getTitle(), notifyMessage.getMessage());
                } catch (Exception e) {
                    log.error("send markdown warn message error", e);
                }
            });
        }
 }

    
    protected abstract void doSendMarkdown(String title, String message) throws Exception;

    
    protected abstract void doSendText(String message) throws Exception;
}

5.4、DingTalkWarnService

主要实现了钉钉发送告警信息的逻辑

@Slf4j
public class DingTalkWarnService extends BaseWarnService {

    private static final String ROBOT_SEND_URL = "https://oapi.dingtalk.com/robot/send?access_token=";
    private final String token;

    private final String secret;

    public DingTalkWarnService(String token, String secret) {
        this.token = token;
        this.secret = secret;
    }

    public void sendRobotMessage(DingTalkSendRequest dingTalkSendRequest) throws Exception {
        String json = JSONUtil.toJsonStr(dingTalkSendRequest);
        String sign = getSign();
        String body = HttpRequest.post(sign).contentType(ContentType.JSON.getValue()).body(json).execute().body();
        log.info("钉钉机器人通知结果:{}", body);
    }

    
    private String getSign() throws Exception {
        long timestamp = System.currentTimeMillis();
        String stringToSign = timestamp + "\n" + secret;
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
        return ROBOT_SEND_URL + token + "&timestamp=" + timestamp + "&sign=" + URLEncoder.encode(new String(Base64.getEncoder().encode(signData)), StandardCharsets.UTF_8.toString());
    }

    @Override
    protected void doSendText(String message) throws Exception {
        DingTalkSendRequest param = new DingTalkSendRequest();
        param.setMsgtype(DingTalkSendMsgTypeEnum.TEXT.getType());
        param.setText(new DingTalkSendRequest.Text(message));
        sendRobotMessage(param);
    }

    @Override
    protected void doSendMarkdown(String title, String message) throws Exception {
        DingTalkSendRequest param = new DingTalkSendRequest();
        param.setMsgtype(DingTalkSendMsgTypeEnum.MARKDOWN.getType());
        DingTalkSendRequest.Markdown markdown = new DingTalkSendRequest.Markdown(title, message);
        param.setMarkdown(markdown);
        sendRobotMessage(param);
    }
}

5.5、WorkWeXinWarnService

主要实现了发送企业微信告警信息的逻辑

@Slf4j
public class WorkWeXinWarnService extends BaseWarnService {
    private static final String SEND_MESSAGE_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=%s";
    private final String key;

    private final String toUser;

    public WorkWeXinWarnService(String key, String toUser) {
        this.key = key;
        this.toUser = toUser;
    }

    private String createPostData(WorkWeXinSendMsgTypeEnum messageTye, String contentValue) {
        WorkWeXinSendRequest wcd = new WorkWeXinSendRequest();
        wcd.setMsgtype(messageTye.getType());
        List<String> toUsers = Arrays.asList("@all");
        if (StringUtils.isNotEmpty(toUser)) {
            String[] split = toUser.split("\\|");
            toUsers = Arrays.asList(split);
        }
        if (messageTye.equals(WorkWeXinSendMsgTypeEnum.TEXT)) {
            WorkWeXinSendRequest.Text text = new WorkWeXinSendRequest.Text(contentValue, toUsers);
            wcd.setText(text);
        } else if (messageTye.equals(WorkWeXinSendMsgTypeEnum.MARKDOWN)) {
            WorkWeXinSendRequest.Markdown markdown = new WorkWeXinSendRequest.Markdown(contentValue, toUsers);
            wcd.setMarkdown(markdown);
        }
        return JSONUtil.toJsonStr(wcd);
    }

    @Override
    protected void doSendText(String message) {
        String data = createPostData(WorkWeXinSendMsgTypeEnum.TEXT, message);
        String url = String.format(SEND_MESSAGE_URL, key);
        String resp = HttpRequest.post(url).body(data).execute().body();
        log.info("send work weixin message call [{}], param:{}, resp:{}", url, data, resp);
    }

    @Override
    protected void doSendMarkdown(String title, String message) {
        String data = createPostData(WorkWeXinSendMsgTypeEnum.MARKDOWN, message);
        String url = String.format(SEND_MESSAGE_URL, key);
        String resp = HttpRequest.post(url).body(data).execute().body();
        log.info("send work weixin message call [{}], param:{}, resp:{}", url, data, resp);
    }
}

5.6、MailWarnService

主要实现邮件告警逻辑

@Slf4j
public class MailWarnService extends BaseWarnService {

    private final String smtpHost;

    private final String smtpPort;

    private final String to;

    private final String from;

    private final String username;

    private final String password;

    private Boolean ssl = true;

    private Boolean debug = false;

    public MailWarnService(String smtpHost, String smtpPort, String to, String from, String username, String password) {
        this.smtpHost = smtpHost;
        this.smtpPort = smtpPort;
        this.to = to;
        this.from = from;
        this.username = username;
        this.password = password;
    }

    public void setSsl(Boolean ssl) {
        this.ssl = ssl;
    }

    public void setDebug(Boolean debug) {
        this.debug = debug;
    }

    @Override
    protected void doSendText(String message) throws Exception {
        Properties props = new Properties();
        props.setProperty("mail.smtp.auth", "true");
        props.setProperty("mail.transport.protocol", "smtp");
        props.setProperty("mail.smtp.host", smtpHost);
        props.setProperty("mail.smtp.port", smtpPort);
        props.put("mail.smtp.ssl.enable", true);
        Session session = Session.getInstance(props);
        session.setDebug(false);
        MimeMessage msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress(from));
        for (String toUser : to.split(",")) {
            msg.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(toUser));
        }
        Map<String, String> map = JSONUtil.toBean(message, Map.class);
        msg.setSubject(map.get("subject"), "UTF-8");
        msg.setContent(map.get("content"), "text/html;charset=UTF-8");
        msg.setSentDate(new Date());
        Transport transport = session.getTransport();
        transport.connect(username, password);
        transport.sendMessage(msg, msg.getAllRecipients());
        transport.close();
    }

    @Override
    protected void doSendMarkdown(String title, String message) throws Exception {
        log.warn("暂不支持发送Markdown邮件");
    }
}

6、AlarmAutoConfiguration自动装配类

运用了springboot自定义的starter,再META-INF包下的配置文件spring.factories下,配置上该类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.seven.buttemsg.autoconfigure.AlarmAutoConfiguration

自动装配类,用于装载自定义的bean

@Slf4j
@Configuration
public class AlarmAutoConfiguration {

    // 邮件相关配置装载
    @Configuration
    @ConditionalOnProperty(prefix = MailConfig.PREFIX, name = "enabled", havingValue = "true")
    @EnableConfigurationProperties(MailConfig.class)
    static class MailWarnServiceMethod {

        @Bean
        @ConditionalOnMissingBean(MailWarnService.class)
        public MailWarnService mailWarnService(final MailConfig mailConfig) {
            MailWarnService mailWarnService = new MailWarnService(mailConfig.getSmtpHost(), mailConfig.getSmtpPort(), mailConfig.getTo(), mailConfig.getFrom(), mailConfig.getUsername(), mailConfig.getPassword());
            mailWarnService.setSsl(mailConfig.getSsl());
            mailWarnService.setDebug(mailConfig.getDebug());
            AlarmFactoryExecute.addAlarmLogWarnService(mailWarnService);
            return mailWarnService;
        }
    }

    // 企业微信相关配置装载
    @Configuration
    @ConditionalOnProperty(prefix = WorkWeXinConfig.PREFIX, name = "enabled", havingValue = "true")
    @EnableConfigurationProperties(WorkWeXinConfig.class)
    static class WorkWechatWarnServiceMethod {

        @Bean
        @ConditionalOnMissingBean(MailWarnService.class)
        public WorkWeXinWarnService workWechatWarnService(final WorkWeXinConfig workWeXinConfig) {
            return new WorkWeXinWarnService(workWeXinConfig.getKey(), workWeXinConfig.getToUser());
        }

        @Autowired
        void setDataChangedListener(WorkWeXinWarnService workWeXinWarnService) {
            AlarmFactoryExecute.addAlarmLogWarnService(workWeXinWarnService);
        }
    }

    // 钉钉相关配置装载
    @Configuration
    @ConditionalOnProperty(prefix = DingTalkConfig.PREFIX, name = "enabled", havingValue = "true")
    @EnableConfigurationProperties(DingTalkConfig.class)
    static class DingTalkWarnServiceMethod {

        @Bean
        @ConditionalOnMissingBean(DingTalkWarnService.class)
        public DingTalkWarnService dingTalkWarnService(final DingTalkConfig dingtalkConfig) {
            DingTalkWarnService dingTalkWarnService = new DingTalkWarnService(dingtalkConfig.getToken(), dingtalkConfig.getSecret());
            AlarmFactoryExecute.addAlarmLogWarnService(dingTalkWarnService);
            return dingTalkWarnService;
        }
    }

    // 消息模板配置装载
    @Configuration
    @ConditionalOnProperty(prefix = TemplateConfig.PREFIX, name = "enabled", havingValue = "true")
    @EnableConfigurationProperties(TemplateConfig.class)
    static class TemplateConfigServiceMethod {

        @Bean
        @ConditionalOnMissingBean
        public AlarmTemplateProvider alarmTemplateProvider(TemplateConfig templateConfig) {
            if (TemplateSource.FILE == templateConfig.getSource()) {
                return new YamlAlarmTemplateProvider(templateConfig);
            } else if (TemplateSource.JDBC == templateConfig.getSource()) {
                // 数据库(如mysql)读取文件,未实现,可自行扩展
                return new JdbcAlarmTemplateProvider(templateId -> null);
            } else if (TemplateSource.MEMORY == templateConfig.getSource()) {
                // 内存(如redis,本地内存)读取文件,未实现,可自行扩展
                return new MemoryAlarmTemplateProvider(templateId -> null);
            }
            return new YamlAlarmTemplateProvider(templateConfig);
        }


    }
    @Bean
    public AlarmAspect alarmAspect(@Autowired(required = false) AlarmTemplateProvider alarmTemplateProvider) {
        return new AlarmAspect(alarmTemplateProvider);
    }
}

四、总结

主要借助spring的切面技术,以及springboot的自动装配原理,实现了发送告警逻辑。对业务代码无侵入,只需要在业务代码上标记注解,就可实现可插拔的功能,比较轻量。

以上就是利用AOP实现系统告警的方法详解的详细内容,更多关于AOP系统告警的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

利用AOP实现系统告警的方法详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Golang与RabbitMQ实现系统监控和告警的方案

Golang与RabbitMQ可以通过以下方案实现系统监控和告警:1. 使用Golang编写监控程序:使用Golang编写监控程序来定期检查系统的各项指标,例如CPU利用率、内存使用情况、网络流量等。该监控程序可以使用系统库或第三方库来获取
2023-10-08

Win7系统使用过程中提示系统资源不足警告的解决方法

在使用电脑公司Win7系统的过程中,弹出了提示系统资源不足的警告,要怎么处理?其实会出现提示系统资源不足的操作编程有很多,例如在打开某一游戏程序时,在打开EXCEL时,甚至在打开控制面板时,都会出现类似提示。下面介绍四个通用的解决方法。一、
2023-06-08

zabbix3.4.15用python实现钉钉机器人告警的方法是什么

zabbix3.4.15用python实现钉钉机器人告警的方法是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。zabbix服务器监控到异常,可以通过钉钉机器人进行报警通
2023-06-05

利用node.js实现反向代理的方法详解

本文主要给大家介绍的是关于利用node.js实现反向代理的相关内容,分享出供大家参考学习,下面话不多说,来一起看看详细的介绍: 跨域问题是前端开发很常见的问题解决方案有很多种jsonp返回Access-Control-Allow-Origi
2022-06-04

Java利用StampedLock实现读写锁的方法详解

在jdk8以后,java提供了一个性能更优越的读写锁并发类StampedLock,该类的设计初衷是作为一个内部工具类,用于辅助开发其它线程安全组件。本文就来和大家一起学习下StampedLock的功能和使用
2022-11-13

nodejs实现发出蜂鸣声音(系统报警声)的方法

本文实例讲述了nodejs实现发出蜂鸣声音(系统报警声)的方法。分享给大家供大家参考,具体如下: nodejs 有时候需要发出报警声音,使用以下代码可以调用系统蜂鸣器发声process.stdout.write('x07')另外附上我自己的
2022-06-04

详解如何利用C#实现设置系统时间

这篇文章主要为大家详细介绍了如何利用C#实现设置系统时间功能,文中的示例代码讲解详细,对我们学习C#有一定的帮助,感兴趣的小伙伴可以跟随小编一起了解一下
2022-12-20

利用Vue3实现可复制表格的方法详解

表格是前端非常常用的一个控件,本文主要为大家介绍了Vue3如何实现一个简易的可以复制的表格,文中的示例代码讲解详细,需要的可以参考一下
2022-12-12

Spring切面优先级与基于xml的AOP实现方法详解

这篇文章主要介绍了Spring切面的优先级与基于xml的AOP的详细步骤,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2022-11-13

win8系统开机正常进入系统却弹出一个syslevelup警告窗口的原因及解决方法

很多用户在安装windows8系统的时候都遇到这样的问题,开机正常进入系统的时候,自动弹出一个syslevelup警告窗口,显示一大串英文,很多用户都不知道如何解决,网上搜索解决方法,但是都比较复杂,下面编程网的小编就带来本教程和大家分享w
2022-06-04

Android中利用Xposed框架实现拦截系统方法

一、前言 关于Xposed框架相信大家应该不陌生了,他是Android中Hook技术的一个著名的框架,还有一个框架是CydiaSubstrate,但是这个框架是收费的,而且个人觉得不怎么好用,而Xposed框架是免费的而且还是开源的,网上也
2022-06-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录