当我们在写代码时,我们到底在写什么

当我们在写代码时,我们到底在写什么

前序

从实习到正式参加工作以来,我都编写着服务端代码。对于服务端业务代码,总的来说,就是简单的“增删改查”,因为当我们在设计编写某个功能的实现时,到了软件开发这一步,基本上就从数据库的设计开始了。

软件开发,或者说是编程,本质上是用机器语言对我们的显示世界进行建模,针对一个又一个的业务需求,程序员在乎的已经不再是我们如何对这个需求进行建模,而是如何对这个业务产生的数据进行建模。对于数据的建模,简单来说,就是对于存储下来的业务数据怎么保存,怎么查询,怎么更新,更直白的来说,大部分都是考虑数据库表的设计。这么多年以来,我一直遵循着这一办事“基本准则”进行软件的开发。对于数据的存储,大多情况下还是基于关系型数据库,MySQL\Oracle数据库在这一方面大行其道,同时夹杂着NoSQL的流行,像Redis这类K-V数据库也流行到几乎每家公司的面试笔试都会提上一笔,哪怕面试官也仅仅会使用框架自带的工具进行相对基础的操作或者使用一些相对高阶一点的用法,除此之外,大数据发展的前景下普遍使用的HBase、ElasticSearch等在各类应用中不乏有表现自己特性的机会,在软件的整体基础设施中贡献自己的能力。

回到程序员开发后台业务代码的“基本准则”,这么多年来,没有什么人质疑过为什么要设计库表结构,因为这样的确对于业务的开发,功能的确定有极大的帮助,尤其是团队开发中。但是从编程的角度,我们仅仅是对业务的数据进行了建模,我们只知道业务与数据的关系,对于所有的业务逻辑,我们所能做的是如何操作数据,编写一个又一个的函数去搬弄我们一开始定义的业务数据。使用面向对象的编程语言,对于存储在数据库里的数据,从我们的应用软件层面对其进行建模。是的,数据库软件对业务数据进行建模,而我们的编程语言对数据库数据进行建模,而在我们的开发过程中,程序执行的方向,我们的应用程序往往是在数据库前面。用户通过浏览器或者手机APP上的一次操作,请求将先打到我们的应用服务器,然后再经过数据库,当然也不是所有操作都会经过数据库,这完全取决于我们的应用程序。因为存在数据库的设计,我们的编写的代码也间接的对现实世界进行了建模。除了后端应用程序,前端浏览器页面,手机APP界面,本质上也是对现实世界的一次建模,只是从前端的角度来讲,需要考虑的是我要更靠近现实世界一点,还是更靠近为为现实世界建模的数据库建模的后端应用软件一点,两者从软件的表现上,虽然不会有较大的差别,但是从开发的角度上,虽然代码逻辑基本机制,但是代码结构却大相径庭。

程序员理应是虚拟世界的诗人,是哲学家。哲学家通过思考,对世界进行归纳,而程序员通过思考,也对世界进行归纳,片面的说,程序员面向对象的思想,决定了他在软件开发道路上的上限。程序员之所以是诗人,是因为编写代码不是自然语言,而是基于特定规则有着特定语法的语言,虽基于英文单词,但英文单词仅仅是提供了26个字母而已,当然在开发过程中对于世界进行建模时,我们通过英文单词也仅仅是为了提高代码的可读性,为了其他程序员能够更好的理解我们的想法。面向对象的思想,带来了各种各样的设计模式,代码结构随之变得干净整洁,整体结构严谨。程序员双手在键盘上敲击输入一行行代码,正如诗人提笔在纸上写下一行行诗篇。


从前端到后端

当我们在手机APP上或者浏览器上打开一个页面,或者点击一个按钮,发生了哪些事情?

在互联网世界里,我们将一切提供用户交互的软件都当作前端,包括浏览器打开的页面和手机打开的APP。前端的一次交互,如果需要与后端通信,从用户角度,用户委托所使用的前端软件,向具体的后端应用发起一次网络请求,或是从后端应用服务获取某些信息,或是向后端服务传达某些指令。前端软件可以是用户正在使用的浏览器,也可以是用户正在打开的APP,但本质上无论是浏览器还是APP,它们都不具备在网络上传输信息都能力,真正具备这项能力的是所使用的设备,电脑或者手机里安装的的网络设备,而事实上,前端软件也并不能直接驱动设备发起网络请求,它们只能委托操作系统,让操作系统去驱动网卡,从而实现网络调用。

