后端主要包括
- 计算
- 通过编程语言告诉计算机该怎么算
- 记忆
- 数据库
- 静态文件
计算
计算机只能读懂机器码,不认识各种编程语言
编程语言
- 低级语言
- 高级语言
- 编译型语言
- 需要将代码编译成机器码
- 支持跨平台,不同系统编译不同机器码
- 如:c,c++,c#,go等
- 解释型(脚本)语言
- 不用编译,需要程序解释运行的语言
- 如:js、node用chrome v8引擎解释,java用jvm解释,python用CPython解释
- 编译型语言
- 应用场景
PHP
- 擅长构建低成本WEB,也是世界上最好的语言(狗头)
c
- 擅长操作系统、嵌入式
Go
- 天生高并发属性
R
- 科学计算、统计无人能及
Python
- 在AI领域独占鳌头
Java
- 工业级应用服务及Android
node
- 事件驱动、非阻塞式 I/O 的模型
- 擅长io密集型场景,不擅长计算密集型
效率
- 执行效率
- 机器语言 > 汇编语言 > 编译型语言 > 解释型语言
- 解释型语言里,静态类型 > 动态类型
- 执行效率越高,内存、cpu消耗更少,响应更快,服务器成本更低
- 开发效率
- 与执行效率正好相反
- 开发效率越高,人工研发成本更低,交付更快
- 生态
- 生态越丰富,轮子越多,开发效率越高
- 总结
- 开发应用不能单纯追求 执行效率 或 开发效率,而是在两者和领域生态之间取平衡
后端语言现状
- java凭借多年积累优化和丰富生态排名第一
- php凭借生态和较高的开发效率,
- go凭借类型推断、简洁的语法和编译型语言的高性能,增长较快
- node用的人不多,但我觉得早晚会火,能用js写的最终都会用js写
记忆
数据库
- 类型
- 关系型
- 非关系型(nosql)
- mongodb
- 常用非关系型数据库
- redis
- 内存读写,快
- tablestore
- 按量付费
- 存储计算分离
- 必须建立索引才能查询,否则只能主键查询
- 异步更新索引有延迟,即数据与索引不一致
- LedgerDB
- 中心化“区块链”数据库,数据不可篡改,适合记账等场景
- 思路不错,但处于公测阶段,不适合生产使用
- mongodb
- 类型区别
- 关系型数据库 事务支持、数据一致性更好,可以连表查询
- 非关系型数据库 性能更高,扩展性更好,可以复杂查询
- 展望
- 未来的数据库应该会往 存储计算分离 方向发展,以应付数据量的增长带来的横向扩展问题
- 云原生数据库是大趋势
- 基于云计算的海量扩展能力,及其优化
- 基于硬件、协议创新的性能提升,如 绕过cpu,直接访问内存
静态文件
- 特点:占用网络io,读写io
- 一般使用云存储,不与后端接口部署在一起,避免占用接口网络io
- cdn加速,更快访问速度,更低成本,PCDN更便宜
技术选型
- 稳健
- 语言用java或php,生态丰富
- 数据库用mysql,缓存用redis或本地文件
- 数据量变大后,mysql要分库分表,或换PolarDB
- 并发量变高后,买服务器,用k8s做分布式服务
- 激进
- 用serverless架构,并发量变高后,自动扩容
- 语言用node,
- 一般后端接口可以说99%的时间都是在等待数据库的网络io,计算费时极少。
- 后端是io密集型,正适合node,开发效率也高。
- node生态 npm包丰富
- 某些计算密集型场景用go写
- 用uniCloud的免费云函数,MongoDB,云存储,CDN
任务
目标
- 点击服务员,客人+1
- 点击经理,显示客人总数
流程
- 创建阿里云函数计算
- customRuntime自带node,可以删掉
bin/node
,加快部署 修改bootstrap
#!/bin/bash
node index.js
删除bin
目录
- 安装koa
yarn add koa
let Koa = require('koa')
let app = new Koa()
...
app.listen(9000)
- 添加静态文件
yarn add koa-static-router
let static = require("koa-static-router")
app.use(static({
dir:"static", //静态资源目录对于相对入口文件index.js的路径
router:"/" //路由命名
}))
static/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- import CSS -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
</head>
<body>
<div id="app">
<div style="display: flex;align-items: center;justify-content: center;height: 100vh;">
<div style="position: relative;width: 400px;height: 400px;display: flex;flex-wrap: nowrap;align-items: flex-end;">
<img v-for="(item,key) in 我" :src="`http://static-da62cb94-518a-45ab-ae1e-f76afc49948d.bspapp.com/21-10/what'sFrontEnd/${key}.png`"
:style="{
position:(item.左||item.右||item.上||item.下) ? 'absolute':'',
left:item.左+'px',
right:item.右+'px',
top:item.上+'px',
bottom:item.下+'px',
}"
@click="()=>item.说?item.说():''"
>
</div>
</div>
</div>
</body>
<script>
let 我 = {}
let 欢迎光临=()=>{
console.log("欢迎光临");
console.log("");
}
let vm = new Vue({
el:"#app",
data(){
return {
我:我,
}
},
})
Vue.set(vm,"我",{
脸:{},
左眼:{
左:60,
上:40,
},
右眼:{
右:60,
上:40,
},
嘴:{
左:100,
下:20,
说:欢迎光临
},
})
</script>
<style>
img{
object-fit: contain;
}
</style>
</html>
- 部署代码
- 配置域名(不配置域名,会自动下载,导致不能访问静态html,阿里的免责措施)
dns解析
添加自定义域名
- 安装router
yarn add koa-router
let router = require("koa-router")()
...
app.use(router.routes()).use(router.allowedMethods());
- 实现 客人+1 添加接口 addGuest
let guestCount = 0
router.post("/api/addGuest", async ctx => {
guestCount++
ctx.body={
code:200,
msg:`客人+1`,
}
});
前端添加点击事件
axios.post("/api/addGuest").then(res=>{
console.log(40,res)
let {code} = res.data
if(code!==200){
return vm.$message({
message:"添加失败 "+res.data.msg,
type: 'error',
})
}
vm.$message({
message:res.data.msg,
type: 'success',
})
})
- 显示客人总数
统计接口 guestCount
router.get("/api/guestCount", async ctx => {
ctx.body={
code:200,
msg:`共${guestCount}个客人`,
}
});
添加static/manger.html 添加点击事件
axios.get("/api/guestCount").then(res=>{
console.log(40,res)
let {code,data} = res.data
if(code!==200){
return vm.$message({
message:"获取失败 "+res.data.msg,
type: 'error',
})
}
vm.$message({
message:res.data.msg,
type: 'success',
})
})
因为是用变量guestCount
存储客人总数在内存中,所以一重启,客人总数就会丢失,所以需要数据库来存储客人总数
- 添加gg-thinkorm
yarn add gg-thinkorm
global.M=(name)=> {
const {BaseModel} = require("gg-thinkorm");
// console.log(16, BaseModel);
let modelClass = class extends BaseModel {
init() {
this.modelName = name;
}
};
let re = new modelClass({
db_type: 'mysql', //support postgresql,mysql...
db_host: '',
db_port: 3306,
db_name: 'test',
db_user: '',
db_pwd: '',
db_charset: 'utf8'
});
return re.field("*");
}
写入mysql链接密码
- 修改接口addGuest,改用数据库存储添加客人记录
router.post("/api/addGuest", async ctx => {
await M("guest").add({
creat_time:parseInt(Date.now()/1000),
})
ctx.body={
code:200,
msg:`客人+1`,
}
});
- 修改统计接口 guestCount
router.get("/api/guestCount", async ctx => {
let guestCount = await M("guest").count()
ctx.body={
code:200,
msg:`共${guestCount}个客人`,
}
});
- 加个需求,点眼睛只统计今天的客人 static/manger.html 引入moment.js
<script src="https://cdn.bootcss.com/moment.js/2.20.1/moment.min.js"></script>
添加点击事件
右眼:{
右:60,
上:40,
说:()=>{
let startStr = moment().format("YYYY-MM-DD 00:00:00")
let start = moment(startStr ).unix()
console.log(75,start,startStr)
axios.get("/api/guestCount",{
params:{
start:start
}
}).then(res=>{
console.log(40,res)
let {code,data} = res.data
if(code!==200){
return vm.$message({
message:"获取失败 "+res.data.msg,
type: 'error',
})
}
vm.$message({
message:"今天"+res.data.msg,
type: 'success',
})
})
}
},
修改接口
let {query} = ctx
let guestCount = await M("guest").where({
creat_time: {
">=":query.start || 0
}
}).count()