【置顶】个人阶段性学习和规划总结(技能树)

  本人专注后台服务端开发一晃已经好久了,自我感觉上不仅经验增加了很多,同时接触到的东西确实不少,在增加见识的同时博文也批量更新了很多。大多人看来内容很多很杂,此处做一阶段性整理和总结吧,给自己也梳理梳理一下。
  其实不仅仅是后台服务端开发这个方向,就整个软件开发来说,其知识构成也是有所层次的。个人毕竟不是正规计算机科班出身,也就是大家所说的半路出道、自学成才的野程序员,优点就是不会被那些所谓正规计算机教育的所束缚禁锢住,但是很多时候感觉自己的知识构成还是有所缺陷。既然励志要靠撸代码吃饭养家,那么长痛不如短痛,晚补不如早补,该学的终究跑不掉!

【置顶】博客资源收录大全

  虽然当下微信公众号席卷自媒体市场之势如火如荼,但是针对文章展示的话个人还是偏向于独立博客的形式,主要是因为个人可控制化的东西比较多。我想,在被条条框框束缚的无以喘息的情况下,这个时代没有什么比个性和自由更为重要的了吧。
  下面是一些网络知名人士的博客,以及平时在搜索资料过程中遇到的好站点,都被一一记录下来了,真心喜欢的话可以用RSS订阅这些站点,其中很多都是全文输出的。因为个人的兴趣取向问题,除了将不感兴趣的前端、移动端外,过于偏向于Java/Nodejs等语言化的博客也被KO了(即使编程思想是独立于语言而存在的),希望平时没事多看看吧!

关于Thrift RPC之C++服务端和客户端的一个封装

  之前也介绍过,Thrift总体架构是分层设计的,用户就可以根据自己的需求对各个层进行个性化选择和定制,但这也带来了弊病:细节多了的话就增加了复杂性,尤其Thrift相关文档严重匮乏已经被诟病许久,这么一来无疑增加了初用者的门槛,就类似于一个工具无论再强大,但是man出来有几十上百个选项参数,估计再有能耐的人也会立马蔫了一半。其实大多数服务端项目的需求都相似,而且一旦固定下来就不会变更了,所以选择常规并符合业务模型的选项组件,将其封装好服务端和客户端调用框架,这样业务开发者就可以方便的用来做业务开发,因而整体说来是一件很有意义的事情。
  Thrift已经在项目中被广泛使用,总体来说性能不俗、稳定可靠,而且很多公司自己开发的RPC框架都主动兼容Thrift协议,说明这货使用还算挺广泛的,虽然之前也说到使用Thrift有些小坑需要注意,比如TSocket Timeout参数,服务端、客户端阻抗匹配等问题。还有就是Thrift是一个纯粹的RPC,功能相对比较单一,不像Zeroc-ICE除了做RPC外还集成了大量的服务治理的功能组件:服务发布和发现、负载均衡、配置更新和同步、服务自动尝试等,但是有时候想想,Do one thing and do it well不正是UNIX的哲学精髓么,ZeroC-ICE不是一时半会儿可以玩溜的,而这些服务治理的功能完全可以用自己熟悉的组件来实现对应功能。
  在之前的开源项目tzmonitor中已经使用Thrift了,基本按照目录结构和封装在其中的添加自己的业务逻辑代码就可以直接使用了,这里主要描述一下新建一个Thrift接口需要的步骤和流程。
apache-thrift

(a) 编写.thrift文件
  在thrifting/source中,可以任意添加.thrift文件,在这个文件中我们会常规的定义数据类型struct和服务接口service,相关的语法请直接参考Thrift相关文档;

关于自己写的两个小项目tzhttpd和tzmonitor

  前面的文章提到了自己开发的tzhttpdtzmonitor,虽然不是什么厉害的东西,但是个人感觉对于后台开发者来说算是比较有用,同时也是自己认真开发和维护的小项目。在这篇文章中,我就将这两个项目实现中的小细节和大家分享一下。

