polaris

Polaris中间件实现

起因

polaris虽然是模仿tornado开发,但我觉得作为一个go的web框架,还需要提供一些额外的扩展支持。

polaris现在已经支持session以及middleware,主要参加django。

polaris对于这些额外功能的支持,采取的是注册 + json配置驱动的方式。这个跟go的database/sql有点类似,任何模块都提供一套类似如下的接口:

type Obj interface {

}

type Driver interface{
    Open(jsonConfig json.RawMessage) (Obj, error)
}

func Register(name string, driver Driver) error
func Open(name string, jsonConfig json.RawMessage) (Obj, error)

如果我们需要自定义功能,只需要实现自己的driver以及对应的obj,然后Register进去,后续就可以通过Open直接使用了。

对于每个模块的配置,因为polaris的整体配置是json,所以我也强制要求参数是json格式的,也就是json.RawMessage,各个模块自行进行Unmarshal处理。

session

对于一个session对象,无非就是Set,Get,Delete等,polaris需要关心的是这个session对应的store。store可以理解为该session的持久化保存位置,可以是db,redis,cookie或者memory。

polaris提供的store接口如下:

type Store interface {
    //get a session by id
    //if no session exist, regenerate another id to new a session
    Get(id string) (*Session, error)

    //delete session from store
    Delete(*Session) error

    //Save session to stroe
    Save(*Session) error
}

type Driver interface {
    Open(jsonConfig json.RawMessage) (Store, error)
}

现阶段,只提供了redis的支持,这里特别说明一下,我是在现在才知道redis有一个setex命令,想想以前经常用set + expire来设置一个key以及超时,想想都汗颜。

对于session的持久化,polaris提供了codec的接口,外部可以注册自己的序列化方式,同时在相应的store里面实现。对于一个codec,接口如下:

//codec for session encode and decode
type Codec interface {
    Encode(values map[interface{}]interface{}) ([]byte, error)
    Decode(buf []byte) (map[interface{}]interface{}, error)
}

func RegisterCodec(name string, codec Codec) error
func GetCodec(name string) (Codec, error)

现阶段,polaris提供了gob方式的codec。外部通过GetCodec(“gob”)就可以获取到。

middleware

polaris的middleware主要提供如下接口:

type Middleware interface {
    ProcessRequest(env *context.Env) error
    ProcessResponse(env *context.Env) error
}

context.Env是该次请求的上下文环境,对于每次http请求,polaris会首先调用middleware的ProcessRequest,在处理实际对应的restful接口,然后再调用ProcessResponse。

如果Process的时候,返回error,或者env已经finished,polaris会终止后续的process操作。这套处理流程是否合适后续在好好考量。

现阶段,polaris提供了session middleware的支持。

Todo

polaris采用注册 + json配置的方式,我觉得可以很好的处理后续模块功能的添加问题,后续可以参考django等框架继续完善。

Polaris 一个用Go实现的Restful Web框架

介绍

polaris是一个用go实现的支持restful的web框架,主要参考tornado进行设计。

虽然在go里面搭建一个http server非常的简单,这里强烈推荐gorilla,但并没有很好的对restful模型进行支持。考察了很多开源实现,我决定还是重新造一个轮子,毕竟难度也不怎么大,而且能够根据项目的使用慢慢调整完善。

使用

设计polaris还是参考了tornado,虽然一段时间不用python了,但是我还是对tornado念念不忘。

polaris,我现在只支持get,post,put,head和delete这几种method,剩下的貌似我也没用到过。只要一个对象,它提供了以上一个或多个接口,就能注册进polaris的router中。

先上一个例子:

type Handler1 struct {

}

func (h *Handler1) Prepare(env *Env) {
    fmt.Println("hello prepare")
}

func (h *Handler1) Get(env *Env) {
    env.WriteString("hello get")
}

func (h *Handler1) Post(env *Env) {
    env.WriteString("hello post")
}

type Handler2 struct {

}

//id is a captured submatch for regexp url below
func (h *Handler2) Get(env *Env, id string) {
    env.WriteString("hello " + id)
}

r = NewRouter()

r.Handle("/handler1", new(Handler1))
r.Handle("/handler2/([0-9]+)", new(Handler2))

http.Handle("/", r)
http.ListenAndServe("127.0.0.1:11181", nil)

上面,我们实现了两个handler,对于每一个handler,需要实现以下一个或多个接口:

Get(env *Env)
Post(env *Env)
Head(env *Env)
Put(env *Env)
Delete(env *Env)

Env用以表明该次请求的上下文环境,主要是为了纪念一个内部库stars,以此命名。

鉴于tornado也提供了prepare支持,所以,如果handler提供了Prepare(env *Env)接口,每次调用相应的函数的时候,会优先调用Prepare。

在handler2中,我们可以看到,Get函数多了一个参数id,这主要是为了处理对应的正则url里面含有group的情况。上面handler2对应的url里面有一个([0-9]+) group,所以当url匹配成功之后,我们会将该group实际的数据传递给handler2。

因为正则匹配的结果都是string,所以Get后面的参数都必须是string类型的。实际的类型转换需要handler自行处理。

路由规则

polaris的路由规则比较简单,参考nginx分为literal pattern和regexp pattern两种。当有请求需要处理的时候,首先查看是否在literal pattern中,如果不是,则根据注册的regexp pattern依次匹配。

上面的例子中,/handler1就是literal pattern,而/handler2/([0-9]+)则是regexp pattern。在go中,判断一个字符串是否是正则表达式,只要通过regexp.QuoteMeta就可以了,如果得到的值跟原字符串一样,该字符串就是literal pattern。

实现

polaris核心实现是基于reflect,这里列举一些:

判断一个handler是否具有相关接口

  • reflect.ValueOf(handler)得到handler的value v
  • v.MethodByName(“Get”)得到get function的value m
  • m.Kind() == reflect.Func判断是否存在Get函数

判断接口是否满足规范

  • m.NumIn()获取该函数输入参数的个数
  • m.In(0).Kind() == reflect.Ptr和m.In(0).String() == “polaris.Env”判断第一个参数必须为\Env
  • m.In(i).Kind() == reflect.String判断第i(i>=1)个参数必须为string

根据传入的参数调用相应的函数

  • 根据传入的http request method找到对应的函数 m
  • values = make([]relfect.Value, nArgs)
  • 通过reflect.ValueOf填充参数,第一个参数为*Env
  • m.Call(values),使用Call进行函数调用

后续

现在的polaris只支持基本的restful模型,后续我考虑将自己开发的其他库,譬如log,mysql等进行整合,使其真正成为一个可用的restful web framework。

代码在这里https://github.com/siddontang/polaris,欢迎大家反馈。