Giter Site home page Giter Site logo

ruhip / wsp Goto Github PK

View Code? Open in Web Editor NEW

This project forked from simplejia/wsp

0.0 2.0 0.0 41 KB

golang webserver framework, simple and flexible【超简单,超有价值的golang web框架,附赠最佳编程实践】

License: BSD 2-Clause "Simplified" License

Go 100.00%

wsp's Introduction

中文 README

wsp (go http webserver)

Original Intention

  • Simple and reliable, fully take advantage of golang and have not added anything else complicated. Advantages: much easier to update with golang and reduce learning cost of users.
  • The routing pattern like controller/action provided by yii is commonly used, wsp has already supported.
  • It is easy to use java annotation, in the wsp, we can define the invoking of filter method via annotation.
  • We cannot reduce the QPS of original golang http webserver due to the introduction of wsp.

Installation

go get -u github.com/simplejia/wsp

Occassion

  • Offer service via http webserver
  • Backend api service

Case

  • Massive internet social project

Implementation

  • The routing is generated automatically. Providing the implementation code of controller/action according to the requirements, wsp will analyze the project code, generate routing table automatically and record it in demo/WSP.go. In addition, when controller/action defines code, it has to comply with the definition: func(http.ResponseWriter, *http.Request) and should have method receiver. demo_set.go
package controller

import (
	"net/http"

	"github.com/simplejia/wsp/demo/service"
)

// @prefilter("Login", {"Method":{"type":"get"}})
// @postfilter("Boss")
func (demo *Demo) Set(w http.ResponseWriter, r *http.Request) {
	key := r.FormValue("key")
	value := r.FormValue("value")
	demoService := service.NewDemo()
	demoService.Set(key, value)

	json.NewEncoder(w).Encode(map[string]interface{}{
		"code": 0,
	})
}

WSP.go

// generated by wsp, DO NOT EDIT.

package main

import "net/http"
import "time"
import "github.com/simplejia/wsp/demo/controller"
import "github.com/simplejia/wsp/demo/filter"

func init() {
	http.HandleFunc("/Demo/Get", func(w http.ResponseWriter, r *http.Request) {
		t := time.Now()
		_ = t
		var e interface{}
		c := new(controller.Demo)
		defer func() {
			e = recover()
			if ok := filter.Boss(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
				return
			}
		}()
		c.Get(w, r)
	})

	http.HandleFunc("/Demo/Set", func(w http.ResponseWriter, r *http.Request) {
		t := time.Now()
		_ = t
		var e interface{}
		c := new(controller.Demo)
		defer func() {
			e = recover()
			if ok := filter.Boss(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
				return
			}
		}()
		if ok := filter.Login(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
			return
		}
		if ok := filter.Method(w, r, map[string]interface{}{"type": "get", "__T__": t, "__C__": c, "__E__": e}); !ok {
			return
		}
		c.Set(w, r)
	})

}
  • wsp will analyze the project code, find the annotation that meets its requirements(referring to demo/controller/demo_set.go ) and generate filter invoking code automatically in demo/wsp.go. The annotation of filter is divided into prefilter and postfilter. Its format is as @prefilter({json body}),{json body} standing for the input parameter which complies with the definition format of json array(removing the brackets at the beginning and at the end) and is able to include string value or object value. The definition of filter function meets func (http.ResponseWriter, *http.Request, map[string]interface{}) bool. The filter function is as the following: method.go
package filter

import (
	"net/http"
	"strings"
)

func Method(w http.ResponseWriter, r *http.Request, p map[string]interface{}) bool {
	method, ok := p["type"].(string)
	if ok && strings.ToLower(r.Method) != strings.ToLower(method) {
		http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
		return false
	}
	return true
}

When inputting the parameter map[string]interface{} with filter, it will automatically set "T"(time.Time) whose value is the starting time and can be used to record the consuming time. The automatically set "C",{Controller} type whose value is {Controller} instance and it can access relevant data via the interface which is more simple and practical than via context package in accessing data. The automatically set “E",its value is the returned value of recover(), which is used to monitor errors and deal with them(postfilter has to recover()).

  • The instances of project main.go main.go
package main

import (
	"log"

	"github.com/simplejia/clog"
	"github.com/simplejia/lc"

	"net/http"

	_ "github.com/simplejia/wsp/demo/clog"
	_ "github.com/simplejia/wsp/demo/conf"
	_ "github.com/simplejia/wsp/demo/mysql"
	_ "github.com/simplejia/wsp/demo/redis"
)

