/images/avatar.png

我的2018

距离上次博客又过去了几个月。自从换了工作之后时间总是比较紧张,一直是996的节奏。每周只有一天的休息日,往往这天会安排着调整下节奏、与朋友聚会,属于自己独处的时间减少了。 当然这不是不写博客的理由了,核心原因还是懒,毕竟真想做什么,怎么都能挤出时间。 一年的总结 2018对我来说过得很慢的一年,一年里做了挺多的事情。我自己对过去的一年还算满意。 没有女朋友:意识到自己的年龄也挺大了,但是还没有女朋友。 学会了做饭:按照王刚的教程学习做了几个菜。 书籍50余本:10本为技术书籍,17本小说,8本互联网、游戏行业逸闻,10本社科类书籍,还有一些漫画、小品文章。 影视剧50余部:涉及国漫、日漫、欧美电影、日本电影、国产电影。 游戏10个:大作认真玩的只有两部,塞尔达、荒野大镖客2。还有一些开拓游戏叙事的小品游戏,比如弗洛伦丝、伊迪丝芬奇的记忆。 三地:上海、江西、杭州。我挺喜欢杭州的,希望以后有机会去那发展吧。 代码:代码文本行数应该在6w左右。数量还算满意。 明年的展望 工作 通过三年的阅读与实践,我能说把计算机系很多本科基础知识都补充了。数据结构、设计模式、计算机组成、网络、数据库、编程语言等等吧,都有了一定程度的理解。 现在大概处于一个瓶颈阶段,想要更进一步,还是有很多东西需要去改进。 然后是希望未来的一年开发的游戏能够成功上市,完成一段心愿。 打字 问题: 我打字是通过小学时候不断的聊QQ学习的,现在暴露出来几个缺陷。 英文单词经常打错,比如linux输入一些命令,10个以内的字母,经常打一半就出错。 中文打字速度不算快,且容易错。 对键盘符号的位置不熟悉,还不能做到完全盲打的程度。 解决方案: 专门的打字练习 改用双屏 代码的复制粘贴 问题: 不是指大段代码的复制粘贴,而是指对一些单个变量、函数的粘贴。 原因: emacs的补全不是很好用 整个项目里的变量一直使用复制粘贴,没有那么熟悉, 会打错字 解决方案: 强制的不复制粘贴,改为手打。 英文 问题: 英文很差,导致我现在对于英文的文档比较露怯。 解决方案: 一般来说技术书使用的英文都不是太难,考虑到我提升英语的目的是为了更好的阅读英文技术资料。所以完全可以通过直接开始阅读英文技术资料提高,同时也能够达到提高技术的目的。 持续的英文技术资料的阅读。 普通英文咨询的阅读。 源码阅读 今年一年阅读了比较多的书籍,但是除了项目没有怎么阅读优秀开源项目的源码,视野比较窄,急需扩展。同时能够吸收项目之外的优秀设计。 编程练习 项目之外的造轮子少,整体还是懒,经常阅读了书籍没有去实践。编程这个东西,只有练习才能够真正的理解设计。 生活 生活总是一成不变的,希望在未来的一年里能够多发展几项技能。 锻炼 身体越来越差,体重越来越大。每周能抽出3天时间进行专项的锻炼,初期以跑步为主。 摄影 随着年龄的增长,越来越想留住时间。很遗憾的是之前没有做好记录,每年都没有全家福。

clean code读书笔记

命名,很多人戏称为编程中最难的事情。从实践经验出发来看,一个好的命名能够让代码阅读者迅速的知道代码的实际用途,而坏的命名不仅仅是让人摸不着头脑,而且可能误导他人。 本文主要是clean code第二章有意义的命名的笔记,该章节系统的讲述了什么才是一个好的名字。 名副其实 如果命名需要注释,则不是一个好的命名。 命名需要表达出准确的含义,不应该使用magic num或者i、j、k这类无意义的变量名。 避免误导 0和O、1、I和l。这类视觉上容易混淆的名字不需要使用。 假设存在一个vector,里面存储这actor_rid,但是你命名为actor_list,导致阅读者误认为是用链表实现的。 做有意义的区分 类似a1、a2、a3不能够带来任何信息的变量命名 类似thexx、xxdata、xxinfo、xxobject这类没有意义的前缀、后缀 不使用匈牙利命名法带上类型信息,现在ide随时可以看到类型 变量的命名需要带有含义,不要带上冗余的无效的信息。 使用读得出来的名称 如果变量难易阅读的话,对阅读者总是负担,难以记住也难易和他人交流 使用可搜索的名称 作用域越大变量名越长,便于搜索同时不会重复。 避免使用编码 匈牙利命名法在现代ide没有必要,同时影响ide的自动补全,修改类型名同时需要修改变量名 类似m_的前缀没有必要 避免翻译 缩写 类名 类名应该是名词或者名词短语,不应该是动词 方法名 方法名应该是动词或者动词短语,类似set_xx、is_xx 别扮可爱 不要使用难以联想到的词,即使你认为很精妙,直接了当的命名。 每个概念对应一个词

