This commit is contained in:
chenyan 2022-12-15 00:17:32 +08:00
parent dd3ed3ab36
commit 10f1972d13
17 changed files with 300 additions and 53 deletions

7
Gemfile Normal file
View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
source "https://gems.ruby-china.com"
# gem "rails"
gem "jekyll"

69
Gemfile.lock Normal file
View File

@ -0,0 +1,69 @@
GEM
remote: https://gems.ruby-china.com/
specs:
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
colorator (1.1.0)
concurrent-ruby (1.1.10)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
eventmachine (1.2.7)
ffi (1.15.5)
forwardable-extended (2.6.0)
http_parser.rb (0.8.0)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
jekyll (4.3.1)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 1.0)
jekyll-sass-converter (>= 2.0, < 4.0)
jekyll-watch (~> 2.0)
kramdown (~> 2.3, >= 2.3.1)
kramdown-parser-gfm (~> 1.0)
liquid (~> 4.0)
mercenary (>= 0.3.6, < 0.5)
pathutil (~> 0.9)
rouge (>= 3.0, < 5.0)
safe_yaml (~> 1.0)
terminal-table (>= 1.8, < 4.0)
webrick (~> 1.7)
jekyll-sass-converter (2.2.0)
sassc (> 2.0.1, < 3.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.3)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (5.0.1)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.3)
rouge (4.0.0)
safe_yaml (1.0.5)
sassc (2.4.0)
ffi (~> 1.9)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
unicode-display_width (2.3.0)
webrick (1.7.0)
PLATFORMS
x86_64-linux
DEPENDENCIES
jekyll
BUNDLED WITH
2.3.26

View File

@ -4,8 +4,6 @@ timezone: 'Asia/shanghai'
encoding: 'utf-8' encoding: 'utf-8'
url: https://feling.net url: https://feling.net
plugins:
- jekyll-sitemap
collections: collections:
posts: posts:

View File

