大学生书单推荐

经常阅读的人和不常读书的人是不一样的,都说「书中自有黄金屋,书中自有颜如玉」,但是多数人都跑到抖音、快手、微博这些平台上面如饥似渴的找黄金,在这个嘈杂的社会中能静心读书变成了一件幸福的事情,也尝试了一下牛客网的「书单」功能,但是有的书我在里面搜索不到,所以直接在这里发了。 在开头先说一下,有的书籍其实没必要去买实体书,尤其在一二线城市打拼的青年,如果在搬家时有太多的书籍简直就是一场噩梦。但是实体书也有另外一个好处,纸张的触感、可以拿笔在上面书写是电子书无法代替的,而且大部分人喜欢用手机去看电子书,很容易就被微信弹出来的消息给打扰了,在全民浮躁的年代能够聚精会神专注的做一件事简直是奢侈。 书也不是你读的越多越好,而是要看你把什么读进去了,内化成为自己的知识才算读进去了;也不是每一本书都要读完,如果发现一本书不适合自己那么撇到一边完全没有问题,何必浪费时间去做那些形式上的表演呢?我个人比较喜欢实体书的原因是,我看实体书的时候能够连续不间断读两个小时,只有专注去读一本书的时候才能让自己有所收获。 我推荐的书籍不一定适合你,但是你也要有意识的去读书,从自己喜欢的内容开始去阅读,什么言情啊、武侠啊、文学啊都可以,重要的是去阅读,逐渐的你就会爱上阅读,自己喜欢的内容会带领你逐步的去探索领域的边界,进而拓展到其它领域,靠自己的探索去获得新知识能够让你记忆更深刻,希望你也能变成一个读书人。 首先推荐李笑来的《把时间当作朋友》和《财富自由之路》,这两本书写的真不错,我身边有好多人都是因为这两本书而改变了人生轨迹,虽然李笑来的人设倒下了,但是不影响这两本书的质量,也不要因为一次人设倒下就全盘否定一个人,其实李笑来那一波操作之后人们反倒更加崇拜他了。 ​ 季羡林先生的书值得一读,大概读几本季老先生的文集就可以了,从文字里面去感受季老求真务实的态度,什么才是做学术的态度,可能读几本季老的书会抑制不住多读几本,大师之所以为大师大师,不仅仅是因为天资聪慧,还有他的为人处世、求真务实。 朱光潜先生《给青年的十二封信》一类的小册子非常值得读一读。比如我在读《谈美》的时候,本以为会是一次艰涩深奥的学习过程,但是没想到老先生用朴实的笔触,深入浅出地把我带进了一个栩栩如生的抽象世界,从青年人所熟知的文艺作品和生活事例出发,将美学的诸多原理娓娓道来。而且书虽取名为《谈美》,但又不仅是讲美学的书,入世出世的态度、青年人培养生活情趣和人生趣味的指南等等都有涉及。 ​ 《小狗钱钱》、《24 堂财富课——与女儿谈创业》、《穷爸爸富爸爸》、《好好赚钱》中可以选一两本阅读,大学只教了我们生存的技能,并没有教我们如何理财,而这又是每个人不得不面对的问题,这几本入门书籍,可以帮助您建立财富理念。再读一本《指数基金投资》,定投指数基金是最较适合小白的投资方式,尤其对于没有时间研究股票和基金的上班族来说,定投指数基金是一个很不错的方式。如果在学生时代有一点闲钱去尝试的话,就是一件更好的事情,因为学生时代几百几千看起来很多,但是当你参加工作了之后这点钱根本就不会放在眼里,试错成本低的阶段尽量多去尝试。 ​ 《浪潮之巅》、《数学之美》、《大学之路》等等,吴军博士的书籍都值得一看,《数学之美》是比较好的人工智能入门书籍,晦涩难懂的道理在吴军博士的文字中变的简单了,感受一下心态、格局等的提升会给人生带来多么重要的改变,我个人目前还没有读完。吴军老师还出了《见识》、《态度》、《格局》几本书,同时他在「得到」平台上的课程也是很值得学习的,不过建议得到和书籍选一个就行了,他新出的书籍很大一部分就是在得到上面稿子整理的。 ​ 像《长乐路》、《明朝那些事儿》、《以纸为桥》、《显微镜下的成都》、《邓小平时代》一类的书籍是可以读读的,应该去了解一下历史的细节,你所看到的不一定是真实的,光芒的背后是另一片黑暗,历史是有规律的,我们的生活可以在历史中找到影子。其中《邓小平时代》我有完整的版本,如果有需要可以微信联系我发你。 ​ 《人类简史》、《人性的弱点》、《心灵七游戏》等心理、哲学类书籍选几本读读,哲学是所有学科的抽象,到一个新的高度看看世界,提高自己的情商,让整个世界都为您张开拥抱的双臂。别好不容易谈个女朋友,因为情商不够又分了。 ​ 读不动名著小说,可以读《偷影子的人》,每个人都有相似的童年,只是大部分人都很羞涩,不好意思说出来而已,主人公就好像在经历自己曾经经历过的生活一样,从没有想过普普通通的童年居然能被描写的如此动情。《月亮与六便士》也是不错的选择。 ​ 都知道莫言先生获得了诺贝尔文学奖,我以前是很害怕读这种大家的书的,巧合看了电影《红高粱》产生了对原著的好奇,于是就去看了《红高粱家族》,结果一看就停不下来了。莫言把人性描述的淋漓尽致,每个人的性格、特点都异常鲜明,电影只是书籍的一部分,书籍比电影更精彩。高密东北乡在莫言的笔下算是绝了,莫言的语言驾驭能力,让各色人物活灵活现,就连那纯种的高粱也时刻在你的脑海里摇曳一番,姿态万千。 ​ 大部分学生都出生于农村家庭,农村的局限性不仅限制了我们的父辈,也限制了我们自己的,好不容易打破限制走到了大城市,一定不能被父母的传统思维影响到了,对父母不是要言听计从,能自己独立思考是成年人的标志,我们要学会对父母灌输给我们的思想取舍。《你当像鸟飞往你的山》英文名为《Educated》,是美国一位 30 多岁名叫塔拉的回忆录,一个从大山、摩门教家庭走出的女孩完成了最终的蜕变。每个人都有自己爹山,你当像鸟一样飞往你的山,过程中经历的转变、蜕变、虚伪、背叛,就是教育。这本书也是比尔盖茨年度荐书第一名、亚马逊年度编辑选书第一名...... ​ 大家应该都看过《美丽心灵》这部电影,电影中的主角纳什是博弈论的发明者,也是普林斯顿大学的教授,普林斯顿除了纳什外,还有很多我们所熟知的科学大牛。比如同为计算机科学之父和人工智能之父的艾伦·图灵,现代计算机之父冯·诺伊曼,出版了《了不起的盖茨比》的著名作家菲茨杰拉德......《自由的老虎》是一个进入普林斯顿的中国女孩用详实的档案与第一手采访资料,谱写的这些大牛挣脱束缚、追寻内心自由与价值的故事。 ​ 如果有可能的话,在大学最好认认真真谈一场恋爱,学习如何去爱一个人,在人生最好的年纪多一点洒脱,离开高中后大家就逐渐变得会算计了,心中有爱生活才会幸福。《霍乱时期的爱情》可以读一读,刚好和今年的新冠疫情有些契合。对了,像《非洲小扎》这样饶有趣味的异国生活小故事集读起来也很不错。 ​ 《荒诞医学史》和读库出版的《医学大神》全套十四册,非常值得一读,有的人大学白读了,这十四本小册子有助于培养自己的科学思维,多点医学知识总不是坏事,江湖骗子骗你的难度也会增加。科学的每一次进步都来之不易,现在人人都知道的勤洗手、用酒精消毒等常识,在前几个世纪居然是被科学界极力反对的做法,如果让我在本文里面只选择一套书籍,那么我会毫不犹豫的选择《医学大神》。在这里我建议你直接订阅读库的全年内容,这里有一篇为啥人人都爱《读库》的老六?的文章,你读完之后就会产生对读库的购买欲望了。 ​ 张立宪的真诚平实少有人及,《闪开,让我歌唱八十年代》中那些呈现细节的文字细腻的像个女子!可以读出来均是老六脑子里的一些记忆,记忆这东西多少都会出现点偏差,不必拿着学术精神去考究,那样就会失去读此书的欢乐了,对那个不属于我的时代是挺好奇的,里面还提到了很多不为人知的经典。 前一段已经提到读库了,如果你不太喜欢自己去挑选书籍的话,那么我强烈推荐你订阅「读库2021」,因为读库没有电子版所以你只能买纸质版,很多有深度的内容你在别的地方是看不到的。而且有意思的是每次打开读库就像打开盲盒一样开心。 比如东东枪采访郭德纲,梅兰芳先生特辑,生物基因究竟是怎么回事儿,这些东西,专门用几万字老老实实的笔锋讲清楚,完全没有那些华丽的东西,也没有那些拼流量的技巧,就是老老实实的文章说老老实实的事儿,在这个人人都抢流量的时代是难能可贵的。 《The Almanack of Naval Ravikant:A Guide to Wealth and Happiness》、《原则》、《奈飞文化手册》几本书介绍了很多人生应该践行的准则。前面两本算是创始人的人生原则清单, 指出不少我们应该避免的事情,也列出来了很多我们应该坚持的东西,比如写作、用计算机节省人力等等。《奈飞文化手册》我觉得算是集体智慧,虽然内容是站在企业角度写的,但对个人也有很大的人生指导意义。 ​ 最后再推荐一本《你是你吃出来的》吧,我们中国人的饮食基本是不健康的。身体所必须的营养一定要注意均衡摄入,不能因为不想吃饭或是学校的饭难吃就不吃,吃东西不仅要关注能量,更要注重七大营养素(碳水化合物、脂类、蛋白质、维生素、矿物质、水、膳食纤维)。鸡蛋、牛奶、蔬菜、水果、坚果、肉类、(深海)鱼、动物肝脏、米饭/面食等,都需要均衡摄入。《你是你吃出来的》里面做了大量苦口婆心的讲解,也纠正了大家平时的一些错误观点,比如很多人生病了不舒服就喝喝粥打发,觉得粥里面很有营养,实际上粥是没有什么营养的;粉条、土豆、红薯这些都是算作主食这一类的,尤其很多人把粉条当做菜来吃是绝对错误的,某一餐有粉条话就应该主动减少米饭的摄入量;很多地方晚上都习惯吃面食,觉得面食容易消化,但面食里面的主要要成分是碳水化合物,碳水化合物摄入过多的结果只会让你越来越胖。 ​ 「一席」上的大部分演讲是值得听的,让您对中国社会有一个更好的了解;「TED」、「网易公开课」上国外高校的视频可以多看看,讲的真心不错;国内出观众、大部分二流学校的演讲能不去就不去,还不如把这种时间拿去睡睡觉,补充补充精力。 多阅读高于自己的作品,远离精神毒品,互联网时代的阅读不再限于书籍了,良心大佬的博客也是一个不错的选择,只有阅读高于自己的作品才能获得成长,少刷抖音、微博、知乎一类的产品,里面是有一些有趣的人生,但是一刷就是几个小时,太浪费时间。
Read More ~

