Zacard's Notes


  • 首页

  • 分类

  • 归档

  • 标签

  • 关于

  • 搜索
close
Zacard's Notes

LinkedBlockingQueue,我所忽略的并发安全细节

发表于 2021-01-19 | 分类于 软件技术 | | 阅读次数

首先抛出一个问题:

LinkedBlockingQueue首尾2把锁是如何保证并发安全的?

背景

我们知道,要保证共享变量的多线程并发读写安全需要用同一把锁。但是LinkedBlockingQueue有putLock和takeLock2把锁,是如何保证并发安全的呢?

先来看下LinkedBlockingQueue源码上的注释:

A variant of the “two lock queue” algorithm

“two lock queue” algorithm的一个变种。那什么是”two lock queue” algorithm呢?

阅读全文 »
Zacard's Notes

日志采集高可用之重试

发表于 2021-01-13 | 分类于 软件技术 | | 阅读次数

背景

最近在开发自研日志平台的日志采集agent。这里记录下高可用设计的一些思考和技术细节。

当遇到发送到下游失败需要重试的场景。我们如何合理设计重试?如何优雅重试呢?

首先,我们需要明确重试的本质是什么?

重试的本质

重试的本质是我们认为这个故障是暂时的,而不是永久的,所以我们会去重试

因此,我们需要清晰的定义什么情况下需要重试,什么情况下重试是没有意义的

阅读全文 »
Zacard's Notes

日志收集Agent,阴暗潮湿的地底世界

发表于 2019-06-15 | 分类于 软件技术 | | 阅读次数

分享我参与严选技术工作组的日志平台项目中的时候,在日志收集agent这块遇到的一些问题,深入到每个底层细节和大家谈谈。

日志agent对于使用日志平台的用户来说,是一个黑盒。对于用户来说,agent有些不好的地方:

  • agent私底下偷偷摸摸都做了些什么事情呢?(阴暗的)
  • agent的设计实现其实有一些dirty、creepy的地方(潮湿的)

所以我觉得日志收集agent对大家来说是一个阴暗潮湿的地底世界:

就让我举起火把,照亮所有dark、dirty、creepy的地方。

阅读全文 »
Zacard's Notes

程序员的自我修养

发表于 2019-04-04 | 分类于 软件技术 | | 阅读次数

程序员的自我修养

这里主要和大家分享我在开发DQC(数据质量中心)的过程中,对产品需求、架构设计、编码、自我提升这4个环节的反思与经验。

产品需求

产品需求阶段,我们能做什么,该做些什么?

我认为有2点很重要:

  • 沟通:高效沟通,可以减少歧义,加快开发
  • 参与:参与其中,可以减少返工,保持扩展

那如何做到这2点呢?我认为有个非常重要的概念:领域通用语言

阅读全文 »
Zacard's Notes

X-UID:分布式唯一id服务

发表于 2018-04-24 | 分类于 软件技术 | | 阅读次数

背景

之前开发的分布式唯一id比较简陋。这段时间重新设计优化,变成一个独立的项目X-UID。这里记录下设计优化的一些东西

基本算法还是基于snowflake。因为需求就是要一个long型数字,snowflake算法简单高效。

snowflake简单介绍

算法生成的id结构图:

说明:

  • 1-bit:符号位,一般不设置,默认为0
  • 41-bit:时间戳,大约能用69年
  • 10-bit:工作机器id,最大支持1024个workerId
  • 12-bit:序列号,毫秒内的自增序列。这也决定了其QPS的上限为400w/s这个数量级
阅读全文 »
Zacard's Notes

mysql等数据库索引为什么偏爱b+tree

发表于 2018-03-30 | 分类于 软件技术 | | 阅读次数

背景

类似mysql等数据库偏爱用b+tree这个数据结构作为索引,这是为什么呢?要解释这个原因,必须先讲下计算机组成原理中的磁盘数据存取原理。

磁盘数据读取原理

这里指普通的机械磁盘。

先看下磁盘的结构:

阅读全文 »
Zacard's Notes

elasticsearch写入优化记录

发表于 2018-03-27 | 分类于 软件技术 | | 阅读次数

背景

  • 基于elasticsearch-5.6.0
  • 机器配置:3个阿里云ecs节点,16G,4核,机械硬盘

优化前,写入速度平均3000条/s,一遇到压测,写入速度骤降,甚至es直接频率gc、oom等;优化后,写入速度平均8000条/s,遇到压测,能在压测结束后30分钟内消化完数据,各项指标回归正常。

阅读全文 »
Zacard's Notes

阿里中间件6轮面试被砍的血泪总结

发表于 2018-03-20 | 分类于 面试 | | 阅读次数

背景

