全部产品
弹性计算 会员服务 网络 安全 移动云 数加·大数据分析及展现 数加·大数据应用 管理与监控 云通信 阿里云办公 培训与认证 更多
存储与CDN 数据库 域名与网站(万网) 应用服务 数加·人工智能 数加·大数据基础服务 互联网中间件 视频服务 开发者工具 解决方案 物联网 智能硬件
云监控

事件监控最佳实践

更新时间:2017-10-26 17:09:46

应用场景

服务在运行过程中,难免出现异常情况,有些异常通过重试等手段可以自动恢复,有些则不能,严重异常甚至会中断客户业务。所以我们需要一个系统来记录这些异常,并且在满足特定的条件时触发报警。传统方法是打印文件日志,通过收集日志到特定的系统,例如开源的ELK(ElasticSearch, Logstash, Kibana)中。 这些开源的系统往往是由多个复杂的分布式系统组成,自行维护面临着技术门槛高、成本高的问题。 云监控提供了一个事件监控功能,能很好解决这些问题。

下面通过几个例子简单说明下如何使用事件监控功能。

实战案例

第一步:上报异常

事件监控提供了JAVA SDK和Open API两种上报数据的方式,这里介绍通过JAVA SDK 上报数据。

Step1 添加 Maven 依赖

  1. <dependency>
  2. <groupId>com.aliyun.openservices</groupId>
  3. <artifactId>aliyun-cms</artifactId>
  4. <version>0.1.2</version>
  5. </dependency>

Step2 初始化SDK

  1. // 这里的118代表云监控的应用分组ID,可以以应用的角度来对事件归类, 可以到云监控应用分组列表中查看分组的ID。
  2. CMSClientInit.groupId = 118L;
  3. // 这里的地址是事件系统上报的入口,目前是公网地址。accesskey和secretkey用于身份识别。
  4. CMSClient c = new CMSClient("https://metrichub-cms-cn-hangzhou.aliyuncs.com", accesskey, secretkey);

Step3 考虑是否异步上报数据

云监控事件默认提供了同步的上报策略。 好处是编写代码简单、 保证每次上报事件的可靠,不丢失数据。

但是同步策略也带来一些问题。因为要在业务代码中嵌入事件上报代码,如果网络出现波动,可能会出现阻塞代码执行,影响正常的业务。有很多业务场景并不需要100%要求事件可靠不丢,所以我们需要一个简单的异步上报封装。将事件写到一个LinkedBlockingQueue中,然后通过ScheduledExecutorService异步在后台批量上报。

  1. //初始化queue与Executors:
  2. private LinkedBlockingQueue<EventEntry> eventQueue = new LinkedBlockingQueue<EventEntry>(10000);
  3. private ScheduledExecutorService schedule = Executors.newSingleThreadScheduledExecutor();
  4. //上报事件:
  5. //每一个事件都包含事件的名称与事件的内容,名称用于识别事件,内容是事件的详细信息,支持全文搜索。
  6. public void put(String name, String content) {
  7. EventEntry event = new EventEntry(name, content);
  8. // 这里事件队列满后将直接丢弃,可以根据自己的情况调整这个策略。
  9. boolean b = eventQueue.offer(event);
  10. if (!b) {
  11. logger.warn("事件队列已满,丢弃事件:{}", event);
  12. }
  13. }
  14. //异步提交事件,初始化定时任务,每秒执行run方法批量上报事件。可以根据自己的情况调整上报间隔。
  15. schedule.scheduleAtFixedRate(this, 1, 1, TimeUnit.SECONDS);
  16. public void run() {
  17. do {
  18. batchPut();
  19. } while (this.eventQueue.size() > 500);
  20. }
  21. private void batchPut() {
  22. // 从队列中取出99条事件,用于批量上报
  23. List<CustomEvent> events = new ArrayList<CustomEvent>();
  24. for (int i = 0; i < 99; i++) {
  25. EventEntry e = this.eventQueue.poll();
  26. if (e == null) {
  27. break;
  28. }
  29. events.add(CustomEvent.builder().setContent(e.getContent()).setName(e.getName()).build());
  30. }
  31. if (events.isEmpty()) {
  32. return;
  33. }
  34. // 批量上报事件到云监控, 这里并未重试, SDK也没有重试, 如果对事件可靠度要求高需要自己加重试策略。
  35. try {
  36. CustomEventUploadRequestBuilder builder = CustomEventUploadRequest.builder();
  37. builder.setEventList(events);
  38. CustomEventUploadResponse response = cmsClient.putCustomEvent(builder.build());
  39. if (!"200".equals(response.getErrorCode())) {
  40. logger.warn("上报事件错误:msg: {}, rid: {}", response.getErrorMsg(), response.getRequestId());
  41. }
  42. } catch (Exception e1) {
  43. logger.error("上报事件异常", e1);
  44. }
  45. }

Step4 事件上报Demo

Demo1:http controller的异常监控

主要目的是监控http请求是否有大量异常,如果每分钟异常次数超过一定数量就报警。实现原理是通过spring的拦截器或者servlet filter等技术对HTTP请求拦截,如果出现异常就记录日志,最后通过配置报警规则来达到报警的目的。

上报事件的demo如下:

  1. // 每个事件应该有丰富的信息来帮助我们搜索和定位问题,这里使用的map来组织事件, 最后转成Json格式作为事件的content。
  2. Map<String, String> eventContent = new HashMap<String, String>();
  3. eventContent.put("method", "GET"); // http 请求方法
  4. eventContent.put("path", "/users"); // http path
  5. eventContent.put("exception", e.getClass().getName()); //异常类名,方便搜索
  6. eventContent.put("error", e.getMessage()); // 异常报错信息
  7. eventContent.put("stack_trace", ExceptionUtils.getStackTrace(e)); // 异常堆栈,方便定位问题
  8. // 最后使用前面封装好的异步上报方法提交事件,这里是异步上报,并且没有重试,可能会小概率丢事件,但是已经能很好的满足http未知异常报警这个场景了。
  9. put("http_error", JsonUtils.toJson(eventContent));
  10. ![image.png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/864cf095977cf61bd340dd1461a0247c.png)

Demo2:后台定时任务执行情况的监控与消息消费情况的监控

同上面的http事件,有很多类似的业务场景需要报警,例如后台任务与消息队列消费等,都可以通过类似的方式上报事件达到监控的目的。当异常发生时,第一时间收到报警。

  1. //消息队列的事件组织:
  2. Map<String, String> eventContent = new HashMap<String, String>();
  3. eventContent.put("cid", consumerId); // 代表消费者的身份
  4. eventContent.put("mid", msg.getMsgId()); // 消息的id
  5. eventContent.put("topic", msg.getTopic()); // 消息的主题,
  6. eventContent.put("body", body); // 消息的主体
  7. eventContent.put("reconsume_times", String.valueOf(msg.getReconsumeTimes())); // 消息失败重试的次数
  8. eventContent.put("exception", e.getClass().getName()); // 发生异常时的异常类名
  9. eventContent.put("error", e.getMessage()); // 异常信息
  10. eventContent.put("stack_trace", ExceptionUtils.getStackTrace(e)); // 异常堆栈
  11. // 最后上报事件
  12. put("metaq_error", JsonUtils.toJson(eventContent));

上报后查看事件:message

对队列消息消费异常设置报警:alert

Demo 3:记录重要事件

事件还有一种使用场景是用来记录一些重要的业务发生,但是不需要报警,方便日后翻看。 例如重要业务的操作日志,改密码,修改订单,异地登录等。

查询事件

本文导读目录