那些鲜为人知的微信使用技巧,让微信更便捷高效

本文整理的技巧基于两种目的,一种是实用方便用来解决生活中的小问题的,另外一种是用来装逼去撩小妹妹的,可能大多数人都更喜欢装逼撩妹的技巧吧。 被人@时掉表情包 在微信群内聊天时如果发送的消息触发了某个关键词就会掉下来表情包,为聊天增添了一些乐趣。我们可以通过人为的方式让别人一@你,就自动掉表情包。操作起来很简单,就是在群聊昵称末尾添加一些不同的字符,比如我在群聊昵称末尾加了「คิดถึง」顶部就掉下来了满屏的星星✨。 其实把这个泰文翻译一下就知道没有那么神秘了,微信内置的翻译功能将「คิดถึง」解释为「想念」,这下就明白为什么会掉星星✨了吧。「สุขสันต์วันเกิด」解释为「生日快乐」就会掉蛋糕,其它还有很多可以自己翻译一下就可以了。 同一条朋友圈别人评论/点赞不再提示 如果某个朋友发了一条朋友圈,你在下面评论或是点赞了,那么当你们的共同好友也对同一条朋友圈点赞或评论时,就会在「发现」有消息提示,但很多时候我们并不想要这样的提示,那怎么办呢?点到朋友圈的消息页中,iOS 向左滑动(安卓长按)就可以看到有个「不再通知」按钮,点击之后共同好友再点赞或评论就不会收到提示啦! 用好微信内置的代办事项 微信内置的代办事项功能估计没多少人知道,微信无疑是我们打开频率最高的 APP,把代办事项放在微信就更不容易忘事了。 可以看到它被置顶在微信的首页界面,每次启动微信都能看到,非常的方便,那么如何把代办事项置顶呢? 第一步:打开微信 > 我 > 收藏 > 右上角➕号 > 右下角图标 > 代办 第二步:右上角三个点 > 左下角在聊天中置顶 重要事件提醒 我们经常会在微信上答应别人一些事情,为此可能去找一些清单软件来提醒自己,问题是清单软件又不常打开,常常会导致事情的忘记,给别人留下一个言而无信的形象。其实微信自身就已经带了提醒功能,长按任何一条消息,上面有一个提醒按钮,点击设置提醒时间,到了提醒时间后,会在「服务通知」中有推送的提醒。 改善公众号阅读体验 微信公众号阅读文章的体验不佳,不支持分组,也不支持进行上一次阅读,这时可以选择公众号任意一篇文章,点击右上角之后找到「在微信读书中打开」,如果没有这个功能,可能需要升级微信或者下载微信读书 App。 建立一个人的微信群 怎么建立只有一个人的微信群呢?选择 -> 添加朋友 -> 面对面建群 -> 输入数字 -> 建群完成。这个只有自己的群就相当于一个信息分组,可以把自己平时的想法、待阅读的文章、写作灵感等发到里面,就自己一个人看,而且还可以对微信群进行置顶操作,不会受到其他微信群信息的影响,处理完的信息也可以像删聊天记录一样删掉,当然也可以选择发给文件助手或者自己。 使用微信编辑图片 我们有时候为了别人能更方便的理解自己想表达的意思,或者图片上的某些位置不想被别人看到,可以在添加图片的时候选择「预览」,就可以对图片进行编辑了。最实用的就是斗图的时候,对方一张图过来,如果手里没有合适的图片,可以直接基于对方的图片进行编辑,怼回去。 利用微信收藏快速拼图 选择「收藏」,在里面添加图片,然后选择右上角三个...的按钮,之后选择保存为图片,这样你添加的图片就拼接为了一张长图了,非常方便的操作,有的朋友喜欢分享聊天记录,就可以这么拼接。 善用「搜一搜」 微信的「搜一搜功能很强大,比如有一天你无意在朋友圈看到一条信息,当时没有发现它的价值,过了一段时间因为其他事,突然恍惚记起了以前某条朋友圈提到过相关的情况,想查一查。这时就可以通过「搜一搜」来快速查找了。除此之外,「搜一搜」还能搜索文章,方便你快速查看与某个主题相关的文章;以及表情包搜索等功能,在斗图的时候不会再为没有表情包而烦恼。 僵尸粉检测 相信很多人都遇到过别人给你发一条消息,上面注明是为了清僵尸粉的,但是这样会打扰到绝大部分微信好友,也给别人留下很不好的印象,我很反感收到别人的清粉消息。如果你怀疑某个人删除了你,你试着转账测试一下就可以了。 推荐几个小程序 微信发票助手,经常出差的朋友或者行政小姐姐,常遇到的问题是在开公司发票的时候,需要填写一些信息,一般有记不住的,可以使用这个小程序填写好,之后每次需要的时候打开就好了,或者别人需要的时候,你分享二维码给他就可以了。发票信息填完后,会在「个人信息」底部多一个「我的发票抬头」,很方便的哦。 微信指数,有时候可能会做一些调查,比如写文章的朋友,文章标题到底是用“旅游”还是“旅行”好呢?这个时候就可以用微信指数来做调查,可以查看不同词汇之间的热度。 文章截图,IOS 用户比较苦恼的应该就是截长图了吧,有人就专门做了一个小程序,只需要把文章的链接复制过去,就可以自动生成截图,还可以生成 PDF 文件哦。 蚂蚁清单,很多人都有使用待办事项管理软件,但是有时候又忘记打开,这时不妨把它放到日常打开频率最高的微信里面去,而且还能为手机节省一点空间,蚂蚁清单的体验很好,可以自己去尝试。 一条人生经验 最后说一条人生经验吧,敏感和违法不和谐的话题不要聊。
Read More ~

