这是一份面向前端人员的服务端学习指南,不管是web端/H5端/小程序端还是原生APP开发人员,都有必要了解下后端的知识体系动态。本文侧重于基础技能,实际操作和目的介绍。
后端时一门综合性的学科,编程只是其中的一个很重要的小点。
这边把前后端简单定义:
- 前端解决数据采集,数据展示,人与数据之间交互的问题。
- 后端解决数据持久化,数据加速,数据与数据之间互联互通的问题。
后端软件结构
no MVC
为什么不讲MVC
,因为随着技术分工的发展,View展示层逐渐退出服务端开发的舞台(也有特殊行业例外,比如地图行业大规模数据展示,SSR
)。后端逐渐往纵深方向深化发展,分布式集群、微服务、海量数据存储和处理、容器化自动编排部署逐渐成为后端标配。
经典后端心理模型图
经典软件数据流向总览
详细数据流向:
典型部署结构
这种部署是通用方案,任何实际方案只是在这个模型上面增减多少的问题。
比如下面是一个完整的实际例子:
了解服务器
现在我们开始详细了解下服务器。服务器可以本质上一台电脑,跟个人电脑类似,只是服务端服务器是放在远程机房的,没有也看不到显示器,需要通过网络进行连接管理。
现在公司使用服务器一般不直接接触机房服务器硬件,主要是租用云服务器,减少人工维护成本。如果是个人学习,可以直接租用服务器,也可以在个人pc上安装虚拟机,例如virtualbox
。
服务器一般有windows
和linux
两种。windows
服务器通过远程桌面连接后就跟个人电脑一样操作了,比较简单方便;linux
也是后端应用最广的操作系统,优势是资源占用少,利用率高,缺点是没有交互界面,只能通过命令行操控。当前使用最广泛的linux
版本是centos
,这里以centos
为例,介绍下怎么操作你的服务器。
ssh
熟悉服务器的第一步,是连接你的服务器。
- 直连
直连服务器一般要使用ssh
客户端软件,可选的有Xshell
,SecureCRT
,Putty
。正常连接一台服务器需要IP,端口,用户名,密码4项。登录成功以后,进入命令行交互界面。 - 跳转连接
公司一般有多台服务器,服务器出于安全考虑,一般不会直接允许随便ssh
连接服务器,会有个统一的入口作为跳板,先登录跳板机,然后再通过命令行间接登录到其他服务器。间接跳转可以跨多级在任意服务器之间任意跳转。
所以这里需要分成2步,第一步直连跳板机,第二步通过ssh
命令行连接其他服务器。
[~]> ssh root@10.81.45.156
如果要退出来回到上一个登录的服务器,直接输入exit
命令退出当前服务器即可。
系统文件目录
centos
操作系统有一系列自己的目录,如下图
一般情况下,不用关心这些目录具体是干什么的,只需要重点了解下命令行所在目录即可:
- /: 根目录,一般根目录下只存放目录,不要存放文件
- /home: 系统默认的用户家目录,新增用户账号时,用户的家目录都存放在此目录下,~表示当前用户的家目录。
- /bin:/usr/bin: 可执行二进制文件的目录,如常用的命令
ls
、tar
、mv
、cat
等 - /sbin:/usr/sbin:/usr/local/sbin: 放置系统管理员使用的可执行命令,如
fdisk
、mount
等。与/bin不同的是,这几个目录是给系统管理员root
使用的命令,一般用户只能"查看"而不能设置和使用。
当然也可以不管上面的目录,遇到使用的命令想找下目录,使用which
命令:
# 查找ls命令路径
[~]> which ls
如何操控服务器文件
后端开发需要知道如何操作服务器上面的文件,比如文件传输,创建,编辑,复制,移动和删除等等。
# 服务器之间传输文件
[~]> scp /home/test.txt root@10.81.45.156:/home/
# 创建文件
[~]> touch test.txt
# 复制文件
[~]> cp test.txt test2.txt
# 移动文件
[~]> mv test.txt /data
# 删除文件
[~]> rm test.txt
# 编辑文件
[~]> vim test.txt
其中重点是编辑文件,命令行界面会进入编辑模式。一般需要熟练掌握的快捷键功能是移动光标定位,删除行,复制行,跳转到指定行,跳转到行尾,删除字符,插入字符,关键词搜索,正向搜索,反向搜索,正则搜索等。
如何安装软件
centos
系统安装软件一般用yum
命令。
yum [options] [command] [package ...]
# 查找软件包命令
yum search <package_name>
# 安装指定的软件命令
yum install <package_name>
# 删除软件包命令
yum remove <package_name>
Linux命令和shell脚本
Linux
有一些常用命令,了解这些命令可以提高工作效率。可以简单的分成几类,一类是文件处理相关,上面已经介绍过了,一类是管理软件和启停服务和进程,一类是硬件相关,比如监控CPU,内存状态,监控网络和端口等等。
实际工作中会出现一些需求,比如定时任务,临时处理下日志文件等,写代码又有点大动干戈的意思,简单一个shell脚本可以很高效处理很多简单事务。shell脚本本质上是多个linux
命令的合集,用来组合多个命令来完成一个综合性任务。
#!/bin/bash
echo "exec test.sh begin" #1,打印日志
chmod u+x ./test.sh #2,使脚本具有执行权限
./test.sh #3,执行脚本
echo "exec test.sh end" #4,打印日志
了解服务器网络
网络是服务器的核心知识,了解网络是后端必备的技能之一。
- 端口
端口是每台服务器与外界沟通的渠道,每个服务器应用服务在启动以后一般同时会启动监听一个或者多个特定的端口,用来接收或者发送数据,提供服务。
# 查看8080端口的监听程序
lsof -i:8080
- 防火墙
每台linux
服务器都有一套自身的防火墙服务,比如centos
,可以开启也可以关闭服务。centos7
之前用的是iptable
,centos7
之后改成了firewall
服务。防火墙是管理端口的工具,可以精细化地控制每个端口的状态。如果采用云服务商的服务器,实际上每台机器的防火墙已经不需要在机器上单独配置了,默认关闭每台机器自身防火墙服务,通过云服务商提供的安全组管理机制可以很方便地进行端口管理。 - 正向代理和反向代理
正向代理:客户端通过它访问任意网站并且隐藏客户端自身来源,例如上网代理,socks
,VPN
等。
反向代理:客户端通过它访问防火墙后面的服务器并且隐藏服务器,比如nginx
, apache
等。
- 端口转发
实际工作种有个比较常用的场景,比如要访问云端内网中某个服务器的端口,但是外网用户是没有办法直连的,这个时候可以在跳板机上建立ssh
端口转发解决这个问题,直接将远程访问变成本地端口访问。 - 端口映射
端口映射简单理解就是访问某个端口,实际上访问的是另外一个端口。典型例子是docker
容器端口映射,可以将服务固定端口改成部署指定的任意其他端口。
认识数据库
数据库有很多种类型,可以大致分成传统通用关系数据库和特定领域用途数据库,也有人很简单分成sql
和nosql
数据库,但是不是很好理解。这里以通用和特定用途为例来列举分析。
传统关系数据库
传统数据库指的是那些经久不衰的数据库,比如MySQL
,Oracle
,SQLServer
。这些数据库的优点是稳定,性能好,而且实现了ACID
特性,事务处理是其强项,适合固定结构的数据存储和维护。
-
ACID
,解决多个客户端同时访问数据库时数据库的一致性问题,即数据不乱
原子性(Atomicity
)、一致性(Consistency
)、隔离性(Isolation
)、持久性(Durability
)。 - 范式,数据库设计规范,数据库表结构设计时数据结构不乱
实际工作中设计规范会与性能进行权衡,但是规范的设计会极大地提高数据的清晰度。 -
SQL
,数据库操控语言,必备技能
缓存数据库
缓存数据库主要用于加速数据获取速度,主要通过简化的数据结构,如key-value
,来获取大大高于传统数据库的读取速度,典型的如redis
。
缓存数据库典型的应用场景是用来加速其他类型数据库:
搜索数据库
搜索数据库主要用于加速数据搜索过程,主要通过细粒度的索引机制,来获取大大高于传统数据库的查询速度,典型的如elasticsearch
。
搜索数据库的核心是倒排索引
:
文档数据库
文档数据库相比传统数据库,主要有以下几个特点:
- 非结构化数据存储和查询,松散数据结构
- 数据记录通过版本解决并发访问问题,而非
ACID
数据管理 - 大数据量下更加易于扩展
典型的如MongoDB
,couchBase
时序数据库
时间序列数据库主要用于指处理带时间标签(按照时间的顺序变化,即时间序列化)的数据,比如监控数据,app位置坐标上报数据,日志数据等等。
时间序列数据库采用特殊数据存储方式,极大提高了时间相关数据的处理能力,相对于关系型数据库它的存储空间减半,查询性能远超过关系型数据库。
典型的如TimescaleDB
,InfluxDB
服务部署
当我们把任何用到的软件都成称之为服务的时候,服务其实都是需要部署的,如何部署服务是一门学问。部署从传统的手动安装部署,到脚本化部署,再进化到容器化部署,然后再集群化部署,方式产生了巨大的变化。
服务器手动运行服务
一般本地运行程序
java -jar XXX.jar #java程序
node XXX.js #node程序
但是服务器这样运行时不行的,linux
程序必须指定后台运行才能一直运行,否则退出远程服务器,当前程序就立即退出了。需要加上nohup
和&
才能正常后台运行。
nohup [command] &
加上输出日志:
nohup [command] >> output.log 2>&1 &
命令 | 功能 |
nohup | 免疫关闭 |
& | 免疫使用 |
>> | 日志重定向,追加 |
2>&1 | 错误级别日志也写入正常日志 |
解释下linux
日志输入输出约定表示数字:
命令 | 功能 | java | node |
1 | 标准输出 | System.out.print | console.log |
2 | 错误输出 | System.err.print | console.error |
docker运行服务
# 运行开源公共服务,比如mysql
docker run --name mysql5.7 -p 3306:3306 -d -v /data/mysql/data:/var/lib/mysql -v /data/mysql/my.cnf:/etc/mysql/mysql.conf.d/mysqld.cnf -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
# 关闭服务
docker stop mysql5.7
# 删除服务实例
docker rm mysql5.7
# 删除服务镜像
docker rmi mysql:5.7
docker run参数解析:
命令 | 功能 |
–name mysql5.7 | 运行程序取个好听的名字 |
-p 3306:3306 | 可以改变服务本身的固定端口号 |
-d | 后台运行,类似上面“nohup &”功能 |
-v /data/mysql/data:/var/lib/mysql | 容器内部的/var/lib/mysql目录实际使用外部/data/mysql/data目录 |
-e MYSQL_ROOT_PASSWORD=123456 | 传一些初始化参数进去 |
集群运行服务
集群解决什么问题:
- 资源浪费 服务器按照使用量自动扩展和收缩
- 部署复杂 自动分配节点自动部署,多套环境切换
我这里把很多种部署统一称之为集群部署,包括swarm
,compose
,k8s
,以及PAAS
,BaaS
,FaaS
,Serverless
等方式。集群如果不是专门干这个的公司,不建议自己搭建,特别是k8s
集群,直接用服务商的即可,页面操作简单易用。
重点了解下Serverless
是什么:
‘If your PaaS can efficiently start instances in 20ms that run for half a second, then call it serverless. ’ Adrian Cockcroft
- 对使用者来讲,
Serverless
=http
接口封装成函数+后端服务,也就是未来前端直接使用函数与后端打交道。 -
Serverless
是未来的大趋势,目前已经有很成熟的应用环境。得益于nodejs
的冷启动能力,nodejs
比java
目前更加适合这种场景。
服务端开发
语言要求
初学者可能认为语言是自由和开放的,想怎么写就怎么写,或者只要能看懂就行了。事实可能会让人失望了,语言从来都不是自由奔放的表达工具,它是流水线的工具。为什么呢?
任何语言最终都是为商业利益服务的,后端语言也不例外。商业的本质是什么,用最少的成本,卖出最多的产品。什么产品最挣钱? 标准化流水线生产的产品,所以开发本质上是一个把复杂需求和思维转换成标准化,流水线式流程的游戏。
那么什么样的语言比较适合干这件事情:
- 不容易出错
强类型,编译期间检查 - 运行时错误管理简单,故障隔离
堆栈跟踪,多线程,运行快照 - 功能强大
OOP,函数式编程,泛型,反射 - 易于多人合作
成熟规范化,流程标准化,工程化
代码规范
- 语言本身规范
比如换行,缩进,标点符号,命名规范,结构清晰。
这类规范一般可以程序化,比如JavaScript
的JSLint
工具,Java
的CheckStyle
工具。 - 逻辑规范
这类规范是无法程序化的,但是也是区分个人能力的关键,好的代码能力源自于好的编码习惯,好的习惯源自于好的思维方式。
虽然很难程序化,但是有很多经典书籍可以学习。比如Effective XX
系列书籍,Java
的Effective Java
,JavaScript
的Effective JavaScript
,初学不懂也没关系,书中的思想会影响深远,在技术飞速发展的今天也可以跨越多年都不过时。 - 设计规范
设计模式是从更高的角度来看待软件工程实践,它强调的是约定俗称的行为模式,这种模式跨越语言,是普世通用的原则。设计模式规范了3件事情:
- 如何创建数据 创建型模式
- 如何组织数据 结构型模式
- 数据之间如何沟通 行为型模式
这里套用一句管理上的名言:搭班子、定战略、带队伍 – 柳传志
写代码三部曲
代码是写给别人看的,写代码本质是确定你的代码如何与外部沟通。
- 确定编码目标,与产品客户沟通
- 确定接口,预先定义接口及其参数,与前端后端沟通
- 考虑如何校验参数,考虑如何处理异常和已知未知错误,与其他模块和大框架沟通
服务端框架
选择一种后端框架,深入阅读官方文档
。
然后选择一个需求,利用框架去实现,遇到问题自己去解决。
服务器日志管理
- 日志打印
console.log("i am 日志"); // 原生日志
logger.info("i am 日志"); // 框架日志
这里以log4X(log4j
,log4js
)为例来说明下常用的日志管理框架。常用日志输出,比如System.out.print
, console.log
直接输出到console窗口。日志管理框架做的事情,就是分解了这个输出过程,把打印日志代码和实际输出逻辑进行了分离,一般分成下面3部分。分离的好处一是可以自由定义输出目标,二是可以动态调试日志输出级别。
应用结构:
- 日志收集
日志打印以后不是就结束了,这些可以用于数据统计,也可以用于故障排查。因为服务端一般是集群架构,日志会分散在多个不同的服务器上面,需要收集,整理合并,用来快速检索。常用的分布式收集框架比如ELK
。
日志搜索查询界面:
代码测试
我们总希望自己的代码没有任何问题,但是很遗憾代码有bug是一种常态,被打脸也是一种常态,太自信的人往往并不适合写代码。
- 如何测试自己的代码
简单业务可以使用单元测试,比如jest
,spring-boot-starter-test
。
如果还涉及到测试前数据初始化和测试后数据回滚,这个就需要引入支持事务测试框架。有些非事务性数据库,比如elasticsearch
,如果要测试就需要自己编写相关初始化和清楚的测试代码了。 - 如何给别人提bug
有时候会发现别人的bug,不管是其他部门同事还是提交到开源社区,需要准备基本的用例规范,这是提高沟通效率的方式。MCVE
(Minimal, Complete, and Verifiable example),简单说就是要提供一个最小化,完整和可以复现的例子来呈现问题。
关于如何学习
很遗憾,就算看了这些内容,对后端开发可能依然是雾里看花。要知道真正值钱的东西大都需要死磕某个痛点,比如压榨性能,需要深入理解内存模型,IO模型;比如框架改写,当通用框架并不能完全满足业务需求时,特定业务需求需要改写并重构某个开源框架源代码。千里之行始于足下。
关于路线选择
在这个技术快速进步的社会,技术人员呈现不同的发展路线,有些人擅长并喜欢研究底层,会在某一个特定领域专研很深。有些人热衷于发现和创建新技术,并不断扩展自己的知识边界,最终迈向了全栈全能的方向。不管哪种线路,都有人很成功,也有人很迷茫。不管怎么选择,只要找到自己内心兴趣的方向,并持之以恒,加以时日,当突破了几次认知瓶颈后,最终都会迎来巨大的收获。