func init() {
	lc.Init(1e5)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.NotFound(w, r)
	})
}

func main() {
	clog.Info("main()")

	log.Panic(http.ListenAndServe(":8080", nil))
}

Miscellaneous

  • Under the same condition of (8 CORE, 8G), by wrk pressure measurement tools, the dry run QPS of wps is 9 million.
  • It is much easier to add middleware(func(http.Handler) http.Handler). Actually, we recommend support such similar functions via defining the filters more.
  • It is much easier to edit the following test case:

  • To provide a simple and easily expansive project stub

Original Intention

  • Simple and reliable, to fully take advantage of golang and have not added anything else complicated. Advantages: much easier to update with golang and reduce learning cost of users.
  • Providing common components, as the following:
    • Config, providing automatic analysis of the configuration files(referring to conf). You can redefine the parameters in the configuration files via passing env and conf parameters, such as ./demo -env dev -conf='app.port=8080;clog.mode=1', multiple parameters are separated by ;
    • redis,using github.com/garyburd/redigo, provide automatic analysis of the configuration files, referring to redis
    • mysql,using database/sql, provide automatic analysis of the configuration files, referring to mysql,At the same time, to facilitate the object mapping, it provides the most commonly used orm components, referring to orm

Project guidance

  • Project Tree:
├── WSP.go
├── clog
│   └── clog.go
├── conf
│   ├── conf.go
│   └── conf.json
├── controller
│   ├── base.go
│   ├── demo.go
│   ├── demo_get.go
│   ├── demo_get_test.go
│   ├── demo_set.go
│   ├── demo_set_test.go
│   └── init_test.go
├── demo
├── filter
│   ├── boss.go
│   ├── login.go
│   └── method.go
├── main.go
├── model
│   ├── demo.go
│   ├── demo_get.go
│   └── demo_set.go
├── mysql
│   ├── demo_db.json
│   └── mysql.go
├── redis
│   ├── demo.json
│   └── redis.go
└── service
    ├── demo.go
    ├── demo_get.go
    ├── demo_set.go
  • Controller path: in charge of the parameter analysis of request and service invoking
  • Service path: in charge of dealing with logic and model invoking
  • Model path: in charge of dealing with data
  • In api implementation, recommending one api matched one file, such as controller/demo_get.go, service/demo_get.go, model/demo_get.go

中文

wsp (go http webserver)

实现初衷

  • 简单可依赖,充分利用go已有的东西,不另外增加复杂、难以理解的东西,这样做的好处包括:更容易跟随go的升级而升级,降低使用者学习成本
  • yii提供的controller/action的路由方式比较常用,在wsp里实现一套
  • java annotation的功能挺方便,在wsp里,通过注释来实现过滤器方法的调用定义
  • 不能因为wsp的引入而降低原生go http webserver的性能

安装

go get -u github.com/simplejia/wsp

使用场景

  • 以http webserver方式对外提供服务
  • 后台接口服务

使用案例

  • 大型互联网社交业务

实现方式

  • 路由自动生成,按要求提供controller/action的实现代码,wsp执行后会分析项目代码,自动生成路由表并记录在文件demo/WSP.go里,controller/action定义代码必须符合函数定义:func(http.ResponseWriter, *http.Request),并且是带receiver的method demo_set.go
package controller

import (
	"net/http"

	"github.com/simplejia/wsp/demo/service"
)

// @prefilter("Login", {"Method":{"type":"get"}})
// @postfilter("Boss")
func (demo *Demo) Set(w http.ResponseWriter, r *http.Request) {
	key := r.FormValue("key")
	value := r.FormValue("value")
	demoService := service.NewDemo()
	demoService.Set(key, value)

	json.NewEncoder(w).Encode(map[string]interface{}{
		"code": 0,
	})
}

WSP.go

// generated by wsp, DO NOT EDIT.

package main

import "net/http"
import "time"
import "github.com/simplejia/wsp/demo/controller"
import "github.com/simplejia/wsp/demo/filter"