互相看不起|断舍离|《良医》

以前在朋友圈提到过这样一个现象,重庆人和四川人说的都是四川话,但是大部分重庆人会说他们说的是重庆话,说「川渝是一家」的通常也都是四川人。 在深圳也有一个很怪的现象,两个客家人谈话会用客家话,两个潮汕人谈话会用潮汕话,两个广东人谈话会用粤语,反正就是尽可能用更小众的语言。 想了一下,故意用第三方听不懂的语言,实际上是很欠考虑的,如果是刚见面用方言寒暄几句我觉得还行,但是后面谈话就应该使用大家都能听懂的语言了。 疫情期间大家都没法出去玩,我和老叔倒是出去爬了爬山,村里的荔枝山别人进不去,整座山就我和叔两个人,单从疫情这个角度讲,荔枝山是比大部分地方都要安全的。 我自己可以在疫情期间爬爬山,结合我自己的感受,加上前段时间的「大奔进故宫」事件。我发现人们并不是痛恨特权,而是痛恨自己没有特权。大部人痛恨的不是腐败,痛恨的是自己没有腐败的机会。 上面四川和深圳两个例子也差不多是出于这样的优越感,鉴于四川除了成都外,其它地方投资的回报率太低,穷地方的人总会羡慕富有的地方,说川渝一家的人大概率不是成都人。 春节期间看了一本《断舍离》,它讲究的是内部的自觉自省,虽然整本书挺啰嗦的,完完全全可以用一篇几千字的文章代替,但是它传达的人生整理理念很值得参考,感兴趣的读者大人可以在微信读书中阅读此书。下面是一段摘自书中的内容。 我们习惯于思考「有效性」,却往往忽略了作为「有效性」前提的「必要性」,对物品也常常陷入这样的定式思维,导致家里各种杂物堆积,这些杂物给人的压迫感,以及狭窄空间给人的阻塞感,会让人的思维变得迟钝、行动变得迟缓! 借助「断舍离」的理念,我删了 400 多个微信好友,处理了一些不会再使用的家具和书籍,才发现之前一直舍不得扔的那些东西扔了对我的积极作用更大,以前写过的一篇你如果只是一直囤干货,那永远也不可能进步,核心思想和断舍离基本一致,遗憾的是自己当时写下这篇文章后,竟然不懂得延伸到其它领域。 可能一部分人有读书摘抄语录的习惯,我个人在阅读技术书籍或是扫除我知识盲点的时候,我也会通过记笔记来加深自己的理解。想想自己强迫症式的记笔记面面俱到其实也是在浪费时间,大部分笔记自己都不会再去看第二遍的,舍弃一些不必要的形式会让自己的阅读更有收获。 还发现自己另外一个错误观点,我不管是写字还是看书都比大部分人慢,一直都认为是自己看书的方法不对,现在才发现问题的根本原因。是因为我对具体的领域不熟悉,所以看这个领域的书籍才会速度很慢,如果对这个领域熟悉了,那一目十行甚至几十行的速度应该都可以达到。结论就是书读的少了导致读书的速度慢。 推荐一部美剧——《良医》,全剧的场景基本是在医院,但有深度的内容基本都和医院无关,除了最基本的医疗科普外,更多的是对家庭、爱、职场、心理等的探讨,下面是我摘的两句台词。 Whenever people want you to do someting they think is wrong, they say it’s “reality”. 当人们想让你做他们认为错误的事时,他们总会说这就是现实。 Very few things that are worthwhile in life come without a cost. 人生中那些有意义的事大多是有代价的。
Read More ~

Vue 入门避坑——Vue + TypeScript 项目起手式