一、tzhttpd - 一个通用的HTTP开发框架


  在程序员界有一个说法:每个后台开发工程师都有一个自己的Web Server。这个说法也非空穴来风,尤其是对于做C++后台开发的,虽然C++程序酷爱造轮子自然是一个原因,但究其根本是C++语言标准本身没有集成一个HTTP框架,甚至C++标准还没有敲定一个标准的网络库,而大家风风火火的造轮子也没有形成一个事实上工业标准的HTTP框架,所以这种尴尬的境地就这么一直持续着。
  自己在做后台开发以来,就使用了boost.asio开发了一个简易的HTTP框架,主要目的是用来做应用程序网关相关的东西,不过那个时候自己的经验和能力有限,而最近工作中越发感觉对一个稳定好用的HTTP框架的需求,在对生产服务端系统的各项要求有了更深入的理解之后,将之前的项目进行了再整理和优化,就得到了上面提到的tzhttpd了。

1.1 特性

  a. 他是基于boost.asio高性能异步框架开发的,所以理论上可以支持很高的并发度支持。因为我的工作环境迫切要求是吞吐量而不是并发度,所以也没有对他做更深入的并发测试和验证,不过框架本身的性能还是不错的;
  b. 虽然只支持HTTP协议的GET/POST方法,但是也可以应对绝大多数应用场景了。HTTP请求的报文、数据都进行了解析和存储,业务层可以很方便的取用;支持Keep-alive长连接;支持VHost虚拟主机。
  c. 支持表达式描述的请求URI匹配,将请求转发到匹配的handler中去。除了将handler写死在项目中之外,还可以通过配置文件和so动态链接库的方式,动态增加URI和对应的handler,这个过程可以方便的部署、更新业务,其他业务不停机不受影响;
  d. 虽然最初是用来做应用程序网关使用的,但是在后期我也增加了VHost、Cache-Control、Content-Type、Redirect的支持,而且还计划支持FastCGI协议,在一定的程度上做一个可用的WebServer;
  e. 附带一个internal_manage的管理界面,方便不停机对服务进行某些更新和维护;

Git工作流的使用

  最近好久都没有更新博客了。
  一方面是自己“三十 而未立”也当爸爸了,在幸福洋溢之余也还有很多的事情需要去做,很多时候下班和周末没有时间开电脑了;另外就是工作上面的事情比较繁琐,而且与此同时挤出零碎的时间完善了一个HTTP服务端框架tzhttp和一个简易的信息收集系统tzmonitor,关于这两个系统的细节我后面会单独描述,此处暂且不表了。不过在写这两个项目的时候,我使用了Git Workflow和Git Submodule特性,感觉确实很方便实用——如果说Git是一个强大的版本控制工具的话,那么Git Workflow就是在现实工作中活用这个工具的“术”了,Git Workflow就像是一个Git在团队实践项目中的工作模式,套用这个模式可以清晰完美解决工作中的版本控制、团队合作、迭代发布的需求,其本质提现的是有效的项目流程管理和各个参与者对于开发维护工作的协同约定。
  说到Git Workflow就不得不谈大名鼎鼎的A successful Git branching model了,这里祭出他的Workflow框图:
git-workflow

人到中年,是该给自己配份保险了

  随着爸妈年纪越来越大、新的生命即将诞生,从来没有感觉自己的责任越来越重,而且“享受”独身子女政策光芒照耀的人,会这种无他可依的孤独感尤为的强烈。太太很久之前就说到保险的事情,现在看来确实是很有必要——倒不是说怕死,而是作为家庭的顶梁柱,已经突破个人的价值,而是全家的依托之所在,虽然中国人从心底里有一种抵触保险的情节(而他的产生感觉毫无根据可言),除了强制的社保、医保、车险之外几乎不会涉及其他的商业保险,身边买其他商业保险的人主要也是身边的朋友是干买保险这一行的,不过在中国这种国情和大环境下,也只有通过保险才是最稳妥的方式手段,借用杠杆提升家庭的抗风险能力。
  年底前也话了不少时间,从一个保险文盲小白,成长为一个对人寿保险略知一二的人,下面就是个人对保险的认识和理解,以及和我相似处境同伴的一些建议,希望能对大家提高保险意识、选择保险产品尽一份绵薄之力。

  言归正传,除了央行、证监会和银监会,中国还有一个相对低调的保监会。因为央行和银监会和大家手里的资金以及社会生活息息相关,所以大家会很熟悉;中国股市跟中国足球一个德行,正所谓恶名传千里,证监会几乎天天被大家问候,曝光率自然很高;中国家庭绝大多数都是保守的储蓄理财,保险除了我们这一代年轻人才有所关注之外,老一代人除非银行渠道推销,受众范围小了自然也显得低调很多。保险按照类型也分为财产保险、养老保险、人身保险,财产保险只有家产万贯的大佬才会考虑,作为一个都快活不下去的穷屌丝自然无须关注;养老保险基本也是钱多的人烧的慌,随着货币不断贬值,感觉还是买房养老更可靠些;人身保险主要以人的健康为目标的保险,每个人都不能保证自己永远健康无疾而终,这个是涉及到人生存本能的需求,也是这篇文章所关注的险种。此外还需要说一句,就像支付公司需要央行的支付业务许可证牌照一样,保险公司也需要保监会的保险牌照才行,不是你有钱就能玩的,而且保险牌照还有地区、险种的限制。

