丁丁爱历险


  • 首页

  • 分类

  • 归档

  • 标签

  • 搜索
close

UML类图中的关系

发表于 2018-03-14   |   分类于 软件设计   |  

在软件设计过程和源码学习时,经常会用到UML类图。
通常对依赖、继承和实现没有什么问题,但正确区分理解关联、组合、聚合的作用和差异常容易搞迷糊。
需要说明一下,这篇总结并不是入门,是对关键点或是被忽略的内容的一个总结。主要参考wikipedia上的说明,注意英文版和中文版不太一样,某些说法上不统一,以英文版为准。

UML类图中的关系

UML Notations

继承 Inheritance

继承指的是一个类(称为子类、子接口)继承另外的一个类(称为基类、父类、父接口)的功能,并可以增加它自己的新功能的能力。

需要提一下常看到的另一个词:泛化(Generalization)

如果说继承是子类向父类继承,那么泛化就是与继承的反方向。比如:

1
Animal a = new Dog();

实现 Realization/Implementation

没啥好说的,理解下面向接口编程、规约这些词的含义就明白为什么要用接口了。

依赖 Dependency

A uses a B

类A用到了类B,被依赖的对象只是作为一种工具在使用,而并不持有对它的引用。而这种使用关系是具有偶然性、临时性的、单向的、非常弱的,但是B类的变化会影响到A;
表现在代码层面,为类B作为参数被类A在某个method(方法)中使用。

1
2
3
4
5
6
7
8
// 选课服务
public class EnrollmentService {
public Record enroll(Student s, Course c){
Record r = new Record(s,c);
r.save();
return r;
}
}

关联 Association

A has a B + 只是用到基本没啥从属关系

关联代表一系列的连接。有四种:双向,单向,聚合(包括合成、聚合)和反射。
双向举个例子:机组和航班。
其他的后面再说。

1
2
3
public class Order {
private Customer customer;
}

聚合 Aggregation

A has a B + 整体——部分 + 但个体独立

聚合是一种关联。聚合是一个较大的“整体”类中包含一个或多个较小的“部分”类的关系。相反,较小的“部分”类是“整个”较大类的一部分。

1
2
3
public class Playlist {
private List<Song> songs;
}

组成/组合/合成 Composition

A has a B + 整体——部分 + ownership 同生共死

组成是一种更强大的关联。“整体”负责创建或销毁其“部分”。

1
2
3
4
5
6
7
public class Apartment {
private Room bedroom;
public Apartment() {
bedroom = new Room();
}
}

关联、聚合、组成的区别

这里要强调一点,看了不少资料,只有wikipedia和Stack Overflow上明确表达了上面提到过的关系:聚合、组成是两种程度更紧递紧的关联,如下图。我觉得是靠谱的,在中文资料中基本没提到过这个。

三者关系图
聚合 组成
生命周期 各自拥有各自的生命周期 都使用owner的生命周期
子对象 子对象们都属于一个独立的父对象 子对象们都属于一个独立的父对象
关系 Has—A Owns
性质 collection mixture
举例 车和司机 车和车轮

最后说下个人的一点体会:

  1. 三者区别主要在强度上,可以用强度高的表述就不用强度低的表述。
  2. 聚合和组成如果分不清的时候不要太纠结,不一定一定能从代码层面分清楚,业务上了解关系就可以了。
  3. 关联关系,甚至是聚合关系,通常在类图中不会都展示出来,只展示关键的就行了。比如大雁——雁群这种,不画出来关系也很明确。
  4. 读代码更重要的是看抽象(继承、实现),区分聚合和组成其实意义不大。

资料

wiki英文
wiki中文
UML实践详细经典教程
What is the difference between association, aggregation and composition?
UML类图java代码实现

Npm使用

发表于 2017-09-30   |   分类于 开发工具   |  

网上的教程

  • 中文官方
  • 入门

常用命令

初始化一个新的项目

1
yarn init

添加一个依赖包

1
2
3
yarn add [package]
yarn add [package]@[version]
yarn add [package]@[tag]

更新一个依赖包

1
2
3
yarn upgrade [package]
yarn upgrade [package]@[version]
yarn upgrade [package]@[tag]

删除一个依赖包

1
yarn remove [package]

安装所有的依赖包

1
yarn

or

1
yarn install

查看包信息

1
yarn info react

升级某个全局包