在此前我使用的前端框架是 Angular,使用过 TypeScript 后你就会讨厌 JS 了,我学习 Vue 时的最新版本是 2.5,相信大部分同学都不会认为 Vue 那样又细又长的代码很美观吧,简单看了一些网络博客后,我毅然决然引入了 TypeScript 进行开发,本文仅整理记录我自己遇到的一些坑。 使用 Cli 脚手架是一个比较方便的工具,这里需要注意的是@vue/cli和vue-cli是不一样的,推荐使用npm i -g @vue/cli安装。 安装完成后,可以直接使用vue create your-app创建项目,你可以选择使用默认配置亦或是自己手动选择配置,按提示一步一步向下走即可,它会根据你的选择自己创建比如tsconfig.json等等配置文件。这里推荐使用less开发样式,sass老是在安装的过程中出问题。 当然你也可以使vue ui命令启动一个本地服务,它是一个 Vue 项目管理器,提供了一个可视化的页面供你管理自己的项目,它的样子如下图所示,还是比较清新的。 使用 vue-property-decorator Vue 官方维护了 vue-class-component 装饰器,vue-property-decorator 则是在vue-class-component基础上增强了更多结合Vue特性的装饰器,它可以让 Vue 组件语法在结合了 TypeScript 语法后变得更加扁平化。 截止本文时间,vue-property-decorator共提供了 11 个装饰器和 1 个Mixins方法,下面用@Prop举个例子,是不是看起来引起极度舒适。 import { Vue, Component, Prop } from 'vue-property-decorator' @Component export default class YourComponent extends Vue { @Prop(Number) readonly propA: number | undefined @Prop({ default: 'default value' }) readonly propB!: string @Prop([String, Boolean]) readonly propC: string | boolean | undefined } // 上面的内容将会被解析成如下格式 export default { props: { propA: { type: Number }, propB: { default: 'default value' }, propC: { type: [String, Boolean] } } } 使用 Vuex 关于怎么使用Vuex此处就不再做过多说明了,需要注意的一点是,如果你需要访问$store属性的话,那么你必须得继承Vue类,坑的地方是在某些情况下即使你没有继承Vue,它也能通过编译,只有在程序运行起来的时候才报错。 class ExampleApi extends Vue { public async getExampleData() { if (!this.$store.state.exampleData) { const res = await http.get('url/exampleData'); if (res.result) { this.$store.commit('setExampleData', res.data); return res.data; } else { promptUtil.showMessage('get exampleData failed', 'warning'); } } else { return this.$store.state.exampleData; } } } 使用自己的配置(含代理) vue.config.js是一个可选的配置文件,如果项目的根目录中存在这个文件,那么它会被@vue/cli-service自动加载,它的配置项说明可以查看配置参考。 我们再开发过程中都会使用代理来转发请求,代理的配置也是在这个文件中,它的官方说明在devserver-proxy中,下面是一个简单的vue.config.js文件例子。 module.exports = { filenameHashing: true, outputDir: 'dist', assetsDir: 'asserts', indexPath: 'index.html', productionSourceMap: false, transpileDependencies: [ 'vue-echarts', 'resize-detector' ], devServer: { hotOnly: true, https: false, proxy: { "/statistics": { target: "http://10.7.213.186:3889", secure: false, pathRewrite: { "^/statistics": "", }, changeOrigin: true }, "/mail": { target: "http://10.7.213.186:8888", secure: false, changeOrigin: true } } } } 让 Vue 识别全局方法和变量 我们在项目中都会使用一些第三方 UI 组件,比如我自己就使用了 Element,但是在使用它的$message、$notify等方法时就直接报错了,究其原因就是$message等属性并没有在 Vue 实例中声明。 官方对此给出了很明确的解决方案,使用的是 TypeScript 的 模块补充特性,可以查看增强类型以配合插件使用。既然知道是因为没有声明导致的错误,那我们就给它声明一下好了,在src/shims-vue.d.ts文件中添加如下代码即可,如果没有该文件请自行创建。 看到网上也有一部分人说的是src/vue-shim.d.ts,反正不管是怎么命名这个文件的,它们的作用是一样的。 declare module 'vue/types/vue' { interface Vue { $message: any, $confirm: any, $prompt: any, $notify: any } } 这里顺道提一下,src/shims-vue.d.ts文件中的如下代码是为了让你的 IDE 明白以.vue结尾的文件是什么玩意儿。 declare module '*.vue' { import Vue from 'vue'; export default Vue; } 路由懒加载 Vue Router 官方有关于路由懒加载的说明,但不知道为什么官方给的这个说明在我的项目里面都没有生效,但使用require.ensure()按需加载组件可以生效。 // base-view 是模块名,写了相同的模块名则代码会被组织到同一个文件中 const Home = (r: any) => require.ensure([], () => r(require('@/views/home.vue')), layzImportError, 'base-view'); // 路由加载错误时的提示函数 function layzImportError() { alert('路由懒加载错误'); } 上面的方式会在编译的时候把文件自动分成多个小文件,编译后的文件会以你自己命名的模块名来命名,如果代码之间有相互依赖,依赖部分代码编译后的文件会以两个模块名相连后进行命名。 但是需要注意的是,这样拆分小文件之后引入了另外一个新的问题,因为客户端会缓存这些编译后的 js 文件,如果功能 A 同时依赖了a.js和b.js两个文件,但用户在使用其它功能时已经把a.js缓存到本地了,使用功能 A 时需要请求b.js文件,这时程序就很容易报错,因为此时在客户端这两个文件不是同一个版本,所以可能导致a.js调用b.js中的方法已经被删了,进而导致客户端页面异常。 关于引入第三方包 项目在引入第三方包的时候经常会报出各种奇奇怪怪的错误,这里仅提供我目前找到的一些解决办法。 /* 引入 jquery 等库可以尝试下面这种方式 只需要把相应的 js 文件放到指定文件夹即可 **/ const $ = require('@/common/js/jquery.min.js'); const md5 = require('@/common/js/md5.js'); 引入一些第三方样式文件、UI 组件等,如果引入不成功可以尝试建一个 js 文件,将导入语句都写在 js 文件中,然后再在main.ts文件中导入这个 js 文件,这个方法能解决大部分的问题。例如我先建了一个lib.js,然后在main.ts中引入lib.js就没有报错。 // src/plugins/lib.js import Vue from 'vue'; // 树形组件 import 'vue-tree-halower/dist/halower-tree.min.css'; import {VTree} from 'vue-tree-halower'; // 饿了么组件 import Element from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; // font-awesome 图标 import '../../node_modules/font-awesome/css/font-awesome.css'; import VueCookies from 'vue-cookies'; import VueJWT from 'vuejs-jwt'; Vue.use(VueJWT); Vue.use(VueCookies); Vue.use(VTree); Vue.use(Element); // src/main.ts import App from '@/app.vue'; import Vue from 'vue'; import router from './router'; import store from './store'; import './registerServiceWorker'; import './plugins/lib'; Vue.config.productionTip = false; new Vue({ router, store, render: (h) => h(App), }).$mount('#app'); 因为第三方包写的各有特点,在引入不成功的时候基本也只能是见招拆招,当然如果你的功底比较深厚,你也可以自己写一个index.d.ts文件,实在不行的话,那个特殊的组件不使用 TypeScript 来写也能解决,我目前还没有找一个可以完全解决第三方包引入错误的方法,如果您已经有相关的方法了,希望能与你一起探讨交流。
Read More ~

2019 年个人总结

写总结的习惯是从 2015 年开始的,我的大学学费是县政协资助的,叔叔阿姨们唯一的要求就是每年给他们写个总结汇报一下学习情况,毕业后敦促我写总结的人则从外力转为内心。 一点感动 上半年我还很年轻,那时候还会经常使用 QQ、Soul、同桌、一罐 等等社交产品,无意结识了一个还在读高中的同性恋女孩子,我没学过心理学不知道用什么专业名词描述她的情况,反正就是心理上有严重的问题,玻璃心、想自杀等等。几次拼了我老命陪她聊到半夜两三点,现在完完全全能正视自己的情况了。 让我感动的是有次她问我在干啥,我随便拍了一张自己的被汗水打湿衣服发给她,告诉她自己正在打羽毛球。小姑娘给我说我穿的衣服不好看,说我才多大穿的衣服太老了,我也就随口一说叫她给我这个老年人推荐推荐衣服,因为她要上课后面就一直没有回我消息。 第二天早上睡醒了看到了小姑娘的几十条消息,是半夜两点多发的,给我挑了好几件衣服裤子,还给我画了几张示意图(如下),瞬间收获一份小感动。我也遵从小姑娘的意见买了两件上班穿穿,结果一到部门就是众目睽睽之下给我说穿的好酷,穿几次了都还是会引来大家不一样的目光,个性低调的我还是选择走大众程序员路线,老就老吧。 前几天小姑娘给我发了她暗恋的小姐姐的照片,虽然极少时候还是会上课偷偷玩手机,但也在努力的备战高考。我做的不好的就是她多次给我讲自己在龙岗,我每次都把她当成龙华的,希望写了这篇总结之后不再记错吧。 赚钱理财 这个小标题起的有点大,仅说说我自己的实际情况吧。凭着运气,2019 年的银行理财收益在 4.5% 左右,基金收益在 7% 左右。我没有去玩股票,网上各种理财课程可能都会给你讲股票的收益多么多么高,但是他们从来不会给你说玩股票的风险有多高,更不可能给你讲玩股票会严重影响自己的心情,可能连自己的本职工作都会搞砸,所以我不建议职场新人进入股市。 房东忙的时候我会帮他带房客看房,他也给了我小几千块钱的介绍费,加上每个月没交网费直接用他的,还时不时蹭蹭房东的饭局,也给自己省下来周末出去散步的费用了。上半年也给别人分享过两三个课程,在群里分享过一点小技能,大家给发了点红包,交个朋友、图个开心。 总的来讲,理财这方面做得很差,没有花什么时间去学习,我们的大学也没有教给学生一点金融知识,这一年只读了几本写给小白的理财书,今年在这个领域要多花一点功夫,希望能入得了门吧。 写书失败 快要毕业的时候和电子工业出版社签了一份合同,合同内容就是我要写一本叫做《知识图谱:自顶向下方法》,这本书的计划内容是我的毕业设计,已经写了一百多页的内容了,但现在确定以失败告终。 一者我手里现有的数据属于机密数据,没办法拿来直接使用;二来书中有很大一部分内容涉及到网络爬虫,上半年网上曝了很多因为抓数据而入狱的案例,出版社和我都害怕;三者知识图谱所需要的数据量很大,而且我写的那个领域又是中国特有的经济责任审计领域,大量数据都得从政府网站来,更害怕了;最重要的原因是自己懒,写书的那几个月确实非常的累,想想自己都还是个菜鸟呐,有啥资本教给别人知识,心里给了自己后退的理由。 小时候曾夸下海口说要给父亲写个传记,也不知道有没有那么一丢丢可能性实现,写家里的狗时,发现写这样的内容会增加我的多巴胺分泌,以后不开心了就写这样的小故事。 运动健身 在深圳校友会骑行社师兄师姐们的带领下,同时也得益于一起入职的小伙伴送了我一辆 MERIDA,我喜欢上了骑行这项运动,基本上每周五都会出去骑几十公里,中间还参加了环漓江骑行和 TREK100 骑行,锻炼的同时也见到了美丽的风景。深圳对自行车是不太友好的,基本没有什么自行车道,所以我们大部分时间都是等到晚上车少,交警下班了之后才开始骑行。 除了骑行每周一也会打两小时羽毛球,谈不上专业,但至少打的不再是广场球了。偶尔也会出去爬爬山啥的,身体确实比上学时候要好很多,而且多锻炼能让自己的精神面貌更好,精气神好也能稍稍掩盖长得丑的缺点。以前每年再怎么也会因为感冒一类的问题进几次医院,19 年仅一次因为智齿发炎去过医院。 削减迷茫 大概在四五月份的时候吧,几乎天天失眠,经常夜里只睡了三四个小时,有时甚至通宵未眠,心里很清楚是因为迷茫了,大概就是「晚上想了千条路,早上醒来走原路」的状态。好在自己的调节能力还不算差,同时也有楼下的叔叔、自己的好朋友能唠唠嗑,差不多两个月就回归正常状态了。 从几个比我晚入职半年的小伙伴那里了解到,他们现在的情况和我四五月份的情况差不多,我想绝大部分普通人都会经历这个迷茫期吧,大部分人也都能通过时间调节过来,调节不过来的那部分人就成为了媒体比较喜欢的人。 现在迷茫的雾气已经没有那么浓了,初入社会肯定有很多的不成熟,但谁不是这样过来的呢?更何况我并不像多数程序员那样交友严重同质化,周末也不会死宅在家里不出去,猜测我应该比大多数人更潇洒自在的,嘿嘿。 新的思想 大家基本都是看着金庸武侠小说(相关影视作品)长大的,没有人写武侠小说能超过金庸。偶然一天在推特上刷到一条评论,大意是:没有人写武侠小说能超过金庸不正代表着社会的进步吗?金庸的成就如此巨大,一个很重要的历史背景是那时候大家没有那么多小说可看呀,哪里像今天遍地的网络小说。咱们没必要去争论这个观点的对错,重要的是它告诉了我们一个不一样的角度去看待问题。 上面只是一个特例,思维方式是一点一点改变的,认知水平是一点一点提升的,一年时间修正了不少我此前狭隘的观点,这样的修正还在继续,我也会让这样的修正持续下去。 写在最后 巴黎圣母院被烧、凉山火灾、女排十连冠、NBA 事件、无锡高架桥倒塌......等等发生在 2019 年的大事,不知道还有多少朋友会记起来。时间从来不会等谁,网友也都是不长记性的,成熟的一部分无非是经历的多了,失望的多了,然后变得更耐操一点,总之生活依旧得继续,人总会亦悲亦喜,那为啥不把悲缩小喜放大呢? 成功没有银弹、没有捷径,少讲大道理,多解决小问题。
Read More ~