年前在v2ex遇到阿里中间件的哥们,内推面试。从2018.02.01开始到今天2018.03.20,一场浩浩荡荡,跨年,持续了1个半月时间的残酷面试终于尘埃落定。遗憾的未能加入阿里中间件部门这个大家庭,让我深感痛惜。

最终未能如愿的原因是“名额有限,有更适合的同学”。这也可能是内推大哥为了顾及我的感受的说辞,可能是最后一面面的不好。

这里凭借隐约的记忆,总结下面试经过。让我自己引以为戒,奋发自强,继续前行

阅读全文 »
Zacard's Notes

分布式锁研究

发表于 2018-03-20 | 分类于 软件技术 | | 阅读次数

背景

公司使用基于redis setNX的分布式锁偶现失效,对此深入研究一番

场景

大体来说,分布式锁的场景有两种:

  1. 为了效率:相当于去重,避免各个系统做重复的事情。比如重复发送一封email
  2. 为了正确性:不允许出现任何的失效,不然就可能造成数据不一致

基于单节点的redis实现

为什么强调单节点?因为我们就一个redis主从,没有redis集群。而且redis有官方的分布式锁redlock是基于redis集群的,这个对我们不适用,而且感觉有点过重。

我们一开始的方案是基于setNX(key,value,timeout)。后来发现原来这是jedis的封装,这其实是2个redis命令:setnx+expire。也就是说这不是个原子操作,很可能setnx成功,但是设置过期时间失败导致锁永远无法释放

翻看redlock的套路才知道,应该这样操作:

  1. 获取锁:set key randomValue NX PX 3000

    redis的set操作有NX(if not exist)选项和PX(过期时间)选项,可以实现原子操作

  2. 释放锁:需要使用LUA脚本实现复合操作的原子性:

    1
    2
    3
    (1) get key
    (2) 比较random value
    (3) random value一致即删除key
阅读全文 »
Zacard's Notes

rocketmq源码阅读之message发送

发表于 2018-03-18 | 分类于 软件技术 | | 阅读次数

背景

基于rocketmq 4.2.0

先不关注顺序消息和事务消息,后面独立看。

发送消息入口

DefaultMQProducer#send(Message)

默认的是同步发送。最终调用的是DefaultMQProducerImpl#sendDefaultImpl,直接看代码:

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
69
70
71
72
73
74
75
76
77
78
private SendResult sendDefaultImpl(
Message msg,
final CommunicationMode communicationMode,
final SendCallback sendCallback,
final long timeout
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
// 确保producer已经启动
this.makeSureStateOK();
// 校验message,例如topic不能为空
Validators.checkMessage(msg, this.defaultMQProducer);
// 目前来看,仅在日志输出时标示此次调用
final long invokeID = random.nextLong();
long beginTimestampFirst = System.currentTimeMillis();
long beginTimestampPrev = beginTimestampFirst;
long endTimestamp = beginTimestampFirst;
// 获取topic发布路由信息 -- 会先尝试缓存中获取,其次从namesrv中获取
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
if (topicPublishInfo != null && topicPublishInfo.ok()) {
MessageQueue mq = null;
Exception exception = null;
SendResult sendResult = null;
// 同步发送默认重试3次
int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;
int times = 0;
String[] brokersSent = new String[timesTotal];
for (; times < timesTotal; times++) {
String lastBrokerName = null == mq ? null : mq.getBrokerName();
/*
* 选择一个消息队列
* 规则:1)默认情况,不开启发送的延迟容错策略时,RoundRobin形式的选择一个不属于上一次发送的broker队列
* 2)开启延迟容错策略时:
* 2.1)优先从上一次的发送的broker中RoundRobin形式选择一个可用队列
* 2.2)其次按照可用性排名(是否可用>延迟时间>开始时间)从前半数中RoundRobin选
* 2.3)最次,啥都不管,RoundRobin选
*/
MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
if (mqSelected != null) {
mq = mqSelected;
brokersSent[times] = mq.getBrokerName();
try {
beginTimestampPrev = System.currentTimeMillis();
// 核心消息投递方法
sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout);
endTimestamp = System.currentTimeMillis();
// 更新延迟容错信息
this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);
switch (communicationMode) {
case ASYNC:
return null;
case ONEWAY:
return null;
case SYNC:
if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) {
continue;
}
}
return sendResult;
default:
break;
}
} catch (RemotingException e) {
// ...省略部分代码
}
} else {
break;
}
}
// ...省略部分代码
}
// ...省略部分代码
}
阅读全文 »
12…7
zacard

zacard

优生笑,菜鸟哭

65 日志
2 分类
105 标签
RSS
GitHub Weibo ZhiHu
Links
  • DingDang's Notes
© 2015 - 2021 zacard
由 Hexo 强力驱动
主题 - NexT.Mist