这几个月的一点随感

又有几个月没写博客了,自上次Redis系列之后,三个月过去。中间结束一个项目、面试了很多次、换了个工作、搬了次家、在家度过了一次国庆长假。 结束一个项目 结束的一个项目,算是基本独立完成的比较大的一个项目。项目团队四个人,两程序、两自动化。 出差到施工现场三个月,在那里完成了从客户端界面到服务端大部分编码测试工作,独立解决各种现场需求。很顺利的完成了工作,结题一个多月。项目在之前做的一个十多万行的项目基础上开发,新增了几万行代码,需要对程序进行设计架构的要求少,大部分还是在原来代码基础上修改,增加删除功能。做完这个项目之后,感觉编码上能遇到的问题越来越少,到了一个算是瓶颈的位置。 原公司是一个做轧钢工业控制系统的公司,十几个人,一半是程序员,一半是自动化工程师。毕业时候进去的。它给了我成为程序员的机会,工作氛围也很好,让我在基础薄弱的时候自由学习,并应用到项目之上。 从技术上看,软件需要对工业设备进行控制,对稳定性安全性要求高,程序崩溃或者控制逻辑上产生问题,可能导致很大的生产事故。用户量小,一般只需要一个服务器,搭配几个客户端界面。 公司依靠自动化起家,软件部门薄弱。老板对于软件的认识不足,认为编码简单,只要找些应届生学学就能上。又由于行业比较小,难以从社会上补充有经验的人员。不过话说回来,从公司需求来说,确实不需要大牛。维持住业务,两年三年工作经验程序经验即可胜任,基础好的应届生也能完成大部分工作。工作的主要难度在业务的理解上,这也是我能成为程序的原因。 刚进去的时候,之前的干了八年的主程序因故出走,剩下的都是只有一年经验的程序。还好老板在之后请来了老同学(20年经验),管理队伍。但是由于工作性质,需要长期出差,大部分代码在施工现场完成,一般一两人负责一个项目,没有完善的流程,更多是靠个人的自我约束,自我驱动去完成项目。从代码质量来看,肯定不会好。 如果要对我过去两年工作进行一个总结的话,那就是野蛮生长,整个过程中很少有人能够给予我指导,都是依靠个人去搜索资料、学习知识。对于程序更多的停留在编码阶段,对与程序设计、软件工程的管理认识浅薄。 面了很多次试 项目结束之后,休了个一周的长假。顺便也投了很多简历,面了很多次试。 面试的本意是看看自己在市场上到底值多少,了解市场的需求。八月份,不是招聘旺季,拉勾上C++的岗位只有三百多一点,适合我这个经验的100多个。由于之前的工作业务关系,干什么都是转行,同时不管是客户端还是服务端都干过,很多工作对我来说即适合又不适合。尝试着投递了一些岗位。后来感觉面试邀约不多,陆陆续续投递简历半个多月,最后发现快把适合我这个经验的岗位投递完了。一百来份简历,最终收到不到20个面试邀请。 从市场上看,C++需求的主要是两种windows客户端和linux服务端开发。需求最旺的依次是游戏、金融、工业控制领域。面试考察的大部分还是计算机原理、c++基础、数据结构、tcp网络基础,这可能由于我没行业背景有关。个人感觉我对于C++基础、数据结构没有太多问题,计算机原理稍薄弱,tcp网络了解很少,行业知识有限,对Linux开发没有经验。对于市场上很多岗位不能很好的匹配,于是给我面试机会的相对也比较少。 换了个工作 本意是出去见见世面的我,最终换了一份工作。面试的多了,愈加感觉到自己的不足。虽然在过去两年里还算努力的进行学习(总是有惰性,要是真的非常努力,肯定混的比现在好)。但是由于自身视野的问题和业务性质,我觉得要想更快速的更进一步,是时候去一个新的地方闯荡了。 于是,最终选择加入游戏公司成为linux服务端程序员。面试的时候,新公司主程序对我的评价是很对的:一个处在进阶阶段的程序。在这么一个阶段,需要更多的环境上的促进。这样我才能接触到更多的问题,发现一个更大的世界,逼着我去处理各种问题,不断的前进。很多时候,对于程序员来说,如果没有接触到问题的环境,你很难去想象到怎么处理问题,这也是很多公司要求行业经验的原因。不仅仅是业务上的熟悉,同时也是你只有在那个环境,比如用户量达到一定量级,才可能接触到问题,才可能知道怎么去处理问题。 另一个原因是我个人的心愿与理想。我是一个喜欢游戏的人,一直希望能够进入游戏公司,去看看怎么做游戏的。毕业的时候,一直在尝试找一个游戏策划的工作,但最终只收到一个小公司的游戏测试岗位offer,没有去。最后成员工业控制程序员,有一部分原因是c++在游戏领域用的很多,以后有机会跳槽。 现在能有机会进入一家在我看来还算不错的游戏公司,也算是了却一个心愿。虽然网上对于国内游戏开发有很多负面评论,但是不管怎么样,我也要去看看。不管未来这个工作会不会真的喜欢,我也要去尝试。 搬了次家 又搬了次家,这次是由于新工作离原来住的地方有点远,接近两小时的路程,必须得搬家了。毕业两年,包括离校的那次,搬了七次家,算下来三四个月就要搬一次家。过去两年里,出差过三个地方,广东湛江、河南安阳、辽宁营口,一出差就是几个月半年的,天南海北的。 飘临。 我算是一个适应能力很强的一个人了,在一个新地方能够比较快的熟悉起来。但是这么多次搬家,又经常长期出差到不熟悉的地方,那种人在异乡的感觉就愈加强烈了。以前觉得出差没什么,反正单身狗一个,一人吃饱全家不饿的,但是真的出差久了,就发现你跑到一个陌生的地方,人要重新熟悉,城市要重新熟悉,又不能带很多东西。这意味着你之前的积累都没了,你只能带着其实就是你这个人。外物积累都没了,周围的熟悉事物没了,你其实对于一个新的环境来说是一个异物,你是个格格不入的东西。但是我又不是旅游,我必须去熟悉,因为要做项目,要与人打交道,要在这生活一段时间。可熟悉了,立马又要进入一个新的陌生环境。搬家一样,搬到一个新的地方,室友不认识,周围环境不熟悉,又要重新认识熟悉。 很多精力,就在这样的过程中被磨灭了。 在家度过了一个国庆 上一次回家是过年的时候了,中间的一些假期不是在加班就是在出差。项目结束的假期又在面试。这样一算又是大半年没回去了。 时间。 很多时候你没有意识到,其实离开家已经很久了。现在24岁,人生的前12年在小镇上度过,初高中到了新余市,过了6年。大学到了上海4年,加上毕业2年,已经6年了。人生的四分之一是在外面度过的,可以预料到的是之后很长一段时间依旧会在上海。在家的时间越来越少。 我是喜欢回家的,在家的话很多事情不需要考虑,饭来张口衣来伸手,每天看看电脑,看看电视,啥事不管。对我来说,最大的幸福就是和自己爱的人待在一个地方,可以不说话,只要在一起,互相能够感觉到对方。回家对我来说就是这样一件事情,大家都很熟悉了,不管是父母、亲戚、朋友,只要在一起,互相有默契。这种长时间磨合之后产生的默契的感觉是最让我留恋的。

