面试复习纪要.md

到 30 岁了,突然发现自己还是对编程理解的很浅,在此梳理一下技术栈,并准备新年的面试。

作为一个服务器开发工程师,涉及的技术相当之多,部分角度的深入理解需要花费大量时间,因此到最后一般是精通某个/几个方向,并对其他方向也有涉猎。

必备知识

  1. 基本数学知识。包括:数据结构、算法;

  2. web 相关技术。对于一般以业务为主的公司,这个就是吃饭的本事。细分如下:

    • 通信相关基础知识,如Http,TCP,UDP,Socket编程等;
    • 一门编程语言,一个或多个 web 框架。如 PHP + Laraval, Python + Flask, Ruby On Rails, Java + Spring 等;
    • 理解框架底层原理,如select,epoll或者Java的NIO等设计机制;理解CSP和Actor并发模型;理解大并发下常见的优化策略;
    • RDMS,不管是 oracle、mysql 或者 postgresql,除了基本的使用增删改查以外,还要了解常见的优化措施。随着 NewSQL 的兴起,大量分布式云数据库开始出现,如 TiDB, RedShift 和 HyriaDB 等.
    • NoSQL,如 Redis,MongoDB 或者 cassandra;
    • 此外还有内存数据库,其使用和 rdms 基本一致,如 Apache ignite;
    • 以上数据库针对不同场景和数据量级,做不同的技术选型;
    • Linux 的常见使用,运维方向的一些基础知识(Python 和 shell 是必备技能);
    • 常见应用层协议:http, https, websocket,rpc
  3. 分布式相关技术。随着业务的扩展,后端架构的复杂度会指数级上升,单机的性能无法满足业务需求,必须引入分布式系统。

    • 基本理论。分布式系统非常复杂,必须熟读相关理论,如 CAP,Paxos 等,知道常见的问题和解决方案;
    • 常见组件,如 docker, etcd等,各自的使用场景和原理;
    • 常见框架,如 Spring Cloud 以及最新的 Service Mesh 的实现;
  4. 测试/运维相关技术。如果在小公司,一个后端开发一般同时担任部分运维和测试的职责。

    • 版本控制相关,严格来说正确使用 git/svn 是所有开发的基本能力;
    • 自动构建,快速部署。Docker 相关技术;
    • 线上监控,告警系统;
    • 自动化测试系统;
  5. 大数据相关技术。所谓大数据,其实本质上就是 OLAP,由于数据量过大,rdms 已经无法承载对应的数据量和性能需求。

    • 数据收集:如 flame 等;
    • 数据存储,如 HBASE,hive 等;
    • 数据分析:如 spark、Hadoop 等;
  6. 扯淡的人工智能。 对于大部分公司而言,人工智能是不应被涉足的领域。

深入领域

  1. 整体架构

并发模型

目前web架构,单体应用的并发模型,perfork是典型的多进程思路,一个master进程用于接受并分配请求,fork出的worker进程用于处理实际的请求;而Java的BIO是典型的多线程思路;Go对每个连接启用一个goroutine,是典型的多协程思路。

perfork的worker进程可以用epoll等同时处理多个请求,这是I/O多路复用。Java的NIO也是类似的思路,本质是用事件机制达到更好的cpu利用率,同时加大负载能力。Nginx,uwsgi都是perfork架构的,由于python的GIL问题,uwsgi的worker一般只有一个线程。当然python还有tornado这种框架,也是用event loop的方式实现i/o多路复用。或者说,协程的方式。

对于异步编程,业界最流行的是async/await其实就是协程模型,协程本质是用户态下的线程,用户可以自己手动切换上下文进行让渡(await即可);相比之下,goroutine的调度更加简单,因为他是有栈协程,一旦发生IO阻塞,调度器会自行切换协程,用户在编写代码的时候无需关心这一点(当然也可以自己手动切换,用channel阻塞即可)。在和已有第三方组件的对接上,有栈协程可以吊打promise,因为它就是正常的写阻塞代码就行,而async/await会污染所有相关代码,比较麻烦。

对于Java,默认的BIO就是传统的多线程模型,比如web服务器就是简单的一个请求一个线程,发生IO事件的时候线程也要阻塞等待,浪费cpu;NIO就是收到事件通知(数据就绪)才开始IO(阻塞),也就是io多路复用;AIO就更牛逼了,系统直接告诉你读完了,调你的回调就行。可惜AIO需要操作系统支持,目前只有Windows上的IOCP可以满足这个条件,所以一般不讨论。

