Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Blockchain-CN/httpsvr

Repository files navigation

httpsvr

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)
}

基础功能

路由框架 router

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()
}

服务器选项 option

通过提供回调函数的形式来实现变参设置参数。

// ServerOption option赋值回调函数
type ServerOption func(o *option)

接口定义 idl

规定了IController接口,用户定义controller必须实现此接口。

// IController ...
type IController interface {
 // 此方法获得接口参数的结构体,框架会直接将req.body直接unmarshal到这里面
 GenIdl() interface{}
 // 实际处理的handle函数
 Do(interface{}) interface{}
}

流量控制/优雅退出 accesscontrol

  • 流量控制 通过路由之后,在处理入口进行入口控制: 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

About

http框架

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

AltStyle によって変換されたページ (->オリジナル) /