1
yarn global upgrade generator-jhipster

和 npm 对比

NPM YARN 说明
npm init yarn init 初始化某个项目
npm install/link yarn install/link 默认的安装依赖操作
npm install taco —save yarn add taco 安装某个依赖,并且默认保存到package.
npm uninstall taco —save yarn remove taco 移除某个依赖项目
npm install taco —save-dev yarn add taco —dev 安装某个开发时依赖项目
npm update taco —save yarn upgrade taco 更新某个依赖项目
npm install taco –global yarn global add taco 安装某个全局依赖项目
npm publish/login/logout yarn publish/login/logout 发布/登录/登出,一系列NPM Registry操作
npm init yarn init 初始化某个项目
npm run/test yarn run/test 运行某个命令,可以在script脚本中去配置

使用淘宝镜像

  1. 查看当前的源

    1
    2
    yarn config get registry
    # -> https://registry.yarnpkg.com
  2. 改成taobao源

    1
    2
    3
    4
    5
    yarn config set registry 'https://registry.npm.taobao.org'
    #yarn config v0.17.3
    #success Set "registry" to "https://#registry.npm.taobao.org".
    #✨ Done in 0.06s.

Oracle执行计划

发表于 2017-08-01   |   分类于 数据库   |  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
--set autotrace on;
--Session级别启用收集
alter session set STATISTICS_LEVEL = ALL; -- 捕捉下一次执行SQL的执行信息
select d.dept_name,u.user_name from sy_org_user u ,sy_org_dept d
where u.dept_code = d.dept_code;
-- 参数:SQL_ID,Child number,Format,
--select * from table(SYS.DBMS_XPLAN.DISPLAY_CURSOR(null,null,'ADVANCED ALLSTATS LAST PEEKED_BINDS'));
select * from table(SYS.DBMS_XPLAN.DISPLAY_CURSOR('bkx0s41y98z0c',null,'ADVANCED ALLSTATS LAST PEEKED_BINDS'));
-- 下面讲解这三个参数的获取
-- 获取SQL_ID,通过sql_text,获得相应的SQL_ID,这里为bkx0s41y98z0c
select * from v$SQLAREA where sql_text like 'select d.dept_name,u.user_name from sy_org_user u ,sy_org_dept d%';
-- 获取Child number,一般指定为null,获取所有的子游标.如果要特别获取,使用以下方式:
-- 父游标
select * from v$SQLAREA where sql_id = 'bkx0s41y98z0c';
-- 子游标:执行计划和优化环境
select * from v$SQL where SQL_id = 'bkx0s41y98z0c';
-- 计划:
select * from v$SQL_PLAN where SQL_id = 'bkx0s41y98z0c' ;
-- 优化环境:
select * from v$SQL_OPTIMIZER_ENV where SQL_id = 'bkx0s41y98z0c';
-- 设置Format: 可以选ALLSTATS=IOSTATS+MEMSTATS
-- IOSTATS 显示该游标累计执行的IO统计信息(Buffers, Reads)
-- MEMSTATS 累计执行的PGA使用信息(Omem 1Mem Used-Mem)
-- LAST 仅显示最后一次执行的统计信息
-- Advanced 显示outline\Query Block Name\Column Projection等信息
-- PEEKED_BINDS 打印解析时使用的绑定变量
-- Typical 不打印PROJECTION,ALIAS
-- 语句级别收集
select /*+ gather_plan_statistics*/ d.dept_name,u.user_name from sy_org_user u ,sy_org_dept d
where u.dept_code = d.dept_code;
--收集all stats有额外的负载,结束后需要回设成默认值:
alter session set STATISTICS_LEVEL=TYPICAL;

开始使用Docker

发表于 2017-07-13   |   分类于 服务端   |  

安装

Docker for Mac

OSX 10.10以后在Mac上使用Docker变得极简单(不需要装虚拟机及其linux),下载安装即可。

官网下载

阿里云下载

使用阿里加速器

  1. 注册阿里云开发者账号
  2. 进入Docker Hub 镜像站点
  3. 记住您的专属加速器地址
  4. 打开Docker for Mac。Preferences–>Daemon–>Basic–>Registry mirrors,点+,将你的地址贴到这里。然后Apply&Restart。

使用DaoCloud加速器(更新于2018-03-27)

最新发现阿里云加速器不好使了,代理会报错,尝试了https改成http也不行。

