现在正在做一个监控的工作,看似简单,但如果多想想还是有很多技巧和问题可以探讨的
需求场景:
- 客户端应该是一个静态类
- 客户端调用该类的记录信息API频度可能很大
- 该类记录信息的API应该是异步的,保证主体性能
- 能够提供多种记录的方式
- 能够记录多种数据方式
需求其实可以分为两部分:
- 对于需要记录的信息最终可能有多种形式来表达
- 使用什么方式(媒体)来表达,比如log,socket,或者消息系统
所以设计的时候,在最开始用Recorder和Out两个接口来表达。
其中Recorder中定义以下方法
/**
* 信息记录者借口
* @author dongxuan.lb
*
*/
public interface Recorder {
/**
* 记录信息
* @param keys
*/
void record(String... keys);
/**
* 该Recorder的名称
* @return
*/
String getName();
void setName(String name);
/**
* 将记录输出
* 同时清理内部数据
* 内部使用Out接口
*/
void free();
}
Out接口很简单:
/**
* 记录输出接口
* @author dongxuan.lb
*
*/
public interface Out {
void output(String info);
}
复杂的输出由具体的子类来完成。
到这里,这两个接口是完全解耦的,彼此没有什么关系。
========================================================================
到这里,可以开始设计具体的Recorder类了。(不同的业务需求可以自己实现Recorder)
这里需要具体化的需求:
写道
主体会接收到一个字符窜数组,不定长度
该API能够在一定时期后输出出现过的key以及该key的次数
比如A.record("a","a","b")
可能的结果是
a=2|b=1
当然这里record中的字符串长度不会非常大,通常小于100个,但是调用该api的频度非常大,可能在1000至10000
监控方面的设计,在使用API记录是不能同步阻塞住 主体业务,所以使用线程方式进行异步。
在这里使用了ThreadPoolExecutor作为作为线程池。
protected ThreadPoolExecutor recorderPool = new ThreadPoolExecutor(
coreSize, coreSize * 2, 30, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(60),
new RecorderThreadFactory("recorderPool"),
new ThreadPoolExecutor.CallerRunsPolicy());
这里需要注意的是coreSize和maxPoolSize的设置,当然这里也是可以调优的地方。
当然使用ThreadPoolExecutor最主要的是RejectedExecutionHandler的具体实现可以高度自由化
当核心处理不过来的时候可以选择自己实现的RejectedExecutionHandler,以满足业务需求
AbstractKeyCountRecorder中代码
private Integer coreSize = Runtime.getRuntime().availableProcessors();
/*
* 记录者线程池
*/
protected ThreadPoolExecutor recorderPool = new ThreadPoolExecutor(
coreSize, coreSize * 2, 30, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(60),
new RecorderThreadFactory("recorderPool"),
new ThreadPoolExecutor.CallerRunsPolicy());
protected abstract Runnable getWorker(String... key);
@Override
public void record(String... keys) {
recorderPool.execute(getWorker(keys));
}
========================================================================
回到具体需求上来。
首先为了记录key的次数,需要一个map来作为记录存数,其中map的K中存储key的名称,map的V中存储key的出现次数。
但这里有两个问题:
- 使用hashMap线程不安全,get和put方法均有可能出现并发问题,导致记录错误
- map中的V存在自加操作,在操作也会有线程问题
事实证明,
map的同步影响的是性能问题,而V得类型选择则非常影响最终结果的正确与否(当然背后还是同步问题)
========================================================================
map的同步选择:
- 选择hashtable
- 使用Collections.synchronizedMap()方式
- 使用ConcurrentHashMap
- 自己手动维护同步
在这里,不想过多介绍这个问题,不过比较奇怪的是,我测试下来,Collections.synchronizedMap()方式比较耗性能
最终为了方便,直接选择ConcurrentHashMap
========================================================================
这里需要重点指出的是,map中V的选型,通常就使用Integer就可以了,但是在这里,虽然map的put,get方法已经同步,但是当多个线程去取同一个V的值时可能看到的是一样的,这样当自加后在put进去,就丢掉了几次记录。
所以如果使用Integer作为计数器类,结果必然小了很多。
那么自然想到的便是AtomInteger类了。内部使用的机制比较复杂,在《java Thread》那本书上有提到过。
写道
private Map<String, AtomicInteger> hitMap = new ConcurrentHashMap<String, AtomicInteger>(1000);
========================================================================
之前说过,Recorder和Out很好的解耦了,到了具体的Recorder中,由传入的out负责具体的输出部分
外部注入out实体
public LevelKeyCountRecorder(Out out) {
this();
this.out = out;
}
当需要输出时调用Out的output方法
@Override
public void free() {
if(!hitMap.isEmpty()) {
StringBuilder value = new StringBuilder();
for (final Entry<String, AtomicInteger> entry : hitMap.entrySet()) {
value.append(
entry.getKey()
+ "="
+ entry.getValue()
+ "|");
}
out.output(value.toString());
hitMap.clear();
}
}
=======================================================================
最后 ,使用日志输出结果
Logger logger = Logger.getLogger(LevelKeyCountRecorder.class);
Out logOut = new LogOut(logger);
Recorder levelRecorder = new LevelKeyCountRecorder(logOut)
- 大小: 29.6 KB
分享到:
相关推荐
它能有效监视、控制和记录内部电脑在互联网上的活动,实时记录局域网内计算机所有收发的邮件、浏览的网页以及FTP上传下载的文件,监视和管理网内用户的聊天行为,控制网内用户访问指定网络资源或网络协议。...
书名:《Visual Basic.NET自动化系统监控--RS-232串行通信》(清华大学出版社.范逸之.廖锦棋)。PDF格式扫描版,全书共分为9章,共475页。 介绍 Visual Basic .NET为广大Visual Basic用户打开了通往程序设计殿堂的...
3 监控系统不能拷到桌面运行,建议放在C盘根目录下运行(例如C:\nv700x_5.0) 4 必须把颜色设为真彩色32位,否则回放会出现底片等不正常的效果 5 如果板卡上印着ver4.1,请安装新板驱动程序 说明书含常见问题...
1.单机版:属于单机管理软件,集服务器和客户端于一体,可以满足区域内实施视频监控的需求(普通的社区/校园/楼宇/商场/交通均可适用),您目前下载的即为单机版免费软件。 2.分发版:功能更加完备,可以满足视频分发...
支持批量SQL语句的运行:用监控器监控每条语句的运行,在运行过程中可以中断/暂停/继续正在运行的语句,甚至可以更正出错误的语句 o 方便高效的块操作,以满足特殊需求: (1)“Format SQL” 按钮能将语句块中...
最近写了一个异步mysql客户端的封装,想与传统的串行方式做下性能对比。包括运行时间、内存使用情况等信息。在github和packagist上搜索并没有找到自己想要的,xhprof又太大了,结果也太复杂,不符合现在的需要。所以...
-输出客户端版本 -显示帐户及其订阅状态 -交互式外壳; 需要pry -显示或更改API端点 -根据API进行身份验证并存储令牌 实时监控发生的情况 进行(已验证的)API调用并打印出结果 -生成对提交问题有用的报告 列...
厂区监控系统的设计方案 1. 系统概述 安全防范项目是企业管理的一个重要组成部分,也是体现安全保卫功能先进性的重要 环节。为了充分体现现代大型企业的时代特色,我们采用了国内最先进的基于数字安防 产品,为贵...
它能够实时捕捉到MySQL服务器端或客户端的请求,并以友好的格式输出抓取内容。输出信息详尽,包括但不限于访问时间、用户、来源IP、数据库、命令执行耗时、返回的行数以及具体的执行语句等。此外,该工具支持批量...
客户端录像:将网络传送的压缩数据以文件形式保存到客户端的主机上; 云台控制:对网络硬盘录像机各监控通道所连接的云台及镜头进行控制; 开关控制:对网络硬盘录像机所连接的报警盒进行输出控制; 远程配置:通过...
在使用本监控卡之前,请详细阅读本说明书所涉及的相关事项,熟悉 硬件、软件各部分的功能后,方能使用,以确保该系统为您发挥最佳功能。 否则一切后果自负。 本说明书将向您详细阐述全实时“视频监控系统”的安装、...
实现了 清楚internet临时文件、Cookie的清除、游览器地址栏历史地址的清除、清楚表单自动完成历史记录、清楚自动密码历史记录、清除收藏夹中的内容、清除RAS自动拨号历史记录、清除系统临时文件夹、清空回收站、清除...
2.3.5 客户端消息发送模块设计 2.3.6 服务器端控制台模块设计 第3章 SQL数据表提取器模块 3.1 概述 3.2 关键技术 3.2.1 如何备份数据库 3.2.2 如何还原数据库 3.2.3 如何附加数据库 3.2.4 如何分离数据库 3.2.5 ...
2.3.5 客户端消息发送模块设计 2.3.6 服务器端控制台模块设计 第3章 SQL数据表提取器模块 3.1 概述 3.2 关键技术 3.2.1 如何备份数据库 3.2.2 如何还原数据库 3.2.3 如何附加数据库 3.2.4 如何分离数据库 3.2.5 ...
拥有以下功能:1、关键词拦截2、关键词替换3、关键词记录4、SQL注入保护5、文件保护6、日志分析【关键词拦截】可以任意设置100个关键词,当网页中出现这些词时,系统会拦截当前网页,不让其输出到客户端。...
由于本方案设计的门禁系统是整个智能一卡通系统的组成部分,因此,通过系统扩展 ,很容易实现考勤、电梯控制、停车场管理、POS消费、食堂餐饮消费、保安巡更管理等 功能。一张智能卡可通用于所有子系统,所有子系统...
ReadTestquestion 7.5. 课程设计作业 第8章 日历记事本 8.1. 设计内容 8.2. 设计要求 8.3. 总体设计 8.4. 具体设计 8.4.1. 运行效果与程序发布 8.4.2. 主类CalendarPad 8.4.3. 记事本...
2.3.5 客户端消息发送模块设计 48 2.3.6 服务器端控制台模块设计 52 第3章 SQL数据表提取器模块 3.1 概述 56 3.2 关键技术 56 3.2.1 如何备份数据库 56 3.2.2 如何还原数据库 57 3.2.3 如何附加...
2.3.5 客户端消息发送模块设计 2.3.6 服务器端控制台模块设计 第3章 SQL数据表提取器模块 3.1 概述 3.2 关键技术 3.2.1 如何备份数据库 3.2.2 如何还原数据库 3.2.3 如何附加数据库 3.2.4 如何分离数据库 3.2.5 ...
Oracle SQL Handler,是专为 Oracle 数据库开发人员及操作人员精心打造的一款 Oracle 客户端工具: 超智能的SQL编辑器;超方便的表格操作(可以通过修改表格的单元格数据去更新数据 库表);多种格式的数据导出包括...