1.鹅厂微创新Golang缓存组件TCache介绍
2.MDN 的小组自动完成搜索是如何实现的
3.ä¸çå°±ä¼çè¶
å®ç¨å°ç»ä»¶ä¹LoadingButton
4.vue-template-compilerï¼
鹅厂微创新Golang缓存组件TCache介绍
一个 Golang 自研小组件,TCache 介绍
作者:frank、小组maxy、小组lark 等。小组
TCache 是小组一个 Golang 团队自研的缓存组件,旨在优化视频会员场景下高并发请求的小组mycom源码压力,减少底层存储压力,小组提升系统可用性。小组设计时,小组我们考虑了开源组件如布隆过滤器、小组位图、小组localcache 的小组特点和优劣,以业务需求为出发点,小组集成这些组件形成整体解决方案。小组
TCache 设计目标
主要目标是小组为视频会员服务提供高效缓存,应对大量 APP 请求,减轻存储层压力,并增强系统稳定性。经过调研,我们发现现有开源组件适合不同场景,因此决定整合这些组件,公开自用指标源码通过配置化设计,让业务根据自身需求选择合适的缓存策略。
整体架构
TCache 分为四层架构:业务场景层、中间件层、组件层与算法层。业务场景层直接与应用交互,中间件层集成了多种缓存算法,组件层基于开源组件实现,算法层则深入研究缓存技术原理。
组件结构
TCache 集成了多种缓存组件,包括 KV 型结构 Cache、BitMap、BloomFilter 与大型计数器 Hyperloglog。此外,我们计划集成更多组件以覆盖更多业务场景。
Cache 组件设计
提供了统一的 cache 接口,支持用户自定义底层缓存实现,包括默认实现与本地缓存组件 localcache 的接口定义。
BitMap 组件设计
BitMap 组件集成经典 BitMap 与 Roaring 位图算法,提供单一操作 API,ipad源码阅读app便于业务集成使用。组件结构清晰,代码接口明确。
开发过程
TCache 的开发过程始于团队转型 Golang 时的技术积累与开源组件分析,通过源码阅读、论文研读,深入了解组件技术,最终形成组件化设计。团队持续研究缓存替换算法、位图算法,通过实验对比分析,提炼出业务适用的缓存策略。
功能分析
本地缓存强调数据一致性与吞吐量,支持多线程访问与内存限制,适用于缓存热点数据。常见组件如 freecache、fastcache、bigcache 等,提供线程安全、高命中率与高效管理的gdb调试源码信息特性。
源码分析
深入研究开源组件,如 BigCache、BloomFilter、RoaringBitmap,通过建模与代码分析,了解组件实现原理与优化策略。
算法研究
研究缓存替换算法,包括 Belady 最优策略、随机策略、先进先出、最近不使用、最不经常使用、重引用间隔预测等。通过实验对比分析,提炼出适用于不同场景的缓存策略。
实验研究
通过功能与性能对比研究,推荐不同缓存组件在特定场景下的应用,如 freecache、bigcache、fastcache、spring博客源码localcache 等,以及针对数据持久化与热启动的组件。
组件化
整合多种组件形成 TCache,通过组件化设计,让业务灵活选择缓存策略,提高系统性能与稳定性。
总结
TCache 的开发是一个无心插柳的成果,整合了团队的技术积累与业务需求。通过研究、实验与优化,我们找到了适合视频会员服务的缓存解决方案。未来,结合 AIGC 等新技术,开发出更多原创组件,有可能推动开发行业的变革。
MDN 的自动完成搜索是如何实现的
上个月,我和 Gregor Weber 为 MDN Web Docs 添加了 自动完成搜索(autocomplete search)功能,有了这个功能,你可以通过输入文档的部分标题来快速查找并跳转到想查看的文档。这篇文章我会介绍这个功能是如何实现的。如果你坚持看到文章末尾,我还会分享一个 “彩蛋” 功能,一旦你学会使用它,你一定会成为派对上最靓的仔。不过,或许你只是想比普通人更快的浏览 MDN。
简单来说,输入框上有一个onkeypress 事件监听器 用于过滤 (每个地区的)完整的文档标题列表。在我写这篇文章时,English US 有 , 个不同的文档标题和对应的 URL。你可以打开 developer.mozilla.org/e... 来预览这些文档。没错,这个文件很大,但还没大到无法被全部放进内存。毕竟,执行搜索逻辑的代码只会在发现用户要输入某些内容时,该文件才会被加载。而提到文件大小,由于文该件通过 Brotli 算法进行了压缩,所以在网络上该文件大小仅为 KB。
实现细节:默认情况下,加载的 JavaScript 代码 只有一小段 shim 代码,用于设定监听 搜索框的 onmouseover 和 onfocus 。还有一个绑定在 document 上用于监听输入特定按键的事件监听器。在任何地方输入 /,和你用鼠标把焦点放在 是一样的。一旦 focus 事件被触发,首先会 下载两个 JavaScript 包 来将 转变为更高级的东西。简单来说(通过伪代码),就是这样的:
而这里加载的/static/js/autocomplete.js 才是最神奇的。通过伪代码我来深入解释一下:
正如你看到的,这是对实际工作原理的过度简化,但现在还不是深入这些细节的时候。下一步 就是展示匹配项。我们使用 (TypeScript) React 来实现,但是下面的伪代码应该更容易理解:
然后通过一些 CSS,我们会把这些匹配项变成一个浮层,然后简单放在 下方。除此之外,我们还会根据 inputValue 突出展示每个文档标题,当通过上下按键浏览时,各个事件监听器会突出展示你正在浏览的行。
好的,我们再深入一下实现细节:我们只创建了一次FlexSearch 索引,并在每一次新的键击出现时复用它。由于用户在等待网络响应时,可能会输入更多东西,所以当全部的 JavaScript 和 JSON XHR 都加载完毕,才会执行实质上的搜索。
在我们深入FlexSearch 是什么之前,我想先说一下我们实际上是如何展示搜索结果的。我们使用了一个 React 库 downshift 来处理交互、展示 并确保搜索结果具有可访问性(Accessible,译者注:无障碍相关,国外页面对于 让残障人士更加便利的访问 比较重视)。 downshift 是一个很成熟的库,解决了我们在构建这个小组件时遇到的很多挑战,尤其是让搜索结果具有可访问性。
那么, FlexSearch 是一个怎样的库呢?它是我们引入的另一个第三方库,确保在标题上的搜索是以自然语言为基础的。它将自己描述为 “Web 上最快,内存最灵活 的 零依赖 全文搜索库”,它比简单的在字符串中搜索要准确高效很多。
决定优先展示哪些结果:有一说一,假设用户输入了foreac ,从 ,+ 的文档标题列表找到那些标题包含 foreac 的项 并不困难,在这之后我们需要决定优先展示哪些结果。我们根据 PV 数据来实现这一点。我们会记录每一个 MDN URL,如果一个页面获得很多的 PV ,那可能它是 “受欢迎” 的。大多数人选择访问的文档就是受欢迎的,也最有可能是用户想要搜索的。
我们在生成search-index.json 文件的 构建阶段 可以知道每个 URL 的 PV 量。我们实际上并不关心绝对数字,我们真正关心的是其中的相对差异。例如,我们知道 Array.prototype.forEach() (文档标题之一)比 TypedArray.prototype.forEach() 更受欢迎,我们就会利用这一点,在 search-index.json 中对条目进行排序。现在,通过 FlexSearch 进行简化,我们利用数组的 “自然顺序” 来为用户提供他们可能在搜索的文档。这实际上和我们在全站搜索中使用的 Elasticsearch 是相同的技术。详见: How MDN’s site-search works 。
彩蛋:如何通过 URL 搜索:事实上,这个彩蛋可不是闹着玩的,而是一个功能,利用自动完成用来帮助我们的内容创作者。当你创作 MDN 中的内容 时,你会启动一个本地的 “预览服务器”,它是所有文档的完整拷贝,但运行在本地,作为一个静态站点 运行于 /post/
vue-template-compilerï¼
vueæä¹è§£å³
ä½ å¥½ï¼
æ¥éåå ï¼é常åºç°äºä¸äºä¾èµåºçæ´æ°æè å®è£ æ°çä¾èµåºä¹å(å¯ä»¥è®¤ä¸ºnpmupdateå·²ç»æ为ä¸ç§ä¹ æ¯)ï¼å¯¼è´äºvueåvue-template-compilerççæ¬ä¸ä¸è´ã
解å³æ¹æ¡ï¼ç»ä¸vueåvue-template-compilerççæ¬
1"vue":"2.3.4",
2"vue-template-compiler":"2.3.4",
ï¼æ³¨ï¼å¦ä¸æ示çæ¬å·åä¸ºæ ·ä¾ï¼å ·ä½å®ç°è¯·å°ä¼ä¼´ä»¬æ ¹æ®èªå·±ççæ¬å·å¯¹åºï¼
ç¶åæ§è¡ï¼npmupdateå°±å¯ä»¥äºã
éå ï¼
è¥ä¸è¿°æ¹æ³æªè§£å³é®é¢ï¼åå¯è½æ¯å 为åä¾èµåºä¹é´äº§çäºå²çªï¼è¿éæ们ä¹ææ´æ´åç解å³æ¹æ³ã
é¦å æ¥ç项ç®æ ¹ç®å½ä¸æ¯å¦çæè¿package-lock.jsonçæ件ï¼è¥æï¼å ä¹ã
ç¶åå é¤æ´ä¸ªnode_modulesç®å½ï¼éæ°é ç½®package.json(å½ç¶ï¼è¦è®°å¾ç»ä¸vueåvue-template-compilerççæ¬)
é 置好ä¹åéæ°æ§è¡ï¼npminstallï¼
OKï¼é®é¢è§£å³ã
element饿äºä¹çvueæ¡æ¶ï¼è¿è¡æ¥é已解å³ï¼Vue2.1.5å°_héå½å为_cï¼èElementç®ååççæ¬é½æ¯ç¨ä»¥åçcompilerç¼è¯çï¼å¯¼è´æ°çruntimeæ æ³è¿è¡Elementãç®åç解å³æ¹æ¡æ¯éå®Vueççæ¬ä¸º2.1.4
éå®vueç¸å ³çæ¬
#éæ°å®è£ ä¸ä¸çæ¬
"vue-template-compiler":"2.1.4"
"vue-loader":".0.0"
"vue":"2.1.4"
å ·ä½å½ä»¤å¦ä¸ï¼
npmremove#å¸è½½æ个çæ¬
npmremovevue
npmremovevue-template-compiler
npmremovevue-loader
npminstallvue@2.1.4#å®è£ æå®çæ¬
npminstallvue-template-compiler@2.1.4
npminstallvue-loader@.0.0
-æä¹è®©vueæå çæ¶åç¦ç¨angularç±äºæå å®æåï¼åéæ æ³ä½¿ç¨
æå°è¯è¿æ ·åï¼ä»¥åvue-cli3çåæ³ï¼é½æ æ³åå°ç¦ç¨ä¸¥æ ¼æ¨¡å¼
image.png
package.json
"devDependencies":{
"@vue/cli-plugin-babel":"~4.5.0",
"@vue/cli-plugin-eslint":"~4.5.0",
"@vue/cli-plugin-router":"~4.5.0",
"@vue/cli-plugin-vuex":"~4.5.0",
"@vue/cli-service":"~4.5.0",
"babel-eslint":"^.1.0",
"babel-plugin-transform-remove-console":"^6.9.4",
"copy-webpack-plugin":"^4.0.1",
"electron":"^9.0.0",
"electron-devtools-installer":"^3.1.0",
"eslint":"^6.7.2",
"eslint-plugin-vue":"^6.2.2",
"sass":"^1..5",
"sass-loader":"^8.0.2",
"svg-sprite-loader":"^6.0.",
"vue-cli-plugin-electron-builder":"~2.0.0",
"vue-template-compiler":"^2.6.",
"webpack-bundle-analyzer":"^3.9.0"
},
"eslintConfig":{
"globals":{
"__static":true
},
"root":true,
"env":{
"node":true
},
"extends":[
"plugin:vue/essential",
"eslint:recommended",
"plugin:vue/recommended"
],
"parserOptions":{
"parser":"babel-eslint"
},
"rules":{ }
}
å¯ä»¥ä½¿ç¨replace-in-fileå ¨å±æ¿æ¢æ"usestrict"å符串
ä¸¥æ ¼æ¨¡å¼ä¸éä¸¥æ ¼æ¨¡å¼
æå¨IEæµè§å¨ä¸,使ç¨strict模å¼.åç°,ä¸¥æ ¼æ¨¡å¼ä¸,ä¸å 许ä¸ä¸ªå±æ§å¤å¤å®ä¹çé误æ示,è¿ä¸ªæä¹è§£å³?
JavaScriptä¸¥æ ¼æ¨¡å¼
JavaScriptçä¸¥æ ¼æ¨¡å¼è¯¥æä¹ç解ï¼ä»å¤©çäºes6ï¼å讲å°ä¸¥æ ¼æ¨¡å¼ä½æä¸ç´ä¸ç解ï¼ä¸¥æ ¼æ¨¡å¼æ¯å¹²å¥ç(=_=)
vueä¸å¦ä½ç¦ç¨ES6ä¸¥æ ¼æ¨¡å¼ã
vue项ç®ä¸å¼ç¨å¤é¨çjsæ件ä¹åï¼æ¥éå¦ä¸ï¼è¯·é®æä¹è§£å³ï¼
nuxtæå å°ç¬¬ä¸æ¹å å离
å¦é¢ï¼å¨nuxt.config.jsä¸é ç½®äºvendoråï¼æå ååºç°äºapp.chunkhash.jsï¼common.chunkhash.js,manifest.chunkhash.jsä½æ¯ææ³å°é¡¹ç®ç¨å°ç第ä¸æ¹åºåç¬æå æä¸ä¸ªæ件ï¼å¦ä½¿ç¨äºvue,axios,vuex,element-uiï¼ææææå åæ¯vue.js,axios.js,vuex.js,element-ui.jsãå°è¯ä½¿ç¨äºCommonsChunkPlugin没ææå
pyinstaller第ä¸æ¹å æå é®é¢
pyinstalleræå æ¶æ¥é:raiseJSONDecodeError("Expectingvalue",s,err.value)fromNonejson.decoder.JSONDecodeError:Expectingvalue:line1column1(char0)
3k
webpackå¦ä½æå 第ä¸æ¹åºçèµæº?
æå¨åä¸ä¸ªæ®éçä¼ ç»é¡¹ç®ï¼ç±äºä½¿ç¨äºes并ä¸ä¸äºé¡µé¢ä¸çå°ç»ä»¶ä½¿ç¨äºreactï¼æå 项ç®ä¸ºäºçäºå¿å°±ä½¿ç¨äºwebpackãåæ¥åç°jqueryçä¸äºæ件ä¸ä½¿ç¨äºéæèµæº(å¦jquery.jBox,ztreeççè¿äºæ件é½æèªå·±çå¾çåcss)ï¼è¿äºèµæºè¯¥å¦ä½å¤çå¢ï¼
javascriptä¸¥æ ¼æ¨¡å¼æ¥é
jsä¸¥æ ¼æ¨¡å¼ä½¿ç¨è´¹ä¸¥æ ¼æ¨¡å¼ç代ç æ¥é'caller','callee',and'arguments'propertiesmaynotbeaccessedonstrictmodefunctionsortheargumentsobjectsforcallstothems
3åç2.6ké 读?已解å³
Webpackå¦ä½æå 第ä¸æ¹çuiæ¡æ¶ï¼
æè¿å¨çwebpackï¼äºè§£äºåºæ¬åçãå®è·µä¸ä¹å¨åç¹ä¸è¥¿ãæ³é®ä¸ï¼å¦ænpmåºä¸æ²¡æçjsåºï¼éè¾¹æå¾çï¼æcssï¼æèªå·±çç®å½ç»æï¼è¯¥å¦ä½å¼ç¨è¿ç±»åºå°èªå·±ç项ç®ä¸å¢ï¼
2.8k
èªé¡¶åä¸å¦Reactæºç
ä»ç念å°æ¶æå°å®ç°å°ä»£ç ï¼éå½»ç解
å端æ§è½ä¼å设计é®
å端æ§è½ä¼å,é¢è¯çå¿ é®é¢ç®
vueè¿è¡æ¥éepf-fast@1.0.4devçæ¬ä¸ä¸è´ãvueçæ¬åvue-template-compilerçæ¬ä¸ä¸è´å°±ä¼åºç°è¿è¡æ¥éepf-fast@1.0.4devã解å³æ¹æ³ï¼
1ãå°node_modulesæ件夹å å é¤ã
2ãå°package.jsonæ件çVueçæ¬å·åvue-template-compilerçæ¬å·æ¹ä¸ºä¸è´ï¼2.6.7ï¼ï¼éé«ççæ¬ï¼éæ°è¿è¡npmrundevå³å¯ã