HttpServer. 简单的http框架,只实现了option、router、accesscontrol、idl功能 目前仅支持POSTJSON
step1:创建httpsvr对象。 step2:添加路由。 step3:开始服务。 optional: 优雅退出。
func main() {
// step1:创建httpsvr对象。
s := httpsvr.New(
"127.0.0.1:10024",
httpsvr.SetReadTimeout(time.Millisecond*200),
httpsvr.SetWriteTimeout(time.Millisecond*200),
httpsvr.SetMaxAccess(2),
)
// 优雅退出
go GracefulExit(s)
// step2:添加路由。
s.AddRoute("POST", "/test/api", &ctrls.DemoCtrl{})
// step3:开始服务。
s.Serve()
}
// GracefulExit 优雅退出
func GracefulExit(svr *httpsvr.Server) {
sigc := make(chan os.Signal, 0)
signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
<-sigc
println("closing agent...")
svr.GracefulExit()
println("agent closed.")
os.Exit(0)
}
1、使用"github.com/julienschmidt/httprouter"作为核心路由。 2、使用原生ListenAndServe方法进行监听(内部调用net/http.router接口)。 3、通过实现ServeHTTP()方法来实现net/http.router接口,并在其中实现路由。
// AddRoute ...
func (s *Server) AddRoute(method, path string, ctrl IController) {
s.router.Handle(method, path, handle)
}
// ServeHTTP implement net/http.router
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
s.router.ServeHTTP(w, req)
}
// http listen
oriSvr *http.Server
s.oriSvr = &http.Server{Addr: addr, Handler: s}
// Serve ...
func (s *Server) Serve() error {
return s.oriSvr.ListenAndServe()
}
通过提供回调函数的形式来实现变参设置参数。
// ServerOption option赋值回调函数
type ServerOption func(o *option)
规定了IController接口,用户定义controller必须实现此接口。
// IController ...
type IController interface {
// 此方法获得接口参数的结构体,框架会直接将req.body直接unmarshal到这里面
GenIdl() interface{}
// 实际处理的handle函数
Do(interface{}) interface{}
}
-
流量控制 通过路由之后,在处理入口进行入口控制: 1、首先判断是否允许进入(标志为会在优雅退出时打开,平时关闭)。 2、再向chan中写入标志,如果chan已满(到达最大并发数)则无法写入,经过等待时间窗口(100ms)后如果还写不进去就退出。 3、请求执行完毕时从chan中读一个标志为,代表当前人数减1。
-
优雅退出 1、在允许进入后,每次放入一个信号,就WaitGroup.Add(1),代表当前又有1个在执行。 2、请求执行完毕时,就WaitGroup.Done(),代表有一个请求执行完了。 3、退出时首先先将标志位用原子操作进行赋值,防止后续请求接着进入(参看流量控制.1)。 4、再等待所有请求执行完毕,用WaitGroup.Wait()
// InControl 入口控制
func (a *Access) InControl() error {
if atomic.LoadInt32(&a.closed) == 1 {
return errors.New("server is closing")
}
select {
case a.bucket <- struct{}{}:
a.wg.Add(1)
case <-time.After(time.Millisecond * 100):
return errors.New("server is busy please try later")
}
return nil
}
// OutControl 出口注销
func (a *Access) OutControl() {
<-a.bucket
a.wg.Done()
}
// Stop 优雅退出
func (a *Access) Stop() {
if !atomic.CompareAndSwapInt32(&a.closed, 0, 1) {
return
}
// 第一种判断桶内为空
a.wg.Wait()
/*第二种判断桶内为空
for {
if len(a.bucket)== 0 {
return
}
}*/
}
1、路由测试:直接发送请求,遍历路由。 测试结果:PASS 2、并发控制测试:设置并发数量为1,并设置句柄中sleep(10s),检测第二个请求是否可以进入。 测试结果:PASS 3、panic测试:设置并发数量为1,在处理句柄中Panic,检测后续请求是否可以进入。 测试结果:PASS 注意:这里一定要自己实现panic-recover,因为上层是无法在接入计数上减去1的,会导致"panic请求"WaitGroup.Add(1),recover之后没有WaitGroup.Done(),因此WaitGroup.Wait()死锁,同理流量控制的chan 4、优雅退出:设置并发数>1 ,并设置句柄中sleep(10s),第一个正常请求进入后,快速关闭server,测试第二个请求访问接口的返回值。 测试结果:PASS 由于server没有立刻关闭(第一个请求还在处理),因此后续请求还是能访问,但是没有进行处理,直接返回了。
见[使用]
curl -H 'content-type: application/json' -X POST -d '{"Name":"luda","Age":111}' http://127.0.0.1:10024/test/api //正常
curl -H 'content-type: application/json' -X POST -d '{"Name":"luda","Age":11}' http://127.0.0.1:10024/test/api //Panic