当一个年轻人空降:改造腾讯混元的 300 天
- 开始训练前,姚顺雨用自己的旧东家 OpenAI 举例给团队鼓劲:直到今天,OpenAI 的基础模型也没有依赖过什么神秘的技术,甚至底层架构与 2023 年比没有什么根本性的变化。这也是他如此看中数据的原因。他认为,做大模型没有魔法,也不要相信别人有魔法。真正难的是把最基础、确定能做对的事情都做对 —— 做到这些,足以让混元挤进中国的第一梯队。
搜索所有内容。
输入关键词开始搜索
没有找到相关内容
养成费米估算的习惯,主动查证关键数据,并运用上下限分析法。我注意到许多我认识的最聪明的人都会这样做:他们不会轻信任何说辞,而是自行验证其合理性。例如,听到某个事实时,他们会亲自查证以确保属实 —— 因为人们引用的内容常常存在虚假或片面性。
大多数人根本不这样做 —— 他们不进行 “量化思考”,结果很容易上当受骗。推特上时不时会流行这样的梗:“如果伊隆把造火箭的钱分给每个美国人,等等” 这类言论会疯传,就是因为人们没有算账。
费米估算由物理学家费米提出,他曾在课堂上问学生:“芝加哥有多少钢琴调音师?”—— 没人能查数据,但通过拆解估算,答案数量级和实际值高度吻合 。
费米估算的原理:
1970 年,波音就已经引入了 CAD 电脑画图。但交付给下个部门的时候,却是把结果打印出来,让下一个部门的人用眼睛读、用脑子理解、再靠脑子去执行。
星巴克的咖啡师做咖啡的速度提高 100 倍,星巴克未必能多赚很多钱。星巴克的瓶颈不在做咖啡的速度,在于客流、选址、供应链管理。
同理,你们公司的瓶颈,大概率也不在于每个员工手头的任务完成速度。而在于部门和部门之间的信息流动,在于协调层。
那个在公司里被称为 “很能干” 的人,你仔细观察一下,他到底在干什么?大概率是这样的:他最熟悉各个项目的上下文,能在不同部门之间穿针引线,能把复杂的事情讲清楚让所有人都理解。
他的核心价值,是在一个信息流通不畅的组织里,做信息中介。他们也恰恰是 AI 最应该替代的角色。
用 AI 做会议纪要,本质上在做什么?
把一个小时的会议录音,变成一份文字摘要。以前要半小时,现在十分钟。确实快了。 但你有没有想过 —— 这份会议纪要,是给谁看的?怎么用的?
大概率是这样的:发到群里,你扫一眼,记住几个关键点,其他的全忘了。下次开会,大家又从头开始对齐,“上次说到哪了?那个问题有没有跟进?”
信息经过了人类的眼睛、大脑这个极度低带宽的通道,从一个丰富的多维度文档,退化成了你脑子里残存的十分之一。然后再退化,再退化。
AI 在这里做了什么?它替代的,是原来的助理、书记员、秘书。省的是记录的时间成本。
但组织协调的根本问题,一点没变。
真正的 AI 化,是组织共享一个被持续维护的事实库
你们有没有一个关于某个项目的 “总文档”,每次开完会,这个项目的进展、决策、待跟进事项,会自动更新进去?下次开会,大家是基于这个文档来开,还是靠各自脑子里的残存记忆来开?
如果是后者,那不管你们用了多少 AI 工具,本质上还是 1985 年波音工程间的状态 —— 每个人都有 CAD,但图还是打印出来开会。
真正的 AI 化不是每人一份会议纪要,而是整个组织共享一个被持续维护的事实库。会议是一个更新这个事实库的节点,不是一个独立的、被总结然后被遗忘的事件。
当 AI 真的接管了信息共享这一层,开会就会变成另一个样子:每个人只接收和自己相关的那部分,异步同步信息,只有真正有分歧的地方才需要大家坐在一起

