1 前言
工作多年,总想写点东西把自己总结下,不看得失,只是回顾下,这些年自己对工作与学习的一些感受。中间迷茫过,痛苦过,也畅快过,遇到过一个问题就脱不过的那种沮丧与焦虑,也经历过项目上线那种紧张与畅快,也和别人吵过架,倔强的强调自己的技术方案好,今天把这些写下来,让看到的人有个参考,让初学者又个提纲吧。
要说技术,自我觉得多长时间都说不完,经历过的项目不同,采用的技术也不同,需要关注的点也不同,绝大多数时候是项目决定技术方案,毕竟有项目做了,有钱赚了我们才能生存下去。
我大学学习的是C++,第一个工作,第一个项目也是C++,最近的项目也是C++主导,所以C++语言贯穿我工作的始终。大家都说C++难学难精,如果你学会了C++,学习其他的语言会变得轻松很多。工作到现在使用过的开发语言也多,如C++,Java,C#,Python,Lua等,每个语言都有自己的优缺点,合适的选择确实会大大加速项目的开发。
虽说在自己的开发生涯中,经历过许多开发语言,但是C++贯穿始终,所以总结也以C++引导来完成这次的总结。注意这篇文章不是个人的感触总结,更多的是开发中遇到过的技术点总结。
2 我的项目概况总结
从写第一行代码开始,经历过形形色色的项目,有PC端的,也有移动端的,经历过项目从0-1的过程,做过程序员,也做过架构师,甚至还做了一段时间的产品经理,经历复杂,感慨也多。
纵观自己的项目生涯,可以归结为一下几个方面
2.1 PC应用软件
写过传统的PC软件,如办公,监控方面的软件,也写过网络游戏方面的软件,这主要会涉及到以下技术:
- STL,Boost的使用
- 线程,进程的使用,并且需要你可以调试
- 日志记录与分析,记录重要节点,分析出现异常的原因
- 网络编程,要能自己定义需要的网络协议
- 了解数据库知识,掌握程序语言最基本的读取,更新,查询操作
- 这类项目一般多是单机项目
2.2 互联网软件
这类项目主要面向移动端,如App(Android,IOS),小程序,H5,这类项目主要涉及到以下技术:
- 可能需要掌握不同平台特有的语言,如Android的Java,H5的TS,PHP等
- 需要了解各个平台安装包的打包方式,还有各个云平台的限制等
- 对于后台运行的服务器,可以选择的语言很多,除了C++,可以选择的有Java,GO等
- 后台需要你了解高并发,备份,快速恢复等知识,对网络要有较深入的了解
- 对于后台掌握一门脚本语言很重要,特别是是将脚本语言嵌入到主程序中的技术,如将python,或者lua嵌入到C++中,除了能提高开发效率,也能增加程序的健壮性
2.3 内部工具,自动化测试,运维部署
这类项目可选择性很多,不确定性也多,工作量可能也很多,主要可以归结为以下几点:
- 掌握一门或者几门脚本语言,建议学习python
- 掌握最基本的系统知识,如windows下的dos,linux下的shell,了解各个平台基本的网络操作,程序部署等
- 开发语言可以不选择运行效率最高的,但是一定要选择开发效率高的,要能快速迭代
- 如果测试接口只提供了C,或者C++接口的,如测试某一个封装的dll,那就选择C++进行二次包装,修改成命令行再测试
- 如果要开发GUI界面,且只在windows下测试,那就选择C#,使用winform开发
- 如果做自动化测试的操作,建议使用python开发,python是面向对象语言,同时可以使用C对其进行扩展
- 如果做自动运维测试方面的工作,可以使用python+shell结合来开发
- 对于多环境测试,可以使用docker,或者虚拟机来测试,增加测试量
3 C++基础知识
3.1 C++程序的编写
关键字,语法是我们必须要掌握的,同时也要注意C++与C的区别,注意各平台特有的IO函数的使用。
但是编写程序时,也要注意以下几点:
- 掌握一种命名规则,合理的使用命名空间,避免命名冲突
- 定义的变量,不管是局部变量,还是全局变量,都要初始化
- 对于类,构造函数,析构函数尤为重要,定义的函数最好都有返回值
- 正确理解面向对象的三大特性(封装,继承,多态),避免过度封装,继承
- 注意宏定义的使用,定义不能过于复杂,否则代码阅读性很差
- 合理的文件拆分,文件过大,导致编译速度慢
- 注意头文件的包含关系,避免出现环形关系
3.2 C++之多态虚表
C++的三大特性之一多态,分为静态联编和动态联编,在编译过程中进行联编称为静态联编,编译器必须生成能够在程序运行时选择正确的代码,称为动态联编。
虚函数是动态联编的关键,使用virtual关键字创建虚函数,在派生类中进行重写。发生动态绑定的条件是:
- 只有被定义为虚函数的成员函数才能在派生类重写(不是重载),非虚函数不能进行动态绑定
- 必须通过基类类型的指针或引用进行函数调用
对于虚表,我们还要理解以下几点:
- 一个类中的虚函数的地址都会储存在虚表中,所以虚表里存的是一个个地址,而类中的虚指针指向虚表,在发生动态绑定时基类虚函数的地址会被替换成子类重写函数的地址。
- 派生类会继承基类的虚表,父类和子类都有自己的虚表指针,有自己的虚表。
- C++的编译器把虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。虚表指针的名字也会被编译器更改,所以在多继承的情况下,类的内部可能存在多个虚表指针。通过不同的名字被编译器标识。虚函数表中可能还存在其他的内容,如用于RTTI的type_info类型。
- 每个有虚函数的类或者虚继承的子类,编译器都会为它生成一个虚表。同时,子类继承父类时,会获得继承下来的__vptr,就像继承普通成员变量一样。
最后我们需要理解在发生单继承,多继承,虚继承时,类对象和虚表的内存布局。在设计程序时,尽量坚持一下原则,会让你的程序逻辑清晰,阅读简单
- 尽量使用单继承,避免多继承,多重继承复杂多变,这一点可以多想想java为什么没有多重继承
- 如果发生多重继承,看看能不能使用聚合
- 特别设计接口时,使用纯虚函数,让程序更加健壮可靠
3.3 C++编译器
要将C++写的程序编译成可执行的二进制程序,才是我们最终的目的。编译就要使用编译器。
编译器就是将“高级语言”翻译为“机器语言(低级语言)”的程序。一个现代编译器的主要工作流程:源代码 (source code) → 预处理器 (preprocessor) → 编译器 (compiler) → 汇编程序 (assembler) → 目标代码 (object code) → 链接器 (Linker) → 可执行程序 (executables)
C++主流的编译器有:
- GCC/G++:linux下编译器,该软件只负责编译,最后的链接是其他程序完成的
- MSVC:微软家族的编译器,在widnows下最好用的编译器,默认集成在vs系列的开发ide中
- Clang/LLVM:MacOS下使用的编译器,Wasm使用的编译器,可以将C++,Rest等编译成wasm文件
主流编辑器介绍:https://jasonkayzk.github.io/2022/05/28/C++主流编译器总结/
3.4 C++编译过程
3.4.1 预处理(Preprocess)
这一步由预处理器完成,对源程序中的伪指令(以#开头的指令)和特殊符号进行处理,伪指令包括宏定义指令、条件编译指令和头文件中包含的指令
- 处理预定义宏,如#define,DATE、FILE,#error,并将宏定义进行宏展开
- 处理所有条件编译指令,如#if、#ifdef、#ifndef、#else、#elif、#endif等
- 处理 #include预编译指令,将被包含的头文件内容插入该预编译指令的位置,如果是多重包含的话会递归执行;
- 添加行号和文件标识,以便于编译时编译器产生调试用的行号信息及编译时产生编译错误和警告时可以把行号打印出来
- 处理所有注释(C++的//,C语言的/**/),一般会用一个空格来代替连续的注释
- 最终生成了*.i文件
3.4.2 编译(Compilation)
这一步由编译器完成,对预处理后的文件进行词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件
- 词法分析:输入源程序,对构成源程序的字符串进行扫描和分解,识别出一个个的单词(亦称单词符号或简称符号),如基本字(begin、end、if、for、while),标识符、常数、运算符和界符(标点符号、左右括号)
- 语法分析:在词法分析的基础上,根据语言的语法规则,把单词符号串分解成各类语法单位(语法范畴),如“短语”、“句子”、“程序段”和“程序”等
- 语义分析:对语法分析所识别出的各类语法范畴,分析其含义,并进行初步翻译(产生中间代码)。这一阶段通常包含两个方面的工作。首先,对每种语法范畴进行语义i安插,例如,变量是否定义、类型是否正确等等。如果语义正确,则进行另一方面工作,即进行中间代码的解释。这一阶段所依循的是语言的语义规则,通常使用属性文法描述语义规则。也就是说,语义分析结合上下文推导出语句真正的含义
- 优化:对前段产生的中间代码进行加工变换,以期在最后阶段能产生出更为高效(省时间和空间)的目标代码。优化的主要方面有:公关子表达式的提取、循环优化、删除无用代码等等
- 生成*.s的汇编文件
3.4.3 汇编(Assemoly)
由汇编器完成,将汇编代码转变成机器可执行的二进制代码(机器码),并生成目标文件
- 之所以要经过预处理、编译、汇编这么一系列步骤才生成目标文件,是因为在每一阶段都有相应的优化技
- 生成二进制*.o目标文件
3.4.4 链接(Linking)
由链接器完成,主要解决多个文件之间符号引用的问题,即symbol resolution,采用动态链接,或者静态链接,最终生成在计算机上可执行文件
- 静态链接:在链接期,将静态链接库中的内容直接装填到可执行程序中,在程序执行时,这些代码都会被装入该进程的虚拟地址空间中
- 动态链接:在链接期,只在可执行程序中记录与动态链接库中共享对象的映射信息,在程序执行时,动态链接库的全部内容被映射到该进程的虚拟地址空间。其本质就是将链接的过程推迟到运行时处理
3.5 程序运行时,内存模型
程序启动时,一般按照虚拟地址做如下内存划分:
- 代码区:是编译后代码(如函数)的主体,也就是程序的机器指令。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等
- 全局/静态区:全局和静态(static)变量被分配到同一块内存中
- 常量存储区:存放常量(C中的字符串和#define定义的常量),不允许修改(通过非正当手段也可以修改),用const修饰的全局变量是放入常量区的,但是使用cons修饰的局部变量只是设置为只读起到防止修改的效果,没有放入常量区,可以认为是放在栈区
- 栈区:是那些编译器在需要时分配,在不需要时自动清除的存储区。存放局部变量、函数参数。存放在栈中的数据只在当前函数及下一层函数中有效,一旦函数返回了,这些数据也就自动释放了
- 堆区:保存程序中动态分配的内存,比如C的malloc申请的内存,或者C++中new申请的内存,堆向高地址方向增长。平常我们说的内存泄漏,内存碎片就是这里引起的
3.6 全局变量,局部变量,静态全局变量,静态局部变量
-
全局变量:只需要定义在一个文件中,就可以作用于所有的源文件
-
局部变量:局部变量只有局部作用域,局部变量具有屏蔽全局变量的功能
-
静态全局变量:它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被static关键字修饰过的变量具有文件作用域
-
静态局部变量:它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见
-
四者相比较而言,
- 全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间
- static 全局变量改变作用范围,不改变存储位置
- static 局部变量改变存储位置,不改变作用范围
- 局部变量与其他三个相比,生命周期不同,分配时间不同,分配位置也不同
3.7 线程池,内存池,对象池,链接池
池(Pool)技术在一定程度上可以明显优化服务器应用程序的性能,提高程序执行效率和降低系统资源开销,池带来了一下优点
- 减少对应资源频繁创建带来的系统开销
- 对象复用,加快程序执行的效率,如链接池,使用已经存在的链接,少了不必要的握手步骤
- 减少资源浪费,如内存池减少了内存泄漏的风险,也减少了内存碎片的产生
- 便于管理,可以有效动态调整,更加合理的分配系统资源
3.8 指针,引用,const使用
对于这三个概念我们都清楚,但是我们要牢记他们的不同
- 指针,引用都是指向了内存中的地址,所以引用本质上也是一个指针
- 引用不可以为空,当被创建的时候,必须初始化,而指针可以是空值,可以在任何时候被初始化
- sizeof 引用得到的是所指向的变量(对象)的大小,而sizeof pointer”得到的是指针本身的大小,在64位机器下为8字节
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有const指针,但是没有const引用,指针可以有多级,但是引用只能是一级
- 如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄漏
- 在语法概念上,引用就是一个别名,没有独立空间,和其引用实体共同使用同一块内存空间,但是对于底层实现来说,实际上是有空间的,我们可以发现底层实现引用和指针的方式一模一样
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理,引用比指针使用起来相对更安全
3.9 拷贝构造函数,==,=,<, >操作符重载
对于一个类而言,如果实现上面几个函数的操作在使用上类将变得安全,方便
- 拷贝构造函数,=操作符重载将帮助我们更安全的使用类,特别注意深度拷贝,防止内存丢失,或者越界访问,引起程序奔溃
- ==,<,>操作符重载,方便我们对同类对象进行比较,同时在我们使用stl排序算法是更加安全,方便
同时我们也有清楚一下区别
- 拷贝构造函数生成新的类对象,而赋值运算符不能
- 由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先把内存释放掉
在下列行为发生时,会调用拷贝构造函数
- 一个对象以值传递的方式传入函数体
- 一个对象以值传递的方式从函数返回
- 一个对象需要通过另外一个已存在的对象进行初始化
3.10 左值,右值
左值:可以放到等号左边的东西叫左值。可以取地址并且有名字的东西就是左值,一般包含函数名,变量等
右值:又可以区分如下
- 纯右值:在编程时,不可以取地址的东西,一般如字符串常量
- 蒋亡值:本身可以被取地址,但是马上就不能被取地址了的东西,一般如将要被移动的对象、T&&函数的返回值、std::move函数的返回值、转换为T&&类型转换函数的返回值
右值是能够赋值给左值,但是左值不能赋值给右值
3.11 main函数之前执行什么操作
操作系统创建进程之后,会把控制权交给程序的入口函数,接下来会做如下几件事情
- 设置栈指针,初始化static静态和global全局变量,即data段的内容
- 将未初始化部分的赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容
- 运行全局构造器,估计是C++中构造函数之类的吧
- 将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数
3.12 泛型编程
编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础
3.12.1 函数模版
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
template
返回值类型 函数名(参数列表){}
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:
- 隐式实例化:让编译器根据实参推演模板参数的实际类型
template
void Swap(T &num1, T& num2)
{
...
}
//交换整形
int a = 10, b = 20;
Swap(a,b); //隐士实例化
- 显式实例化:在函数名后的<>中指定模板参数的实际类型
//显示指定模板参数类型为int
int a = 10, b = 20;
Swap(a, b); //显示实例化
//显示指定模板参数类型为float
float c = 10.55f, d = 3.14f;
Swap(c, d); //显示实例化
3.12.2 类模版
template
class 类模板名
{
// 类内成员定义
};
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
3.12.3 模版特化
通常情况下使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。
对于模版的特殊,归纳总结如下:
- 必须要先有一个基础的函数模板
- 关键字template后面接一对空的尖括号<>
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型
- 函数形参表必须要和模板函数的基础参数类型完全相同
格式如下:
template< > //空括号
bool IsEqual(const char* const& left, const char* const& right)
{
return strcmp(left, right) == 0;
}
特化根据初始化量,特化可以分为
- 全特化:即是将模板参数列表中所有的参数都确定化
- 偏特化:任何针对模版参数进一步进行条件限制设计的特化版本
4 功能模块归纳总结
4.1 STL: Standard Template Library
- 标准模版库
- 四大组件:算法、容器、函数、迭代器,适配器,分配器
- 优点
- 和编译器无关,是C++的一部分
- 数据和操作分离,数据由容器类别加以管理,操作则由可定制的算法定义,迭代器是中间者,使算法可以和容器交互运作
- 高性能,高可用性,高移植性,跨平台
- 缺点
- 更新慢,并且不是线程安全的
- 内部结构复杂,使用模版实现,导致代码冗余膨胀
4.2 boost:https://www.boost.org/
- Boost是为C++语言标准库提供扩展的一些C++程序库的总称。Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一,是为C++语言标准库提供扩展的一些C++程序库的总称
- 各种模块(工具,时间日期,异步,线程,进程,智能指针,函数,文件IO)
- http://zh.highscore.de/cpp/boost/introduction.html(简易文档)
4.3 zeromq:https://zeromq.org/
- 官网:https://zeromq.org/
- 跨平台,同时支持android,同时支持多种语言API,如C++,Java,Go等
- 能够提供进程内(inproc)、进程间(IPC)、网络(TCP)和广播方式的消息信道,只需要修改协议类型就可以启用(从"ipc:///xxx"改为"tcp://xxx)
- 需要主要理解一下几种基本通信模式
- 请求-应答(REQ-REP):最基本的客户端请求,服务器应答模式,类似CS
- 发布-订阅(PUB-SUB):PUB只发送消息,SUB订阅消息,可以实现N-M的消息传递
- 推拉模式(PUSH-PULL):当你需要做并行处理时你会用到这种模式,他允许你将任务推送到并行执行的Worker节点 上,Worker执行完成后再将结果发送Pull节点上做结果合并
- 可伸缩,组网灵活,支持路由功能,可以部署成集群
4.4 压缩:一个具有高压缩比的压缩解压库
- https://github.com/madler/zlib:可以压缩,解压文件
- boost zlib:需要boost_zlib库支持,侧重于二进制内存数据
4.5 加解密:OPENSSL,Crypto++,Botan,Botan
- crypt:https://github.com/weidai11/cryptopp
- Botan:https://botan.randombit.net
- 对称加密(对称加密就是加密解密都用同一个秘钥,比如DES、3DES(TripleDES)和AES等)
- 非对称加密(加密和解密不是用的同一种秘钥,比如RSA算法、DSA算法、ECC算法、DH算法等,可用于数字签名和数字鉴别)
- 国密:https://gitee.com/mirrors/GmSSL,特别需要熟悉SM2,SM3,这两个级别使用最多
- 证书:使用X.509v3加密,使用者如ssl, https, fabric等
4.6 log4cpp:https://log4cpp.sourceforge.net
4.7 消息序列化
消息序列化和反序列化是网络消息传递最基本的处理,最常见的是xml,json,二进制,通过比较二进制是序列化,反序列化,网络传输最高的消息协议。
- protobuf:https://github.com/protocolbuffers/protobuf
- RapidJSON:https://github.com/Tencent/rapidjson
- cjson:https://github.com/DaveGamble/cJSON
- TinyXML:https://github.com/leethomason/tinyxml2
使用比较
- 二进制序列协议,一般用于系统内部之间的消息传递,也可以用于自己客户端与服务之间消息的传递,一般不作为对外的消息格式
- xml,json既可以作为系统内部之间的消息传递,也可以作为对外通信的消息格式,尤其是json,具体可参考学习RESTful设计风格
4.8 RESTfull设计风格
- 即Representational State Transfer的缩写,一种软件设计风格,并不是标准,他只是提供了一组设计原则和约束条件
- 在url中可以体现版本号,可以根据http不同的method,进行不同的资源操作
- 有返回值,而且格式为统一的json格式
- 在C++方面可以参考如下:https://github.com/Nykseli/c-rest-api
4.9 分布式远程调用
在分布式系统中,通过RPC远程调用构建了服务之间的相互连接,使所有的服务组成一个有机整体,对外提供完整服务。所以RPC中间件是服务之间通信的基础,所有的RPC框架都有以下共同的点
- 完整的序列化/反序列化协议
- 完善且安全的远程通信方式,一般通过TCP,UDP实现
Grpc,Ice是比较常用的两种rpc框架,各有优缺点
4.9.1 GRPC:https://github.com/grpc/grpc
- 由 google开发的一个高性能、通用的基于ProtoBuf(Protocol Buffers) 序列化协议开源RPC框架,同时支持大多数流行的编程语言
- 主要面向移动应用开发且基于HTTP/2协议标准而设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特
- 可以利用插件的形式支持多种授权机制,如SSL,OAuth2
- 浏览器支持较少,Protobuf 将 gRPC 消息压缩成非可读格式,需要反序列化才拿到消息格式,不好调试
4.9.2 ICE:https://github.com/zeroc-ice/ice
- 跨平台支持,支持Android,IOS,Linux,macOS,Windows等
- 多程序语言支持,支持C++,Java,Objective-C,Python等
- 高效,高性能的二进制协议,具有类型安全的API,使用程序语言无关的IDL,Slice定义
- 通过利用平台的本机SSL/TLS堆栈,支持安全,加密的通信
- 支持客户端调用和服务器端分派的同步和异步调用
- 提供了Ice-Grid工具,类似DNS服务器,支持服务器部署,复制,监控,负载平衡等
- 利用 Glacier 机制,可以方便地实现穿越防火
4.10 网络库
- libevent:https://github.com/libevent/libevent
- boost.asio:boost异步组件网络部分
- 此外还有ace,libev,POCO等网络库,有C++版本的,也有C语言版本的
对于网络方面,我们不止要知道如何使用网络库,一些网络最基本的概念和原理也要掌握
- 链接的五元组分别是什么,了解常用协议工作的网络层(TCP/IP,HTTP,ICMP,FTP等),百万并发的原理是什么
- TCP/IP是如何建立连接的,理解三次握手,四次挥手的原理,他们分别发生在那些阶段(listen,accept,connect)
- TCP/IP协议基本结构,理解滑动窗口,TCP是怎么保证可靠传输的
- 理解TCP/UDP,区分TCP与UDP的使用场景,能否使用UDP实现可靠传输
- UDP网络穿透是怎么回事,穿透了有什么好处,P2P是什么原理
- TIME_WAITE是怎么回事情,出现大量TIME_WAITE是什么原因引起的,如果出现了怎么解决
- 阻塞与非阻塞的区别是什么,数据从send开始到发送,都经历过那些阶段
- 编写网络程序时,我们最好建立发送与接收的缓冲区
特别需要了解一下epoll,epoll是一种I/O事件通知机制,是linux 内核实现IO多路复用的一个实现
- 水平触发:
- 对于读操作,只要缓冲内容不为空,LT模式返回读就绪
- 对于写操作,只要缓冲区还不满,LT模式会返回写就绪
- 边缘触发:
- 当被监控的文件描述符上有可读写事件发生时,有事件通知
- 边缘出发有可能会出现“惊群”效应,短时间内唤醒所有等待线程
注意:虽然epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调
4.11 线程
- boost.asio中关于线程的模块
- 线程与进程的定义,他们之间又何关联
- 进程内资源可以共享访问,线程在进程内运行,一个进程至少有一个线程运行(主线程)
- 线程最多可以开启多少个(2N+2),这个数值是怎么来的
- 线程之间需要同步,才能保证数据的访问正确
- 互斥量:强调资源之间的访问互斥:每个线程在对共享资源操作前都会尝试先加锁,加锁成功才能操作,操作结束之后解锁,未解锁时,其他线程阻塞于枷锁函数上
- 读写锁:读写锁和互斥量类似,是另一种实现线程同步的方式,但是它将操作分为读、写两种方式,可以多个线程同时占用读模式,这样使得读写锁具有更高的并行性。相较于互斥锁而言读写锁有一定的性能提升,应对的是单写多读模型
- 条件变量:本质上也是一个多线程间共享的全局变量,它的功能是阻塞线程,被阻塞的线程直到接收到“条件成立”的信号后才能继续执行
- 信号量:本质上是一个非负的整数计数器,用于控制公共资源的访问,也被称为PV原子操作。信号量分为有名信号量和无名信号量,无名信号量用于线程同步,有名信号量一般用于进程之间管理
- 如何建立线程池,线程之间消息的传递
4.12 进程
- 进程之间独立运行,单一进程奔溃不会影响其他进程的运行,进程之间的资源是不能相互访问的
- 进程之间要通信,有以下几种方式,只列举常用的
- 管道:特别需要了解有名管道
- 信号:用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身,linux一般使用函数sigal来链接信号处理函数
- 共享内存:通信效率最高,使得多个进程可以访问同一块内存空间,往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥
- socket:主要用于远端不同机器之间的通信,同机器之间也可以使用
- 对于socket通信方式的特别强调
- 分布式网络库:如ice,grpc等
- 常链接如ace,libevent等
- 其他的建立在TCP的协议如telnet,http,ftp等
- 如果进程有对同一资源的竞争,需要用到进程锁,本人经历的项目中没有用到过
- 守护进程:一种特殊的运行于后台的进程,用于执行特殊任务,很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。linux后台就运行了很多守护进程
4.13 数据库
数据库有MySQL、Oracle、SqlServer、SQLite、INFORMIX、Redis、MongoDB、HBase、Neo4J、CouchDB等
下面三种数据库最具代表性
4.13.1 sqlite:https://sqlite.org/index.html
- 嵌入式数据库,没有额外依赖,储存在单一磁盘文件中的一个完整的数据库,便于移动,备份等
- 完全开源,支持多种开发语言,C, C++, PHP, Perl, Java, C#,Python, Ruby等
- 功能齐全的的SQL实现、支持事务操作
- 无网络延迟、用户认证、权限管理,单机速度极快
4.13.2 mongodb:https://www.mongodb.com/
- C++ API:http://mongocxx.org/api/mongocxx-v3/index.html
- 基于分布式文件存储的数据库,是非关系型数据库,支持的数据结构非常松散,是类似json的bson格式
- 支持集群分布式部署,可以使用分片集群提升查询性能,支持复制和自动故障转移
- 每个数据库都是独立的,有自己的用户,权限,独立存储集合
4.13.3 mysql:https://www.mysql.com/
- 免费,社区活跃丰富,学习资料也特别丰富
- 最流行的关系型数据库管理系统之一,为多种编程语言提供了 API。这些编程语言包括 C、C++、Python、Java、Perl、PHP、Eiffel、Ruby,.NET和 Tcl 等
- 支持多种存储引擎,熟悉下列两种核心
- InnoDB:事务安全的存储引擎,它具备提交、回滚以及崩溃恢复的功能以保护用户数据,还支持外键约束
- MyISAM:既不支持事务、也不支持外键、其优势是访问速度快,但是表级别的锁定限制了它在读写负载方面的性能,因此它经常应用于只读或者以读为主的数据场景
- 同样支持集群部署,主从备份
4.13.4 总结
- 从sql请求量来看,查询的量要远远大于写入量
- 无论使用哪种数据库,对于索引要特别注意,好的索引会大大加速查询的效率
- 单表不能太大,太大了到一定级别,效率会急剧下降
- 注意数据库的安全性,注意密码,端口等安全重点
4.14 程序异常
现在高级语言基本上都支持异常的处理,如Java,C#等,C++也不例外,只是C++的异常需要主动抛出来,如果不抛出来,程序有可能直接当机。
4.14.1 C处理错误
- 终止程序,如assert
- 返回错误码
- C 标准库中setjmp和longjmp组合
4.14.2 C++ 处理错误
由于C++兼容C,所以它可以完全使用C的错误处理机制。
除了使用C的错误机制之外,C++还提供了另外一种错误处理方式异常,使用过程如下
- throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
- catch: 在您想要处理问题的地方,通过异常处理程序捕获异常.catch 关键字用于捕获异常,可以有多个catch进行捕获。
- try: try 块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个 catch 块。
try {
// 保护的标识代码
}
catch( ExceptionName e1 ) {
// 捕获处理e1异常
}
catch( ExceptionName e2 ) {
// 捕获处理e1异常
}
catch( ... ) {
// 忽略所有异常
}
使用C++的异常具有以下优点
- 异常对象的抛出,相比于错误码的方式可以清晰准确的展示出错的各种信息,甚至还可以包含堆栈的信息,这样可以很好的定位程序的Bug
- 使用异常,可以更好的处理程序错误,并且可以将异常逐级上报
- 异常可以自定义,可以更加精确的帮助我们处理错误信息
5 此外值得研究的开源代码或者关注的知识点
5.1 redis:https://redis.io
即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
5.1.1 Redis五种基本数据类型
- 字符串:String是Redis最基础的数据结构类型,它是二进制安全的,可以存储图片或者序列化的对象,值最大存储为512M
- 哈希:在Redis中,哈希类型是指v(值)本身又是一个键值对(k-v)结构
- 列表:列表(list)类型是用来存储多个有序的字符串,一个列表最多可以存储2^32-1个元素
- 集合:集合(set)类型也是用来保存多个的字符串元素,但是不允许重复元素
- 有序集合:已排序的字符串集合,同时元素不能重复
5.1.2 使用Redis的好处
- 数据存在于内存中,读写速度非常快
- Redis支持事务、持久化、LUA 脚本、LRU 驱动事件
- 支持多种网络模式
- 主从模式:至少需要两台redis服务器,一台主节点(master)、一台从节点(slave),组成主从模式的Redis集群。通常来说,master主要负责写,slave主要负责读,主从模式实现了读写分离
- 哨兵模式:Redis Sentinel是Redis的高可用实现方案,它可以实现对redis的监控、通知和自动故障转移,当redis master挂掉之后,可以自动拉起slave提供业务,从而实现redis的高可用。为了避免Sentinel本身出现单点故障,Sentinel自己也可采用集群模式
- 集群模式:redis的集群模式中可以实现多个节点同时提供写操作,redis集群模式采用无中心结构,每个节点都保存数据,节点之间互相连接从而知道整个集群状态,集群模式最复杂,他涉及功能分片
- Redis常常用来开发分布式锁
5.1.3 Redis高效的原因:
- 基于内存的实现,数据直接读取,没有IO操作,方便快捷
- 高效的数据结构,如在redis中,列表是双端列表,哈希是字典外加压缩链表
- 合理的数据编码,
- 合理的线程模型,Redis是单线程模式,这里的单线程只是单指数据操作与访问在单线程中进行,采用actor设计模式
5.1.4 使用Redis需要注意以下几点
- 缓存穿透:通俗点说,读请求访问时,缓存和数据库都没有某个值,这样就会导致每次对这个值的查询请求都会穿透到数据库,这就是缓存穿透。
- 缓存雪崩:指缓存中数据大批量到过期时间,而查询数据量巨大,请求都直接访问数据库,引起数据库压力过大甚至down机
- 缓存击穿:指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到db
5.1.5 推荐博文:
- Redis它主要用来什么的:https://blog.csdn.net/u014723137/article/details/125658176
- Redis详解:https://blog.csdn.net/weixin_45683550/article/details/122565733
- 读懂Redis:https://developer.aliyun.com/article/878802?utm_content=m_1000332476
5.2 nginx:http://nginx.org
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。
Nginx作为负载均衡服务:Nginx 既可以在内部直接支持 Rails 和 PHP 程序对外进行服务,也可以支持作为 HTTP代理服务对外进行服务。
Nginx代码完全用C语言从头写成,已经移植到许多体系结构和操作系统,包括:Linux、FreeBSD、Solaris、Mac OS X、AIX以及Microsoft Windows。Nginx有自己的函数库,并且除了zlib、PCRE和OpenSSL之外,标准模块只使用系统C库函数。而且,如果不需要或者考虑到潜在的授权冲突,可以不使用这些第三方库。
了解Nginx,需要了解一下知识点:
- 了解它的4大功能,并且了解相关的配置,特别是代理,负载均衡
- 限流控制,黑白名单
- 高级一点,还应该了解nginx的模块开发
基于Nginx的相关特性,Nginx就成为了构建分布式系统必不可少的组成部分,它主要负责接收外部请求,代理转发,负载均衡,访问控制等,一起组成一个服务集群。
5.3 EOSIO:https://github.com/EOSIO
EOSIO(Enterprise Operation System)是区块链行业活跃度很高,代码也很完整的开源程序,采用C++代码实现。
区块链相关的资料,可以通过如下链接去做简单了解
- 什么是区块链:https://baike.baidu.com/link?url=1PTQ9hQsf12LSnzI2c37c96snxKhNGrN0LThrstJ4guhM2DGcWQJ_OMXlgxntRew5iUdFvTOIj0nbQeueqMuArDcW0o5Ek78MCZmbTL8dUTEQMgZRzgeucj4Rd9uUGM_
- 区块链主要技术点:https://www.cnblogs.com/flyxiao2000/p/13687143.html
- 区块链相关纵览:https://blog.csdn.net/whg1016/article/details/126453497
EOSIO的代码值得去详细研究,理由如下:
- 区块链一个很前瞻的行业,国家也在鼓励大家区研究区块链,特别是联盟链
- EOSIO代码完整,跨平台,编译完成,可直接部署
- EOSIO的代码结构清晰简单,所有关键模块采用插件式方式加载,比较独立,方便阅读
- 里面大量使用了boost相关的知识点,如网络,线程池,大数字(256整数),线程同步,内存映射等
- 通过Http_Plugin插件,了解如何在C++实现rest风格的接口
- 仔细阅读pack/unpack, reflect代码掌握一种全新高效的序列化。反序列化方法
- 通过chainbase掌握一种基于文件内存映射的高效数据库设计方案
- 阅读cleos代码,了解如何更简便快捷的去开发命令行程序
- 通过阅读钱包代码,了解加密解密,公私钥等相关库的使用
- 仔细阅读fc里面的代码,在代码里面可以看到rpc,反射,职能指针,时间等操作,甚至可以修改成为自己的开发库
- 熟悉整个交易的执行流程,除了接触到线程池,多索引容器等Boost模块,更会让你对C++异常处理有新的认识,学习利用异常来进行错误处理
- 阅读CDT代码,可以加深你对编译器的了解
5.4 ffmpeg:https://github.com/FFmpeg/FFmpeg
随着短视频的火热,音视频编程也变的热门,要掌握音视频编程,首先须了解音视频方面的基本概念
- 视频帧:是视频的一个基本概念,表示一张画面,如上面的翻页动画书中的一页,就是一帧。一个视频就是由许许多多帧组成的。
- 分辨率:分辨率可以理解为视频或者图片的大小,如常见的1280 * 720,1980 * 1080等等
- 除了上面两个最基本的概念,还应该知道码率,帧率,采样的频率,比特率等
音视频为什么要压缩,了解I帧,P帧,B帧,需要知道编码与解码。
了解相关传输协议:如HTTP/HTTPS,RTMP,RTSP/RTP,WebRTC等
5.4.1 ffmpeg模块介绍
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。
FFmpeg是一套跨平台程序,同时也支持移动平台。比如在Android下使用,需要使用NDK对FFmpeg重新编译。
5.4.2 ffmpeg主要包含以下模块
- libavformat:用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能;
- libavcodec:用于各种类型声音/图像编解码
- libavutil:包含一些公共的工具函数
- libswscale:用于视频场景比例缩放、色彩映射转换
- libpostproc:用于后期效果处理
- ffmpeg:该项目提供的一个工具,可用于格式转换、解码或电视卡即时编码等
- ffsever:一个 HTTP 多媒体即时广播串流服务器
- ffplay:是一个简单的播放器,使用ffmpeg 库解析和解码,通过SDL显示
5.4.3 备注
了解ffmpeg核心从以下几个方面入手了解
- 重要的结构,如AVpacket,AVFrame等
- 阅读ffplayer,明白如何读取一个流文件,如何使用ffmpeg进行解码
- 继续阅读ffsever,让我们对推流,拉流的了解
- ffmpeg是一个命令行工具,提供了视频操作的各种命令,了解它会使我们明白,在直接使用ffmpeg时,怎么使用它的那些库函数,继而慢慢的加深对ffmpeg的了解
- 建议在windows下通过一个具体的操作调试ffmpeg程序,这样能更加深刻的加深对ffmpeg的内部了解
了解了音视频的基本概念,以及ffmpeg编解码库的使用,那么接下来就应该了解时下比较热火的音视频直播领域,以下知识点是需要我们了解的:
- 推流:将采集到的音视频推送到流服务器,涉及音视频采集,编码,最终采用那种协议进行内容推送
- 拉流:从流服务器获取推送上来的音视频流,涉及拉流协议,解码,View播放
- 了解音视频服务器的后端架构,为什么需要使用CDN,百万直播是如何实现的
5.5 额外需要关注的知识点
除了了解一些必要的开源项目,掌握其使用和原理之外,下面一下额外的知识也需要我们去简单了解一些(下面都是作者用到过的)
- 一些网络攻击,如DOS,DNS攻击,重要端口防范等
- CDN概念,CDN加速,及其工作流程
- Raft,Kafka等一致性协议及其概念
- DPos,Pow等共识协议
6 使用过开发库或者平台
6.1 QT:https://www.qt.io/
跨平台的C++ GUI库,它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器,使用QT,我么需要掌握以下技术点
- 信号和槽,以及事件机制
- 元对象系统,特别要理解moc工具动态创建中间代码的过程
- 支持 2D/3D 图形渲染,支持 OpenGL
- 了解和熟练使用以下重要模块
- Qt Core:所有基于Qt的应用程序的基础,其他模块使用的核心非图形类。提供信号与槽的对象间通信机制,并发和多线程,容器,事件系统,插件和I/O设施。
- Qt GUI:最重要的GUI模块。图形用户界面 (GUI) 组件的基类。包括 OpenGL。
- Qt Network:网络抽象层。完整支持TCP, UDP, HTTP, TLS, SSL(Qt4)和SPDY(Qt5.3)。
- Qt Network:网络抽象层。完整支持TCP, UDP, HTTP, TLS, SSL(Qt4)和SPDY(Qt5.3)。
- Qt Multimedia:音频、视频、广播和相机功能类。
- Qt SQL:包含使用SQL进行数据库集成的类。
- Qt WebEngine:一套新的Qt Widget和基于Chromium的QML webview api。
6.2 Devexpress:https://www.devexpresscn.com
功能丰富,应用简便,而且界面华丽,更方便定制的GUI开发库,特别是window平台下C#和Devexpress结合,使GUI开发更加方便快捷,使用Devexpress我么需要掌握以下几点
- Windows Controls
- Web Controls
- Reporting / Printing Suites
- IDE Productivity Tools
- Business Application Frameworks
6.3 Unity:https://unity.com/
实时3D互动内容创作和运营平台。包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助Unity将创意变成现实。 Unity平台提供一整套完善的软件解决方案,可用于创作、运营和变现任何实时互动的2D和3D内容,支持平台包括手机、平板电脑、PC、游戏主机、增强现实和虚拟现实设备。
使用Unity,着重要掌握以下几点:
- 理解Monobehavior类,Component类等重要类
- 理解对象,摄像机,碰撞等概念
- 掌握预制体,Assetbundle,各个平台打包方式
- 理解Unity的资源组织方式,掌握重要文件夹的作用,如Resources,Plugin,Editor,StreamAssets
- 掌握一种热更新方式,理解热更新的原理,知道热更新的代码是如何加载进入执行空间的
- 掌握在Unity中多线程的使用,注意Unity是单线程
- 掌握材质,进而掌握shader
- 掌握如何开发Unity工具,插件的方法,可以显著提高项目开发的效率
6.4 Cocos2dx:https://www.cocos.com/
Cocos2d-x 是一款国产的开源的手机游戏开发框架,基于MIT许可证发布。引擎核心采用C++编写,提供C++、Lua、JavaScript 三种编程语言接口,跨平台支持 iOS、Android 等智能手机,Windows、Mac 等桌面操作系统,以及 Chrome, Safari, IE 等 HTML5 浏览器。
使用cocos,着重需要以下几点知识:
- 掌握重要类,特别是CCObject类以及类中的引用计数
- 不同平台的打包方式,及对应的资源组织方式
- 掌握最基本的模块应用,如图形渲染,资源管理,本地存储,网络支持等
- 特别掌握脚本绑定,以便于更好理解cocos的热更新方案
- 深入研究代码,进而可以掌握NDK,理解如何在Android中使用C++编程
7 容器
7.1 Docker
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口
了解Docker首先需要掌握以下概念:
- 容器:一个Docker镜像可以实例化出来多个容器,而每一个容器之间是独立运行的,没有任何依赖,程序在docker实际上运行出来是容器
- 镜像:Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)
- 仓库:Docker仓库提供一个注册服务器,用于存储多个仓库,而仓库是用来存储镜像位置的
那么我么为什么要使用Docker:
- 方便运营:将需要部署的程序和运行环境制作成镜像,可以很方便的维护与部署,而且docker运行实际上就是启动了一个进程,它的系统开销很小,可以快速启动和停止
- 资源隔离:Docker是一个沙箱机制,所以不同之间的Docker容器资源是不互相干涉的,可以很好的起到保护和隔离资源的作用
- 制作方便:可以使用Docker语言,快捷方便的完成部署
- 提供Rest风格API:通过API可以很方便的管理Docker,并且和我么的项目进行有机结合
7.2 VMWare
vmware可以通过模拟内存,硬盘等系统硬件,可以在同一机器上安装各种操作系统。
虚拟机的使用和一般软件的使用步骤一致,一般遵循以下步骤:
- 安装VMware软件
- 安装需要的操作系统
- 在虚拟中安装必要的软件
- 在虚拟中开发项目,或者部署项目
7.3 使用选择
- 在这里特别强调一下虚拟机中的快照功能,如果在开发调试项目中,遇到重要的节点,可以通过打快照,保存当时运行的环境,建立还原点,如果系统崩溃坏掉,可以通过快照快速恢复,加速我么的开发,这一点在测试中尤为重要,可以给我们模拟各种系统环境。
- 在实际使用中,综合docker的优点,比方说启动块,资源消耗少,交付简单,所以会应用多点,虚拟机多用在测试环境中,用于模拟不同的工作环境
8 推荐的书籍
市面上,技术方面的书籍很多,有系统性介绍的,也有专题介绍的,看书建议可以遵循一下几点:
- 如果是初学者,建议去看系统性方面的书,看完可以对技术有个整体的认识
- 如果你有基础了想提高,建议去看专题方面的书,关注其中一个点
- 如果你觉得书上提到的还不满足,那就建议你去看开源项目
- 现在不像过去,获取知识的途径很多,电子书一大堆,网络课程到处都是,按自己喜欢的方式去学习
下面对于C++方面,推荐一些书:
- c++ primer:主要了解C++的关键语法,概念,命名方式等基本概念,简单来说,这本书是个工具书
- c++ 编程思想:着重理解面向对象的概念,理解何为对象(一切皆为对象),如何合理的封装
- Effective C++:理解重要的知识点,写出高效的C++程序
- Effective STL:深入理解STL,并且高效的使用STL,如容器的动态增缩,容器中对象知道copy的重要性,删除对象时的陷阱
- 深入探索C++对象模型:理解C++对象在内存中的布局,更加深刻理解多态,理解虚表,理解字节对齐等
- C++高效编程(内存与性能优化):介绍一些编程技巧,如对象池,谁申请谁释放原则,数据存储和运行时的载入方式等
- 深入浅出设计模式:介绍程序设计的思路与模式,以达到低耦合,高内聚等效果,更需要理解掌握如单例模式,工厂模式,建造者模式,生产消费模式,适配器模式,观察者模式,访问者模式等
- windows核心编程:介绍windows下如线程,进程,网络,IO,Socket等方面的编程
- unix网络编程:主要介绍unix下的编程,同时需要掌握unix下线程,进程方面的编程,理解守护进程的概念
- ffmpeg基础库编程开发:介绍了ffmpeg音视频库最基本的使用,属于入门级别
9 总结
总管自己十几年的程序生涯,关于工作与学习,总结大体经验如下:
9.1 实际项目是第一老师
- 尽量参与核心项目,核心项目才能商业落地的可能,更能碰触实际中生产问题
- 多看同僚之间的代码,想想它为什么这么写,这么写有哪些好处,那些坏处
- 主动和你的技术上级多沟通,也要多和直接与你合作的同时沟通技术问题
- 看代码不要局限于一种代码,比方说一个项目它可能是多个语言合作完成的,那么你要都去了解
- 有精力了,部署测试的代码也要去了解,他能帮你了解整个项目的运行逻辑
9.2 阅读开源代码是第二老师
在项目中用到的开源库或者技术,一般在开源网站都能找到相似的,找一个同类的开源项目,仔细阅读,可以获得一下感受
- 不同的设计理念,不同的思路,不同的代码实现,帮助我么对一个问题的思考
- 开源项目中,一般都有demo,容易上手
- 比较有名的开源项目,一般有大神写的源码分析,可以加速对技术的理解
9.3 看视频,阅读技术博客,攻击重要点,难点是第三老师
一个人的精力始终是有限的,要有自己专精的技术,同时也要扩展自己的技术,了解的足够多,足够深,才能更好的提出解决方案。
对于项目中的难点,建议去找专门的技术博客,或者视频,去听听那些大神,同行的理解,或许会给你不一样的理解,比如说你细分的去了解如下技术点
- 如何去实现一个线程池,是自己写好,还是使用现有的库,如boost
- epoll是如何实现高并发的
- tcp/ip协议是个什么样子,它是如何实现可靠传输的
- C++的自动类型推导是怎么回事
9.4 基础知识要扎实,同时扩宽自己的知识面
比方说在C++方面
- 熟练掌握C++的使用以及相关特性
- STL,BOOST是必须要熟练使用的
现在的项目,系统越来越复杂,一个完整的项目,可能需要好多技术有机结合才能完成,掌握混合编程
比方说在游戏服务器中我们可以这样做
- 利用C++编写核心模块,如战斗,登陆,道具等模块
- 利用python编写活动部分,便于动态更新与修正
- 利用lua便携GM接口部分,便于调试
- 如果内部工具只在windows下使用,强烈建议使用C#去实现,不止界面好看,开发效率也高
9.5 一定要有从0到1的项目经验
有了从0-1的项目经历,你会对软件开发有一个认识的升华。永远记住,做项目就为了服务某种群体,服务群体就是为了赚钱。
在整个的项目周期中,在不同的阶段你可以学到不同的知识。
- 项目立项:了解项目的背景,使用场景,现阶段的目标,以后可能出现的困难等等
- 技术选型:了解了项目的出发点,接下来开始做技术预研,提前分析出项目的难点,要有必要的预案,最终确定技术方案
- 系统架构:参与这个过程,会让你对项目的整个体系有个通俗认识,排查问题会更得心应手
- 代码实现:架构确定后,按部就班一步步代码实现,注意有可能是多种语言混合开发进行,此段经历可能会让你成为一个多面手
- 内部测试:测试有可能伴随开发同步进行,有些模块可以进行自动化测试,有些模块必须手动进行,参与测试会让你的技术得到升华
- 运维部署:最后就是部署,放到真正的生产环境来运行,这个经历会让你明白在真实环境中,软件是怎么运行的
9.6 其他
- 要时刻学习,善于总结,这一点看文章也行,听讲座也可以,总之努力学习,技术是向前发展的
- 要有实践精神,动手去跑跑程序,调试下代码
- 多写写,加深自己的记忆,同时锻炼自己的语言功底
- 多关注行业信息,丰富自己的视野,看看最近有哪些现象级的作品产生