换成DaoCloud的加速器后恢复正常,超快。注意要更新Docker for Mac到18+版本。

基本使用

查看版本

1
docker version

搜索可用docker镜像

1
docker search 镜像名字

下载容器镜像

1
docker pull 用户名/镜像名

运行容器里的命令

1
docker run 用户名/镜像名 命令........

在容器中安装新的程序(ubuntu环境)

1
docker run 用户名/镜像名 apt-get install -y 程序名

查看容器信息

1
docker ps -l

查询到的信息如下:

1
2
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
029e4b87152e learn/tutorial "apt-get install -..." 37 seconds ago Exited (0) 33 seconds ago dazzling_dijkstra

保存对容器的修改

1
docker commit 容器ID前3-4位 用户名/新的镜像名

这里有一个镜像的基本概念,我们用docker run对容器进行操作,有一些会改变它里面的状态、内容。
如果不commit的,下次启动容器,这些状态就丢失了。所以如果有状态变化,后面一定记得commit,保存镜像。

检查运行中的镜像

1
docker inspect efe

发布docker镜像

1
docker push 用户名/镜像名

参考资料

Docker中文
Docker安装(在Macbook中安装Docker)后配置阿里云加速器

在Mac上安装RabbitMQ

发表于 2017-07-11   |   分类于 服务端   |  

安装

mac下使用Homebrew安装

1
brew install rabbitmq
安装步骤

默认安装至/usr/local/opt/rabbitmq

启动

配置自启动

见上图两条命令行:

