From 10f1972d13685261ceae8105d2d653b915a2e329 Mon Sep 17 00:00:00 2001 From: chenyan Date: Thu, 15 Dec 2022 00:17:32 +0800 Subject: [PATCH] .. --- Gemfile | 7 ++ Gemfile.lock | 69 ++++++++++++ _config.yml | 2 - _includes/goTop.html | 2 +- _includes/head.html | 1 - _includes/header.html | 4 +- _posts/2015-05-17-project-http-proxy.md | 2 +- _posts/2015-05-22-git-config.md | 2 +- _posts/2021-11-28-review-2021.md | 10 -- _posts/2021-12-07-hdd.md | 3 + _posts/2021-12-09-exquisite-redis.md | 133 +++++++++++++++++++++++- _posts/2022-01-01-about-calendar.md | 6 +- _posts/2022-12-11-book-list.md | 65 ++++++++++++ base64/index.html | 2 +- index.html | 27 +---- json/index.html | 16 ++- redis/index.html | 2 +- 17 files changed, 300 insertions(+), 53 deletions(-) create mode 100644 Gemfile create mode 100644 Gemfile.lock delete mode 100644 _posts/2021-11-28-review-2021.md create mode 100644 _posts/2022-12-11-book-list.md diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..4fe3d6e --- /dev/null +++ b/Gemfile @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +source "https://gems.ruby-china.com" + +# gem "rails" + +gem "jekyll" \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..cf02486 --- /dev/null +++ b/Gemfile.lock @@ -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 diff --git a/_config.yml b/_config.yml index e29a5c1..d9557ce 100644 --- a/_config.yml +++ b/_config.yml @@ -4,8 +4,6 @@ timezone: 'Asia/shanghai' encoding: 'utf-8' url: https://feling.net -plugins: - - jekyll-sitemap collections: posts: diff --git a/_includes/goTop.html b/_includes/goTop.html index 29d1410..23e0a77 100644 --- a/_includes/goTop.html +++ b/_includes/goTop.html @@ -7,7 +7,7 @@ $('div.go-top').hide(); }); $('#content').scroll(function() { - if ($(window).scrollTop() > 400) + if ($('#content').scrollTop() > 400) $('div.go-top').show(); else $('div.go-top').hide(); diff --git a/_includes/head.html b/_includes/head.html index 65d8224..c6ed25f 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -59,7 +59,6 @@ body { } #content { padding: 10px 10px 0px 10px; - overflow: initial; } footer { margin-top: 10px; diff --git a/_includes/header.html b/_includes/header.html index 5923aae..3a0c998 100644 --- a/_includes/header.html +++ b/_includes/header.html @@ -15,12 +15,12 @@ {% if page.url contains "/redis/" %}class="uk-active"{% endif %} {% if page.url contains "/timestamp/" %}class="uk-active"{% endif %} >开发小工具 -
  • 博客
  • +
  • 笔记
    • -
    • 博客首页
    • +
    • 笔记
    • 网页版 redis 客户端
    • diff --git a/_posts/2015-05-17-project-http-proxy.md b/_posts/2015-05-17-project-http-proxy.md index c67b6fc..17b4be1 100644 --- a/_posts/2015-05-17-project-http-proxy.md +++ b/_posts/2015-05-17-project-http-proxy.md @@ -282,7 +282,7 @@ def do_tunnel(host, port, soc): return soc.send(TUNNEL_OK) 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)) ``` diff --git a/_posts/2015-05-22-git-config.md b/_posts/2015-05-22-git-config.md index 57cf7c1..ba4135f 100644 --- a/_posts/2015-05-22-git-config.md +++ b/_posts/2015-05-22-git-config.md @@ -9,7 +9,7 @@ tags: [git] ## .gitconfig -``` +```sh [user] name = chenyan email = chenyan@feling.net diff --git a/_posts/2021-11-28-review-2021.md b/_posts/2021-11-28-review-2021.md deleted file mode 100644 index 6d98215..0000000 --- a/_posts/2021-11-28-review-2021.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -layout: pages -title: 2021 回顾下近几年, 重新出发 -categories: 杂谈 -published: false ---- - - -* 文章目录 -{:toc} \ No newline at end of file diff --git a/_posts/2021-12-07-hdd.md b/_posts/2021-12-07-hdd.md index 48d4d32..ad3f5b6 100644 --- a/_posts/2021-12-07-hdd.md +++ b/_posts/2021-12-07-hdd.md @@ -7,10 +7,12 @@ tags: [os, middleware, hdd] ## 引言 +--- 固态硬盘已经出现n年了, 大概还是价格的原因, 工程师们依旧与机械硬盘奋战着. 举几个例子, 感受下 if else 之外的代码乐趣. ## QMQ 中针对硬盘IO的优化 +--- qmq 是去哪儿网自研的消息队列. 相比于 RabbitMQ, Kafka 等消息中间件, qmq 在复杂业务场景的适用性上拥有绝对的优势. 延迟消息, 广播消息, tag过滤, 轨迹跟踪, 事物消息, Consumer扩容 这些在使用其他消息队列时让人叫苦连天的特性, 对qmq来说都不是事儿. @@ -30,6 +32,7 @@ qmq 是去哪儿网自研的消息队列. 相比于 RabbitMQ, Kafka 等消息中 ## MYSQL 数据多了查询就会慢吗? +--- 有过这样的压测经历, 压测前需要造数据量, 在mysql单表中插入了几千万条数据, 但是这些数据都是跑不通接口跑不通业务流程的脏假数据. 然后手动生成了一份可用的数据, 压测时就用这一份能跑通流程的数据作参数来压. diff --git a/_posts/2021-12-09-exquisite-redis.md b/_posts/2021-12-09-exquisite-redis.md index 4e79cc1..3878992 100644 --- a/_posts/2021-12-09-exquisite-redis.md +++ b/_posts/2021-12-09-exquisite-redis.md @@ -1,24 +1,147 @@ --- layout: pages -title: redis 的使用可以再精致点--锁, 击穿, 穿透, 雪崩 +title: redis 的调用可以再精致点 categories: 杂谈 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 + + + ## 穿透 +--- + + ## 雪崩 +--- ## 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} \ No newline at end of file diff --git a/_posts/2022-01-01-about-calendar.md b/_posts/2022-01-01-about-calendar.md index 83d033e..ccc2cc8 100644 --- a/_posts/2022-01-01-about-calendar.md +++ b/_posts/2022-01-01-about-calendar.md @@ -6,7 +6,11 @@ published: false --- -## 不完美 +## 前言 + +不必追求完美。 + + diff --git a/_posts/2022-12-11-book-list.md b/_posts/2022-12-11-book-list.md new file mode 100644 index 0000000..e447137 --- /dev/null +++ b/_posts/2022-12-11-book-list.md @@ -0,0 +1,65 @@ +--- +layout: pages +title: 书单 +categories: 杂谈 +--- + + +看过五六年的书,有些一点也想不起来的书中的内容或名字了。列个单子记一下吧。 + + +--- + +三四郎。外面,有好多世界。明明是一百年前的大学生活,完全感受不到时间的距离。 + +月亮与六便士。八卦流言不分时代与国界。我都没想过安度晚年,你凭啥呀。 + +刺猬的优雅。你有一个很好的藏身之所。还把生命停在了最美好的时刻。 + +非自然死亡。就是因为无法分开才会发生这些案件的。 + +秒速五厘米 + +夏目友人帐 + +人类简史 + +数学之美 + +四月是你的谎言 + +--- + +深入理解 Java 虚拟机 + +大型网站系统与 Java 中间件实践 + +--- + +西决。揉面团。 + +东霓 + +南音 + +后来的事。 ? + +三体 + +活着 + +白夜行 + +一九八四。她说两块钱。 + +美丽的新世界 + +百年孤独 + +天才在左疯子在右 + +西厢记。银样镴枪头。 + +浮生六记 + +小王子。 diff --git a/base64/index.html b/base64/index.html index 72385d5..3667705 100644 --- a/base64/index.html +++ b/base64/index.html @@ -88,7 +88,7 @@ description: 在线 Base64 编码、解码。支持文字、图片。智能适 autofocus spellcheck="false" v-model="base64" v-on:input="onInputBase64">
    -
    +

    拖拽图片到此处 / 点击上传图片

    结果图片0 diff --git a/index.html b/index.html index 45ca2a1..533a11f 100644 --- a/index.html +++ b/index.html @@ -59,7 +59,7 @@
    @@ -225,31 +225,6 @@
    -
  • base64编码解码 应该满足用户的哪些使用需求?
  • -
  • 人性化的base64编码解码工具 让程序员的工作变得更加轻松/a>
  • -
  • 给大家推荐一款好用的base64图片编码解码在线工具
  • -
  • base64编码解码---图片传输的灵魂
  • -
  • 浅谈base64图片在网站制作中使用的优缺点,值得收藏!
  • -
  • base64图片转换工具应有怎样利于用户的特点?
  • -
  • 运用base64编码解码技术,可以更快速传输数据
  • -
  • base64图片,漳州开发区聆熵信息技术服务工作室为您服务
  • -
  • base64图片,你了解多少?
  • -
  • base64编码解码发挥什么样的信息技术服务
  • -
  • 漳州开发区聆熵信息技术服务工作室,是您json格式验证的选择
  • -
  • 5G时代,base64编码解码技术帮你快速解决网页图片难题!
  • -
  • base64编码解码的过程中需要注意的问题
  • -
  • 从json的发展史我们看在线json格式验证
  • -
  • 为什么使用base64编码解码
  • -
  • 你知道base64编码解码吗
  • -
  • json格式验证困扰编程新手怎么办?
  • -
  • json的简单科普、base64 编码的的应用举例
  • -
  • 在线json格式验证:一键格式更规范,阅读更方便!
  • -
  • 教你了解base64图片处理
  • -
  • base64图片编码的相关知识
  • -
  • 在前端怎么解析base64图片
  • -
  • 如何正确认识base64图片
  • -
  • 对base64图片的认识
  • -
  • json格式验证,feling.net是您不错的选择
  • 理解 Base64 编码
  • 垃圾页面提示
  • 404页面提示
  • diff --git a/json/index.html b/json/index.html index f55c815..8b32407 100644 --- a/json/index.html +++ b/json/index.html @@ -145,7 +145,7 @@ description: 更直观的了解接口返回值格式、结构。格式化、高
    -
    +
    @@ -569,4 +569,18 @@ description: 更直观的了解接口返回值格式、结构。格式化、高 $('.header-extend-item').appendTo('#header-extend') + + diff --git a/redis/index.html b/redis/index.html index 2a121e2..a895bd2 100644 --- a/redis/index.html +++ b/redis/index.html @@ -4,7 +4,7 @@ layout: default_tool keywords: [redis, redis GUI, redis client, redis 客户端] description: fredis 是一个网页版 redis 客户端,基于“ws2s项目”开发。有基本的GUI图形界面,能编辑保存服务器信息、提供 redis 命令行终端、保存历史命令。所有数据存储在 localStorage, 保证数据安全。 --- -
    +