httprouter的使用
restlet测试插件
api
3-6
总体思路:
handleer->validation{1.request,2.user}->business login ->response
- data model
- error handler
这个错误接口以后都可以这样写了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package defs
type Err struct { Error string `json:"error"` ErrorCode string `json:"error_code"` }
type ErrResponse struct { HttpSC int Error Err }
var ( ErrorRequestBodyParseFailed = ErrResponse{HttpSC: 400, Error: Err{Error: "Request body is not correct", ErrorCode: "001"}} ErrorNotAuthUser = ErrResponse{HttpSC: 401, Error: Err{Error: "User authentication failed.", ErrorCode: "002"}} ErrorDBError = ErrResponse{HttpSC: 500, Error: Err{Error: "DB ops failed", ErrorCode: "003"}} ErrorInternalFaults = ErrResponse{HttpSC: 500, Error: Err{Error: "Internal service error", ErrorCode: "004"}} )
|
3-7
好好理解这里的数据库为什么这样设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| create table comments ( id varchar(64) not null, video_id varchar(64), author_id int(10), content text, time datetime default current_timestamp, primary key(id) );
create table sessions ( session_id tinytext not null, TTL tinytext, login_name text ); alter table sessions add primary key (session_id(64));
create table users ( id int unsigned not null auto_increment, login_name varchar(64), pwd text not null, unique key (login_name), primary key (id) );
create table video_del_rec ( video_id varchar(64) not null, primary key (video_id) );
create table video_info ( id varchar(64) not null, author_id int(10), name text, display_ctime text, create_time datetime default current_timestamp, primary key (id) );
|
第三范式(Third Normal Form,3rd NF)
就是指表中的所有数据元素不但要能唯一地被主关键字所标识,而且它们之间还必须相互独立,不存在其他的函数关系。也就是说,对于一个满足2nd NF 的数据结构来说,表中有可能存在某些数据元素依赖于其他非关键字数据元素的现象,必须消除。
3-8
数据库连接还是参考自己写的这个吧demo吧。
3-10
api_test.go
参考Golang练习笔记23测试讲的更详细。
3-12
实现和验证video
3-13
Comments
评论是需要列表出现的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| func ListComments(vid string, from, to int) ([]*defs.Comment, error) { stmtOut, err := dbConn.Prepare(`SELECT comments.id, users.login_name, comments.content FROM comments INNER JOIN users ON comments.author_id = users.id WHERE comments.video_id = ? AND comments.time > FROM_UNIXTIME(?) AND comments.time <= FROM_UNIXTIME(?) ORDER BY comments.time DESC`)
var res []*defs.Comment rows, err := stmtOut.Query(vid, from, to) if err != nil { log.Printf("%s", err) return res, err }
for rows.Next() { var id, name, content string if err := rows.Scan(&id, &name, &content); err != nil { return res, err }
c := &defs.Comment{Id: id, VideoId: vid, Author: name, Content: content} res = append(res, c) }
defer stmtOut.Close()
return res, nil }
|
3-15
session
会话,
session和cookie的区别?
session负责的流程图:
3-17
middleware
duck typing内容参考Golang练习笔记15。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| type middleWareHandler struct { r *httprouter.Router }
func NewMiddleWareHandler(r *httprouter.Router) http.Handler { m := middleWareHandler{} m.r = r return m }
func (m middleWareHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { //check session validateUserSession(r)
m.r.ServeHTTP(w, r) }
func RegisterHandlers() *httprouter.Router { router := httprouter.New() router.POST("/user", CreateUser) ... }
|
1 2 3 4 5 6 7 8 9 10
| //response type SignedUp struct { Success bool `json:"success"` SessionId string `json:"session_id"` }
{ "success":XXXXX, "session_id":XXXX }
|
Streaming
4-1
Streaming(视频播放):
- 静态视频,非RTMP(直播的那些都是RTMP)
- 独立的服务,可独立部署
- 统一的api格式
4-3
流控
token bucket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
type ConnLimiter struct { concurrentConn int bucket chan int }
func NewConnLimiter(cc int) *ConnLimiter { return &ConnLimiter { concurrentConn: cc, bucket: make(chan int, cc), } }
func (cl *ConnLimiter) GetConn() bool { if len(cl.bucket) >= cl.concurrentConn { log.Printf("Reached the rate limitation.") return false }
cl.bucket <- 1 return true }
func (cl *ConnLimiter) ReleaseConn() { c :=<- cl.bucket log.Printf("New connction coming: %d", c) }
|
4-5
在http middleware中嵌入流控
scheduler
5-1
scheduler
任务调度器,某些不能马上处理的任务放到scheduler,让它定期或者延时触发。
5-3
runner的生产消费者模型实现
5-5
task
api->videoid->mysql
dispatcher->mysql->videoid->datachannel
executor->datachannel->videoid->delete video
5-6
timer
setup->strat{runner task}
5-7
前端
6-1
Go的模版引擎
- 模版引擎是将html解析和元素预置替换生成最终页面的工具
- Go的模版有两种text/template和html/template
- Go的模版采用动态生成的模式
6-2
前端代码架构
client.go就是起了proxy转发作用,避免跨域作用。
6-3
静态页面渲染
6-4
sh build.sh
6-6 api透传!!!
6-7 proxy转发
net/url
net/http/httputil
1 2 3 4 5 6 7 8 9 10 11
| func proxyVideoHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { u, _ := url.Parse("http://" + config.GetLbAddr() + ":9000/") proxy := httputil.NewSingleHostReverseProxy(u) proxy.ServeHTTP(w, r) }
func proxyUploadHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { u, _ := url.Parse("http://" + config.GetLbAddr() + ":9000/") proxy := httputil.NewSingleHostReverseProxy(u) proxy.ServeHTTP(w, r) }
|
6-11
js
部署
7-4
公共配置,即各模块的config文件。
7-5 vendor
7-6 SLB讲解与配置
阿里云负载均衡配置。
7-7
session容错
7-8 ECS配置
7-10 部署脚本
在bin文件夹添加conf.json文件
1 2 3 4
| { "lb_addr": "127.0.0.1", "oss_addr": "oss.aliyun.com" }
|
buildprod.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #! /bin/bash
cd ~/work/src/github.com/avenssi/video_server/api env GOOS=linux GOARCH=amd64 go build -o ../bin/api
cd ~/work/src/github.com/avenssi/video_server/scheduler env GOOS=linux GOARCH=amd64 go build -o ../bin/scheduler
cd ~/work/src/github.com/avenssi/video_server/streamserver env GOOS=linux GOARCH=amd64 go build -o ../bin/streamserver
cd ~/work/src/github.com/avenssi/video_server/web env GOOS=linux GOARCH=amd64 go build -o ../bin/web
|
deploy.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #! /bin/bash
cp -R ./templates ./bin/
mkdir ./bin/videos
cd bin
nohup ./api & nohup ./scheduler & nohup ./streamserver & nohup ./web &
echo "deploy finished"
|
7-11 部署
sh buildprod.sh 编译完成后提交二进制文件。或者提交代码到服务器上直接编译
检查对应server是否启动ps aux | grep <api?stream?scheduler?web>