1
2
3
4
配置开机自启动:
ln -sfv /usr/local/opt/rabbitmq/*.plist ~/Library/LaunchAgents
然后启动它:
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.rabbitmq.plist

手动启动

当然我们的mac不是服务器,开发时使用下面的手动启动就可以了

安装目录下,通过以下命令启动服务

1
sbin/rabbitmq-server

启动后服务默认占用5672端口,管理端web在15672端口

web管理页面

管理页面默认地址:http://localhost:15672
管理端默认账号:guest/guest

注意这个账号是admin权限的。

有了这个图形化界面,创建用户、分权限啥的就不用敲命令了,想了解命令行的使用请查看参考资料,里面有说。

参考资料

官方mac环境安装说明
图解RabbitMQ安装与配置

启动 mysql 失败 Warning:The /usr/local/mysql/data directory is not owned by the 'mysql' or '_mysql'

发表于 2017-07-07   |   分类于 数据库   |  

Warning:The /usr/local/mysql/data directory is not owned by the ‘mysql’ or ‘_mysql’

这应该是某种情况下导致/usr/local/mysql/data的宿主发生了改变。

解决方法:

打开终端运行 sudo chown -R mysql /usr/local/mysql/data 即可。

mac 下运行 sudo chown -R _mysql:wheel /usr/local/mysql/data 。

  -c 显示更改的部分的信息

  -f 忽略错误信息

  -h 修复符号链接

  -R 处理指定目录以及其子目录下的所有文件

  -v 显示详细的处理信息

基于redis的服务架构(未成文)

发表于 2017-06-19   |   分类于 服务端   |  

本文探索Redis在企业服务架构中的作用,目前仅收集资料,后期总结心得,逐步完善知识体系。

资料

架构

Redis/Twisted系统整合
时序数据库

在云上搭建大规模实时数据流处理系统
通过Netty通信,采集设备现场GPS数据,并存放在redis服务器
Netty傻瓜教程(五):不能不谈Redis
Redis在游戏服务器中的应用
阿里云redis

redis怎么做消息队列?

测试

Redis 的性能幻想与残酷现实

知乎问题:
如何解决redis高并发客户端频繁time out?
现在业务上每天有5亿+的请求,平时redis的操作在2K+每秒左右。到了高峰有3K+,这时候客户端就会频繁的报connect time out的异常。
但是,资料上说redis可以达到10W每秒。3K远远不到w这个级别啊,请问有什么建议优化现在的情况么?

使用

Jedis对redis的操作详解
Java中使用Jedis操作Redis

使用Redis之前5个必须了解的事情
redis3.2新功能–GEO地理位置命令介绍
redis 常用整理

综合

Hello_Nick_Xu的redis系列文章

资源

各种redis相关项目-oschina

MySql merge分表

发表于 2017-06-19   |   分类于 服务端   |  

MySql的存储引擎

常用的几种引擎类型:

  1. InnoDB 是业务系统里最常用的,支持事务处理,不支持全文本搜索
  2. MyISAM 性能极高的一个引擎,支持全文本搜索,但不支持事务处理
  3. MEMORY 功能等同于MyISAM,但数据存储在内存中,速度更快,适合于临时表

InnoDB和MyISAM的区别:

  1. MySQL默认采用的是MyISAM。
  2. MyISAM不支持事务,而InnoDB支持。InnoDB的AUTOCOMMIT默认是打开的,即每条SQL语句会默认被封装成一个事务,自动提交,这样会影响速度,所以最好是把多条SQL语句显示放在begin和commit之间,组成一个事务去提交。
  3. InnoDB支持数据行锁定,MyISAM不支持行锁定,只支持锁定整个表。即MyISAM同一个表上的读锁和写锁是互斥的,MyISAM并发读写时如果等待队列中既有读请求又有写请求,默认写请求的优先级高,即使读请求先到,所以MyISAM不适合于有大量查询和修改并存的情况,那样查询进程会长时间阻塞。因为MyISAM是锁表,所以某项读操作比较耗时会使其他写进程饿死。
  4. InnoDB支持外键,MyISAM不支持。
  5. InnoDB的主键范围更大,最大是MyISAM的2倍。
  6. InnoDB不支持全文索引,而MyISAM支持。全文索引是指对char、varchar和text中的每个词(停用词除外)建立倒排序索引。MyISAM的全文索引其实没啥用,因为它不支持中文分词,必须由使用者分词后加入空格再写到数据表里,而且少于4个汉字的词会和停用词一样被忽略掉。
  7. MyISAM支持GIS数据,InnoDB不支持。即MyISAM支持以下空间数据对象:Point,Line,Polygon,Surface等。
  8. 没有where的count()使用MyISAM要比InnoDB快得多。因为MyISAM内置了一个计数器,count()时它直接从计数器中读,而InnoDB必须扫描全表。所以在InnoDB上执行count()时一般要伴随where,且where中要包含主键以外的索引列。为什么这里特别强调“主键以外”?因为InnoDB中primary index是和raw data存放在一起的,而secondary index则是单独存放,然后有个指针指向primary key。所以只是count()的话使用secondary index扫描更快,而primary key则主要在扫描索引同时要返回raw data时的作用较大。

merge分表策略

  1. 使用merge的话,数据插入策略只能存在FIRST或LAST一张表里,分表策略自然只能选择按时间。
  2. mysql单表不要超过500万数据。我们目前巡线轨迹数据30秒一条,一天按12小时算,单用户一天1440条数据。如果按天,单表支持3472个用户;如果按月(31天)单表支持112个用户。单表极限用户数(30秒1条,1天10小时)。当然都是比较理想的情况,实际可以等当前表差不多到500万了,再加新表。

优点

  • 不需要改代码。
  • 数据库改动小,也很灵活。

    缺点

  • merge只能用在mysql,oracle用的是按范围分区,也可以达到类似的效果。其他数据库暂未研究。

一般步骤

  1. merge简介分表就是把N条记录的表,分成若干个分表,各个分表记录的总和仍为N。
    分表的方法有很多,用merge来分表,是最简单的一种方式.merge是mysql的一种存储引擎,它把一组MyISAM数据表当做一个逻辑单元.
    1
    2
    3
    4
    5
    6
    7
    8
    CREATE TABLE `t` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `data` VARCHAR(45) NOT NULL,
    PRIMARY KEY (`id`)
    )
    ENGINE = MERGE
    UNION = (t1, t2)
    INSERT_METHOD = LAST;

其中ENGINE = MERGE表示,使用merge引擎。另外ENGINE = MRG_MyISAM是一样的意思。UNION = (t1, t2)表示,挂接了t1,
t2表INSERT_METHOD = LAST表示,插入方式。0不允许插入,FIRST插入到UNION中的第一个表,LAST插入到UNION中的最后一个表.

  1. merge数据存储结构mysql中MyISAM引擎下每一张表都对应三个文件: .MYD数据文件,.MYI索引文件,.frm表结构文件.但是Merge引擎下每一张表只有一个.MRG文件.MRG里面存放着分表的关系,以及插入数据的方式。它就像是一个外壳,或者是连接池,
    数据存放在分表里面.

一个完整的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
CREATE TABLE `cdp_gps_record_old` (
`ID` varchar(40) NOT NULL COMMENT '主键',
`TASKRECORDID` varchar(40) NOT NULL COMMENT '巡线记录ID,外键',
`LONGITUDE` decimal(12,9) NOT NULL COMMENT '经度,原始坐标',
`LATITUDE` decimal(12,9) NOT NULL COMMENT '纬度,原始坐标',
`X` decimal(20,3) NOT NULL COMMENT '投影X',
`Y` decimal(20,3) NOT NULL COMMENT '投影Y',
`SPEED` decimal(8,0) NOT NULL COMMENT '速度',
`OPM_ID` varchar(40) NOT NULL COMMENT '巡线工ID,外键',
`TIME` varchar(30) NOT NULL COMMENT '时间',
`SAVETIME` varchar(30) DEFAULT NULL COMMENT '入库存储时间',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='实时记录数据old';
CREATE TABLE `cdp_gps_record_now` (
`ID` varchar(40) NOT NULL COMMENT '主键',
`TASKRECORDID` varchar(40) NOT NULL COMMENT '巡线记录ID,外键',
`LONGITUDE` decimal(12,9) NOT NULL COMMENT '经度,原始坐标',
`LATITUDE` decimal(12,9) NOT NULL COMMENT '纬度,原始坐标',
`X` decimal(20,3) NOT NULL COMMENT '投影X',
`Y` decimal(20,3) NOT NULL COMMENT '投影Y',
`SPEED` decimal(8,0) NOT NULL COMMENT '速度',
`OPM_ID` varchar(40) NOT NULL COMMENT '巡线工ID,外键',
`TIME` varchar(30) NOT NULL COMMENT '时间',
`SAVETIME` varchar(30) DEFAULT NULL COMMENT '入库存储时间',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='实时记录数据now';
CREATE TABLE `cdp_gps_record` (
`ID` varchar(40) NOT NULL COMMENT '主键',
`TASKRECORDID` varchar(40) NOT NULL COMMENT '巡线记录ID,外键',
`LONGITUDE` decimal(12,9) NOT NULL COMMENT '经度,原始坐标',
`LATITUDE` decimal(12,9) NOT NULL COMMENT '纬度,原始坐标',
`X` decimal(20,3) NOT NULL COMMENT '投影X',
`Y` decimal(20,3) NOT NULL COMMENT '投影Y',
`SPEED` decimal(8,0) NOT NULL COMMENT '速度',
`OPM_ID` varchar(40) NOT NULL COMMENT '巡线工ID,外键',
`TIME` varchar(30) NOT NULL COMMENT '时间',
`SAVETIME` varchar(30) DEFAULT NULL COMMENT '入库存储时间',
PRIMARY KEY (`ID`)
) ENGINE = MRG_MyISAM DEFAULT CHARSET=utf8 INSERT_METHOD=LAST COMMENT='实时记录数据总表'
UNION =(`cdp_gps_record_old`,`cdp_gps_record_now`);

参考资料

MyISAM和InnoDB的区别
MyISAM和InnoDB的区别

MySQL存储引擎–MyISAM与InnoDB区别

MySQL使用方案
如果单表的数据达到300万,是否考虑分表?
Mysql分表查询引擎Merge技术总结
mysql分表方法—–MRG_MyISAM引擎分表法

oracle数据分表使用实例

Ehcache使用JGroups做组播集群

发表于 2017-06-16   |   分类于 服务端   |  

关于缓存的几点小想法

开篇先跑跑题。最近在研究集群环境下的缓存同步问题,Web开发领域常见的几种缓存技术:Ehcache、Redis和memcached。
Ehcache是我们现在用的,它的最大优点是它是纯Java的,跟应用跑在同一个JVM里,不存在传输上的开销;
Redis在用作缓存时常同memcached对比,我们知道他可以做缓存服务之外,其实他本身是个数据库,甚至自身就可以做消息队列。

从Ehcache、Redis中的测试报告来看,都说自己更快,这是因为他们都偷换了概念,以己之长比别人的短处。
Ehcache长处在JVM运行,但这只是单点,跟Redis比单点肯定有优势。免费方案只能考虑做组播,组播对网络环境要求较高,而且如果集群节点很多,组播次数呈级数上升,形成组播风暴,可用性很差。
Redis单点可能比不过Ehcache,但可以集中或集群式部署缓存服务,供分布式系统中的其他业务服务使用。各个节点采用订阅模式,当某一节点相关缓存更新,通知Redis,Redis在发布给订阅的节点更新缓存。有多少节点就发多少次。

这里不做过多发散,直接说我的小想法——用Ehcache和Redis做二级缓存。
Ehcache做一级缓存,查询优先从这里查数据,如果没有再去二级缓存Redis上查找,如果还没有,再去数据库查询。

但是很不幸。。已经有人这么做过了——J2Cache
思路一致,不过好像太监了。1.3版都没提交中心库。

Ehcache使用JGroups做组播集群

那既然组播有缺点,为啥还要写这个呢?因为如果你只是做2-3个节点的负载均衡,其实这东西还是靠谱的,又由于很多资料不靠谱,我觉得还是把我梳理的写出来。

跑题结束,下面说正事。

jar依赖添加

jgroups官方文档2.1节中有如下内容:

1
2
3
4
5
6
7
8
9
10
2.1. Requirements
JGroups up to (and including) 3.5.0.Final requires JDK 6.
JGroups 3.6.x to (excluding) 4.0 requires JDK 7.
JGroups 4.0 will require JDK 8.
There is no JNI code present so JGroups should run on all platforms.
Logging: by default, JGroups tries to use log4j2. If the classes are not found on the classpath, it resorts to log4j, and if still not found, it falls back to java.util.logging logger. See Logging for details on log configuration.

  • 3.6.0(不含)以下的版本需要JDK6
  • 3.6.x 以上到4.0(不含)需要JDK7
  • 4.0以上的需要JDK8
  • 需要log4j或者log4j2

比如,我们的一个项目依赖如下:
JDK6
ehcache-core-2.6.11.jar
jgroups-3.5.1.Final.jar

另外还需要下面这个jar包,用来整合jgroups和ehcache,点击可以下载:
ehcache-jgroupsreplication-1.7.jar

最后还有一些jar包:
slf4j-api-1.6.6.jar
slf4j-jdk14-1.6.6.jar
log4j-1.2.13.jar

组播策略

组播策略是我们在编写配置文件前需要考虑的问题,我们选择的是通知失效策略。
即各节点独立维护自己的缓存,当一个节点的某缓存发生变化时,并不将改变化同步复制到其他节点。而是通知其他节点清除该缓存。

选择这种策略的原因是我们的应用服务器在Nginx上使用了IP哈希策略,一个设备的用户请求会固定由一个应用服务器处理。这种情况下,
很多数据是不用在其他节点缓存具体数据的,只在自己的节点有就行,使用通知失效策略减小了同步缓存的开销。

而ehcache可以对不同的数据配置不同的策略,比如在线用户名单之类的全局数据我们依然可以使用同步复制的策略,保持各节点下该缓存的数据是一致的。

集群策略

一开始我们用了两台机器,不在同一个网段。采用UDP,自动发现的情况下没有问题;但是采用TCP,指定IP的时候就死活不成功,直到挪到同一台,不同端口才成功。(没试同一网段,以后再说。)

配置文件

需要修改ehcache配置文件ehcache.xml,我还把jgroups的配置文件独立了出来,所以还要新建一个jgroups_tcp.xml。

jgroups_tcp.xml

官方默认配置例子在jar包里的tcp.xml。下面的例子只修改了两个应用的ip及端口(bind_port、initial_hosts配置),如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<!--
TCP based stack, with flow control and message bundling. This is usually used when IP
multicasting cannot be used in a network, e.g. because it is disabled (routers discard multicast).
Note that TCP.bind_addr and TCPPING.initial_hosts should be set, possibly via system properties, e.g.
-Djgroups.bind_addr=192.168.5.2 and -Djgroups.tcpping.initial_hosts=192.168.5.2[7800]
author: Bela Ban
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:org:jgroups"
xsi:schemaLocation="urn:org:jgroups http://www.jgroups.org/schema/jgroups.xsd">
<TCP bind_port="40000"
recv_buf_size="${tcp.recv_buf_size:5M}"
send_buf_size="${tcp.send_buf_size:5M}"
max_bundle_size="64K"
max_bundle_timeout="30"
use_send_queues="true"
sock_conn_timeout="300"
timer_type="new3"
timer.min_threads="4"
timer.max_threads="10"
timer.keep_alive_time="3000"
timer.queue_max_size="500"
thread_pool.enabled="true"
thread_pool.min_threads="2"
thread_pool.max_threads="8"
thread_pool.keep_alive_time="5000"
thread_pool.queue_enabled="true"
thread_pool.queue_max_size="10000"
thread_pool.rejection_policy="discard"
oob_thread_pool.enabled="true"
oob_thread_pool.min_threads="1"
oob_thread_pool.max_threads="8"
oob_thread_pool.keep_alive_time="5000"
oob_thread_pool.queue_enabled="false"
oob_thread_pool.queue_max_size="100"
oob_thread_pool.rejection_policy="discard"/>
<TCPPING async_discovery="true"
initial_hosts="${jgroups.tcpping.initial_hosts:localhost[40000],localhost[40001]}"
port_range="1"/>
<MERGE3 min_interval="10000"
max_interval="30000"/>
<FD_SOCK/>
<FD timeout="3000" max_tries="3" />
<VERIFY_SUSPECT timeout="1500" />
<BARRIER />
<pbcast.NAKACK2 use_mcast_xmit="false"
discard_delivered_msgs="true"/>
<UNICAST3 />
<pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
max_bytes="4M"/>
<pbcast.GMS print_local_addr="true" join_timeout="2000"
view_bundling="true"/>
<MFC max_credits="2M"
min_threshold="0.4"/>
<FRAG2 frag_size="60K" />
<!--RSVP resend_interval="2000" timeout="10000"/-->
<pbcast.STATE_TRANSFER/>
</config>

ehcache.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false"
monitoring="autodetect" dynamicConfig="false">
<!--start count -->
<defaultCache maxElementsInMemory="100000" eternal="true"
overflowToDisk="false" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
diskPersistent="false" statistics="true"
diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU">
<terracotta clustered="false" />
<!-- 默认采用失效通知策略,各节点独立维护自己的缓存,缓存发生变化时,通知其他节点清除 -->
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=true, replicatePuts=false, replicateUpdates=false,
replicateUpdatesViaCopy=false, replicateRemovals=true "/>
<!-- cacheEventListenerFactory属性properties说明 -->
<!--
replicateAsynchronously : 对象同步是否异步完成,默认为true。如果比较紧急就设为false。
在一致性时间性要求不强的时候,设为异步可大大提供性能,因为它是异步立即返回的,而且可以批量提交。
replicateUpdatesViaCopy : 是否将对象变更复制到所有节点,还是只是发送一个失效信息,让对方该缓存失效,当对方需要该缓存时重新计算载入。
默认为true。鉴于对象复制的消耗挺大的,又有锁的问题,而且对方也未必需要该对象,所以此属性建议设为false。
如果业务上真的需要设为true时,就可考虑使用Terracotta了。
replicatePuts : 增加对象时是否同步,默认为true,如果replicateUpdatesViaCopy为false,选择了失效算法,所以replicatePuts 要设为false。
replicateUpdates : 修改对象时是否同步,默认为true。
replicateRemovals : 删除对象时是否同步,默认为true。
-->
</defaultCache>
<!-- 单独设置菜单的缓存,单个菜单文件100KB,避免上万登录用户全部缓存,一般设定为并发用户数1000-2000 -->
<cache name="SY_ORG_USER__MENU" maxElementsInMemory="1000"
eternal="true" overflowToDisk="false" statistics="true"
memoryStoreEvictionPolicy="LRU" >
<!-- 缓存集群同步策略:各节点独立,不同步 -->
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=true, replicatePuts=false, replicateUpdates=false,
replicateUpdatesViaCopy=false, replicateRemovals=false "/>
</cache>
<!-- 单独设置页面缓存,缓存时间5分钟一刷新 -->
<cache name="SimplePageCachingFilter" maxElementsInMemory="2000"
eternal="false" overflowToDisk="false" timeToIdleSeconds="300"
timeToLiveSeconds="300" memoryStoreEvictionPolicy="LFU" >
<!-- 缓存集群同步策略:各节点独立,不同步 -->
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=true, replicatePuts=false, replicateUpdates=false,
replicateUpdatesViaCopy=false, replicateRemovals=false "/>
</cache>
<!--在线用户-->
<cache name="ONLINE_USER" maxElementsInMemory="50000"
eternal="true" overflowToDisk="false" statistics="true" memoryStoreEvictionPolicy="LFU">
<!-- 缓存集群同步策略:各节点随时保持同步 -->
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=true, replicatePuts=true, replicateUpdates=true,
replicateUpdatesViaCopy=true, replicateRemovals=true "/>
</cache>
<!-- 集群 JGroup设置 -->
<cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory"
properties="jgroups_tcp.xml" />
<!-- ehcache monitor -->
<!--<cacheManagerPeerListenerFactory class="org.terracotta.ehcachedx.monitor.probe.ProbePeerListenerFactory"-->
<!-- properties="monitorAddress=localhost, monitorPort=9889, memoryMeasurement=true"/>-->
</ehcache>

配置VM OPTIONS

1
2
3
4
-Dfile.encoding=UTF-8 // 解决console中文乱码
-Djgroups.bind_addr=192.168.6.28 // jgroups基本配置
-Djgroups.tcpping.initial_hosts=192.168.6.28[40000] // jgroups基本配置
-Djava.net.preferIPv4Stack=true

前三个没什么好说的,第四个如果没有,服务启动可能会报错,如下:

1
[2017-05-27 09:55:01.128]<TransferQueueBundler,EH_CACHE,duanyidingdeMacBook-Pro-41155>[WARN ] JGRP000034: duanyidingdeMacBook-Pro-41155: failure sending message to /ff0e:0:0:0:0:8:8:8: java.io.IOException: No route to host (received 7 identical messages from /ff0e:0:0:0:0:8:8:8 in the last 76445 ms) [] org.jgroups.util.SuppressLog.log(SuppressLog.java:47)

原因:
https://gist.github.com/rafaeltuelho/208568668e4205bd9b93
http://colky.iteye.com/blog/1188408

调试和监控

ehcache-debugger-1.7.1.jar

http://www.ehcache.org/documentation/2.8/operations/remotedebugger.html
上面这篇是官方文档,下载到这个jar包,然后执行下面的命令,就可以监控具体缓存了。

1
2
例子:java -jar ehcache-debugger-1.7.1.jar ./../ehcache.xml _CACHE_C_OA_QJ_TYPE_DICT
格式:java -jar ehcache-debugger-1.7.1.jar ehcache配置文件路径 缓存NAME(可选)

然后你就可以看到哗哗的日志了。

Debug断点

在类JGroupsCacheReceiver的receive方法打断点,可以监控节点获取组播消息的情况。

Ehcache-Monitor

安装见:Ehcache-Monitor

资料

JGroups官方文档

几篇不错的blog:
http://blog.csdn.net/kindy1022/article/details/6681299
https://my.oschina.net/u/866380/blog/501082
http://www.cnblogs.com/fangfan/p/4042823.html
http://blog.csdn.net/tengdazhang770960436/article/details/49947383

Ehcache Monitor 安装及使用

发表于 2017-05-17   |   分类于 服务端   |  

Ehcache是Java常用的缓存方案,由于直接运行在JVM里,所以还是有很多场合是Redis无法替代的。

监控Ehcache的手段比较单一,只有Ehcache-Monitor,当然也有自己实现的方法。

查看了不少相关资料,很多链接都不好使了,因为Ehcache的官网改版了。而且似乎刻意去掉了Monitor的信息,官方下载也没有了。

以下是我找到的1.0.3版下载地址:

1
http://terracotta.org/downloads/open-source/destination?name=ehcache-monitor-kit-1.0.3-distribution.tar.gz&bucket=tcdistributions&file=ehcache-monitor-kit-1.0.3-distribution.tar.gz

看README.txt这货针对不同Ehcache不同版本使用方法可能有区别,貌似各版本都能用。我使用的是2.5.1,使用正常。

安装方法最详细的还是官方pdf文档,中文的可以看下面两篇:
http://hck.iteye.com/blog/1732660
http://www.yuananan.cn/html/article/AR64m1684VKax5Ahhp53gp.html

注意注释掉启动文件ehcache-monitor-kit-1.0.3\bin\startup.bat里:-j %PRGDIR%\etc\jetty.xml ^行。

官方参考:
http://www.ehcache.org/documentation/ehcache-2.5.x-documentation.pdf
http://blog.trifork.com/2009/12/22/using-ehcache-monitor/

12
一丁

一丁

诗和远方 永远存在

17 日志
5 分类
35 标签
RSS
GitHub
© 2018 一丁
由 Hexo 强力驱动
主题 - NexT.Mist