春节长假中的学习小结

  今天没有像往年一样做一个年终总结贴出来,主要是技术方面在之前的文章中都慢慢透露出来了,而且年底也公司也填了各种绩效考评,总结性的东西都写的想吐了,所以也不再流水账了。今年放假没回老家,老爸老妈都来深圳照顾我们了,虽然过年少了些热闹和气氛,但是家人围绕一起的感觉也没显孤单,而且少了拜年胡吃海喝那些形式,倒是七天长假有了些许空闲的事件。
  这几天读了几本书:《Rust 程序设计语言(第一版)》、《Go语言实战》、《RabbitMQ实战指南》、《TCP/IP高效编程:改善网络程序的44个技巧》、《MySQL技术内幕 : SQL编程》,有些想说的,就码成一文吧。
dog-year
  前面两个是编程语言类的图书,读他们倒不是想折腾新开发语言的意思:首先C++已经很复杂了,新标准刷存在感似的层出不穷,足够自己喝几壶慢慢折腾的了,虽然C++使用经验也挺多的了,但自己还不敢冒言“精通”之;而且随着年龄增大,对自己的方向和目标也更显的清晰,集中精力在重要、感兴趣、本质性的领域,不爱折腾这么多现象层面的东西了。了解Rust主要是最近区块链越来越热了,据说绝大多数的区块链框架使用Rust开发,所以这个语言是不是很独特?Go作为含着金汤匙出生的幸运儿,在中国搞的风生水起,各种资料、框架、Repo排山倒海般的袭来,所以也想看看这个语言为啥这么快风靡世界!

Lua脚本语言语法速览

  之前先上了Redis Lua的优化,这里把Lua的基本语法也顺便整理一下,后面回顾查阅会比较方便。
  其实关于Lua这个脚本语言,之前在TP-LINK工作的时候就与之擦肩而过,那时候别的小组在用他做路由器软件(基于openWrt)。后面在逛风云大大的博客时候也时常遇到过他的身影,另外在拜读开涛《亿级流量网站架构核心技术》的时候,书中描述针对大流量网站的开发、运维、管理等很多地方也是用Lua的。虽然此时对Lua的使用场景接触有限,但是看过Lua教程后发现他是一个精炼小巧的语言,现在开发圈子系统语言、脚本语言纷繁丛杂,在他们争来争去不可开交的时候,Lua反而在夹缝中找到了适合自己生存的土壤:他不求接触系统底层获取超凡执行性能;也不求建立丰富的数据结构和程序库,培养封闭自己的生态圈;而他把自己变得极为的小巧精简,用ANSI C实现因此可以运行在几乎所有的架构平台上面,也可以寄存在很多成熟的开发组件当中,当别人觉得蹩脚不方便的时候,我能够见缝插针迎难而上,真是个不错的策略啊。
  Lua程序设计这本书通篇200多页,相比于其他语言的教程可以说是很简短了,其中由易到难描述了Lua语言的方方面面。Lua要是深挖的还是有很多东西可以学的,因为我们只是用他来做插件,当前看来不会做很复杂的大规模的开发,就先看看数据结构、语句、函数这类语言的基本知识吧!

一、Lua概述

  Lua的语句不需要分隔符,所以不用纠结换行、分号等这些东西,可以按照自己的习惯写出工整方便理解的句式。Lua解释器会不断的尝试解析输入的每行内容,如果发现不能够形式一个完整的语句块,就会等到更多的输入内容。在交互式模式下,通过EOF或者os.exit()可以退出交互式Lua shell;如果将执行的语句放入文件中作为脚本,在Lua shell中可以通过dofile(“file.lua”)就可以加载这个脚本;如果只想测试某些Lua语句,可以运行lua -e “print(math.sin(12))”的形式,这样就直接运行语句,而不会启动生成交互式的Lua shell。