Redis源码阅读-总结

之前的一系列Redis源码阅读的博客都是基于《Redis设计与实现》的,内容上不免和书有雷同。 本来计划是三月份开始看,但是中间由于接到一个开发速度要求很高的项目,加班很多耽误了些进度,只在这一段时间里看了部分小书(部分书摘录成了博客),耽搁了。 然后快到七月的时候,项目在经过编码、测试,最终投用起来之后,又有了更多的时间来学习。便开始了源码的继续阅读。 阅读redis源码的目的: 为了之后项目如果使用redis的话能够游刃有余。 学习其优秀的编码。 参加工作两年。 第一年是从机械专业学生转变为程序员,更多的是在打基础,看了挺多计算机的基础书籍。那段时间相对是痛苦的,遇到一个问题往往不明白,不断去探究又探究出一个新的不明白的问题。 第二年在积累了几万行之后,开始感觉基础编码方面比较游刃有余了,遇到的问题大多数能提取重点,找到解决方案。但是想着继续提升,于是开始了部分项目的源码阅读。除了公司项目源码的阅读,网上开源项目的源码阅读也提上了日程,在经过了sgi stl的仿写之后,开始了redis的阅读。 redis是我第一个比较完整的看完的现在流行的开源项目源码。不算中间停下来的时间,大概看了三个星期。这三个星期里不能说对redis理解很深透,但是起码整个项目的结构,怎么实现主要的功能,内部模块的互相调用算是理解了。 收获有很多,修炼还需要继续。