func init() {
	http.HandleFunc("/Demo/Get", func(w http.ResponseWriter, r *http.Request) {
		t := time.Now()
		_ = t
		var e interface{}
		c := new(controller.Demo)
		defer func() {
			e = recover()
			if ok := filter.Boss(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
				return
			}
		}()
		c.Get(w, r)
	})

	http.HandleFunc("/Demo/Set", func(w http.ResponseWriter, r *http.Request) {
		t := time.Now()
		_ = t
		var e interface{}
		c := new(controller.Demo)
		defer func() {
			e = recover()
			if ok := filter.Boss(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
				return
			}
		}()
		if ok := filter.Login(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
			return
		}
		if ok := filter.Method(w, r, map[string]interface{}{"type": "get", "__T__": t, "__C__": c, "__E__": e}); !ok {
			return
		}
		c.Set(w, r)
	})

}
  • wsp分析项目代码,寻找符合要求的注释(见demo/controller/demo_set.go),自动生成过滤器调用代码在文件demo/WSP.go里,filter注解分为前置过滤器(prefilter)和后置过滤器(postfilter),格式如:@prefilter({json body}),{json body}代表传入参数,符合json array定义格式(去掉前后的中括号),可以包含string值或者object值,filter函数定义满足:func (http.ResponseWriter, *http.Request, map[string]interface{}) bool,过滤器函数如下: method.go
package filter

import (
	"net/http"
	"strings"
)

func Method(w http.ResponseWriter, r *http.Request, p map[string]interface{}) bool {
	method, ok := p["type"].(string)
	if ok && strings.ToLower(r.Method) != strings.ToLower(method) {
		http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
		return false
	}
	return true
}

filter输入参数map[string]interface{},会自动设置"T",time.Time类型,值为执行起始时间,可用于耗时统计,"C",{Controller}类型,值为{Controller}实例,可通过接口方式存取相关数据(这种方式存取数据较使用context包方式更简单实用),"E",值为recover()返回值,用于检测错误并处理(后置过滤器必须recover())

  • 项目main.go代码示例 (在开头定义go:generate,通过go generate信命令生成WSP.go文件) main.go
//go:generate wsp

package main

import (
	"log"

	"github.com/simplejia/clog"
	"github.com/simplejia/lc"

	"net/http"

	_ "github.com/simplejia/wsp/demo/clog"
	_ "github.com/simplejia/wsp/demo/conf"
	_ "github.com/simplejia/wsp/demo/mysql"
	_ "github.com/simplejia/wsp/demo/redis"
)

func init() {
	lc.Init(1e5)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.NotFound(w, r)
	})
}

func main() {
	clog.Info("main()")

	log.Panic(http.ListenAndServe(":8080", nil))
}

miscellaneous

  • 通过wrk压测工具在同样环境下(8核,8g),wsp空跑qps:9万
  • 更方便加入middleware(func(http.Handler) http.Handler),其实更推荐通过定义过滤器的方式支持类似功能
  • 更方便编写如下的测试用例:

  • 提供一个简单易扩展的项目stub

实现初衷

  • 简单可依赖,充分利用go已有的东西,不另外增加复杂、难以理解的东西,这样做的好处包括:更容易跟随go的升级而升级,降低使用者学习成本
  • 提供常用组件的简单包装,如下:
    • config,提供项目主配置文件自动解析,见conf,可以通过传入自定义的env及conf参数来重定义配置文件里的参数,如:./demo -env dev -conf='app.port=8080;clog.mode=1',多个参数用;分隔
    • redis,使用(github.com/garyburd/redigo),提供配置文件自动解析,见redis
    • mysql,使用(database/sql),提供配置文件自动解析,见mysql,同时为了方便对象映射,提供了最常用的orm组件供选择使用,见orm

项目编写指导意见

  • 目录结构:
├── WSP.go
├── clog
│   └── clog.go
├── conf
│   ├── conf.go
│   └── conf.json
├── controller
│   ├── base.go
│   ├── demo.go
│   ├── demo_get.go
│   ├── demo_get_test.go
│   ├── demo_set.go
│   ├── demo_set_test.go
│   └── init_test.go
├── demo
├── filter
│   ├── boss.go
│   ├── login.go
│   └── method.go
├── main.go
├── model
│   ├── demo.go
│   ├── demo_get.go
│   └── demo_set.go
├── mysql
│   ├── demo_db.json
│   └── mysql.go
├── redis
│   ├── demo.json
│   └── redis.go
└── service
    ├── demo.go
    ├── demo_get.go
    ├── demo_set.go
* controller目录:负责request参数解析,service调用。 注:wsp也会解析子目录
* service目录:负责逻辑处理,model调用
* model目录:负责数据处理
  • 接口实现上,建议一个接口对应一个文件,如controller/demo_get.go, service/demo_get.go, model/demo_get.go

wsp's People

Contributors

simplejia avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.