1 前言
一个适合后端仔排查问题的 iterm2 终端应该是这样的:
交代下为啥要开这么多个窗口,目前阿粉我们的应用是单机部署,一个服务部署在很多台 Linux 服务器上,构建分布式架构。(实际上服务器数量比这个更多:-O)
Java 's Blog
一个适合后端仔排查问题的 iterm2 终端应该是这样的:
交代下为啥要开这么多个窗口,目前阿粉我们的应用是单机部署,一个服务部署在很多台 Linux 服务器上,构建分布式架构。(实际上服务器数量比这个更多:-O)
继续上次的话题,阿粉昨天带着大家伙看了 Lambda表达式,是不是感觉真香,哈哈哈,今天这个绝对是更香的存在,因为之前因为阿粉用JDK7写的代码,还没老宫吐槽了很久,他是什么呢?阿粉来带大家看一下。
前几天阿粉还在和同事抱怨,说现在 JDK 都已经11,12了,结果自己还在用 JDK 7,于是就发生了下面一幕。
大家好,我是鸭血粉丝,自从去了一趟南京以后,我已经深深爱上了鸭血粉丝
无法自拔,真是蜜汁好吃
。
来来来,带你看一下鸭血粉丝是模样(我特意为你找了一张图片,怎么样,我好不?)
这幅图深刻的为你揭示了鸭血粉丝的配方,不行了,馋死我了,我要赶紧把文章整理出来再去吃一碗!!!
公众号后台总有小伙伴留言说能不能把文章汇总一下,能不能把文章分类一下,现在这位小伙伴,不知道你看没看到。我要憋大招了
【Redis 系列】
【Flink 系列】
Flink 基础学习(六)时间 Time 和 Watermark
Flink 基础学习(四)转换 Transformation
【Kafka 系列】
【Http 系列】
面试官问:HTTP 的负载均衡你了解么?你不是说了你们用的Nginx么?说一下把。
关于 HITP 代理,你还需要了解这些,不然面试你是过不去的!
你知道 HTTP 是如何使用 TCP 连接的吗?今天我就来告诉你!
【数据结构系列】
面试必问之 ConcurrentHashMap 线程安全的具体实现方式
你应该知道的 PriorityQueue ——深入浅出分析 PriorityQueue
【灵魂拷问系列】
灵魂拷问:Java的Comparable和Comparator是兄弟俩吗?
灵魂拷问:Java 的 substring() 是如何工作的?
【Java极客技术重磅推荐】
【其他】
hello~各位读者新年好,我是鸭血粉丝(大家可以称呼我为「阿粉」),一位喜欢吃鸭血粉丝的程序员!2019 年,阿粉写了很多 bug,这不前一段时间 OOM
差点就把服务器搞挂。跨年的时刻,阿粉默默立下一个 flag,2020 年再见 bug。
可是没想到还没过几个小时,刚立的 falg 就倒下了,阿粉太难了😭。。。。
新年第一天,运营反馈商户收到对账文件有问题。刚接到电话时,阿粉是一万个不相信,这个代码都跑了这么久,怎么就偏偏今天不对了。可能看到阿粉不信,运营小姐姐随即发了一张商户给的账单截图。
waht???2020 年才刚来,时间怎么变成 20201231 了。。。
哎,阿粉只好先让运营稳住商户,然后赶紧起来,打开了电脑,首先定位一下问题。
生成账单伪代码如下:
上面代码逻辑其实非常简单,将账单数据从数据库取出,然后按照按照相应的格式组装数组,写入文件。
阿粉走查了几遍代码,越看越觉得没问题啊,这么简单的问题没可能出现问题呀。没办法,只好请教一下阿粉的好朋友兼同事小黑。
很快小黑就给阿粉指出 YYYY/MM/dd HH:mm
格式不对,需要使用 yyyy/MM/dd HH:mm
。阿粉修改之后,重新生成对账单,解决这个问题。
虽然解决了问题,但是阿粉其实还是一知半解,所幸元旦也没什么事,阿粉就深入研究一下 YYYY
格式。原来 Java
中YYYY
与 yyyy
分别代表两种不同格式。
Y
代表 Week Year
,表示当天周所在的年份。这种方式将会把一年划分成52 周/53周(类似于闰年的概念,每隔几年将会增加一周)。Week Year
下每周仅属于某一年,如果某年的第一周或最后一周跨年,就会导致部分日期年份与实际不符。
Week Year
存在两种标准:
Calendar 对象可以通过 setFirstDayOfWeek 与 setMinimalDaysInFirstWeek 改变上面默认标准
Java 将会根据系统环境变量决定使用哪种标准,可以通过设置 Locale 改变方式。代码如下:
1 |
|
下面例子我们使用 ISO 8601 标准,分别看一下最后一周跨年以及第一周跨年的例子。
2015 年最后一周跨年,2016 前三天使用 YYYY
最后结果为 2015,时间看起来被回退了。
2020 年第一周跨年,2019 年最后两天使用 YYYY
结果为 2020。
终于弄明白 YYYY
,阿粉顺便也学习一下常用的日期格式。
下面以 2019-12-31 06:06:06:666
时间为例
Letter | 含义 | Example |
---|---|---|
Y | Week Year | YYYY—->2020 |
y | 年 | yyyy——>2019 |
M | 月 | MM——->12 |
m | 分 | mm——–>06 |
D | 一年中天数 | MM——–>365 |
d | 一月中的天数 | dd———>06 |
H | 小时(0-23) | HH———>06 |
h | 小时(1-12) | hh———->06 |
S | 毫秒 | SSS———>666 |
下面开始本篇文章的技术总结:
YYYY
与 yyyy
区别,年份最好统一使用 yyyy
YYYY
的代码写这篇的文章时候,发现社区有些小伙伴也踩到这个坑,哈哈,吾道不孤也。
2021,2022….阿粉相信还会有新的小伙伴将会踩到这个坑,哈哈。在这里,阿粉给未来小伙伴留个言:
未来的的小伙伴你好,当你搜索到这篇文章并且看到这里,我知道你也踩到坑了,哈哈!既然都看到这里了,别忘记点个赞哦!
大家好,我是鸭血粉丝(大家会亲切的喊我 「阿粉」),是一位喜欢吃鸭血粉丝的程序员,回想起之前线上出现 OOM 的场景,毕竟当时是第一次遇到这么 紧脏 的大事,要好好记录下来。
1 事情回顾
2 开始分析
3 性能优化
4 技术总结
5 多说两句
大家好,我是鸭血粉丝,今天是 2020 年的第二天,我翻来覆去,思来想去觉得是时候应该把 Java 极客技术的 2019 年总结一下了,话不多说往下看。
2019 年已经过去了,现在陆陆续续的都要忙着写年终总结,《Java 极客技术》也陪伴了大家十个月的时间了(虽然我鸭血粉丝才出道几天),还记得第一篇文章《Java 诞生的趣事》 发布在 2019 年 02 月 25 日,这段时间也发过很多次文章给大家介绍《Java 极客技术》公号以及对应知识星球的故事,比如今天,我为他们站台!,总结丨两个月干火一个知识星球,看看我们都做了什么 。现在到了年底也该写年终总结了,这篇文章就给大家总结一下,这几个月《Java 极客技术》的故事,且听我阿鸭慢慢道来。
最近没事在看极客时间上刘超老师的《趣谈网络协议》那门课程,其中有一篇讲得非常有意思,也有些难以理解,我以我的角度来谈谈。
在时间 Time
那一篇中,介绍了三种时间概念 Event
、Ingestin
和 Process
, 其中还简单介绍了乱序 Event Time
事件和它的解决方案 Watermark
水位线
(看过多篇文章后,决定喊它水位线,因为窗口触发条件是 Watermark
> Window_end_time
,有点像水流到达水位线后溢出,当然喊它水印也是可以的,全看个人爱好咯~)
前文请翻 时间 Time 和 Watermark,不过前面介绍比较浅,没能很好领会水位线的概念,所以本篇是作为补充,来加深理解~
发布订阅系统在我们日常的工作中经常会使用到,这种场景大部分情况我们都是使用消息队列的,常用的消息队列有 Kafka,RocketMQ,RabbitMQ,每一种消息队列都有其特性,关于 Kafka 的使用和源码分析,公号前面有相关的文章,大家可以前往回顾一下,另外两款消息队列大家有需要可以自行研究,后续我们会出相应的介绍文章。这篇文章主要是给大家介绍 Redis 的发布订阅系统,很多时候我们可能不需要独立部署相应的消息队列,只是简单的使用,而且数据量也不会太大,这种情况下,我们就可以使用 Redis 的 Pub/Sub 模型。
想了解 Java 的 IO 基础知识,看这篇文章就够了!
ConcurrentHashMap 是 Java 并发包中提供的一个线程安全且高效的 HashMap 实现,以弥补 HashMap 不适合在并发环境中操作使用的不足,本文就来分析下 ConcurrentHashMap 的实现原理,并对其实现原理进行分析!
关于 HashMap,以前只知道它是非线程安全,在多线程环境下操作可能会导致程序死循环,CPU直接飙到100%的线上故障,还真是第一次听说……
Kafka 重平衡流程一直是 kafka 比较麻烦和难以理解的地方,此篇文章通过大量的示意图带你了解一下 kafka 重平衡的过程
之前我讲了关于 线程基础方面的相关知识,本篇文章将会带着大家来学习下线程安全相关的知识。
首先我们考虑一个场景:有一个整形数组, 我们希望通过调用一个工具类的排序方法就能对该数组进行排序. 请看下面的代码:
1 |
|
通过上面的代码, 我们能够轻易地对整形数组进行排序, 那么如果现在有了新需求, 需要对浮点类型数据进行排序, 排序工具类应该如何做呢?
或许你会想, 不如就新添加一个排序方法, 方法的参数类型为 float 类型, 把 int 类型数组的排序算法复制一遍不就可以了吗?
那如果我继续追问, 如果现在要对一只猫进行排序, 那应该怎么做呢? 猫的类如下:
1 |
|
你也许会顺着原来的思路回答, 照样 copy 一份排序的算法, 修改方法参数, 然后在比较的地方指定比较猫的年龄或体重不就可以了吗?
1 |
|
但仔细想想, 如果还要继续比较小狗, 小鸡, 小鸭等各种对象, 那么这个排序工具类的代码量岂不是变得很大? 为了能让排序算法的可重用性高一点, 我们希望排序工具中的 sort() 方法可以对任何调用它的对象进行排序.
你可能会想: 到对任何对象都能排序, 把 sort() 方法的参数改为 Object 类型不久可以了嘛. 这个方向是对的, 但是问题是, 当拿到两个 Object 类型对象, 应该根据什么规则进行比较呢?
这个时候我们自然而然地就希望调用工具类进行排序的对象本身就具备自己的比较法则, 这样在排序的时候就能直接调用对象的排序法则进行排序了.
我们把比较法则抽象为 Comparable 接口, 凡是要进行比较的类都要实现 Comparable 接口, 并且定义自己的比较法则, 也就是 CompareTo() 方法.
这样当我们在封装工具时, 就可以直接对实现了 Comparable 接口的对象进行比较, 不用担心比较的细节了.
1 |
|
相信看了上面的 Comparable 接口来由, 大家会感觉整个设计又美好了一些, 但是其中还有漏洞. 我们在 Cat 类的 CompareTo() 方法中, 对猫的比较策略是写死的, 现在我们按猫的年龄比较大小, 如果哪天我们想按照猫的体重比较大小, 又要去修改源码了. 有没有扩展性更好的设计?
我们可以让用户自己定义一个比较器类, 对象可以根据用户指定的比较器比较大小.
整个逻辑是: 如果这个对象需要进行比较, 那么它必须实现 Comparable 接口, 但是它具体是怎么比较的, 则通过具体的 Comparator 比较器进行比较.
当然这里少不了多态, 我们首先要定义一个比较器接口 Comparator, 用户的比较器需要实现 Comparator 接口, 下面上代码:
1 |
|
在上面的例子中, 我们自己定义了 Comparable 接口和 Comparator 接口, 其实这两个接口都是 Java 自带的, 通过上面的代码示例, 想必大家也应该知道了为什么会有这两个接口。
其实 Comparable 定义的就是一种比较的策略, 这里的策略你可以理解为一个功能, 然而策略有了, 我们还需要有具体的策略实现, 于是便有了 Comparator 接口。
在比较对象的 Comparable 和 Comparator 中运用策略模式我认为有以下的好处:
但也有一个主要的缺点:
GC
中文直译垃圾回收,是一种回收内存空间避免内存泄漏的机制。当 JVM
内存紧张,通过执行 GC
有效回收内存,转而分配给新对象从而实现内存的再利用。 JVM
GC
机制虽然无需开发主动参与,减轻不少工作量,但是某些情况下,自动 GC
将会导致系统性能下降,响应变慢,所以这就需要我们提前了解掌握 GC
机制。当面对这种情况时,才能从容不迫的解决问题。另外 GC
机制也是 Java
面试高频考题,了解掌握 GC 是一项必备技能。
其实我们写代码的时候一直都在使用for循环,但是偶尔还是会纠结用哪一个循环。
上一篇文章我讲了关于负载均衡的三种算法,轮询法,随机法,最小连接法,这三种负载均衡的算法,但是关于负载均衡还有其他的算法,我们也需要你去看,而且在面试的过程中,很有可能是会问到的呦。
相信很多小伙伴在使用 Redis 的时候都知道 Redis 有相关慢日志的查询功能,并且多多少少都看过。那 Redis 底层到底是如果创建慢日志以及慢日志的结构是什么样子的呢?这篇文章就带大家认识一下。我们先看一张慢日志的截图
使用
slowlog get 2
命令查看最近的两条慢日志信息,如上图,我们可以看到每条日志中包含的信息有六个部分组成,从上到下编号为 0-5,依次代表的意思是
如上图所示,第一条慢日志的 ID 是 41,命令执行的时间戳是 1575729996,并且执行了 16129 微妙,具体执行的命令就是slowlog get
,ip 和端口是27.38.56.88:8223
,客户端的名称没有设置。
上面我们已经大概的知道的一条慢日志的格式,自然的我们可以想到的问题是一个命令执行多长时间,我们就可以认为是慢查询,以及慢日志最多能保存多少条。
我们可以通过config get slowlog-log-slower-than
命令来查看 Redis 的时长设置,以及通过config get slowlog-max-len
来查看最大慢日志条数。如下图。
上面我们使用config get
命令查看了时长设置和条数设置,相反的我们可以用config set
来设置相关参数,如下图,我们先查看一下配置,然后再通过config set slowlog-log-slower-than 1000
命令和 config set slowlog-max-len 64
命令来设置具体的值:
通过上面的操作我们可以看到相关的配置已经更改生效了。
为了验证上面的第二点,我这边将slowlog-log-slower-than
设置为 10 微秒,slowlog-max-len
设置为 5 条来进行试验,首先第一次使用slowlog get
命令查询的时候 5 条慢日志的编号是从 83-87,
再次使用slowlog get
命令查询的编号结果是84-88,说明 ID 为 83 的那一条已经被删除了。
1 |
|
slowlog_entry_id
属性的初始值是 0 每创建一条慢日志的时候就会增加 1。slowlogEntry
结构组成的,每个slowlogEntry
代表一条慢日志。slowlog_log_slower_than
和 slowlog_max_len
是前面服务器配置的相关参数。1 |
|
slowlogEntry
实体的相关字段含义如下:
如上图就表示了在执行命令set number 520
发生了慢日志,命令执行耗时 10 微妙。
知道了慢日志的存储结构,我们就需要考虑在执行命令的时候,如何根据条件去创建慢日志。
首先我们需要在命令的执行前后记录时间戳,然后相减计算出命令的执行耗时,然后根据 Redis 服务器配置slowlog-log-slower-than
进行对比,决定是否记录慢日志,另外在记录慢日志的时候需要根据slowlog_max_len
值判断是否要删除最久的日志信息。伪代码如下:
1 |
|
slowlogPushEntryIfNeed
函数主要用来判断是否插入数据,以及是否删除旧数据。
1 |
|
我们先判断服务器是否配置了超时参数,如果超时参数小于 0 则直接返回,否则再比较命令执行时间是否超时,如果超时则插入慢日志;最后在比较慢日志的条数是否达到上限,如果达到则进行删除。
Redis 在日常工作中经常会使用,其中还有很多细节需要我们慢慢研究和学习,公号已经发过好多关于 Redis 相关的文章了,后续还会有其他内容的相关文章,欢迎关注。最后欢迎大家到我们《Java 极客技术》知识星球中来跟我们一起学习,一起进步。