我在那些爸爸们中间坐了一会儿,但很快就烦了他们。全部的爸爸都是带着女儿来参加这个可爱的小小宴会的,但他们都在讨论股票的事儿——他们不知道怎么和自己的孩子说话,更不知道怎么和孩子的朋友们说话。
别人认为你应该达到什么造诣,但你没有责任来满足他们的想当然。我没有责任成为他们指望我成为的东西。错误是他们犯下的;我无能,与此无关。
“高等研究院”以为我有那么好,就我这方面而言,这和我无能没关系;这事儿是异想天开,明摆着是个错误——从我意识到这事儿可能是他们搞错了的那一刻起,我意识到别的地方,包括我自己所在的这所大学,也是能把事儿搞错的。我就是我,如果他们想当然地以为我很好,主动给我一些钱,那活该他们倒霉。

当我发现圣诞老人并不存在时,我没有感到难过,反而释然了,原来有一个更简单的说法可以解释为什么全世界那么多孩子会在同一天夜里收到礼物。之前的故事太复杂了,我快想不透了
这是我从我父亲那里学来的:不尊重任何权威;不要管话是谁说的,但要关注他说的条件是什么,结论又是什么,然后问自己:“这合理吗”
定义你的,不是你的本质,而是你的所作所为。

AI 的热潮确实让我想起早期低代码和无代码工具的突破。我不怀疑 AI 能成为开发者的有用工具,我知道有些任务它能作为更好的工具来辅助完成。但这些论点总让我再次思考偶然复杂性与本质复杂性的问题。
布鲁克斯在《没有银弹》一文中探讨了新工具对开发者生产力的影响。要像程序员一样思考,你必须理解现实世界的复杂性。编程最好被理解为在混乱的现实之上施加简化的表征——我们称之为_抽象_——通过降低复杂性使其可理解。这让我们能够将特定情境泛化为可层层叠加的抽象层。
即使更好的工具减少了偶然复杂性,本质复杂性依然存在。我们仍需以正确的方式设计抽象和系统——一种优雅、清晰且可维护的方式——这本身就是一项复杂的工作。而这种复杂性不会消失。这类工作需要技能、经验以及从过去系统失败中艰难获得的智慧。
LLM 驱动开发的魅力在于它理应消除摩擦。拥护者们编造着开发团队一天内交付数十个功能的故事,他们指挥着多个自主运行的智能体团队,以越来越奇特的拓扑结构协同工作。我理解,软件开发有时确实繁琐且令人沮丧。能够以相对惊人的速度产出代码,并摆弄那些打磨精美的产品而非原型,这种感觉一定令人无比兴奋。
不过我需要这种摩擦。
当我初次学习一门新语言或框架时,即使是最基本的任务也会让我感到举步维艰。这感觉糟透了!而当我面对一个陌生且不熟悉的代码仓库或数据源时,我需要花上几个小时仔细审视。我常常会进行细致的研读,调出特定文件逐行查看,直到理解它们的上下文以及开发者做出的选择。我知道我可以直接让 LLM 帮我总结项目来节省时间,但我发现我需要这个过程来真正沉浸到代码中。我需要的不只是理解开发者做了哪些选择,更是理解他们为何做出这些选择,以及这些选择如何体现所用语言的约束或惯用法。我在失败中学习,如果 LLM 替我完成了这部分工作,我就无法真正理解自己在做什么。
如果编写提示词的工程师缺乏判断好坏的能力,他们就会陷入反复让 AI 硬编码来消除摩擦的循环。这可能导致一团混乱的抽象层,而留给未来团队的唯一设计文档,只是一份为几年前使用的 AI 模型写的 Markdown 指令。
也许我不使用 LLMs 的最简单原因就是,我实在是太热爱编程了,以至于我不想把它交给机器。就像如果我是艺术家或音乐家,我不会求助于 AI 一样,编程是我表达创造力的一种方式,我不会放弃这种快乐。尽管有时会极其令人沮丧,但从一个模糊的想法塑造出一个真实的系统,尤其是涉及优雅的实现或有趣的问题时,会带来深沉的喜悦。有些晚上,我合上工作笔记本,打开个人笔记本,去探索一些我想构建的有趣新东西。
编程也一直是我在困难时期的慰藉。有研究表明,玩俄罗斯方块是避免创伤后应激障碍的有效方法。其理论依据是,调动大脑中负责排列和旋转形状的部分,会妨碍创伤记忆的形成。幸运的是,我没有患 PTSD(我也并非轻视那些受其困扰的人),但确实能理解这一概念。编程就像解复杂的谜题,有时是我黑暗时刻的慰藉。

