开始
—dirname当前脚本所在文件目录 —filename当前脚本所在目录,process是关于整个node程序的调用情况,argv可以取到启动命令的参数的
基于V8引擎的js运行时环境
内置模块fs
//文件夹
const fs=require("fs")
fs.mkdir("./niu",(err)=>{console.log("创建成功");})
//子文件的名称数组
fs.readdir("./niu",{withFileTypes:true},(err,files)=>{
console.log(files);
files.forEach(item=>{
if(item.isDirectory()){
console.log("文件夹");
fs.readdir(`./niu/${item}`,(err,childrenflie)=>{console.log(childrenflie);})
}else{
console.log("其他文件");
}
})
})
//找到文件夹内所有部署文件夹的文件名
function readDirectory(path){
fs.readdir(path,{withFileTypes:true},(err,files)=>{
files.forEach(item=>{
if (item.isDirectory){
readDirectory( `./${path}/${item.name}`)
}else{
console.log(item.name);
}
})
})
}
readDirectory("./niu")
fs.rename("./niu","./kk",(err)=>{console.log('xx');})
fs.rename("./abc.txt","./cccs.txt",()=>{})
const fs = require("fs");
//同步
const filebuffer = fs.readFileSync("./abc.txt", {
encoding: "utf-8",
});
console.log(filebuffer);
const asyncreadfile = fs.readFile(
"./abc.txt",
{ encoding: "utf-8" },
(err, res) => {
if (err) return; //只有一个有值
console.log(res);
}
);
const promisereadfile = fs.promises
.readFile("./abc.txt", { encoding: "utf8" })
.then((res) => console.log(res));
//文件描述符file descriptors,打开(操作)文件的时候会分配一个文件描述符,数字类型,根据文件描述符找到对应的文件,这是一个标识
//我们可以根据描述符来获取文件描述信息
//open可以为文件分配标识符
fs.open("./abc.txt", (err, fd) => {
console.log(fd);
// fs.promises.readFile(fd),
fs.fstat(fd, (err, info) => console.log(info));
fs.close()
});
const fs=require("fs")
const text="ddd"
//a+ a 写入追加
fs.writeFile("./writefile.txt",text,{encoding:"utf8"},(err)=>{
if(err){ console.log("写入失败",err)}else{ console.log("写入成功");};
})
内置模块Events
const EventEmitter=require("events")
const e=new EventEmitter()
e.on("niu",(data)=>{console.log("dd",data);})
e.emit("niu","xxx")
function handle(...args){}
e.on("xx",handle)
e.emit("xx","dd")
e.off("xx",handle)
//只监听一次
e.once("dd",(data)=>{})
//多个监听器优先级最高
e.prependListener("dd")
//全部移除,可以传入事件名称,就只移除传入的
e.removeAllListeners()
e.eventNames() //事件字符串数组
e.getMaxListeners() //最大监听器数,默认10,可以设置
e.listenerCount("xx") //监听器个数
e.listeners("xx") //获取监听器的函数
数据的二进制
buffer帮我们处理二进制的数据,但如果用二进制展示这不利于阅读,一般会转成16禁止
//通过ascii编码,中文通过utf-8
const buffer=new Buffer('hello')
// 二进制标识
console.log(buffer);
const buf2=Buffer.from("ggg")
//一个中文三个字节,一个字节8个bit,英文一个字节
//utf-8包含ascii编码,默认就是utf8
const buf3=Buffer.from("你好啊","utf8")
console.log(buf3.toString("utf8"));
//申请内存,8个字节,全为0
let res=Buffer.alloc(8)
res[0]=100
//获取字符的ascii编码
res[1]='m'.charCodeAt()
//buffer的底层实现原理,事实上我们创建buffer时,不会频繁的向操作系统申请内存
//他会默认向8*1024个字节大小的内存,也就是8kb,如果第一次填入内容放不下,
//则会扩容8kb,如果太大则直接申请放入的文件大小
Stream
当我们从一个较大的文件中读取数据中,文件的二进制数据源源不断的读取到我们的程序中,这就算流,流是可读可写,虽然我们可以通过readfile等api,但我们无法对读取进行精细化控制,我们只需要读取一部分,主要解决文件读取,和响应数据的解析
文件读写的Stream
const fs = require("fs");
//buffer缺点无法精细化控制,从那里读到哪里,
// fs.readFile("./txt.txt",(err,data)=>{console.log(data);})
//所有流都是eventEmitter的实例
const streamoftxt = fs.createReadStream("./txt.txt", {
start: 8,
end: 20,
highWaterMark: 2,
});
streamoftxt.on("data", (data) => {
console.log(data.toString("utf8"));
streamoftxt.pause()
setTimeout(()=>{streamoftxt.resume()},1000)
});
const fs = require("fs");
const streamoftxt = fs.createReadStream("./txt.txt",{
start: 8,
end: 12,
highWaterMark: 2
});
streamoftxt.on("data", (data) => {
console.log(data.toString("utf8"));
});
streamoftxt.on("open",(fd)=>{console.log(fd,"文件标识符");})
streamoftxt.on("end",()=>{console.log("读取完成");})
streamoftxt.on("close",()=>{console.log("自动关闭读取流");})
const fs=require('fs')
//mac没问题 flag改为r+
const writeable=fs.createWriteStream("./txt.txt",{encoding:"utf-8",flags:"a+",start:5})
writeable.write("xxx",(err)=>{})
//需要手动关闭
writeable.on("close",()=>{})
writeable.on("finish",()=>{console.log("写入流完成");})
writeable.close()
writeable.end("xxx")//写入完成关闭
const fs=require("fs")
fs.readFile("./txt.txt",(err,data)=>{
fs.writeFile("./txt1.txt",data,(err)=>{
console.log("写入完成");
})
})
const read=fs.createReadStream("./txt.txt")
const write=fs.createWriteStream("./txtpipe.txt")
// read.on("data",(data)=>{
// write.write(data)
// })
// write.on("end",(err)=>{
// console.log("写入完成");
// })
read.pipe(write)
HTTP
run web server
这些数字是分隔符,表单的每一项都会前后有这些分隔符
启动服务器
const http=require("http")
//浏览会默认访问两次,一次是访问图标,第二次才是真正的访问
const server=http.createServer((request,response)=>{
//request 会包含请求信息
//响应对象是用于给客户端返回数据
response.end("hhhhh")
})
//监听端口,一般是1024和666535这个区间,1024,计算机使用2个字节来定义端口
//也就是0-6666535,1024都是分配给特殊服务
server.listen(8080,()=>{
console.log("开启成功");
})
//底层new Server
const server1=new http.Server(()=>{})
server1.listen(4000,()=>{})
//热更新 nodemon /node monitor npm i nodemon -g
如何响应数据,在那里返回
const http=require("http")
const server=http.createServer((req,res)=>{
// res.statusCode=400
// res.writeHead(401)
// res.setHeader("Content-Type","application/json;charset=ytf8")
// res.writeHead(400,{
// "Content-Type":"text/plain;charset=utf8"
// })
const resobj=JSON.stringify({dad:"dada"})
res.write("xxxx") //直接写出没有关闭流
res.end("结束") //写出后关闭流
})
server.listen(8001,()=>{
})
解析参数和响应数据
const http=require("http")
const url=require("url")
const server=http.createServer((req,res)=>{
const urld=req.url
const parseurl= url.parse(urld)
const paramsobj= new URLSearchParams(parseurl)
console.log(parseurl.query,paramsobj);
})
server.listen(8001,()=>{
})
// handlebody
const server1=http.createServer((req,res)=>{
//request本质上是一个可读流readable
req.setEncoding("utf8")
req.on('data',()=>{
console.log(data);
console.log( JSON.parse(data));
})
req.on("end",()=>{
})
})
server1.listen(8001,()=>{
})
header
const http=require("http")
const server=http.createServer((req,res)=>{
console.log(req.headers["Content-Type"]);
})
server.listen(8001,()=>{
})
不同请求手动分发
const http=require("http")
const server=http.createServer((req,res)=>{
console.log(req.url);
if(req.url=="login" && req.method === "POST" ){
console.log("用户登录");
}
console.log(req.method);
console.log(req.headers);
})
server.listen(8001,()=>{
})
文件上传原生
const http=require("http")
const fs=require("fs")
// const server=http.createServer((req,res)=>{
// const writestream=fs.createWriteStream("./a.png",{flags:"a+"})
// req.on('data',(data)=>{
// writestream.write(data)
// })
// req.on('end',()=>{
// writestream.close()
// })
// })
// server.listen(8888,()=>{
// })
const server=http.createServer((req,res)=>{
req.setEncoding("binary")//ascii编码
console.log(req.headers);
const boundary= req.headers['content-type'].split("; ")[1].replace('boundary=','')
let imagedata=''
const fileszie=req.headers['content-length']
let curle=0
req.on('data',(data)=>{
curle += data.length
console.log(curle/fileszie *100);
res.write(`文件上传进度${curle/fileszie *100 }%`)
imagedata+=data
})
req.on('end',()=>{
console.log(imagedata);
const imagetype='image/png'
const imagetypeposition=imagedata.indexOf(imagetype)+imagetype.length
console.log(imagetypeposition);
imagedata=imagedata.substring(imagetypeposition)
imagedata=imagedata.replace(/^\s\s*/,'')
imagedata=imagedata.substring(0,imagedata.indexOf(`--${boundary}--`)) //最终的图片二进制
console.log(imagedata,"ss");
fs.writeFile("./dsd.png",imagedata,"binary",()=>{
console.log("写入成功");
})
})
res.write("xxxx") //直接写出没有关闭流
res.end("结束") //写出后关闭流
})
server.listen(8888,()=>{
})
html代码
<input type="file">
<button>上传</button>
<script src="./axios.js"></script>
<script>
const btn=document.querySelector("button")
btn.addEventListener("click",function(){
console.log("xx");
const from=new FormData()
const input=document.querySelector('input')
from.set('photo',input.files[0])
axios({
method:"POST",
url:"http://localhost:8888",
data:from,
headers:{
"Content-Type":"multipart/from-data"
}
})
})
</script>
express
你传入的回调函数就是所谓的中间件
请求解析
//body中json的解析和unrlencoded
const express=require("express")
const app = express()
app.use(express.json())
app.use(express.urlencoded({extended:true}))
app.get("/hello",(req,res,next)=>{
res.end("xxx")
next()
},(req,res,next)=>{
})
app.listen(8000,()=>{})
//文件上传和from表单解析
const express=require("express")
const multer=require("multer")
const app = express()
// const upload=multer({dest:"./image"}) //目的地,指定解析的字段
const upload=multer({
destination(req,file,callback){
callback(null,"./upload")
},
filename(req,file,callback){
callback(null,Date.now()+"_"+file.originalname)
}
})
app.post("/hello",upload.single("avater"),(req,res,next)=>{
console.log( req.file );
res.end("xxx")
next()
},(req,res,next)=>{
})
//多个文件的字段名需要一样
const from=multer() //from.any()就可以解析数据,他也是一个中间件
//upload.fields([{ name: 'profilePic', maxCount: 1 }, { name: 'coverPic', maxCount: 1 }]) //多名称上传文件
app.post("/hello",upload.array("avater"),(req,res,next)=>{
console.log( req.file );
res.end("xxx")
next()
},(req,res,next)=>{
})
app.listen(8000,()=>{})
响应方式
本质上是一个写入流,我们可以res.write()写出数据,res.end()输出并结束这个流
路由
const express=require("express")
const app=express()
app.get("/login",(req,res)=>{
})
const router=express.Router()
router.get("/dd",()=>{})
app.use("/user",router)
app.listen(8001,()=>{
})
返回错误
const express=require("express")
const app=express()
app.get("/login",(req,res)=>{
next(4001)
})
app.use((err,req,res,next)=>{
// err=4001
res.json({
name:"错误"
})
})
app.listen(8001,()=>{
})
静态资源部署
//npm init -y npm i express
const express=require("express")
const app=express()
//这样我们就访问图片 http://127.0.0.1:8001/文件名,我们也可以直接部署一个打包的前端项目,浏览器访问localhost:8001,默认会找index.html
app.use(express.static("./uploads"))
app.get("/login",(req,res)=>{
})
app.listen(8001,()=>{
})
Koa
关于中间件参数说明
req是node的请求对象,request是koa封装的对象,response是koa封装的,res是node封装的
const koa=require("koa")
const app=new koa()
app.use((ctx,next)=>{
})
app.listen(8001,()=>{
})
路由
//比express更加轻量级,我们可以按需导入插件,npm i @koa/router
const koa=require("koa")
const koarouter=require("@koa/router")
const app=new koa()
const userRouter=new koarouter({prefix:"/users"})
userRouter.post("/:id",(ctx,next)=>{
ctx.body= "/users/";
})
app.use(userRouter.routes()) //将路由挂入app
app.use(userRouter.allowedMethods()) //请求方式未定义,返回结果更清晰
app.use((ctx,next)=>{
})
app.listen(8001,()=>{
})
响应结果
const koa=require("koa")
const app = koa()
app.use((ctx,next)=>{
ctx.body=""
ctx.body=Buffer.from("dss")
ctx.type="image/jpeg"
ctx.body=fs.createReadStream("./cc.png")
//数组对象任意 可以返回null
})
app.listen(8001,()=>{
})
上传文件
const koa=require("koa")
const koarouter=require("@koa/router")
const multer=require("@koa/multer") //npm install multer @koa/multer
const app=new koa()
const userRouter=new koarouter({prefix:"/users"})
const upload=multer({ dest:"./upload"})
userRouter.post("/:id",upload.single("imagepropertiesname") , (ctx,next)=>{
ctx.request.file//or files
ctx.body= "/users/";
})
app.use(userRouter.routes()) //将路由挂入app
app.use(userRouter.allowedMethods()) //请求方式未定义,返回结果更清晰
app.listen(8001,()=>{
})
参数解析
const koa=require("koa")
const koarouter=require("@koa/router")
const app=new koa()
/**
* get params /:id query ?id=""
* post json x-www-form-unrlencoded form-data
*/
const userRouter=new koarouter({prefix:"/users"})
userRouter.get("/",(ctx,next)=>{
ctx.params.id
ctx.query
ctx.request.body //ctx.req.on("data",(data)=>{}) or npm install koa-bodyparser app.use(bodyparser())
ctx.request.body // 上面同步
ctx.request.body //npm i @koa/multer multer import @koa/multer const formparser=multer()
ctx.body="gg"
})
app.use(userRouter.routes()) //将路由挂入app
app.use(userRouter.allowedMethods()) //请求方式未定义,返回结果更清晰
app.use((ctx,next)=>{
})
app.listen(8001,()=>{
})
部署静态资源
错误处理
可以自己逻辑处理,返回一个json对象,内包含错误信息和状态码,但跟express一样,这样会导致代码可读性降低
//拿到app实例 本质上是一个eventsEmitter
ctx.app.emit("error",-4001,ctx)
app.on("error",(code,ctx)=>{
let message=""
swtic(code){
case -4001:
messgae:未登录
break
}
ctx.body={
code,
message
}
})
Koa和Express的区别
推出koa的最大原因是如果需要处理异步任务,express中间件如果处理异步任务,洋葱模型会被打破,koa能够处理异步任务,并保证代码的执行顺序,koa更加简洁,核心代码仅1600多行,很多框架都趋于这种方式,更简洁,可扩展,就像vue2早期社区内部还封装了网络请求,后面被去除,建议我们使用axios来发送网络请求
中间件执行顺序
设计核心都是中间件
const app = express()
const k= new Koa()
//express同步与koa没有区别
app.use((req,res,next)=>{
console.log("middleware01")
next()
res.json(res.niuniu)
})
//express在异步情况下,无法处理,next的返回值是void ,koa返回的是promise,所以无法使用
await等待,我们只能在最后返回结果,在最后的中间件的改为异步函数,这里就可以拿到正确的结果
//koa 同步
k.use((ctx,next)=>{
console.log("koa middleware")
next()
//下个中间件执行完成就会来到这里,我们可以向ctx加入属性,在这里拿到
})
k.use((ctx,next)=>{
console.log("koa middleware")
})
//koa异步
k.use( async (ctx,next)=>{
console.log("koa middleware")
await next()
//默认不会等待异步结果,只需要全部改为异步,等待即可
})
k.use( async (ctx,next)=>{
const res= await axios.get({})
ctx.result=res
})
洋葱模型
洋葱模型是什么,从外往里,在从里往外,跟中间件的执行顺序一样,express只有在同步的时候才符合洋葱模型,而在异步情况下不符合
koa和express的源码
执行express()发生了什么,其实内部都是基于http
Mysql
为什么需要数据库,任何软件都需要操作大量的数据,包括数据的迁移,备份,容灾,共享,我们在前端直接管理数据是十分麻烦的,我们还需要考虑查询性能,用户鉴权,查询缓存,关系型数据库通常创建很多个二维表
mysql -uroot -pniuniu,登录进终端infomation-schema信息数据库,包括mysql在维护其他数据库访问权限
perfoemance-schema 性能数据库,记录一些资源消耗信息
mysql 记录管理者的信息权限信息
sys是perfoemance-schema 简易版性能数据库
初体验
安装mysql的时候自带mysql命令行工具,我们也可以通过它找到目录地址,配置环境变量
create database niuniudb;
use niuniudb
create table t_niuniu( name varchat(10),age int)
show tables
insert into t_niuniu value("niuni",88)
select * from t_niuniu
exit
structured query language SQL,关键字通常使用大写,一个语句结尾需要;如果遇到关键字作为表名或者字段名称,可以使用``来包裹
DDL
数据定义语句
使用、创建、删除、修改数据库和表
show database; //查看所有数据库
use dbname //切换数据库
select database() //查看正在使用的数据库
create database dbname //创建数据库
create database if not exit dbname //不存在则创建
dorp database dbname //删除数据库
show tables
desc tablename//查看表结构
create table if not exit tablename(
name VARCHAT(10),
age INT,
height DOUBLE
)
create table niuniu(
id int primary KEy auto_increment,
name varchat(20) unique not null,
level int default 0,
tellphone varchat(20) not null unique,
)
类型说明:数字类型,日期和时间类型,字符串类型,空间类型(x,y,z),JSON数据类型
数字类型:integer(-128-255),int ,smallint,tinyint,mediumint,bigint
浮点:float double 一个四字节,一个8字节,精确数字类型:decimal精度更高,一般使用int 和 double
日期类型:year年份date以YYYY-MM-DD,datetime日期加时间,TIMESTAMP是时间戳,UTC日期时间范围
字符串类型:char固定长度 varchat可变,binary和varbinary类型存储二进制字符串 Blob存储大的二进制文件,text用于存储大的字符串类型(文章)
表约束
主键primary key,区分唯一性,主键是表中唯一的索引,也可以设置多列索引,主键的字段应该与业务无关,应该是not null
unique不可重复,允许有多个null not null 不为空 default 默认值 auto_increment 自动递增
修改表
alter table oldname rename to newname 修改表名
alter table tablename add cloumnname datatime 增加列
alter table tablename add cloumnname int 增加列
alter table tablename drop cloumnname 删除列
alter table tablename change olacloumnname newcloumnname Date 修改列的名称和类型
alter table tablename modify cloumnname INT //修改字段类型
alter table tablename add updateime timestamp Default CURRENT_TIMESTAMP
//增加字段设置默认时间
DML
数据操作语言,增删改
insert into user(name,age) values('niuniu',88)
delete from tablename where id='ddd'
update tablename set name="ddd",age=88 where name="niuniu"
select * from user where name="niuniu"
DQL
数据库查询语句
select * from user where age>18 and/&& name="niuniu"
select * from user where name in ("小吴","小李")
select * from user where price between 1000 and 2000
select * from user where name like "%m_" %多个 _一个
排序
select * from where price<30000 order by score desc //按照score降序 asc升序
分页
select * from users where name="xioawu" limit 20 offset 20 // 20 - 40
select * from users where name="xioawu" limit 40 20 // 40 - 60 前面的是偏移
高级
聚合函数
select avg(price) from product //平均值
select max(price) from product //最大
select min(price) from product //最小
select sum(name) from product //计算个数
select count(*) from product //行数
分组
select sum(price) from product group by 品牌 //分组一般配合聚合函数
round(float,2)//保留两位
select sum(price) from product group by 品牌 having 分数>8 //分组需要配合having来约束
外键
show create table tablename 这样我们就能查看完整的创建表的语句就能看到外键
alter table tablename drop foreign key product_ibfk_1 //移除外键
alter table tablename add foreign key (product_id)
References product(id) on update cascade on delete cascade //添加外键并定义行为
多表查询
//左连接,左表全数据,拼上右表,右表没有符合条件的数据就为null
select * from product as p left join brands as b on p.brand_id = b.id
//取出没有交集的数据
select * from product as p left join brands as b on p.brand_id = b.id where b.id is null
//取出交集的数据
select * from product as p left join brands as b on p.brand_id = b.id where b.id is not null
//右连接,左边可能没数据,很少用
select * from order right join user on user.orderid = order.id
//内连接,左表和右表都有数据 inner join
select * from order join user on user.orderid=order.id
select * from order user where order.id= user.id //查询笛卡尔积的所有数据再过滤
//mysql不支持全连接 使用union,这里的id是判断条件
select * from product left join order on id=id union select * from product rightjoin order on id=id
//两张表没交集的数据
select * from product left join order on id=id where id is null union select * from product rightjoin order on id=id where id is null
多对多,需要关系表来操作,记录两张表的关系
现存在三张表,一张学生表,一张课程表,一张关系表(内存有学生和课程的id对应关系)
查询所有学生选择的课程
select * from student join 关系表 on 关系表.userid=student.id join 课程表 on 关系表.课程id= 课程表id
没有选课的学生
select * from student left join gxb on studnet.id = gxb.userid left join kcb gxb.kcid= kcb.id where kcb is null
查询到数据,部分信息放入一个对象,使用聚合函数json_object(key,value) as orderinfo 这个对象也需要名称(列名)
多对多转成数组 ,多个对象,合并数组,常用多对多,一个学生多个课,课转化成对象,对象放入数组 json.arrayAgg( json_object(课程名称,课程id)) as kcname,我们按照学生id分组
Mysql2数据库驱动
start
性能高,预编译语句,支持promise,防止sql注入
npm i mysql2 //安装
1、创建连接
const mysql = mysql2.createConnection({ host:localhost,port:3306,user:"root",datanbase:"nodedata" })
const sqlstatement = `select * from user`
//structrue query language
//最后是表内字段
mysql,query(sqlstatement,(err,values,fields)=>{
console.log(values)
})
mysql.destory() //销毁
我们也可以编写预处理语句,就像mybatis的mapper.xml一样,框架可以帮我们优化性能和防止sql注入
const sqlstatement = `select * from user` where price>8000
const sqlstatement = `select * from user` where price> ? and score > ?
connection.execute(sqlstatement,[100,8],(err,data,feids)=>{})
Connection Pools
单独创建连接,随后销毁,每次请求都会重新创建连接,太消耗资源
const connectionPool=mysql.createPool({
host:
user:
port:3306
connectionLimit:5
})
使用promise
connectionPool.promise.execute(sqlstatement,[100,0]).then(({values})=>{
console.log(values)
})
Node服务器(待完善)
登录凭证:cookie,别名小甜饼,小型文本文件,某些网站为了辨别用户身份而存储在用户本地终端的数据,在用户发生请求,浏览器会自动携带cookie来发送请求,可以存储在内存中,或者硬盘中,内存cookie浏览器关闭会自动销毁,硬盘cookie只有过期时间到,或者手动清除
通过js来设置内存cookie document.cookie="name=xxx"
通过js来设置硬盘cookie document.cookie="name=xxx;max-age=30" 秒为单位
删除的时候设置时间为0即可,过期时间也可以使用expires类设置时间,cookie的作用域
这决定了浏览器发送请求的时候会不会携带,如果不指定,默认origin,不包括子域名,指定domain
包含子域名 domin:baidu.com, path指定那些路径可以接受cookie
Node设置cookie koa内置修改 ctx.cookies.set("name","xx",{ maxAge:60*1000 })毫秒,ctx .cokies.get("name"),同一域名会自动携带
Seesion是基于cookie实现机制,更加安全,安装koa-session ,KoaSession({ key:"sessionid"
signed:true //加密 ,maxAge:60*1000*5
},app),use(session) , app.keys=["niuniu"] //加盐 ,ctx.session.name="niuniu"
token
cookie需要携带每个http中,增加流量。cookie是明文的,cookie大小限制为4kb,分布式系统如何正确的解析session,使用JWT生成token,采用非对称加密
//对称加密的加密解密
npm install jsonwebtoken
const secretkey="cccccniuniu"
const pyload={name:"",password:""}
jwt.sign(pyload,secretkey,{ expiresIn:60}) 秒为单位
//解密
jwt.verify(token,secretkey)
//非对称加密,在分布式系统的解决,私自发布token的问题,私钥加密,公钥加密
终端生成:openssl 随后生成私钥 genrsa -out private.key 1024 生成生成公钥:rsa -in private.key -pubout -out public.key,直接读出来使用即可,它支持buffer格式 jwt.sign(pyload,传入读出来的buffer,{ expiresIn:60}) ,还需要更改算法,RS256 jwt.verify(tok en,secretkey,{algorithms:['RS256']}) ,签发的时候也要指定
postMan设置全局变量token
const res=pm.response.json()
pm.globals.set('token',res.data.token)
项目部署
购买服务器:ssh root@ip
安装node:dnf search nodejs dnf install nodejs
安装mysql:dnf search mysql-server systemctl start mysqld systemctl status mysqld,systemctl enable mysqld ,设置密码:mysql_secure_installation , 修改mysql配置,让我们能远程连接mysql -uroot -p show database use user,随后 update user set host='%' where user='root',刷新配置生效 FLUSH PRIVILEGES,配置好安全组即可
导出导入数据:导入结构数据,新数据库允许sql文件
上传项目:安装插件Remote -SSH 两个都要安装,连接上直接把项目拖进来启动
但在控制台启动项目,ssh关闭,进程也停止了,使用PM2 npm install PM2. -g
pm2 start 文件 --name niuniu 他也可以起多个进程,负载均衡
扩展:Node作为中间层(待完善)
RPC和AJAX的区别:都是两个计算机之间的通信,需要双方约定一个数据格式,不同点在于,不一定使用DNS作为寻址服务,应用层协议一般不适用http,基于TCP和UDP协议,AJAX使用DNS进行寻址,RPC使用特有服务进行寻址
TCP通信方式:单工通信:只有一方能给一方发送信息,半双工通信,同一时间只有一方能发送信息。也就是轮番单工,全双工通信:客户端服务端能够自由的通信,这三种方式的实现难度和成本 是不同的
二进制协议,更小的数据包体积,更快的编解码速率
RPC传输和多路复用
二进制的编解码使用potocol-buffers,声明协议文件,它可以根据协议来将结构化数据转换成二进制数据,以方便我们进行rpc通信,我们将二进制数据传输给后端,后端也根据他们的potocplbuffer来解码,拿到数据后再返回对应的数据,node再根据potocolbuffers将二进制数据解码,渲染页面
实现单工通信
//服务端
const net=require("net")
const server=net.createServer((socket)=>{
socket.write()
socket.on("data",funtion(buffer){
console.log(buffer.tostring())
})
})
server.listen(4000)
//客户端 单工通信
const socket=new net.Socket({})
socket.connect({
host:
port
})
socket.write()
实现半双工通信