MongoDB 聚合(aggregate)入门

MongoDB 聚合官方文档 聚合管道是一个基于数据处理管道概念建模的数据聚合框架,文档进入一个多阶段的处理管道,该管道最终将其转换为聚合后的结果。 下面的例子来源于官方文档。第一阶段,$match按status字段来过滤文档,并把status字段值为A的文档传递到下一阶段;第二阶段,$group将文档按cust_id进行分组,并针对每一组数据对amount进行求和。 db.orders.aggregate([ { $match: { status: "A" } }, { $group: { _id: "$cust_id", total: { $sum: "$amount" } } } ]) 管道 聚合管道包含很多步骤,每一步都会将输入的文档进行转换,但并不是每个阶段都一定需要对每个输入文档生成一个输出文档,比如某些阶段可能生成新的文档或者过滤掉文档。 除了$out、$merge、$geoNear外,其它的阶段都可以在管道中多次出现,更加详细的内容可以查看 Aggregation Pipeline Stages。 管道表达式 一些管道阶段采用表达式作为操作元,管道表达式指定了要应用到输入文档的转换,表达式自己是一个文档结构(JSON),表达式也可以包含其它的表达式。 表达式仅提供文档在内存中的转换,即管道表达式只能对管道中的当前文档进行操作,不能引用来自其他文档的数据。 写聚合表达式式建议直接参考官方文档,下面列出一些我收集的案例,供深入理解使用。 案例一:将对象数组转换为单个文档 // 转换前 { "_id": "10217941", "data": [ { "count": 2, "score": "0.5" }, { "count": 6, "score": "0.3" }, { "count": 5, "score": "0.8" } ] } // 转换后 { "_id": "10217941", "0.3": 6, "0.5": 2, "0.8": 5 } 需要说明的是,如果上面data属性中的数据格式为{"k": "0.6", "v": 5},那么下面的聚合表达式就不需要$map,这一点可以查看 $arrayToObject。这个案例的难点在于score中有小数点,这个小数点会让聚合表达式懵逼的。 db.collection.aggregate([ { "$addFields": { "data": { "$arrayToObject": { "$map": { "input": "$data", "as": "item", "in": { "k": "$$item.score", "v": "$$item.count" } } } } } }, { "$addFields": { "data._id": "$_id" } }, { "$replaceRoot": { "newRoot": "$data" } } ]);
Read More ~

正则表达式是如何运行的?——浅析正则表达式原理

