完善ghttp,gcache功能

This commit is contained in:
John
2017-12-07 14:57:16 +08:00
parent 2141059ace
commit 8e226d5707
12 changed files with 260 additions and 258 deletions

View File

@ -1,107 +1,2 @@
package ghttp
import (
"net/http"
"time"
"crypto/tls"
"log"
"net/url"
)
// http客户端
type Client struct {
http.Client
}
// http server结构体
type Server struct {
server http.Server
config ServerConfig
handlerMap HandlerMap
}
// 请求对象
type ClientRequest struct {
http.Request
getvals *url.Values
}
// 客户端请求结果对象
type ClientResponse struct {
http.Response
}
// 服务端请求返回对象
type ServerResponse struct {
http.ResponseWriter
}
// http回调函数
type HandlerFunc func(*ClientRequest, *ServerResponse)
// uri与回调函数的绑定记录表
type HandlerMap map[string]HandlerFunc
// HTTP Server 设置结构体
type ServerConfig struct {
// HTTP Server基础字段
Addr string // 监听IP和端口监听本地所有IP使用":端口"
Handler http.Handler // 默认的处理函数
TLSConfig *tls.Config // TLS配置
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int // 最大的header长度
ErrorLog *log.Logger // 错误日志的处理接口
// gf 扩展信息字段
IndexFiles []string // 默认访问的文件列表
IndexFolder bool // 如果访问目录是否显示目录列表
ServerAgent string // server agent
ServerRoot string // 服务器服务的本地目录根路径
}
// 默认HTTP Server
var defaultServerConfig = ServerConfig {
Addr : ":80",
Handler : nil,
ReadTimeout : 60 * time.Second,
WriteTimeout : 60 * time.Second,
IdleTimeout : 60 * time.Second,
MaxHeaderBytes : 1024,
IndexFiles : []string{"index.html", "index.htm"},
IndexFolder : false,
ServerAgent : "gf",
ServerRoot : "",
}
// 修改默认的http server配置
func SetDefaultServerConfig (c ServerConfig) {
defaultServerConfig = c
}
// 创建一个默认配置的HTTP Server(默认监听端口是80)
func NewServer() (*Server) {
return NewServerByConfig(defaultServerConfig)
}
// 创建一个HTTP Server返回指针
func NewServerByAddr(addr string) (*Server) {
config := defaultServerConfig
config.Addr = addr
return NewServerByConfig(config)
}
// 创建一个HTTP Server
func NewServerByAddrRoot(addr string, root string) (*Server) {
config := defaultServerConfig
config.Addr = addr
config.ServerRoot = root
return NewServerByConfig(config)
}
// 根据输入配置创建一个http server对象
func NewServerByConfig(s ServerConfig) (*Server) {
var server Server
server.SetConfig(s)
return &server
}

View File