此外,linux2.6以前i/o多路复用使用select,之后使用epoll. 2.6以前accept存在惊群问题,之后内核只会唤醒等待队列上任一个进程。但是epoll也存在惊群问题,多个worker在事件抵达的时候会被同时唤醒。直到linux3.9加入SO_REUSEPORT特性,允许多个进程监听同一端口,将listen从master进程移入worker进程,这个问题才得到解决。

单体架构

经典的MVC结构。旧时代里,asp/jsp/php以及集大成者的ror都是mvc结构,前后端不分离,直接由服务器渲染出页面。ror之所以流行,是因为其约定大于配置的设计大幅度简化了开发流程,SpringBoot相对于SpringMVC其实只是引入了这一思想进行简化。

自从前端工程化以来,前后端基本分离,后端仅仅提供API接口,这时候的单体应用更加简单。随着服务规模的越发庞大,单体后端承担的职能越来越多,需要对服务进行拆分以减少体积和维护难度。

SOA和微服务

微服务其实只是对SOA做了更细粒度的优化。拆分服务引来的问题包括:服务间通信和分布式事务。服务间通信可以通过一个集中的渠道,即所谓的通信总线(API网关);也可以直接相互通信。使用HTTP的话,直接用DNS和Nginx就可以做API网关了;使用rpc的话,比如grpc也提供了生成API网关的方法。

微服务还引入了服务熔断、配置分发、服务发现等组件,这些也是需要关注的。

Spring Cloud和Service Mesh

这是两种不同的微服务思路,前者是一个框架,由各种组件构成,应用代码里明确知道这些组件的存在并需要进行处理;后者是外挂式的组件,代码对组件是无感知的,比如连接集群和连接单点都是通过proxy,代码不关心自己连的是什么。一个简单的例子,mysql分库分片可以用proxy来完成,也可以在代码里写死。显然service mesh更符合低耦合的思路,也是微服务真正的未来。

  1. 编程语言

虽然理论上来说,通用设计语言可以完成的事情都是一致的,不过各语言的特性和历史的沉淀导致了如今的情况是这样的:

* 前端领域js独占鳌头,es6以后js语言基本完善了;当然强类型的TS也有它的优势;
* 后端领域Java占了半壁江山,这不是因为Java多么牛逼,完全是历史的沉淀导致。Java至今没有协程支持(project loom遥遥无期),编写繁琐,很多人讨厌。Go是冉冉上升的另一个明星,简单的语法、快速的开发速度、强有力的并发模型;当然缺点是不支持泛型和繁琐的错误处理。Go2会解决这些问题,希望早点来到。除了这两个以外,其他的语言在服务端领域基本上都会被淘汰,比如Python其实更适合做快速原型,所以更适合非工程人士,当然拿来写个脚本也是极好的;
* 大数据领域基本还是Java/Scala的天下,当然基本也支持python了。个人感觉Scala过于复杂了,还是尽量灭了吧;
* 高性能领域仍然是C/C++的天下,Rust也开始逐渐普及。不过Rust还是有很多坑,且门槛太高,短时间内个人不再看好;
* 客户端领域语言就比较复杂了。Windows还是C#的天下,Android则是Java和Kotlin,苹果的是swift和oc;新兴框架中,flutter的dart也是很重要的;

总结来说,当前后端开发应该掌握的语言包括:Bash/C/C++/Java/Python和Go,可以学习的语言包括Rust. php/nodejs/ruby的存在完全是历史意义上的,基本没有存在价值。在通信协议上,HTTP1/2/3都会逐渐流行,websocket的效率则更高。grpc是over http2的,这个设计很蛋疼,很多时候不如自己rpc over websocket实用。

  1. 常用框架源码
  • python的常用框架:django/flask/tornado/greenlet/celery/sqlalchemy
  • java的常用框架:spring/springMVC/springBoot/springCloud/Netty
  • go的常用框架:gin/fasthttp
  1. 常用组件源码
  • db级别的估计只能看看sqlite了
  • mq可以研究一下zeromq、kafka和rabbitmq
  • 各类分布式相关组件
  1. 数据库优化
  • mysql通过执行计划、慢查询日志进行优化,各类索引添加等
  • redis使用优化,事务粒度,数据结构,pipeline等
  • es优化,JVM参数,分词,shard设置等