参考内容: 《编译原理》 实现简单的正则表达式引擎 正则表达式回溯原理 浅谈正则表达式原理 最近在一个业务问题中遇到了一个正则表达式性能问题,于是查了点资料去回顾了下正则表达式的原理,简单整理了一下就发到这里吧;另外也是想试试 Apple Pencil 的手感如何,画的太丑不要嫌弃哈。 有穷自动机 正则表达式的规则不是很多,这些规则也很容易就能理解,但是正则表达式并不能用来直接识别字符串,我们还需要引入一种适合转换为计算机程序的模型,我们引入的就是有穷自动机。 在编译原理中通过构造有穷自动机把正则表达式编译成识别器,识别器以字符串x作为输入,当x是语言的句子时回答是,否则回答不是,这正是我们使用正则表达式时需要达到的效果。 有穷自动机分为确定性有穷自动机(DFA)和非确定性有穷自动机(NFA),它们都能且仅能识别正则表达式所表示的语言。它们有着各自的优缺点,DFA 导出的识别器时间复杂度是多项式的,它比 NFA 导出的识别器要快的多,但是 DFA 导出的识别器要比与之对应的 NFA 导出的识别器大的多。 大部分正则表达式引擎都是使用 NFA 实现的,也有少部分使用 DFA 实现。从我们写正则表达式的角度来讲,DFA 实现的引擎要比 NFA 实现的引擎快的多,但是 DFA 支持的功能没有 NFA 那么强大,比如没有捕获组一类的特性等等。 我们可以用带标记的有向图来表示有穷自动机,称之为转换图,其节点是状态,有标记的边表示转换函数。同一个字符可以标记始于同一个状态的两个或多个转换,边可以由输入字符符号标记,其中 NFA 的边还可以用ε标记。 之所以一个叫有确定和非确定之分,是因为对于同一个状态与同一个输入符号,NFA 可以到达不同的状态。下面看两张图就能明白上面那一长串的文字了。 图中两个圈圈的状态表示接受状态,也就是说到达这个状态就表示匹配成功。细心的你应该发现了两张图所表示的正则表达式是一样的,这就是有穷自动机神奇的地方,每一个 NFA 我们都能通过算法将其转换为 DFA,所以我们先根据正则表达式构建 NFA,然后再转换成相应的 DFA,最后再进行识别。 上图的画法在正则表达式很简单的时候还可以,如果遇到很复杂的正则表达式画起来还是挺费力的,如果想对自动机有更加深入的认识可以自行查阅相关资料。下面的图片是使用正则可视化工具生成的,对应的正则表达式是^-?\d+(,\d{3})*(\.\d{1,2})?$,它所匹配的字符串是数字/货币金额(支持负数、千分位分隔符)。 回溯 NFA 引擎在遇到多个合法的状态时,它会选择其中一个并记住它,当匹配失败时引擎就会回溯到之前记录的位置继续尝试匹配。这种回溯机制正是造成正则表达式性能问题的主要原因。下面我们通过具体的例子来看看什么是回溯。 /ab{1,3}c/ 正则 文本 ab{1,3}c abbbc ab{1,3}c abbbc ab{1,3}c abbbc ab{1,3}c abbbc ab{1,3}c abbbc ab{1,3}c abbbc 上表中展示的是使用ab{1,3}c匹配abbbc的过程,如果把匹配字符串换成abbc,在第五步就会出现匹配失败的情况,第六步会回到上一次匹配正确的位置,进而继续匹配。这里的第六步就是「回溯」 正则 文本 备注 ab{1,3}c abbc ab{1,3}c abbc ab{1,3}c abbc ab{1,3}c abbc ab{1,3}c abbc 匹配失败 ab{1,3}c abbc 回溯 ab{1,3}c abbc 会出现上面这种情况的原因在于正则匹配采用了回溯法。回溯法采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。它通常采用最简单的递归来实现,在反复重复上述的步骤后可能找到一个正确的答案,也可能尝试所有的步骤后发现该问题没有答案,回溯法在最坏的情况下会导致一次复杂度为指数时间的计算。 上面一段的内容来源于维基百科,精简一下就是深度优先搜索算法。贪婪量词、惰性量词、分支结构等等都是可能产生回溯的地方,在写正则表达式时要注意会引起回溯的地方,避免导致性能问题。 John Graham-Cumming 在他的博文 Details of the Cloudflare outage on July 2, 2019 中详细记录了因为一个正则表达式而导致线上事故的例子。该事故就是因为一个有性能问题的正则表达式,引起了灾难性的回溯,进而导致了 CPU 满载。 (?:(?:\"|'|\]|\}|\\|\d|(?:nan|infinity|true|false|null|undefined|symbol|math)|\`|\-|\+)+[)]*;?((?:\s|-|~|!|{}|\|\||\+)*.*(?:.*=.*))) 上面是引起事故的正则表达式,出问题的关键部分在.*(?:.*=.*)中,就是它引起的灾难性回溯导致 CPU 满载。那么我们应该怎么减少或避免回溯呢?无非是提高警惕性,好好写正则表达式;或者使用 DFA 引擎的正则表达式。 [0-9] 与 \d 的区别 此问题来源于Stackoverflow,题主遇到的问题是\d比[0-9]的效率要低很多,并且给出了如下的测试结果,可以看到\d比[0-9]慢了差不多一倍。 Regular expression \d took 00:00:00.2141226 result: 5077/10000 Regular expression [0-9] took 00:00:00.1357972 result: 5077/10000 63.42 % of first Regular expression [0123456789] took 00:00:00.1388997 result: 5077/10000 64.87 % of first 出现这个性能问题的原因在于\d匹配的不仅仅是0123456789,\d匹配的是所有的 Unicode 的数字,你可以从 Unicode Characters in the 'Number, Decimal Digit' Category 中看到所有在 Unicode 中属于数字的字符。 此处多提一嘴,[ -~]可以匹配 ASCII 码中所有的可打印字符,你可以查看 ASCII 码中的可显示字符,就是从" "(32)至"~"(126)的字符。 工具/资源推荐 正则表达式确实很强大,但是它那晦涩的语法也容易让人头疼抓狂,不论是自己还是别人写的正则表达式都挺头大,好的是已经有人整理了常用正则大全,也大神写了个叫做 VerbalExpressions 的小工具,主流开发语言的版本它都提供了,可以让你用类似于自然语言的方式来写正则表达式,下面是它给出的一个 JS 版示例。 // Create an example of how to test for correctly formed URLs const tester = VerEx() .startOfLine() .then('http') .maybe('s') .then('://') .maybe('www.') .anythingBut(' ') .endOfLine(); // Create an example URL const testMe = 'https://www.google.com'; // Use RegExp object's native test() function if (tester.test(testMe)) { alert('We have a correct URL'); // This output will fire } else { alert('The URL is incorrect'); } console.log(tester); // Outputs the actual expression used: /^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$/ 文章大部分内容都是介绍的偏原理方面的知识,如果仅仅是想要学习如何使用正则表达式,可以看正则表达式语法或者 Learn-regex,更为详细的内容推荐看由老姚写的JavaScript 正则表达式迷你书
Read More ~

Vim 常用命令快捷查询

参考内容 Learning Vim The Pragmatic Way 《鸟哥的 Linux 私房菜》 Vim 可以认为是 Vi 的高级版本,Vim 可以用颜色或下划线的方式来显示一些特殊信息,您可以认为 Vi 是一个文本处理工具,而 Vim 是一个程序开发工具,现在大部分 Linux 的发行版都以 Vim 替换 Vi 了。在 Linux 命令行模式下有很多编辑器,但是 Vi 文本编辑器是所有 Unix-like 系统都会内置的,因此学会 Vi/Vim 的使用时非常有必要的,对于 Vi 的三种模式(命令模式、编辑模式、命令行模式)这里就不在做说明了,下面是一些比较常用的命令。 一般命令模式下 命令 说明 h、j、k、l 与键盘的方向键一一对应,分别为左、下、上、右,在键盘上着几个字母是排在一起的 Ctrl+f、Ctrl+b 分别对应键盘的「Page Down」、「Page Up」,我更习惯于这两个键,而不是前面的组合键 0、$ 分别对应键盘的「Home」、「End」,即移动到该行的最前面/后面字符处 n<Enter> n 为数字,光标向下移动 n 行 /word、?word 向光标之上/下寻找一个字符串名称为 word 的字符串 n、N 如果我们刚刚执行了上面上面的 /word 或 ?word 查找操作,那么 n 则表示重复前一个查找操作,可以简单理解为向下继续查找下一个名称为 word 的字符串,N 则与 n 刚好相反 :n1,n2s/word1/word2/g 在第 n1 行与 n2 行之间寻找 word1 这个字符串,并将这个字符串替换为 word2,如果前面的 n1,n2 使用 1,$ 代替则表示从第一行到最后一行,最后的 g 后面可以加个 c,即 :1,$s/word1/word2/gc,这样就会在替换钱显示提示字符给用户确认(confirm) x、X 分别对应键盘的「Del」、「Backspace」键 dd、yy 删除/复制光标所在的那一整行 p、P p 将已复制的数据在光标下一行粘贴,P 粘贴在光标上一行 u 恢复前一个操作,类似于 Windows 下的 Ctrl+Z Ctrl+r 重做上一个操作 . 小数点,重复上一个操作 命令行模式下 命令 说明 :w 将编辑的数据写入硬盘中 :w! 若文件属性为只读,强制写入该文件,不过到底能不能写入,还是跟文件权限有关系 :q、:q! 与 w 一样,q 为关闭的意思 :r [filename] 在编辑的数据中读入另一个文件的数据,即将[filename]这个文件的内容追加到光标所在行的后面 :w [filename] 将编辑的数据保存为另一个文件 :set nu/nonu 显示/不显示行号 编辑模式下 组合键 作用 [ctrl]+x -> [ctrl]+n 通过目前正在编辑的这个文件的内容文字作为关键字,予以自动补全 [ctrl]+x -> [ctrl]+f 以当前目录内的文件名作为关键字补全 [ctrl]+x -> [ctrl]+o 以扩展名作为语法补充,以 Vim 内置的关键字予以补全 当我们在使用 Vim 编辑器的时候,Vim 会在与被编辑的文件目录下再建立一个名为.filename.swp的文件,我们对文件的操作都会记录到这个 swp 文件中去,如果系统因为某些原因掉线了,就可以利用这个 swp 文件来恢复内容。如果存在对应的 swp 文件,那么 Vim 就会主动判断当前这个文件可能有问题,会给出相应的提示。 我们也可以给 Vim 环境设置一些个性化的参数,虽然在命令行模式下可以使用:set来设置,但是这样每次设置实在是太麻烦,因此我们可以设置一些全局的参数。Vim 的整体设置值一般放在/etc/vimrc中,我们一般通过修改~/.vimrc这个文件(默认不存在)来设置一些自己的参数,比如: " 该文件的双引号是注释 set nu "在每一行的最前面显示行号 set autoindent " 自动缩进 set ruler " 可显示最后一行的状态 set bg=dark " 显示不同的底色色调 syntax on "进行语法检验,颜色显示,比如 C 语言等 最后附上一张命令速查卡,此图来源于Learning Vim The Pragmatic Way,PDF 版下载链接在这里。
Read More ~

如何加快 Nginx 的文件传输?——Linux 中的零拷贝技术

参考内容: Two new system calls: splice() and sync_file_range() Linux 中的零拷贝技术1 Linux 中的零拷贝技术2 Zero Copy I: User-Mode Perspective Linux man-pages splice() Nginx AIO 机制与 sendfile 机制 sendfile 适用场景 扯淡 Nginx 的 sendfile 零拷贝的概念 浅析 Linux 中的零拷贝技术 Linux man-pages sendfile 今天在看 Nginx 配置的时候,看到了一个sendfile配置项,它可以配置在http、server、location三个块中,出于好奇就去查了一下sendfile的作用。 文件下载是服务器的基本功能,其基本流程就是循环的从磁盘读取文件内容到缓冲区,再将缓冲区内容发送到socket文件,程序员基本都会写出类似下面看起来比较高效的程序。 while((n = read(diskfd, buf, BUF_SIZE)) > 0) write(sockfd, buf , n); 上面程序中我们使用了read和write两个系统调用,看起来也已经没有什么优化空间了。这里的read和write屏蔽了系统内部的操作,我们并不知道操作系统做了什么,现实情况却是由于 Linux 的 I/O 操作默认是缓冲 I/O,上面的程序发生了多次不必要的数据拷贝与上下文切换。 上述两行代码执行流程大致可以描述如下: 系统调用read产生一个上下文切换,从用户态切换到内核态; DMA 执行拷贝(现在都是 DMA 了吧!),把文件数据拷贝到内核缓冲区; 文件数据从内核缓冲区拷贝到用户缓冲区; read调用返回,从内核态切换为用户态; 系统调用write产生一个上下文切换,从用户态切换到内核态; 把步骤 3 读到的数据从用户缓冲区拷贝到 Socket 缓冲区; 系统调用write返回,从内核态切换到用户态; DMA 从 Socket 缓冲区把数据拷贝到协议栈。 可以看到两行程序共发生了 4 次拷贝和 4 次上下文切换,其中 DMA 进行的数据拷贝不需要 CPU 访问数据,所以整个过程需要 CPU 访问两次数据。很明显中间有些拷贝和上下文切换是不需要的,sendfile就是来解决这个问题的,它是从 2.1 版本内核开始引入的,这里放个 2.6 版本的源码。 系统调用sendfile是将in_fd的内容发送到out_fd,描述符out_fd在 Linux 2.6.33 之前,必须指向套接字文件,自 2.6.33 开始,out_fd可以是任何文件;in_fd只能是支持mmap的文件(mmap是一种内存映射方法,在被调用进程的虚拟地址空间中创建一个新的指定文件的映射)。 所以当 Nginx 是一个静态服务器时,开启sendfile配置项是可以大大提高 Nginx 性能的,但是当把 Nginx 作为一个反向代理服务器时,sendfile则没有什么用,因为当 Nginx 时反向代理服务器时,in_fd就是一个套接字,这不符合sendfile的参数要求。 可以看到现在我们只需要一次拷贝就可以完成功能了,但是能否把这一次拷贝也省略掉呢?我们可以借助硬件来实现,仅仅需要把缓冲区描述符和文件长度传过去,这样 DMA 直接将缓冲区的数据打包发送到网络中就可以了。 这样就实现了零拷贝技术,需要注意的是这里所说的零拷贝是相对操作系统而言的,即在内核空间不存在冗余数据。数据的实际走向是从硬盘到内存,再从内存到设备。 Nginx 中还有一个aio配置,它的作用是启用内核级别的异步 I/O 功能,要使aio生效需要将directio开启(directio对大文件的读取速度有优化作用),aio很适合大文件的传送。需要注意的是sendfile和aio是互斥的,不可同时兼得二者,因此我们可以设置一个文件大小限制,超过该阀值使用aio,低于该阀值使用sendfile。 location /video/ { sendfile on; sendfile_max_chunk 256k; aio threads; directio 512k; output_buffers 1 128k; } 上面已经提到了零拷贝技术,它可以有效的改善数据传输的性能,但是由于存储体系结构非常复杂,而且网络协议栈有时需要对数据进行必要的处理,所以零拷贝技术有可能会产生很多负面影响,甚至会导致零拷贝技术自身的优点完全丧失。 零拷贝就是一种避免 CPU 将一块存储拷贝到另一块存储的技术。它可以减少数据拷贝和共享总线操作的次数,消除传输数据在存储器之间不必要的中间拷贝次数,从而有效的提高数据传输效率,而且零拷贝技术也减少了内核态与用户态之间切换所带来的开销。进行大量的数据拷贝操作是一件简单的任务,从操作系统的角度来看,如果 CPU 一直被占用着去执行这项简单的任务,是极其浪费资源的。如果是高速网络环境下,很可能就出现这样的场景。 零拷贝技术分类 现在的零拷贝技术种类很多,也并没有一个适合于所有场景的零拷贝零拷贝技术,概括起来总共有下面几种: 直接 I/O:对于这种数据传输方式来说,应用程序可以直接访问硬件存储,操作系统只是辅助数据传输,这类零拷贝技术可以让数据在应用程序空间和磁盘之间直接传输,不需要操作系统提供的页缓存支持。关于直接 I/O 可以参看Linux 中直接 I/O 机制的介绍。 避免数据在内核态与用户态之间传输:在一些场景中,应用程序在数据进行传输的过程中不需要对数据进行访问,那么将数据从页缓存拷贝到用户进程的缓冲区是完全没有必要的,Linux 中提供的类似系统调用主要有mmap()、sendfile()和splice()。 对数据在页缓存和用户进程之间的传输进行优化:这类零拷贝技术侧重于灵活地处理数据在用户进程的缓冲区和操作系统页缓存之间的拷贝操作,此类方法延续了传统的通信方式,但是更加灵活。在 Linux 中主要利用了「写时复制」技术。 前两类方法的目的主要是为了避免在用户态和内核态的缓冲区间拷贝数据,第三类方法则是对数据传输本身进行优化。我们知道硬件和软件之间可以通过 DMA 来解放 CPU,但是在用户空间和内核空间并没有这种工具,所以此类方法主要是改善数据在用户地址空间和操作系统内核地址空间之间传递的效率。 避免在内核与用户空间拷贝 Linux 主要提供了mmap()、sendfile()、splice()三个系统调用来避免数据在内核空间与用户空间进行不必要的拷贝,在Nginx 文件操作优化对sendfile()已经做了比较详细的介绍了,这里就不再赘述了,下面主要介绍mmap()和splice()。 mmap() 当调用mmap()之后,数据会先通过 DMA 拷贝到操作系统的缓冲区,然后应用程序和操作系统共享这个缓冲区,这样用户空间与内核空间就不需要任何数据拷贝了,当大量数据需要传输的时候,这样做就会有一个比较好的效率。 但是这种改进是需要代价的,当对文件进行了内存映射,然后调用write()系统调用,如果此时其它进程截断了这个文件,那么write()系统调用将会被总线错误信号SIGBUG中断,因为此时正在存储的是一个错误的存储访问,这个信号将会导致进程被杀死。 一般可以通过文件租借锁来解决这个问题,我们可以通过内核给文件加读或者写的租借锁,当另外一个进程尝试对用户正在进行传输的文件进行截断时,内核会给用户发一个实时RT_SIGNAL_LEASE信号,这个信号会告诉用户内核破坏了用户加在那个文件上的写或者读租借锁,write()系统调用就会被中断,并且进程会被SIGBUS信号杀死。需要注意的是文件租借锁需要在对文件进行内存映射之前设置。 splice() 和sendfile()类似,splice()也需要两个已经打开的文件描述符,并且其中的一个描述符必须是表示管道设备的描述符,它可以在操作系统地址空间中整块地移动数据,从而减少大多数数据拷贝操作。适用于可以确定数据传输路径的用户应用程序,不需要利用用户地址空间的缓冲区进行显示的数据传输操作。 splice()不局限于sendfile()的功能,也就是说sendfile()是splice()的一个子集,在 Linux 2.6.23 中,sendfile()这种机制的实现已经没有了,但是这个 API 以及相应的功能还存在,只不过内部已经使用了splice()这种机制来实现了。 写时复制 在某些情况下,Linux 操作系统内核中的页缓存可能会被多个应用程序所共享,操作系统有可能会将用户应用程序地址空间缓冲区中的页面映射到操作系统内核地址空间中去。如果某个应用程序想要对这共享的数据调用write()系统调用,那么它就可能破坏内核缓冲区中的共享数据,传统的write()系统调用并没有提供任何显示的加锁操作,Linux 中引入了写时复制这样一种技术用来保护数据。 写时复制的基本思想是如果有多个应用程序需要同时访问同一块数据,那么可以为这些应用程序分配指向这块数据的指针,在每一个应用程序看来,它们都拥有这块数据的一份数据拷贝,当其中一个应用程序需要对自己的这份数据拷贝进行修改的时候,就需要将数据真正地拷贝到该应用程序的地址空间中去,也就是说,该应用程序拥有了一份真正的私有数据拷贝,这样做是为了避免该应用程序对这块数据做的更改被其他应用程序看到。这个过程对于应用程序来说是透明的,如果应用程序永远不会对所访问的这块数据进行任何更改,那么就永远不需要将数据拷贝到应用程序自己的地址空间中去。这也是写时复制的最主要的优点。 写时复制的实现需要 MMU 的支持,MMU 需要知晓进程地址空间中哪些特殊的页面是只读的,当需要往这些页面中写数据的时候,MMU 就会发出一个异常给操作系统内核,操作系统内核就会分配新的物理存储空间,即将被写入数据的页面需要与新的物理存储位置相对应。它最大好处就是可以节约内存,不过对于操作系统内核来说,写时复制增加了其处理过程的复杂性。
Read More ~

家里的狗

为了防止晚上有人来家里偷东西,几乎家家户户都至少会养一只狗。在我的记忆中,我家一开始是没有狗的。 忘记是哪一年夏天的一个清晨,天还没有大亮,我隐约看见在牛棚后面的空地有个黑影,走近一点仔细一看,原来是一只不知道从哪里来的一只黑狗。 它惊恐的看着我,眼神中夹杂着恐惧与无助,佝偻的身子比弓还要弯,倒是很像一个活着的牛轭。他的身子还没有草高,露水把全身的毛都打湿了,还沾着一些不知名的植物种子。我和它对视着,恐惧慢慢填满了它的眼球,我害怕吓到它,赶紧走开去告诉妈。 妈远远看了一眼,让我别管它。随后妈把装着昨晚剩饭的猪食瓢放到牛棚后面的一块石头上,黑狗看见妈带着武器走近早就跑了,我吃早饭时还不时去望望它在不在,有没有吃妈给放在那里的饭。 妈已经把猪喂完准备下地干活了,仍旧没有再次发现黑狗的踪影,也没见猪食瓢有什么变化,我心里有一点点的失落,黑狗应该是已经逃走了吧。 晚上吃完饭妈去拿猪食瓢,告诉我里面的饭已经被吃的一粒不剩,我心里开始期待和它的再次见面。第二天早晨果然见到它了,身上已经没有昨天那么湿了,显然没有前一天来这里时钻的草丛多,妈依旧用猪食瓢装着米饭和米汤放在牛棚后的那个石头上。 就这样过了几日,黑狗走进了我家的屋檐,它的样子实在太丑了。每一根肋骨都清晰的扎眼,看起来爸的手指都比它的小腿粗,感觉下一秒它就会死去。 我并不喜欢它,甚至还有些讨厌它,我实在找不到更丑的词来形容它,不过是出于心里的怜悯与对生命的敬畏,会在吃饭的时候给它丢几个我不吃的肥肉,被烟熏黑的那一层肉边我也丢给它...... 有一次同村的一个人路过家门口时,看见那只黑狗吓的赶紧往妈身后躲。“有我在,它不敢咬。”,妈说。邻居夸夸妈说:“这个狗儿喂得好肥”。妈自豪的告诉那个人这只狗每天还送林儿(我)上学。 是的,我也不知道什么时候我已经和大黑狗变得如此亲密了,它每天早上会把我送到山顶的学校,我每天下午回家做完作业会和它一起到田间追逐。在学校也常常会给同学们说大黑狗胸前的那长成了“人”字的一片白毛,我一直相信“人”字是老天爷特地印在它身上,用来告诉我大黑狗是他派来的使者。 大黑狗来我家时已经很老很老了,是我读三年级的某一天,它像往常一样把我送到学校,但是我下午回家却不见它的踪影,一直等到晚上都没有见它回来。那些天我放学回家第一件事就是朝我和它常去的那些地方大声的唤它。 不到一个月后的一天早晨,像大黑狗第一次来我家附近时的场景一样,湿漉漉的身子带着些杂草种子,不同的是它身旁还跟着一只背部有些黑毛的小黄狗,小黄狗胸前也有一个很明显的“人”字。我赶紧去用猪食瓢盛满饭放在它面前,它吃了几口就又走了。 就这样,大黑狗离开了我,给我留下了一只小小的黄奶狗。我不知道它是去找它原来的主人去了,还是觉得自己老了,不愿意让我看见它倒下的样子,反正它就是再也没有回来过。 小黄狗长成了大黄狗,我对这只大黄狗的印象很浅,只记得爸妈把这只黄狗送给了外婆家附近的亲戚,我们留下了它生的一只小黄狗。外婆知道我们把大黄狗送人,还狠狠的批评了爸妈,说自己来家里的狗不能送人。 自然小黄狗很快就长成了大黄狗,我像以前一样也偷偷给大黄狗吃肉,逐渐开始懂事的妹妹也会背着爸妈给它肉吃,我和妹都会夹几片我们压根就不吃的肥肉,离开饭桌假装是到外面吃饭,实际上是给大黄狗送肉去了。 我到离家 30 多公里的镇上读高中,每个月才回家一次。每次离家大黄狗都会送我到集市去赶车,我会在寒暑假的黄昏和它到新修的公路去追逐,带它去它自己一个人不敢去探索的地方。 上大学后和大黄狗相处的时间更少了,听爸妈说它会经常跑到外婆家,外婆好吃好喝的招待它,招呼都不打一声就又跑回来了。还经常和邻居家的狗到麦子地打闹,要把一大片麦子弄倒才肯回家。 每学期回家在离家还有四五百米的地方都会听到它的吠叫,因为它把我当陌生人了。但是只要我大喊一声,它就会立刻停止吠叫,飞奔到我这里,兴奋的往我身上爬,把它的前爪往我身上搭;我努力不让它碰到我的衣服,然而每次到家时我都带着一身泥巴做的狗爪印。 现在大黄狗已经 10 多岁了,它就像大黑狗当年送我一样每天送我妹上学。我也已经走入职场开始工作,待在家里的时间更少了,我不知道它还能活多久,生怕哪次爸妈打电话时会给我说大黄狗死了,只要爸妈没有在电话中提及大黄狗,我都是非常开心的,因为那就代表着它依旧健健康康的活着。
Read More ~