@ -7,7 +7,7 @@
$('div.go-top').hide(); $('div.go-top').hide();
}); });
$('#content').scroll(function() { $('#content').scroll(function() {
if ($(window).scrollTop() > 400) if ($('#content').scrollTop() > 400)
$('div.go-top').show(); $('div.go-top').show();
else else
$('div.go-top').hide(); $('div.go-top').hide();

View File

@ -59,7 +59,6 @@ body {
} }
#content { #content {
padding: 10px 10px 0px 10px; padding: 10px 10px 0px 10px;
overflow: initial;
} }
footer { footer {
margin-top: 10px; margin-top: 10px;

View File

@ -15,12 +15,12 @@
{% if page.url contains "/redis/" %}class="uk-active"{% endif %} {% if page.url contains "/redis/" %}class="uk-active"{% endif %}
{% if page.url contains "/timestamp/" %}class="uk-active"{% endif %} {% if page.url contains "/timestamp/" %}class="uk-active"{% endif %}
><a href="/">开发小工具</a></li> ><a href="/">开发小工具</a></li>
<li {% if page.url contains "/pages/" %}class="uk-active"{% endif %}><a href="/pages/">博客</a></li> <li {% if page.url contains "/pages/" %}class="uk-active"{% endif %}><a href="/pages/">笔记</a></li>
</ul> </ul>
<div id="offcanvas-nav" class="uk-offcanvas"> <div id="offcanvas-nav" class="uk-offcanvas">
<div class="uk-offcanvas-bar"> <div class="uk-offcanvas-bar">
<ul class="uk-nav uk-nav-offcanvas" style="padding-top:10px" data-uk-nav> <ul class="uk-nav uk-nav-offcanvas" style="padding-top:10px" data-uk-nav>
<li><a href="/pages/">博客首页</a></li> <li><a href="/pages/">笔记</a></li>
<li class="uk-nav-divider"></li> <li class="uk-nav-divider"></li>
<li><a href="/redis/">网页版 redis 客户端</a></li> <li><a href="/redis/">网页版 redis 客户端</a></li>

View File

@ -282,7 +282,7 @@ def do_tunnel(host, port, soc):
return return
soc.send(TUNNEL_OK) soc.send(TUNNEL_OK)
thread.start_new_thread(dock_socket, (soc, cos, False)) thread.start_new_thread(dock_socket, (soc, cos, False))
thread.start_new_thread(dock_socket, (cos, soc, False)) thread.start_new_thread(dock_socket, (cos, soc, True))
``` ```

View File

@ -9,7 +9,7 @@ tags: [git]
## .gitconfig ## .gitconfig
``` ```sh
[user] [user]
name = chenyan name = chenyan
email = chenyan@feling.net email = chenyan@feling.net

View File

@ -1,10 +0,0 @@
---
layout: pages
title: 2021 回顾下近几年, 重新出发
categories: 杂谈
published: false
---
* 文章目录
{:toc}

View File

@ -7,10 +7,12 @@ tags: [os, middleware, hdd]
## 引言 ## 引言
---
固态硬盘已经出现n年了, 大概还是价格的原因, 工程师们依旧与机械硬盘奋战着. 举几个例子, 感受下 if else 之外的代码乐趣. 固态硬盘已经出现n年了, 大概还是价格的原因, 工程师们依旧与机械硬盘奋战着. 举几个例子, 感受下 if else 之外的代码乐趣.
## QMQ 中针对硬盘IO的优化 ## QMQ 中针对硬盘IO的优化
---
qmq 是去哪儿网自研的消息队列. 相比于 RabbitMQ, Kafka 等消息中间件, qmq 在复杂业务场景的适用性上拥有绝对的优势. 延迟消息, 广播消息, tag过滤, 轨迹跟踪, 事物消息, Consumer扩容 这些在使用其他消息队列时让人叫苦连天的特性, 对qmq来说都不是事儿. qmq 是去哪儿网自研的消息队列. 相比于 RabbitMQ, Kafka 等消息中间件, qmq 在复杂业务场景的适用性上拥有绝对的优势. 延迟消息, 广播消息, tag过滤, 轨迹跟踪, 事物消息, Consumer扩容 这些在使用其他消息队列时让人叫苦连天的特性, 对qmq来说都不是事儿.
@ -30,6 +32,7 @@ qmq 是去哪儿网自研的消息队列. 相比于 RabbitMQ, Kafka 等消息中
## MYSQL 数据多了查询就会慢吗? ## MYSQL 数据多了查询就会慢吗?
---
有过这样的压测经历, 压测前需要造数据量, 在mysql单表中插入了几千万条数据, 但是这些数据都是跑不通接口跑不通业务流程的脏假数据. 然后手动生成了一份可用的数据, 压测时就用这一份能跑通流程的数据作参数来压. 有过这样的压测经历, 压测前需要造数据量, 在mysql单表中插入了几千万条数据, 但是这些数据都是跑不通接口跑不通业务流程的脏假数据. 然后手动生成了一份可用的数据, 压测时就用这一份能跑通流程的数据作参数来压.

View File

@ -1,24 +1,147 @@
--- ---
layout: pages layout: pages
title: redis 的使用可以再精致点--锁, 击穿, 穿透, 雪崩 title: redis 的调用可以再精致点
categories: 杂谈 categories: 杂谈
tags: [middleware, redis] tags: [middleware, redis]
published: false
--- ---
## 分布式锁
消息折叠
redission islock 其他线程锁着也是true。
## 分布式锁
---
### 消息折叠
“折叠” 这个词也是回福州工作后才接触的概念。用得太久以至于现在如果让我重新给它起个名字都毫无灵感了。具体的场景例子是这样的在课程业务中学习进度的计算是一个实时性不太敏感计算量又很重的逻辑。但前面触发进度计算的地方隐匿在系统各处恨不得一秒触发一百次进度百分比的更新。好在中间的位置有个收口大家都是通过同一个MQ消息触发的进度计算。于是在消息的生产者这边加了折叠逻辑 也就是同一个学员同一个课程的进度计算,一分钟内只发送一条。多余的消息给它吞掉(忽略)。
实现的原理, 就是用 userId + courseId 作为key给它上个redis分布式锁。同时把消息也改成延时一分钟的。
上锁的代码是五年前的人写的。某一天我们压测过程中发现有个毛刺一分钟一根就是来自这个MQ消息。打开代码一看。。哎。。 get()、set() 分开调用的,真的是批哩一点的并发概念都没有啊。
像这种地方,改成 setnx() 也就够了。但自己心里要清楚,只从技术角度看,分布式锁的实现还可以再深入很多步,做得更精致。如果有条件,应该把这类代码放入公共包里给更高级的开发人员维护。
### redission
我一直是反感 redission 的(同样反感 spring-data恰恰是因为它的 开源、活力、长期的积累。导致它的模型复杂而庞大。它实现了完整的 jdk 定义的锁的接口。
我一直期待,遇到一个场景,是下面这三个接口方法不够用的。
```java
lock()
tryLock()
unlock()
```
何必引入完整的jdk接口实现呢团队里的人都那么牛逼hold得住嘛
这天时间大概在一年一度的组织架构调整期间收到一个原本是其他小组维护的服务组织架构调整调过来的代码来了但人没来。里面有段bug是这样的。
```java
RLock xLock = null, xxLock = null;
try {
xLock = redissonClient.getFairLock("lockKey0");
xLock.lock(10, TimeUnit.SECONDS);
...
xLock.unlock()
...
xxLock = redissonClient.getFairLock("lockKey1");
xxLock.lock(10, TimeUnit.SECONDS);
...
xxLock.unlock()
...
} catch (Throwable e) {
...
} finally {
if (null != xLock && xLock.isLocked()) {
xLock.unlock();
}
if (null != xxLock && xxLock.isLocked()) {
xxLock.unlock();
}
}
```
它试图用 `xxLock.isLocked()` 判断 “自己是否加锁成功了”,但 `xxLock.isLocked()` 却完全不是这个语义,被别人锁着也是 true。
这是一位应届生职级的同学可能还未系统学习过jdk里的并发包心还大到敢直接顾名思义也没有点进去看源码注释的习惯。编码习惯也不好一边希望减小锁的粒度混在业务代码中途调 unLock(),一边又把 try catch 的范围放到最大。
## 击穿 ## 击穿
---
击穿的意思, 是
@cacheable @cacheable
## 穿透 ## 穿透
---
## 雪崩 ## 雪崩
---
## keys * ## keys *
---
但凡是有点自主学习能力,看过官网的。。。命令介绍里那么黑的加粗说明。
## 以一己之力发明了MemoryCache
---
跟前面讲的消息折叠里遇到的bug一样 也是五六年前的老代码了。顺带跑个题讲讲一个OOM的事情吧。那个服务大概启动一两周左右内存就满了fgc也回收不掉只能重启。
把内存dump下来看到占内存最多的除了日志打印就都是同一个sql语句的缓存。特点是 where 条件里包含 `in (?, ? ...)` , 这里面的 `?` 可能有上万个, 而且不是固定数量。
ORM 框架会把sql语句缓存下来但是因为 `?` 的个数有几万种可能,就缓存了几万遍。
ORM 框架是动不了了,在用的低版本的没法处理这个问题, 又不敢升级版本。保底咱还能把 `?` 分批多执行几次sql至少让内存不爆是吧。但我又挣扎了一下想看看完整的业务逻辑感觉不会有什么功能是必须要用这么奇葩的sql实现的。结果就发现相关的数据库操作明显有优化过一波的痕迹mysql前面加过一层redis确认这个sql查询是没删干净的直接删了就完事。
回到正题,"mysql前面加过一层redis" 它是怎么加的呢,业务逻辑已经记不清了, 但我永远忘不了,它把 list 类型的数据整个 get() 出来, 添加一个元素,再 set() 回去。
## 缓存与DB的数据一致性
---
有这么个不解之谜,缓存里的内容和数据库中的数据不一致了。并且这部分的代码很新,很清晰。
读接口用的 @Cacheable 注解,实现了先从缓存取, 如果缓存里没有再走数据库,还会把数据库里取到的放进缓存里。
写接口用的 @CacheEvict 注解,实现了修改完数据库之后删除缓存。
已经是相对较优的实现了再追求更强的一致性就得去权衡代价和收益了。最终这个bug是被切了无法复现手动清下缓存改小了缓存有效期 就算结了。
咱们从理论上猜一下可能有哪些场景会导致数据不一致。
1. 数据库主从延迟。写接口更新了主库,删除了缓存。读接口从从库里取出来的还是旧数据,并把旧数据又放进了缓存。
2. 读接口 发现缓存里没数据, 走到数据库里去取,取完卡了一下,还没来得及放进缓存。写接口 更新完数据,删完缓存。读接口 从卡住的状态恢复过来,把旧数据放进了缓存。
关键的因素有两个,这两个因素构成了主要矛盾。
一是 读写两个接口之间的通信,只通过缓存的有无来交换信息,信息量不够。
二是 把缓存的修改操作分给了两个接口,这就需要更多信息量的交换才能协作。
我们不好去完全断开两个接口之间的通信与协作, 因为除了数据的新旧,他们还在沟通哪些数据应该进缓存。
所以就只好增加信息量,
1. 写接口可以多说点假设数据中有version字段表示数据的版本。写接口 数据更新时删除缓存的主体内容但保留最新的version值。读接口就能根据版本号判断要放进缓存的是不是旧数据。
2. (读接口,也要有主观能动性)。时不时的抽查下数据质量,不能说缓存里有数据就 100% 认为是合格的数据。那检查抽取样本的标准呢...(哎...越搞越复杂了)
----
* 文章目录 * 文章目录
{:toc} {:toc}

View File

@ -6,7 +6,11 @@ published: false
--- ---
## 不完美 ## 前言
不必追求完美。

View File

@ -0,0 +1,65 @@
---
layout: pages
title: 书单
categories: 杂谈
---
看过五六年的书,有些一点也想不起来的书中的内容或名字了。列个单子记一下吧。
---
三四郎。外面,有好多世界。明明是一百年前的大学生活,完全感受不到时间的距离。
月亮与六便士。八卦流言不分时代与国界。我都没想过安度晚年,你凭啥呀。
刺猬的优雅。你有一个很好的藏身之所。还把生命停在了最美好的时刻。
非自然死亡。就是因为无法分开才会发生这些案件的。
秒速五厘米
夏目友人帐
人类简史
数学之美
四月是你的谎言
---
深入理解 Java 虚拟机
大型网站系统与 Java 中间件实践
---
西决。揉面团。
东霓
南音
后来的事。
三体
活着
白夜行
一九八四。她说两块钱。
美丽的新世界
百年孤独
天才在左疯子在右
西厢记。银样镴枪头。
浮生六记
小王子。

View File

@ -88,7 +88,7 @@ description: 在线 Base64 编码、解码。支持文字、图片。智能适
autofocus spellcheck="false" v-model="base64" v-on:input="onInputBase64"></textarea> autofocus spellcheck="false" v-model="base64" v-on:input="onInputBase64"></textarea>
</div> </div>
<div class="uk-width-1-2 fl-box uk-height-1-1"> <div class="uk-width-1-2 fl-box uk-height-1-1">
<div id="drop_box" class="uk-height-1-1" v-on:dragenter="ignoreDrag" v-on:dragover="ignoreDrag" v-on:drop="drop" v-on:click="clickDropBox"> <div id="drop_box" class="uk-height-1-1" style="overflow: auto;" v-on:dragenter="ignoreDrag" v-on:dragover="ignoreDrag" v-on:drop="drop" v-on:click="clickDropBox">
<p style="width:100%;height:100%;text-align:center;line-height:320px;" <p style="width:100%;height:100%;text-align:center;line-height:320px;"
v-show="base64 === ''">拖拽图片到此处 / 点击上传图片</p> v-show="base64 === ''">拖拽图片到此处 / 点击上传图片</p>
<img alt="结果图片0" style="max-height:512px;" v-bind:src="base64" v-if="base64.startsWith('data:image') || base64.startsWith('http')"></img> <img alt="结果图片0" style="max-height:512px;" v-bind:src="base64" v-if="base64.startsWith('data:image') || base64.startsWith('http')"></img>

View File

@ -59,7 +59,7 @@
</style> </style>
<ul class="uk-navbar-nav"> <ul class="uk-navbar-nav">
<li class="uk-active"><a disabled>开发小工具</a></li> <li class="uk-active"><a disabled>开发小工具</a></li>
<li><a href="/pages/">博客</a></li> <li><a href="/pages/">笔记</a></li>
</ul> </ul>
<div class="uk-navbar-flip"> <div class="uk-navbar-flip">
@ -225,31 +225,6 @@
</div> </div>
<div style="display: block;height: 1px;overflow: hidden;"> <div style="display: block;height: 1px;overflow: hidden;">
<li><a href="/rank/base64-19.html">base64编码解码 应该满足用户的哪些使用需求?</a></li>
<li><a href="/rank/base64-18.html">人性化的base64编码解码工具 让程序员的工作变得更加轻松/a></li>
<li><a href="/rank/base64-17.html">给大家推荐一款好用的base64图片编码解码在线工具</a></li>
<li><a href="/rank/base64-16.html">base64编码解码---图片传输的灵魂</a></li>
<li><a href="/rank/base64-15.html">浅谈base64图片在网站制作中使用的优缺点值得收藏</a></li>
<li><a href="/rank/base64-14.html">base64图片转换工具应有怎样利于用户的特点</a></li>
<li><a href="/rank/base64-13.html">运用base64编码解码技术可以更快速传输数据</a></li>
<li><a href="/rank/base64-12.html">base64图片漳州开发区聆熵信息技术服务工作室为您服务</a></li>
<li><a href="/rank/base64-11.html">base64图片你了解多少</a></li>
<li><a href="/rank/base64-10.html">base64编码解码发挥什么样的信息技术服务</a></li>
<li><a href="/rank/json-06.html">漳州开发区聆熵信息技术服务工作室是您json格式验证的选择</a></li>
<li><a href="/rank/base64-09.html">5G时代base64编码解码技术帮你快速解决网页图片难题</a></li>
<li><a href="/rank/base64-08.html">base64编码解码的过程中需要注意的问题</a></li>
<li><a href="/rank/json-05.html">从json的发展史我们看在线json格式验证</a></li>
<li><a href="/rank/base64-07.html">为什么使用base64编码解码</a></li>
<li><a href="/rank/base64-06.html">你知道base64编码解码吗</a></li>
<li><a href="/rank/json-04.html">json格式验证困扰编程新手怎么办</a></li>
<li><a href="/rank/json-03.html">json的简单科普、base64 编码的的应用举例</a></li>
<li><a href="/rank/json-02.html">在线json格式验证一键格式更规范阅读更方便</a></li>
<li><a href="/rank/base64-05.html">教你了解base64图片处理</a></li>
<li><a href="/rank/base64-04.html">base64图片编码的相关知识</a></li>
<li><a href="/rank/base64-03.html">在前端怎么解析base64图片</a></li>
<li><a href="/rank/base64-02.html">如何正确认识base64图片</a></li>
<li><a href="/rank/base64-01.html">对base64图片的认识</a></li>
<li><a href="/rank/json-01.html">json格式验证feling.net是您不错的选择</a></li>
<li><a href="/pages/base64.html">理解 Base64 编码</a></li> <li><a href="/pages/base64.html">理解 Base64 编码</a></li>
<li><a href="/pages/rank_tip.html">垃圾页面提示</a></li> <li><a href="/pages/rank_tip.html">垃圾页面提示</a></li>
<li><a href="/404.html">404页面提示</a></li> <li><a href="/404.html">404页面提示</a></li>

View File

@ -145,7 +145,7 @@ description: 更直观的了解接口返回值格式、结构。格式化、高
</textarea> </textarea>
</div> </div>
<div id="json_result_box" class="uk-width-10-10"> <div id="json_result_box" class="uk-width-10-10" style="overflow: auto;">
<div class="uk-width-1-1" style="height:30px;"> <div class="uk-width-1-1" style="height:30px;">
<button class="uk-button uk-visible-small" v-bind:class="{ 'uk-button-success': defaultFold == 0 }" v-on:click="unFoldAll(1)">1级</button> <button class="uk-button uk-visible-small" v-bind:class="{ 'uk-button-success': defaultFold == 0 }" v-on:click="unFoldAll(1)">1级</button>
<button class="uk-button uk-visible-small" v-bind:class="{ 'uk-button-success': defaultFold == 997 }" v-on:click="unFoldAll(998)">n级</button> <button class="uk-button uk-visible-small" v-bind:class="{ 'uk-button-success': defaultFold == 997 }" v-on:click="unFoldAll(998)">n级</button>
@ -569,4 +569,18 @@ description: 更直观的了解接口返回值格式、结构。格式化、高
$('.header-extend-item').appendTo('#header-extend') $('.header-extend-item').appendTo('#header-extend')
</script> </script>
<script>
$(function() {
$('#json_result_box').scroll(function() {
if ($('#json_result_box').scrollTop() > 400)
$('div.go-top').show();
else
$('div.go-top').hide();
});
$('div.go-top').click(function() {
$('#json_result_box').animate({scrollTop: 0}, 400);
});
});
</script>

View File

@ -4,7 +4,7 @@ layout: default_tool
keywords: [redis, redis GUI, redis client, redis 客户端] keywords: [redis, redis GUI, redis client, redis 客户端]
description: fredis 是一个网页版 redis 客户端基于“ws2s项目”开发。有基本的GUI图形界面能编辑保存服务器信息、提供 redis 命令行终端、保存历史命令。所有数据存储在 localStorage, 保证数据安全。 description: fredis 是一个网页版 redis 客户端基于“ws2s项目”开发。有基本的GUI图形界面能编辑保存服务器信息、提供 redis 命令行终端、保存历史命令。所有数据存储在 localStorage, 保证数据安全。
--- ---
<div id="content"> <div id="content" style="overflow: auto;">
<style type="text/css"> <style type="text/css">
.center-to-hide-left { .center-to-hide-left {