养成习惯的好处是,在行动时,不必再进行内心的权衡。
不再消耗能量去思考是否要做这件事。
你只管去做。
每个人都害羞。
其他人在等你做自我介绍;
他们在等你给他们发邮件;
他们在等你
约他们出去。
基于Astro,从零重新实现

当 int/long/float/指针/std::string 作为 std::unordered_map 的 key 时,C++底层是如何计算 hash 值的?
gcc/clang 作为使用最多的两种编译器和标准库,它们在这个问题的实现上略有差异。本文将基于二者的源码进行对比分析。
一方面,我越来越擅长自学,也养成了独立思考的能力;另一方面,我变得孤僻,不善与人建立连接。面对人群,我总是局促不安,不知如何表达真实的自己;我害怕袒露想法,总觉得无人理解,也无人愿意理解。
人必须与外部世界建立连接,而表达,就是这场连接中的第一道桥梁。
我才明白,表达不是可有可无的能力,而是一门人生的必修课
真正的表达,不是为了炫耀,也无关于技巧,而是一种自我的袒露。你越愿意将内在推向外部,你就越清楚自己是谁、想要什么、和他人有什么不同。而你越害怕表达、越不敢袒露,你的“自我”就越容易被模糊、被裹挟、被湮没。久而久之,甚至你自己也不认识自己了。
表达像是一面镜子,一面不断校准自我认知的镜子。你表达得越多,越真实,就越靠近那个真实的“自己”
如果你希望存储一段信息,让 100 年后的人也能访问,要怎么做?
三体中有类似的想法:如果人类灭亡,最好的保存文明的方式是什么?答案是:“刻在石头上”
但在一百年的时间尺度上,文章作者认为 GitHub 是存储信息的最好方式:
越来越多的人会把自己的信息搬到 GitHub 上,依托 GitHub 实现曾经人们可望而不可及的”永生”。
几十几百年后,GitHub 将成为世界上最大的数字公墓,注册用户大部分都已去世,然而个人主页,项目,commit 历史 还述说着他们生前做过的事——就比如 Joe 的博客。 这虽然是个比较 creepy 的推论,但从另一个角度想,却证明了人类的巨大进步:对抗死亡是人类文明的永恒主题,而我们已经实现了阶段性胜利。
tk 教主分享在微博上的文章:“粉丝群有人问有没有关于怎么带团队的书。我觉得把《怎样当好一名师长》看明白就够了。这也是我非常推崇的一篇文章。”
要锻炼出创造性的身体,去看电影、听音乐会、去美术馆、看别人的建筑,然后要有想做得比他们更好的意愿,超越前人的勇气,体力衰退了,竞争意识就会变弱。没有了创造性的身体,竞争意识就没了。这两个是一回事,要同时锻炼身体和意志。
我捐出了所有来自苹果的财富,因为财富和权力并非我的追求。我享受生活的乐趣——在我的出生地圣何塞,我资助了许多重要的博物馆和艺术团体,他们以我的名字命名了一条街道以示认可。如今我从事公开演讲并成为行业顶尖,虽然不清楚具体资产数额,但经过 20 年的演讲事业,可能积累了约 1000 万美元和几处房产。我从不寻找任何避税手段,通过劳动获得的收入缴纳约 55%的综合税负。我是世界上最快乐的人。对我而言,生活从来不是关于成就,而是关于快乐,那正是笑容减去愁容的简单公式。这些人生理念在我 18 到 20 岁时就已形成,而我始终坚守至今。
最近看了《成为乔布斯》这本书以及听了播客半拿铁的《苹果简史》。对沃兹的印象就是他和乔布斯最开始的组合就是天使+魔鬼,沃兹是毫无疑问的赤子。沃兹的这个留言更加印证了这个印象。
今天恰好是 woz 的生日,看到了他在论坛上的留言,再次感叹他的人生态度。
我们做任何决定,最终都有可能会后悔会遗憾,但是面对多个选择时,我们应该选择让自己后悔或者遗憾最少的那个。
股市是世界上最可爱的东西。对于股市你可以随便怎么想,怎么想都可以,甚至觉得”川大智胜”和川普有关系都可以。
一切观点、一切想法都可以掏钱验证。如果你是对的,那就不光是对的,还能赚钱。所以如果你这么坚定地相信,就没道理不掏钱。除非,你其实也没那么相信。
社会是一个周期更长的股市。每个人最终都会在这个股市里得到和自己的想法相匹配的回报,一切想法也最终都会变成收益或亏损。而且社会这个股市是由不得你不掏钱的,退不了,缩不了,怂不了。 不光是钱,你的整个人生都会被投入进去。所以收获的也不光是钱,是你整个人生的悲喜荣哀。
所以,你可以随便怎么想,怎么舒服怎么想,或者,认真地思考自己的每一个判断,每一个决定,想清楚自己要的到底是简单、爽,还是有更想要的东西。
C++17 中引入了 std::any,可以非常方便地将任意类型的变量放到其中,做到安全的类型擦除。然而万物皆有代价,这种灵活性背后必然伴随着性能取舍。
std::any 的实现本身也并不复杂,本文将基于 libstd++ 标准库源码 深入解析其实现机制与性能开销。
我们在使用 C++ 的时候,有时会需要在类的内部获取自身的 shared_ptr,这就会用到 std::enable_shared_from_this。在实际使用过程中,std::enable_shared_from_this 有三个陷阱需要注意:
我们有这么一段业务代码,在 Gin 的 API Handler 中,开了一个子 goroutine 写 DB,代码大概是这样:
几乎世界上每个 Golang 程序员都踩过一遍 for 循环变量的坑,而这个坑的解决方案已经作为实验特性加入到了 Go 1.21 中,并且有望在 Go 1.22 中完全开放。
Bigcache是用Golang实现的本地内存缓存的开源库,主打的就是可缓存数据量大,查询速度快。 在其官方的介绍文章《Writing a very fast cache service with millions of entries in Go》一文中,明确提出了bigcache的设计目标:
最近发现 Golang 标准库竟然自带了 varint 的实现,代码位置在 encoding/binary/varint.go,这个跟protobuf里面的varint实现基本是一致的。刚好借助 golang 标准库的 varint 源码,我们来系统地学习和梳理下 varint。
sync.Pool 是 Golang 内置的对象池技术,可用于缓存临时对象,以缓解因频繁建立临时对象带来的性能损耗以及对 GC 带来的压力。
问题是这样的:我在代码里面调用了 os.Chmod("test.txt", 777),希望把该文件的读写及执行权限对所有用户开放。
我们在开发 HTTP Server 的时候,经常有对接口内容做缓存的需求。例如,对于某些热点内容,我们希望做 1 分钟内的缓存。短期内缓存相同内容不会对业务造成实质影响,同时也会降低系统的整体负载。
「优雅终止」指的是当服务需要下线或者重启时,通过一些措施和手段,一方面能够让其他服务尽快的感知到当前服务的下线,另一方面也尽量减小对当前正在处理请求的影响。优雅终止可提升服务的高可用,减少下线造成的服务抖动,提升服务稳定性和用户体验。
一致性 Hash 常用于缓解分布式缓存系统扩缩容节点时造成的缓存大量失效的问题。一致性 Hash 与其说是一种 Hash 算法,其实更像是一种负载均衡策略。
sync.Cond 条件变量是 Golang 标准库 sync 包中的一个常用类。sync.Cond 往往被用在一个或一组 goroutine 等待某个条件成立后唤醒这样的场景,例如常见的生产者消费者场景。
sync.WaitGroup 是 Golang 中常用的并发措施,我们可以用它来等待一批 Goroutine 结束。
依赖注入是一个经典的设计模式,可有效地解决项目中复杂的对象依赖关系。
本文将基于 Golang 源码对 Timer 的底层实现进行深度剖析。主要包含以下内容:
time.Sleep 的实现细节,Golang 如何实现 Goroutine 的休眠。注:本文基于 go-1.13 源码进行分析,而在 go 的 1.14 版本中,关于定时器的实现略有一些改变,以后会再专门写一篇文章进行分析。
Elasticsearch 是一个分布式搜索引擎,底层基于 Lucene 实现。Elasticsearch 屏蔽了 Lucene 的底层细节,提供了分布式特性,同时对外提供了 Restful API。Elasticsearch 以其易用性迅速赢得了许多用户,被用在网站搜索、日志分析等诸多方面。由于 ES 强大的横向扩展能力,甚至很多人也会直接把 ES 当做 NoSQL 来用。
本文整理和记录下自己在运营 个人博客 以及公众号时常使用到的一些工具。主要包含以下方面:
channel 是 Golang 中一个非常重要的特性,也是 Golang CSP 并发模型的一个重要体现。简单来说就是,goroutine 之间可以通过 channel 进行通信。
channel 在 Golang 如此重要,在代码中使用频率非常高,以至于不得不好奇其内部实现。本文将基于 go 1.13 的源码,分析 channel 的内部实现原理。
定时器是网络框架中非常重要的组成部分,往往可以利用定时器做一些超时事件的判断或者定时清理任务等。
本文主要介绍 Filebeat 7.5 版本中 Log 相关的各个配置项的含义以及其应用场景。
一般情况下,我们使用 log input 的方式如下,只需要指定一系列 paths 即可。
filebeat.inputs:
- type: log
paths:
- /var/log/messages
- /var/log/*.log
但其实除了基本的 paths 配置外,log input 还有大概十几个配置项需要我们关注。
这些配置项或多或少都会影响到 Filebeat 的使用方式以及性能。虽然其默认值基本足够日常使用,但是还是需要深刻理解每个配置项背后的含义,这样才能够对其完全把控。
同时,在 filebeat 的日常线上运维中,也会涉及到这些配置参数的调节。
Redis 作为一个单线程高性能的内存缓存 Server 而被人熟知。作为一个典型的 Reactor 式网络应用,Redis 能够达到如此高的性能,必然要依靠足够可靠的事件循环库。
Redis 内置了一个高性能事件循环器,叫做 AE。其定义和实现可以在 ae*.h/cpp 这些文件中找到。
AE 本身就是 Redis 的一部分,所以整体设计原则就是够用就行。也正因为这个背景,AE 的代码才可以简短干净,非常适合阅读和学习。
本文将基于 Redis 5.0.6 的源码分析下其事件循环器 (AE) 的实现原理。
同时本人也提供了一个 Redis 注释版,用以辅助理解 Redis 的源码。
Filebeat 是使用 Golang 实现的轻量型日志采集器,也是 Elasticsearch stack 里面的一员。本质上是一个 agent,可以安装在各个节点上,根据配置读取对应位置的日志,并上报到相应的地方去。
Filebeat 的可靠性很强,可以保证日志 At least once 的上报,同时也考虑了日志搜集中的各类问题,例如日志断点续读、文件名更改、日志 Truncated 等。
Filebeat 并不依赖于 Elasticsearch,可以单独存在。我们可以单独使用 Filebeat 进行日志的上报和搜集。filebeat 内置了常用的 Output 组件, 例如 kafka、Elasticsearch、redis 等。出于调试考虑,也可以输出到 console 和 file。我们可以利用现有的 Output 组件,将日志进行上报。
当然,我们也可以自定义 Output 组件,让 Filebeat 将日志转发到我们想要的地方。
filebeat 其实是 elastic/beats 的一员,除了 filebeat 外,还有 HeartBeat、PacketBeat。这些 beat 的实现都是基于 libbeat 框架。
uber 在 Github 上开源了一套用于服务限流的 go 语言库 ratelimit, 该组件基于 Leaky Bucket(漏桶) 实现。
我在之前写过一篇 《Golang 限流器 time/rate 实现剖析》,分析了 Golang 标准库中基于 Token Bucket 实现限流组件的 time/rate 原理,同时也讲了限流的一些背景。
相比于 TokenBucket 中,只要桶内还有剩余令牌,调用方就可以一直消费的策略。Leaky Bucket 相对来说更加严格,调用方只能严格按照预定的间隔顺序进行消费调用。(虽然 uber-go 对这个限制也做了一些优化,具体可以看下文详解)
还是老规矩,在正式讲其实现之前,我们先看下 ratelimit 的使用方法。
限流器是微服务中必不缺少的一环,可以起到保护下游服务,防止服务过载等作用。上一篇文章 《Golang 限流器 time/rate 使用介绍》 简单介绍了 time/rate 的使用方法,本文则着重分析下其实现原理。建议在正式阅读本文之前,先阅读下上一篇文章。
上一篇文章讲到,time/rate 是基于 Token Bucket(令牌桶) 算法实现的限流。本文将会基于源码,深入剖析下 Golang 是如何实现 Token Bucket 的。其代码也非常简洁,去除注释后,也就 200 行左右的代码量。
同时,我也提供了 time/rate 注释版,辅助大家理解该组件的实现。
本主题为系列文章,分上下两篇。本文主要介绍
time/rate的具体使用方法,另外一篇文章 《Golang 限流器 time/rate 实现剖析》 则着重介绍其内部实现原理。
限流器是后台服务中的非常重要的组件,可以用来限制请求速率,保护服务,以免服务过载。 限流器的实现方法有很多种,例如滑动窗口法、Token Bucket、Leaky Bucket 等。
其实 Golang 标准库中就自带了限流算法的实现,即 golang.org/x/time/rate。该限流器是基于 Token Bucket(令牌桶) 实现的。
简单来说,令牌桶就是想象有一个固定大小的桶,系统会以恒定速率向桶中放 Token,桶满则暂时不放。 而用户则从桶中取 Token,如果有剩余 Token 就可以一直取。如果没有剩余 Token,则需要等到系统中被放置了 Token 才行。
libco 是微信后台开发和使用的协程库,同时也是极少数的直接将 C/C++ 协程运用到如此大规模的生产环境中的案例。
在 《云风 coroutine 协程库源码分析》 中,介绍了有栈协程的实现原理。相比云风的 coroutine,libco 在性能上号称可以调度千万级协程。 从使用上来说,libco 不仅提供了一套类 pthread 的协程通信机制,同时可以零改造地将三方库的阻塞 IO 调用进行协程化。
本文将从源码角度着重分析 libco 的高效之道。
在正式阅读本文之前,如果对有栈协程的实现原理不是特别了解,建议提前阅读 《云风 coroutine 协程库源码分析》。
同时,我也提供了 libco 注释版,用以辅助理解 libco 的源码
C++11 中推出了三种智能指针,unique_ptr、shared_ptr 和 weak_ptr,同时也将 auto_ptr 置为废弃 (deprecated)。
最近在写 C++ 时,有这样一个代码需求:在 lambda 中,将一个捕获参数 move 给另外一个变量。 看似一个很简单常规的操作,然而这个 move 动作却没有生效。
具体代码如下:
std::vector<int> vec = {1,2,3};
auto func = [=](){
auto vec2 = std::move(vec);
std::cout << vec.size() << std::endl; // 输出:3
std::cout << vec2.size() << std::endl; // 输出:3
};
代码可在 wandbox 运行。
我们期望的是,将对变量 vec 调用 std::move 后,数据将会移动至变量 vec2, 此时 vec 里面应该没有数据了。但是通过打印 vec.size() 发现 vec 中的数据并没有按预期移走。
这也就意味着,构造 vec2 时并没有按预期调用移动构造函数,而是调用了拷贝构造函数。
为什么会造成这个问题呢, 我们需要结合 std::move 和 lambda 的原理看下。(最终的解决方案可以直接看 这里)
随着 Golang 的兴起,协程尤其是有栈协程 (stackful coroutine) 越来越受到程序员的关注。协程几乎成了程序员的一套必备技能。
云风实现了一套 C 语言的协程库,整体背景可以参考其 博客。
这个协程库非常轻量级,一共也才 200 多行代码,使用上更贴近于 lua 的写法(众所周知,云风是知名的 lua 粉)。整体基于 ucontext 和共享栈模型实现了有栈协程,代码质量毋庸置疑,本文将详细剖析该协程库的实现原理。
同时,我也提供了 coroutine 注释版,辅助大家理解 coroutine 的代码。
微信云支付 Android 智能 POS 使用 WebSocket 实现了用户订单的实时推送。即,顾客在扫描了门店的付款码,客户端会随即进行语音播报和打印等动作。
在进行 Linux 网络编程开发的时候,免不了会涉及到 IO 模型的讨论。《Unix 网络编程》一书中提到的几种 IO 模型,我们在开发过程中,讨论最多的应该就是三种: 阻塞 IO、 非阻塞 IO 以及 异步 IO。
本文试图理清楚几种 IO 模型的根本性区别,同时分析了为什么在 Linux 网络编程中最好要用非阻塞式 IO。
在客户端开发中,往往会有一些功能对时间要求比较严格,客户端需要获取到当前最准确的时间。但由于客户端环境多种多样,我们无法保证直接在客户端设备上获取到的时间是最准确的时间。 对于某些问题设备来说,设备时间与比当前实际的时间差了几个小时,甚至几天的情况都存在。倘若某功能依赖于当前时间,而客户端所提供的时间不准,就往往会给客户造成一些困扰。
那么,客户端如何能够获取到当前最准确的时间呢?
最近我用 Golang 开发了一个可以将数据库每张表的各个列信息转化成文档的小工具。开发的缘由是因为写后端时,经常需要为数据库写说明文档,对于稍微有些规模的项目来说,就动辄几十张上百张数据表,开发人员在文档中不断的写各个列的列名、类型、描述实在是无聊、枯燥和苦不堪言。所以就有了这个小工具的诞生。
项目地址在 这里
ClassViewer是我最近开发的一个用于展示 jvm class 字节码的小工具。它是一个单纯的静态网页,完全使用浏览器端的 Javascript 开发。之所以开发这款工具,是因为我在开发 ToyJVM 的时候,需要常常校验 class 文件某一部分的字节码, 所以如果一款工具能够很方便的显示 class 文件各个部分的信息和字节码,对于 ToyJVM 的开发将会是一个非常大的帮助。
在开始写代码之前调研了一些类似的产品,主要有 jdk 自带的 javap、国外的 Java-Class-Viewer 以及国人开发的 classpy,它们都是非常不错的 class 文件分析工具,但是也存在着一些算不上缺陷的小问题。所以最终还是决定自己写一个适合自己小工具,同时也加深下 class 结构的理解。
在调研了目前的产品后,我也更加清晰了自己的目标。首先它的受众应该是有兴趣研究 jvm 的程序员,而它应该有这些特性:
最终实现出来的工具是这样的:
4 月 9 号,在武汉参加了人生第一次半程马拉松,风里雨里的 21.0975 公里。虽然已时隔一周,但想到那天一路的奔跑、疲惫、欣喜,还是想记记这半马的流水账。
BloomFilter(布隆过滤器)是一种可以高效地判断元素是否在某个集合中的算法。
在很多日常场景中,都大量存在着布隆过滤器的应用。例如:检查单词是否拼写正确、网络爬虫的 URL 去重、黑名单检验,微博中昵称不能重复的检测等。
前两天把实验室的一台旧台式机装上了 Ubuntu Server,打算当作测试服务器使用着玩。 装上之后意识到一个严重的问题:实验室电脑连接外网时候需要打开浏览器输入学号进行认证。
在此博客之前,我其实也用过新浪博客、CSDN、博客园,作为个人博客的载体,但对每个博客都并不是特别的满意。原因大概有下面几条:
目前该博客是使用 hexo+Next 主题 + Github 进行搭建。 事实上,当开始接触使用 hexo 时,我觉得满足了我对博客的诸多要求。我对它的为程序员而生、高度可定制性非常喜欢。而且,如有机会,可以自己写出满意的主题使用。