实战:李瓶儿博客开发
http 请求概述
从输入 url 到页面显示中间发生了什么
DNS 解析,建立 TCP 连接,发送 http 请求
server 接收到 http 请求,处理,并返回
客户端接收到返回数据,处理数据(如渲染页面,执行 js)
nodejs 处理 http 请求
- get 请求和 querystring
- post 请求和 postdata
- 路由
MySQL
- mysql 介紹、安裝和使用
- nodejs 连接 mysql
- API 连接 mysql
为什么使用 mysql 而不是 mogondb
- mysql 是企业内最常见的存储工具,一般都有专人运维
- mysql 也是社区内最常用的存储工具,有问题可以随时可查
- 另:mysql 本身是一个复杂的数据库软件,本课只讲基本使用
mysql 介绍
- web server 中最流行的关系型数据库
- 官网可免费下载,用于学习
- 轻量级,易学易用
mysql workbench 可视化数据库
mysql 命令
show databases 显示所有的数据库表
mysql 安装步骤
https://zhuanlan.zhihu.com/p/37152572
操作数据库
操作表
增、删、改、查
使用 sql 语句(入门简单,一学就会)
增
insert into users(username, `password`, realname) values('lisi', '123', '李四')
查
查 users 表所有信息
select * from users;
查 users 表中其中 id 和 username 的信息
select id, username from users;
查符合条件的项 where
select * from users where username='zhangsan'
查符合条件的项多个条件 and
和 or
select * from users where username='zhangsan' and realname='111'
模糊查询 like
select * from users where username like '%zhang%'
查 排序 order by id
默认正序,如果倒序 在 id 后加 desc order by id desc
select * from users where username like '%zhang%' order by id desc;
ps:一般不用 * ,耗性能
改
更新 id 为 3 的 realname 为张三
update users set realname='张三' where id='3'
删
delete from users where realname='李四'
但一般来说不用 delete,二是在 users 表中加一个状态,通过状态来判断他是否被删除。这种技术又称软删除
PS:如果你的更新和删除出现 error:1175 处于安全模式,先使用以下代码解除安全模式
SET SQL_SAFE_UPDATES=0;
登录
- cookie 和 session
- session 写入 redis
- 开发登录功能,和前端联调(用到 nginx 反向代理)
cookie
- 什么是 cookie
- javascript 操作 cookie,浏览器中查找 cookie
- server 端操作 cookie,实现登录验证
什么是 cookie?
- 存储在浏览器中的一段字符串(最大 4KB)
- 跨域不共享
- 格式如 k1=v1;k2=v2;k3=v3;因此可以存储结构化数据
- 特点一:每次发送 http 请求,会将请求域的 cookie 一起发送给 server
- 特点二:server 可以修改 cookie 并返回给浏览器
- 特点三:浏览器中也可以通过 javascript 修改 cookie (有限制)
res.setHeader(
'Set-Cookie',
`username=${
data.username
}; path=/; httpOnly; expires=${getCookieExpires()}`,
);
path:根域名下 cookie 共享
httpOnly:只能服务器修改,浏览器不能修改
expires:设置过期时间
session
cookie 的缺点,会暴露 username,很危险,不安全
cookie 可以存储 userid,server 端对应 username
cookie 放不重要的数据,放 userid(一个标识)对应 server 端的 username
就能做到数据的保密
解决方案:session,即 server 端存储用户信息
session 造成的问题
目前 session 直接是 js 变量,放在 nodejs 进程内存中
第一,进程内存有限,访问量过大,内存暴增怎么办?
第二,正式线上运行是多线程,进程之间内存无法共享
系统内存之间有多个线程,线程之间不共享内存
解决方案 redis
- web server 最常用的缓存数据库,数据库放在内存中
- 相比于 mysql,访问速度快(内存和硬盘不是一个数量级的)
- 但是成本更高,可存储的数量更小(内存硬伤)
如果解决
- 将 web server 和 redis 拆分为两个独立的服务
- 双方都是独立的,都是可扩展的(例如都扩展成集群)
- (包括 mysql,也是一个单独的服务,可扩展)
登录等数据放在 redis,数据有关的放在 mysql,都是相对独立的服务
数据分离,可以扩展
为什么 session 适合用 redis?
- session 访问频繁,对性能要求高(redis 内存数据库,速度快)
- session 可不考虑断电丢失数据的问题(内存的硬伤)
- session 数据量不会太大(内存小,相比 mysql 中的数据小的多)
安装 redis
https://www.runoob.com/redis/redis-install.html
启动 redis
redis-server
,要一直连接状态(服务在线)
在命令行中敲 redis-cli
,进入 redis 操作中
常见的 redis 操作
命令 | 解释 | 例子 |
---|---|---|
set key value | 设置 key | set myname hanbo |
get key | 得到 key | get myname |
del key | 删除 key | del myname |
keys * | 显示所有的 key | keys * |
为何要用 redis?不用 redis 会出现什么问题?
redis 适合什么场景?mysql 适合什么场景?
要保存状态机,如果你要一直保存登录状态,就要从内存中读取数据,而不是去请求数据库
请求数据库需要花时间,
redis 适合登录 状态这种情况
与前端联调
- 登录功能依赖 cookie ,必须用浏览器来联调
- cookie 跨域不共享,前端和 server 端必须同域
- 需要用到 nginx 做代理,让前后端同域
使用 nginx 反向代理请求
nginx
- 高性能的 web 服务器,开源免费
- 一般用于做静态服务、负载均衡
- 反向代理
日志
- 系统没有日志,就等于人没有眼睛——抓瞎
- 第一,访问日志 access log (server 端最重要的日志)
- 第二,自定义日志(包括自定义事件、错误记录等)
目录
- nodejs 文件操作,nodejs stream
- 日志功能开发和使用
- 日志文件拆分,日志内容分析
nodejs 文件操作
- 日志要存放到文件中
- 为何不存储到 mysql 中?
- 为何不存储到 redis 中?
const fs = require('fs');
const path = require('path');
const fileName = path.resolve(__dirname, 'data.txt');
// 读取文件内容
// fs.readFile(fileName, (err, data) => {
// if (err) {
// console.log(err)
// return;
// }
// console.log(data.toString())
// })
// 写入文件内容
// const content = '这是新写入的内容\n';
// const opt = {
// flag: 'a' // 追加写入。覆盖用'w'
// }
// fs.writeFile(fileName, content, opt, (err) => {
// if (err) {
// console.log(err)
// }
// })
// 判断文件是否存在
fs.exists(fileName, (exist) => {
console.log('exist', exist);
});
IO 操作的性能瓶颈
- IO 包括”网络 IO”和“文件 IO”
- 相比于 CPU 计算和内存读写, IO 的突出特点就是:慢!
- 如何在有限的硬件资源下提高 IO 的操作效率?
stream 流,一点点流过去,一点点读取
日志分析
如针对 access.log 日志,分析 chrome 的占比
日志是按行存储的,一行就是一条日志
使用 nodejs 的 readline(基于 stream ,效率高)
readline 可以分析 数据,做数据统计用
安全
- sql 注入:窃取数据库内容
- XSS 攻击:窃取前端的 cookie 内容
- 密码加密:保障用户信息安全(重要!)加盐
sql 注入
- 最原始、最简单的攻击,从有了 web2.0 就有了 sql 注入攻击
- 攻击方式:输入一个 sql 片段,最终拼接成一段攻击代码
- 预防措施:使用 mysql 的 escape 函数处理输入内容即可
XSS 攻击
- 前端最熟悉的攻击方式, 方式 server 端更应该掌握
- 攻击方式:在页面展示内容中掺杂 js 代码,以获得网页信息
- 预防措施: 转换生成 js 的特殊字符
密码加密
- 万一数据库被用户攻破,最不应该泄露的就是用户信息
- 攻击方式:获取用户名和密码,再去尝试登录其他系统
- 预防措施:将密码加密,即便拿到密码也不知道明文
总结:
功能模块
- 处理 http 接口
- 连接数据库
- 实现登录
- 安全
- 日志
- 上线
核心知识
- http,nodejs 处理 http、处理路由、mysql
- cookie,session,redis,nginx 做反向代理
- sql 注入,xss 攻击,加密
- 日志,stream,contrab,readline
- 线上环境的知识
使用 express 来开发 web server
express.json() 解析 post 请求
中间件机制
- 有很多 app.use
- 代码中的 next 参数是什么?
- 带着这些疑问,先看一段代码演示
express 总结
中间件原理介绍
分析
- app.use 用来注册中间件,先收集起
- 遇到 http 请求,根据 path 和 method 判断触发哪些
- 实现 next 机制,即上一个通过 next 触发下一个
使用 Koa2
- express 中间件是异步回调,koa2 原生支持 async/await
- 新开发框架和系统,都开始基于 koa2,例如 egg.js
- express 虽然未过时,但是 koa2 肯定是未来趋势
目录
- async/await 语法介绍,安装和使用 koa2
- 开发接口,链接数据库,实现登录,日志记录
- 分析 koa2 中间件原理
async await 要点:
- await 后面可以追加 promise 对象,获取 resolve 的值
- await 必须包裹在 async 函数里面
- async 函数执行返回的也是一个 promise 对象
- try-catch 截获 promise 中 reject 的值
中间件机制
- 有很多 app.use
- 代码中的 next 参数是什么?
app.use 和 express 一致,使用中间件
洋葱模型
实现登录
- 和 express 类似
- 基于 koa-generic-session 和 koa-redis
线上环境
- 服务器稳定性
- 重温利用服务器硬件资源,以便提高性能
- 线上日志记录
PM2
- 进程守护,系统奔溃自动重启
- 启动多进程,充分利用 CPU 和内存
- 自带日志记录功能
PM2 介绍
下载安装
npm install pm2 -g
pm2 --version
基本使用
常用命令
pm2 start [app.js]
启动pm2 restart [app.js]
重启pm2 stop [app.js]
停止pm2 list
查看启动列表pm2 info <AppName>/<id>
查看基本信息pm2 log <AppName>/<id>
查看日志打印pm2 monit <AppName>/<id>
监控这个进程的 CPU 和内存信息