- joice
是 使用 Spring框 架 开发的 分布 式 系 统架构。 使用 Maven对项目 进行模 块化管理 ,提 高 项目的 易 开发性 、扩展性 。公共 功 能 :AOP、缓存、基 类、公共 配置 、工具 类等。系 统功能 :权限控 制 、调度管理 、分布 式事 务、RPC。- 扩展
性 :可 动态扩展集 群 节点,节点之 间通过Dubbo和 MQ进行通信 。
Druid
数 据 库密码加密 和解 密 用 到 了 com.alibaba.druid.filter.config.ConfigTools
,再 定 一 个类扩展com.alibaba.druid.pool.DruidDataSource
即 可 ,具体 代 码实现在org.joice.service.datasource.DecryptDruidDataSource
MyBatis
- 读写
分 离实际上是 动态切 换数据 库。扩展org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
,在 每次 数 据 库调用 前 确定数 据 源 。具体 代 码实现在org.joice.service.aspect.ChooseDataSource
Spring + Quartz,
-
具体 实现在 org.joice.service.support.scheduler.SchedulerManager
对于
大事 务 =小事 务 + MQ
以转账为
结论:
-
发送
消息 失 败,发送方 并不知道 消息 中 间件是 真 的 没 有 收 到 ,还是消息 已 经收到 ,只 是 返 回 response的 时候失 败了如果
是 已 经收到 了 消息 ,但 返 回 response的 时候失 败,如果A系 统执行 回 滚,则会导致A系 统的钱没有 扣,B系 统的钱却增加 了 -
网络调用
放 在 DB事 务前面 ,可能 会 因 为网络的延 时,而导致DB长事务,严重的 时候会 阻塞整 个DB,风险很大
如果
(1) Producer端 准 备一张消息 表 ,把 扣钱update DB和 插入 消息 这两个操作 ,放 在 一 个事务里面
(2) 准 备一个后台 程 序 ,定 时把消息 表 中 的 message发送给消息 中 间件,失 败了,就不断 重 试,允 许消息 重 复
(3) Consumer接收 消息 并做好 幂等处理
该方
RocketMQ
RocketMQ
(1) 发送Prepared消息
(2) 执行本地 事 务
(3) 根 据 本地 事 务执行 结果成功 或 失 败,确认或 取消 Prepared消息
如果
//RocketMQ通 过MessageQueueSelector中 实现的 算法 来 确定消息 发送到 哪一个队列 上
//RocketMQ默 认提供 了 两种MessageQueueSelector实现:随 机 /Hash
//此处根 据 业务实现自己 的 MessageQueueSelector:订单号 相 同 的 消息 会 被 先 后 发送到 通 过一个队列 中
SendResult result = rocketMqProducer.getDefaultMQProducer().send(message, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) { //此处的 arg参 数 就是下面 传入的 orderId
int id = (int) arg;
int index = id % mqs.size();
return mqs.get(index);
}
}, orderId);
joice开发
logback
-
本地 缓存:基 于ConcurrentHashMap
实现,实现类在MapCache。MapCacheDaemon是 一个守护线程,支持 对Map的 缓存持久 化 、缓存失效 策略 等 等 ,使用 方式 可 参考 MapCache测试用例 -
Redis缓存:
基 于ShardedJedis
实现,实现类在ShardedJedisCache
开发这个缓存Spring Cache
AOP + Annotation
org.joice.cache.annotation.Cacheable
标记。
@Cacheable
public BizPayOrder getById(Long id) {
BizPayOrder order = bizPayOrderMapper.selectByPrimaryKey(id);
LogUtil.info(logger, "订单查询结果,order={0}", order);
return order;
}
@Cacheable(key = "'payOrderService_getById_'+#args[0].id", condition = "#args[0].id>3")
public BizPayOrder getById(BizPayOrder order) {
Long id = order.getId();
BizPayOrder ret = bizPayOrderMapper.selectByPrimaryKey(id);
LogUtil.info(logger, "订单查询结果,order={0}", ret);
return ret;
}
-
如果
不 自 定 义key,则该缓存使用 自 动生成 的 key。生成 的 规则是 将 类名、方法 名 、参 数 值一起计算其hashcode,这也意味 着 如果使用 默 认生成 的 key将 不 支持 删除。注意 : 如果是 使用 自 动生成 key,切点 处的方法 参 数 如果不 是 基本 类型而是对象,则该对象必须继承org.joice.cache.to.BaseTO
,这样只 要 参 数 值每次 一致 ,生成 的 hashcode就是相 同 的 。 -
此外,key
支持 Spring EL表 达式,condition也支持 。 -
为了
尽 量 减少内 存 使用 和 对网络带宽的压力,joice-cache
实现了 基 于Hessian
的 序列 化 工具 ,开发者 也可以通过实现org.joice.cache.serializer.Serializer<T>
接 口 自 行 扩展
@CacheDel(items = { @CacheDelItem(key = "'payOrderService_getById_'+#args[0].id") }, condition = "#retVal == true")
public boolean updateOrder(BizPayOrder order) {
return bizPayOrderMapper.updateByPrimaryKey(order) == 1;
}
-
当 需要 修 改 缓存时,为避免 缓存与 数 据 库双写 不一致 ,采 取的 策略 是 先 修 改 数 据 库,成功 以后再 删除缓存。为什么? -
实际业务场景
中 ,修 改 一条数据库数据可能涉及删除多条缓存数据,故 @CacheDel
注解 中 需要 设置@CacheDelItem
数 组,一 个@CacheDelItem
表示 一条需要删除缓存的数据 -
当 修 改 数 据 库成功 以后才能 删除缓存,@CacheDel
可 以通过设置 condition
来 控 制 ,condition
支持 Spring EL表 达式
① 选择
② 针对
③ 给缓
-
概念 : 缓存在 某 个时间点过期,恰好 在 这个时间点 对这个key有 大量 的 并发请求过来,这些请求发现缓存过期一般都会从数据库加载数据并回设缓存,这个时候大 并发的 请求可能 会 瞬 间把后 端 数 据 库压垮 -
如何 解 决:比 较常用 的 做法是 使用 mutex。简单来 说就是 当 缓存失效 时,不 是 立 即 去 lodad DB,而是先 使用 缓存工具 的 某 些带成功 操作 返 回 值的操作 (比 如Redis的 SETNX命令 )去 set一 个mutex key,当 操作 返 回 成功 时,再 进行load DB的 操作 并回设缓存 ;否 则就重 试get缓存的 方法 ,算法 伪代码如下 :
public String get(key) {
String value = redis.get(key);
if (value == null) { //缓存值过期
//设置超 时时间,防止 del操作 失 败的时候,下 次 缓存过期一 直 不能 load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表 设置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //这个时候代表 同 时候的 其他线程已 经load db并回设到缓存了 ,这时候 重 试获取缓存值即可
sleep(50);
get(key); //重 试
}
} else {
return value;
}
}
joice.sql
joice-service
resources
-->config
Zookeeper
,ActiveMQ
[joice-service] --> Run as --> Maven build... --> tomcat7:run