分析一下我司ZooKeeper客户端封装思路

  为了提高系统的可用性,我司现在不少项目已经采用ZooKeeper进行服务发布和服务发现了。
  之前已经对这块内容预学习了很久,虽说ZooKeeper的C API使用起来很简单,核心接口不超过十个左右的函数,但是多线程的库中接口涉及到大量的函数回调,要想在C++中做到傻瓜式的使用还是有点麻烦的,因此同往常一样C++中也有大量针对ZooKeeper Client封装的轮子。当前我们软件研运部使用客户端库的是我们老大传说花了一周心血封装完成的,已经被大量使用并接受了考验,这边文章是对其封装设计思路的总结,所有Idea及版权归原作者所有,抱歉源码无法分享。

  对于服务提供者来说,最为常见的情况就是把服务自身创建注册为一个临时节点,当自己挂掉之后该节点自动消失,监听服务目录的服务调用者感知变化并作出反应。这比较的直观的设计方式,基本ZooKeeper的教科书都会这么举例子,但是也带来一个问题:因为临时节点不支持创建子节点,所以这个服务提供者的其他配置信息就必须丢到node data域里面了,而如果配置信息比较多的时候就需要使用json或者乱七八糟的编码方式,这同时也意味着更新一个配置的话需要将数据全部取出来解码,修改某些字段后再编码打包,最后再将这一坨东西写回去。如果是程序自动协助完成还好,但是要在zkCli.sh的方式临时手动更新的话,这种困难可想而知。
  因此,我们不将服务提供者实现为一个临时节点,而将其创建为一个持久节点,然后在其下面建立属性子节点方便配置和更新,下面就是一个服务提供者所需要考虑的常见属性信息,当然还可以按照需求扩充,下面这些节点容我描述过来。
eink-pdf
  图中的service_name族的节点表示某个具体的服务,服务消费者可以Watch这个节点,而以host:port命令的子节点代表一个个实际的服务提供者,在服务提供者启动的时候会尝试创建或者更新这个永久节点,这个服务提供者节点的子节点包括:

C++中多读少写共享数据的保护操作

  开发过程中读多写少的情况还是很常见的,比如很多服务运行参数或者业务配置参数,这些内容只有在更改配置文件或者数据库的时候才需要重新加载,执行写操作,平常大多数时候都是只读的。对于这些配置,我暂时的处理方式(比较low)是手动向服务发送一个信号(SIGHUP,跟nginx学的),然后在服务的信号处理中进行加载和更新替换操作,虽然多线程情况下信号机制复杂的要死,大神建议的情况就是能不用就不用,但是因为服务端没有HTTP的支持,所以那些RESTful优雅的更新操作也没法使用,实乃无奈之举。曾经也在网上尝试找一些配置管理工具,但是都觉得用起来太重,因此也一直这么将就着了。
  话题转回来,对于读写数据,最常用的方式就是使用一个读写锁来进行保护,其使用基本都是下面的套路来完成的:

1
2
mutable boost::shared_mutex rwlock_;
std::map<std::string, channel_health_t> cached_health_;

  然后,读写访问起来也很清晰,使用shared_mutex和unique_mutex就可以解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bool PbiChannelRoute::is_channel_health(std::string channel_name) const {
boost::shared_lock<boost::shared_mutex> rlock(rwlock_);
auto iter = cached_health_.find(channel_name);
if ( iter != cached_health_.cend()){
return it->second.is_health;
}
return true;
}

void PbiChannelRoute::update_channel_health() {
....
{
boost::unique_lock<boost::shared_mutex> wlock(rwlock_);
cached_health_ = channel_health;
}
...
}

  在读访问的时候,我们使用shared_lock来用作为读锁,而写的时候使用unique_lock排他锁作为写锁,同时在更新的时候我们注意到需要尽量把这个操作的临界区设置的最小,更新前的数据准备工作以及更新完的善后工作都丢到临界区之外执行。这个模型的优势是简单直白,在读操作远远大于写操作的时候没有什么问题,不过其中的lock contention点我们也必须心里有数:整个读操作都是带锁访问的;更新操作是赋值操作,在临界区就有一个析构和拷贝的成本(用智能指针能够优化掉)。