@ -4,8 +4,26 @@ import (
"net/http"
"strings"
"time"
"net/url"
"bytes"
)
// http客户端
type Client struct {
http.Client
}
// 请求对象
type ClientRequest struct {
http.Request
getvals *url.Values // GET参数
}
// 客户端请求结果对象
type ClientResponse struct {
http.Response
}
// http客户端对象指针
func NewClient() (*Client) {
return &Client{}
@ -17,106 +35,104 @@ func (c *Client) SetTimeOut(t time.Duration) {
}
// GET请求
func (c *Client) Get(url string) *ClientResponse {
return c.Request("GET", url, "")
func (c *Client) Get(url string) (*ClientResponse, error) {
return c.Request("GET", url, []byte(""))
}
// PUT请求
func (c *Client) Put(url, data string) *ClientResponse {
return c.Request("PUT", url, data)
func (c *Client) Put(url, data string) (*ClientResponse, error) {
return c.Request("PUT", url, []byte(data))
}
// POST请求提交数据
func (c *Client) Post(url, data string) *ClientResponse {
func (c *Client) Post(url, data string) (*ClientResponse, error) {
resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data))
if err != nil {
//glog.Println(err)
return nil
return nil, err
}
r := &ClientResponse{}
r.Response = *resp
return r
return r, nil
}
// DELETE请求
func (c *Client) Delete(url, data string) *ClientResponse {
return c.Request("DELETE", url, data)
func (c *Client) Delete(url, data string) (*ClientResponse, error) {
return c.Request("DELETE", url, []byte(data))
}
func (c *Client) Head(url, data string) *ClientResponse {
return c.Request("HEAD", url, data)
func (c *Client) Head(url, data string) (*ClientResponse, error) {
return c.Request("HEAD", url, []byte(data))
}
func (c *Client) Patch(url, data string) *ClientResponse {
return c.Request("PATCH", url, data)
func (c *Client) Patch(url, data string) (*ClientResponse, error) {
return c.Request("PATCH", url, []byte(data))
}
func (c *Client) Connect(url, data string) *ClientResponse{
return c.Request("CONNECT", url, data)
func (c *Client) Connect(url, data string) (*ClientResponse, error) {
return c.Request("CONNECT", url, []byte(data))
}
func (c *Client) Options(url, data string) *ClientResponse{
return c.Request("OPTIONS", url, data)
func (c *Client) Options(url, data string) (*ClientResponse, error) {
return c.Request("OPTIONS", url, []byte(data))
}
func (c *Client) Trace(url, data string) *ClientResponse {
return c.Request("TRACE", url, data)
func (c *Client) Trace(url, data string) (*ClientResponse, error) {
return c.Request("TRACE", url, []byte(data))
}
// 请求并返回response对象
func (c *Client) Request(method, url, data string) *ClientResponse {
req, err := http.NewRequest(strings.ToUpper(method), url, strings.NewReader(data))
// 请求并返回response对象,该方法支持二进制提交数据
func (c *Client) Request(method, url string, data []byte) (*ClientResponse, error) {
req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader(data))
if err != nil {
//glog.Println("creating request failed: " + err.Error())
return nil
return nil, err
}
resp, err := c.Do(req)
if err != nil {
//glog.Println("sending request failed: " + err.Error())
return nil
return nil, err
}
r := &ClientResponse{}
r.Response = *resp
return r
return r, nil
}
func Get(url string) *ClientResponse {
return Request("GET", url, "")
func Get(url string) (*ClientResponse, error) {
return Request("GET", url, []byte(""))
}
func Put(url, data string) *ClientResponse {
return Request("PUT", url, data)
func Put(url, data string) (*ClientResponse, error) {
return Request("PUT", url, []byte(data))
}
func Post(url, data string) *ClientResponse {
return Request("PUT", url, data)
func Post(url, data string) (*ClientResponse, error) {
return Request("PUT", url, []byte(data))
}
func Delete(url, data string) *ClientResponse {
return Request("DELETE", url, data)
func Delete(url, data string) (*ClientResponse, error) {
return Request("DELETE", url, []byte(data))
}
func Head(url, data string) *ClientResponse {
return Request("HEAD", url, data)
func Head(url, data string) (*ClientResponse, error) {
return Request("HEAD", url, []byte(data))
}
func Patch(url, data string) *ClientResponse {
return Request("PATCH", url, data)
func Patch(url, data string) (*ClientResponse, error) {
return Request("PATCH", url, []byte(data))
}
func Connect(url, data string) *ClientResponse{
return Request("CONNECT", url, data)
func Connect(url, data string) (*ClientResponse, error) {
return Request("CONNECT", url, []byte(data))
}
func Options(url, data string) *ClientResponse{
return Request("OPTIONS", url, data)
func Options(url, data string) (*ClientResponse, error) {
return Request("OPTIONS", url, []byte(data))
}
func Trace(url, data string) *ClientResponse {
return Request("TRACE", url, data)
func Trace(url, data string) (*ClientResponse, error) {
return Request("TRACE", url, []byte(data))
}
func Request(method, url, data string) *ClientResponse {
// 该方法支持二进制提交数据
func Request(method, url string, data []byte) (*ClientResponse, error) {
return NewClient().Request(method, url, data)
}

View File

@ -0,0 +1,20 @@
package ghttp
import (
"io/ioutil"
)
// 获取返回的数据
func (r *ClientResponse) ReadAll() []byte {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil
}
return body
}
// 关闭返回的HTTP链接
func (r *ClientResponse) Close() {
r.Response.Close = true
r.Body.Close()
}

View File

@ -5,25 +5,12 @@ type Controller struct {
Server *Server
}
// 控制器接口
type ControllerApi interface {
Get(r *ClientRequest, w *ServerResponse)
Put(r *ClientRequest, w *ServerResponse)
Post(r *ClientRequest, w *ServerResponse)
Delete(r *ClientRequest, w *ServerResponse)
Head(r *ClientRequest, w *ServerResponse)
Patch(r *ClientRequest, w *ServerResponse)
Connect(r *ClientRequest, w *ServerResponse)
Options(r *ClientRequest, w *ServerResponse)
Trace(r *ClientRequest, w *ServerResponse)
}
func (c *Controller) Get(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Put(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Post(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Delete(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Head(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Patch(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Connect(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Options(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Trace(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Get(*ClientRequest, *ServerResponse) {}
func (c *Controller) Put(*ClientRequest, *ServerResponse) {}
func (c *Controller) Post(*ClientRequest, *ServerResponse) {}
func (c *Controller) Delete(*ClientRequest, *ServerResponse) {}
func (c *Controller) Head(*ClientRequest, *ServerResponse) {}
func (c *Controller) Patch(*ClientRequest, *ServerResponse) {}
func (c *Controller) Connect(*ClientRequest, *ServerResponse) {}
func (c *Controller) Options(*ClientRequest, *ServerResponse) {}
func (c *Controller) Trace(*ClientRequest, *ServerResponse) {}

View File

@ -0,0 +1,14 @@
package ghttp
// RESTful控制器接口
type ControllerRest interface {
Get(*ClientRequest, *ServerResponse)
Put(*ClientRequest, *ServerResponse)
Post(*ClientRequest, *ServerResponse)
Delete(*ClientRequest, *ServerResponse)
Head(*ClientRequest, *ServerResponse)
Patch(*ClientRequest, *ServerResponse)
Connect(*ClientRequest, *ServerResponse)
Options(*ClientRequest, *ServerResponse)
Trace(*ClientRequest, *ServerResponse)
}

View File

@ -1,35 +0,0 @@
package ghttp
import (
"gitee.com/johng/gf/g/encoding/gjson"
"io/ioutil"
"gitee.com/johng/gf/g/os/glog"
)
type ResponseJson struct {
Result int `json:"result"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// 关闭返回的HTTP链接
func (r *ClientResponse) Close() {
r.Response.Close = true
r.Body.Close()
}
// 返回固定格式的json
func (r *ServerResponse) ResponseJson(result int, message string, data interface{}) {
r.Header().Set("Content-type", "application/json")
r.Write([]byte(gjson.Encode(ResponseJson{ result, message, data })))
}
// 获取返回的数据
func (r *ClientResponse) ReadAll() string {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
glog.Println(err)
return ""
}
return string(body)
}

View File

@ -7,16 +7,38 @@ import (
"crypto/tls"
"time"
"log"
"regexp"
"gitee.com/johng/gf/g/os/glog"
"sync"
"errors"
)
// http server结构体
type Server struct {
hmu sync.RWMutex // handlerMap互斥锁
server http.Server // 底层http server对象
config ServerConfig // 配置对象
handlerMap HandlerMap // 回调函数
}
// http回调函数
type HandlerFunc func(*ClientRequest, *ServerResponse)
// uri与回调函数的绑定记录表
type HandlerMap map[string]HandlerFunc
// 创建一个默认配置的HTTP Server(默认监听端口是80)
func NewServer() (*Server) {
server := Server{}
server.SetConfig(defaultServerConfig)
return &server
}
// 执行
func (s *Server)Run() error {
// 底层http server配置
if s.config.Handler == nil {
s.config.Handler = http.HandlerFunc(s.defaultHttpHandle)
}
// 底层http server初始化
s.server = http.Server {
Addr : s.config.Addr,
Handler : s.config.Handler,
@ -26,16 +48,10 @@ func (s *Server)Run() error {
MaxHeaderBytes : s.config.MaxHeaderBytes,
}
// 执行端口监听
err := s.server.ListenAndServe()
if err != nil {
glog.Fatalln(err)
if err := s.server.ListenAndServe(); err != nil {
return err
}
return err
}
// 获取默认的http server设置
func (h Server)GetDefaultSetting() ServerConfig {
return defaultServerConfig
return nil
}
// http server setting设置
@ -121,23 +137,25 @@ func (s *Server)SetServerRoot(root string) {
// 绑定URI到操作函数/方法
// pattern的格式形如/user/list, put:/user, delete:/user
// 支持RESTful的请求格式具体业务逻辑由绑定的处理方法来执行
func (s *Server)BindHandle(pattern string, handler HandlerFunc ) {
func (s *Server)BindHandle(pattern string, handler HandlerFunc) error {
s.hmu.Lock()
defer s.hmu.Unlock()
if s.handlerMap == nil {
s.handlerMap = make(HandlerMap)
}
key := ""
reg := regexp.MustCompile(`(\w+?)\s*:\s*(.+)`)
result := reg.FindStringSubmatch(pattern)
result := strings.Split(pattern, ":")
if len(result) > 1 {
key = strings.ToUpper(result[1]) + ":" + result[2]
key = strings.ToUpper(result[0]) + ":" + result[1]
} else {
key = strings.TrimSpace(pattern)
}
if _, ok := s.handlerMap[key]; ok {
panic("duplicated http server handler for: " + pattern)
return errors.New("duplicated http server handler for: " + pattern)
} else {
s.handlerMap[key] = handler
}
return nil
}
// 通过映射数组绑定URI到操作函数/方法
@ -148,7 +166,7 @@ func (s *Server)BindHandleByMap(m HandlerMap) {
}
// 绑定控制器控制器需要继承gmvc.ControllerBase对象并实现需要的REST方法
func (s *Server)BindController(uri string, c ControllerApi) {
func (s *Server)BindController(uri string, c ControllerRest) {
s.BindHandleByMap(HandlerMap{
"GET:" + uri : c.Get,
"PUT:" + uri : c.Put,

View File

@ -0,0 +1,46 @@
package ghttp
import (
"net/http"
"crypto/tls"
"time"
"log"
"gitee.com/johng/gf/g/os/gfile"
)
// HTTP Server 设置结构体
type ServerConfig struct {
// HTTP Server基础字段
Addr string // 监听IP和端口监听本地所有IP使用":端口"
Handler http.Handler // 默认的处理函数
TLSConfig *tls.Config // TLS配置
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int // 最大的header长度
ErrorLog *log.Logger // 错误日志的处理接口
// gf 扩展信息字段
IndexFiles []string // 默认访问的文件列表
IndexFolder bool // 如果访问目录是否显示目录列表
ServerAgent string // server agent
ServerRoot string // 服务器服务的本地目录根路径
}
// 默认HTTP Server
var defaultServerConfig = ServerConfig {
Addr : ":80",
Handler : nil,
ReadTimeout : 60 * time.Second,
WriteTimeout : 60 * time.Second,
IdleTimeout : 60 * time.Second,
MaxHeaderBytes : 1024,
IndexFiles : []string{"index.html", "index.htm"},
IndexFolder : false,
ServerAgent : "gf",
ServerRoot : gfile.SelfDir(),
}
// 获取默认的http server设置
func DefaultSetting() ServerConfig {
return defaultServerConfig
}

View File

@ -15,8 +15,8 @@ import (
// 默认HTTP Server处理入口底层默认使用了gorutine调用该接口
func (s *Server)defaultHttpHandle(w http.ResponseWriter, r *http.Request) {
request := ClientRequest{}
response := ServerResponse {}
request.Request = *r
response := ServerResponse {server : s}
request.Request = *r
response.ResponseWriter = w
if f, ok := s.handlerMap[r.URL.Path]; ok {
f(&request, &response)
@ -93,8 +93,7 @@ func (s *Server)listDir(w http.ResponseWriter, f http.File) {
if d.IsDir() {
name += "/"
}
url := url.URL{Path: name}
fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.String(), ghtml.SpecialChars(name))
fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.URL{Path: name}.String(), ghtml.SpecialChars(name))
}
fmt.Fprintf(w, "</pre>\n")
}

View File

@ -0,0 +1,36 @@
package ghttp
import (
"net/http"
"gitee.com/johng/gf/g/encoding/gjson"
)
// 服务端请求返回对象
type ServerResponse struct {
http.ResponseWriter
server *Server // 所属Server对象
}
// 返回的固定JSON数据结构
type ResponseJson struct {
Result int `json:"result"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// 返回信息
func (r *ServerResponse) Response(content []byte) {
if r.Header().Get("Content-Type") == "" {
r.Header().Set("Content-Type", "text/plain; charset=utf-8")
}
r.Write(content)
}
// 返回固定格式的json
func (r *ServerResponse) ResponseJson(result int, message string, data interface{}) {
if r.Header().Get("Content-Type") == "" {
r.Header().Set("Content-Type", "application/json")
}
r.Write([]byte(gjson.Encode(ResponseJson{ result, message, data })))
}

View File

@ -8,35 +8,41 @@ import (
)
const (
gCACHE_GROUP_SIZE = 4 // 缓存分区大小不能超过uint8的最大值
gDEFAULT_CACHE_GROUP_SIZE = 4 // 默认缓存分区大小不能超过uint8的最大值
)
// 缓存对象
type Cache struct {
sync.RWMutex
m map[uint8]*CacheMap // 分区大小数字作为索引
g uint8 // 分区大小
m map[uint8]*CacheMap // 以分区大小数字作为索引
}
// 缓存分区对象
type CacheMap struct {
sync.RWMutex
deleted bool // 对象是否已删除以便判断停止goroutine
closed bool // 对象是否已删除以便判断停止goroutine
m map[string]CacheItem // 键值对
}
// 缓存数据项
type CacheItem struct {
v interface{} // 缓存键值
e int64 // 过期时间
}
// 全局缓存管理对象
var cache *Cache = New()
var cache *Cache = New(gDEFAULT_CACHE_GROUP_SIZE)
// Cache对象按照缓存键名首字母做了分组
func New() *Cache {
func New(group uint8) *Cache {
c := &Cache {
g : group,
m : make(map[uint8]*CacheMap),
}
// 初始化分区对象
var i uint8 = 0
for ; i < gCACHE_GROUP_SIZE; i++ {
for ; i < group; i++ {
m := &CacheMap {
m : make(map[string]CacheItem),
}
@ -158,20 +164,14 @@ func (c *Cache) Size() int {
func (c *Cache) Close() {
c.RLock()
for _, cm := range c.m {
cm.Lock()
cm.deleted = true
cm.Unlock()
cm.Close()
}
c.RUnlock()
c.Lock()
c.m = nil
c.Unlock()
}
// 计算缓存的索引
func (c *Cache) getIndex(k string) uint8 {
return uint8(ghash.BKDRHash([]byte(k)) % gCACHE_GROUP_SIZE)
return uint8(ghash.BKDRHash([]byte(k)) % uint32(c.g))
}
// 设置kv缓存键值对过期时间单位为毫秒
@ -207,26 +207,32 @@ func (cm *CacheMap) Remove(k string) {
cm.Unlock()
}
// 自动清理过期键值对(每间隔60秒执行)
// 关闭缓存分区
func (cm *CacheMap) Close() {
cm.Lock()
cm.closed = true
cm.Unlock()
}
// 是否删除
func (cm *CacheMap) isClosed() bool {
cm.RLock()
r := cm.closed
cm.RUnlock()
return r
}
// 自动清理过期键值对(每间隔3秒执行)
func (cm *CacheMap) autoClearLoop() {
for !cm.deleted {
expired := make([]string, 0)
cm.RLock()
for !cm.isClosed() {
cm.Lock()
for k, v := range cm.m {
if v.e > 0 && v.e < gtime.Millisecond() {
expired = append(expired, k)
}
}
cm.RUnlock()
if len(expired) > 0 {
cm.Lock()
for _, k := range expired {
delete(cm.m, k)
}
cm.Unlock()
}
time.Sleep(60 * time.Second)
cm.Unlock()
time.Sleep(3 * time.Second)
}
}