我们在前端的一个交互,前端软件需要向某个运行后端软件的服务器发起一次网络通信,但是我们实际上并不是按照想象的方式操作的,我们看到的是所谓的统一资源标识符,即URI,这个可能是我们所能理解的,但他不是机器理解的。前面提到的网络设备,即网卡,它们是实现网络通信的设备,全世界所有的网卡都有一个唯一的标识MAC地址,对真正执行网络通信的设备来讲,这个MAC地址才是它所能理解的。但是全世界那么多网卡,不可所有网卡都记录下其他所有的设备,而且总有新设备被生产,总有旧设备被淘汰,并且仅有MAC地址,设备之间的通信,互相都不能理解对方意思也不行,因此,TCP/IP协议来了。TPC/IP协议是从人类角度对网络通信的一次建模,通过几层协议,把一次网络通信的过程拆解,并且配合让人类更好理解的IP,把网络中的设备关联起来。至此,我们只需要向某个IP地址发起网络通信即可,由协议把请求发往正确的设备上,但是从人类的角度,IP地址是一串数字,还是难以记忆,因此,人类发明了域名,并且通过一个叫DNS的东西把域名和IP地址进行了关联,这样人类只需要知道按特定往某个具有特殊含义的单词发起网络通信就可以了,不需要知道具体的网络设备。网络世界被人类使用域名和TCP/IP协议进行了一次建模,当然前者面向普通用户,后者面向的是从事网络有关研究工作的网络工程师、面向网络编程的程序员等。

让我们回到上面的问题,当我们打开一个页面,发生了哪些事情?首先我们假设我们正在使用电脑打开了这个地址:https://yeemin.site/2021/06/26/article1, 浏览器发起网络请求,当然实际上是委托操作系统驱动网卡设备实现的,操作系统收到委托,它也不认识这个地址,毕竟这是个有域名的uri,因此操作系统先问问DNS服务器可否识得此物,显然DNS是直到这个地址的,因此它返回了具体的IP地址,当然也有的DNS不认识这个地址,但是它会向比它更高级的DNS服务器询问该地址,一直询问到这个域名所注册的运营商那边,终于拿到了IP地址。但是仅仅拿到IP地址还不够,它还得知道这个网络设备在哪里,因此,我们的电脑里的网卡在自己所在的局域网里询问是否有存在这个IP地址,这个操作是通过集线器实现的,显然是没有的,因此集线器再去问路由器,路由器不管这个,直接把问题抛出,让问题传达至更大的局域网,并最终到达最大的局域网—-互联网。但实际上请求一定会到达网络运营商,这是真正提供网络通信技术实现的部门,运营商具有无数能力卓越的网络设备参与工作,运营商直到每个IP所对应的设备在哪里,当然这个世界存在很多家网络运营商,因此运营商之间也一定会互相通信。到了运营商后,剩下的依旧是走完这次通信链路,网络将继续以TCP/IP协议的形式一层一层转发,直至目的网卡设备,而这个网卡设备收到网络通信后,将会及时告知操作系统,操作系统再告知具体的应用软件,应用软件收到网络请求之后,解析请求报文并返回所需信息,信息返回就是完全相反的链路再走一遍。在这个问题里,由于我没有服务器,是基于github搭建的博客,域名解析在腾讯云,因此请求将会被腾讯云域名解析服务转换域名,并最终到达github的某台专门用于用户搭建博客的服务器上。

前人通过网络协议对网络进行建模并串联整个网络世界,这么多年下来,这个网络模型依旧坚挺,后续的升级也只是从某些角度的加强与提升而已。


服务端软件

前面提到,服务器网卡收到请求之后,会告知操作系统,操作系统再调度具体的应用软件,这个应用软件是承载某个网站、APP后端的程序,一切的一切,由他发生。。。
【更新与2022年9月20日】

【未完待续】