redis源码阅读-一个命令的旅程

redis服务器会与多个客户端建立网络连接,处理客户端发送的命令请求,在数据库中保存客户端执行的命令,并通过资源管理来维持整个服务器自身的运转。

本文目的是为了理清楚服务器对于命令请求的整个处理过程,说明这个过程中服务器与客户端如何互相交互,服务器内部如何调用内部组件达到对命令的执行。

命令的处理流程

之前在事务代码部分的阅读,已经对于服务器接收客户端的连接并创建命令请求处理器等待命令请求的过程。

现在以一个简单的set命令为例子,看服务器在接收请求之后如何处理。

1
2
redis> SET KEY VALUE
ok

从客户端发送set key value命令到接收回复ok,都做了如下操作:

  • 客户端发送命令。
  • 服务器命令请求处理器接收,引发命令的执行,并产生命令回复处理器。
  • 命令回复处理器发送ok给客户端。
  • 客户端接收ok,并打印。

redis源码阅读-事件

redis源码阅读-事件

redis服务器是一个事件驱动程序。当触发一个事件时,redis会创建一个事件,放入到待处理的队列,依次进行处理。

redis事件分为文件时间和时间事件。

文件事件:文件事件是对套接字操作的抽象,当服务器与客户端进行通讯,会产生出各种文件事件,而服务器则通过监听并处理这些事件来完成一系列网络通讯操作。

时间事件:redis一些操作是需要定时进行执行的,而时间事件就是对这类操作的抽象。

事件的实现

以下是事件结构体定义。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/* File event structure */
typedef struct aeFileEvent {
    int mask; // one of AE_(READABLE|WRITABLE) 类型
    aeFileProc *rfileProc; // 读事件处理器
    aeFileProc *wfileProc; // 写事件处理器
    void *clientData; // 多路复用库的私有数据
} aeFileEvent;

/* Time event structure */
typedef struct aeTimeEvent {
    long long id;  // 唯一标志
    long when_sec; // 事件到达事件s
    long when_ms;  // 事件到达事件ms
    aeTimeProc *timeProc; // 事件处理函数
    aeEventFinalizerProc *finalizerProc; // 事件释放函数
    void *clientData; // 多路复用库的私有数据
    struct aeTimeEvent *next; // 指向下一个时间事件结构,形成链表
} aeTimeEvent;

/* A fired event */
// 触发的事件结构体
typedef struct aeFiredEvent {
    int fd; // 文件事件描述符
    int mask; // one of AE_(READABLE|WRITABLE) 类型
} aeFiredEvent;

// 事件循环结构体
typedef struct aeEventLoop {
    int maxfd;   // 当前注册的最大描述符
    int setsize; // 需要监听的描述符个数
    long long timeEventNextId; // 下一个时间事件ID
    time_t lastTime;     // 上一次时间循环时间
    aeFileEvent *events; // 注册要使用的文件时间
    aeFiredEvent *fired; // 已准备好,待处理事件
    aeTimeEvent *timeEventHead; // 时间事件
    int stop; // 事件处理器开关
    void *apidata; // 处理多路服用库的私有数据
    aeBeforeSleepProc *beforesleep; // 处理事件前要执行的函数
} aeEventLoop;