完成ghttp.Server路由改进工作,增加字段匹配规则特性

This commit is contained in:
John
2018-07-24 22:00:51 +08:00
parent 0ddfdae20c
commit 4af0343851
10 changed files with 273 additions and 370 deletions

View File

@ -84,10 +84,10 @@ func (s *Server) setHandler(pattern string, item *HandlerItem) error {
s.hmmu.Lock()
defer s.hmmu.Unlock()
defer s.clearHandlerCache()
if s.isUriHasRule(uri) {
// 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中
if s.isPatternUriHasFuzzRule(uri) {
// 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中
// 非叶节点为哈希表检索节点按照URI注册的层级进行高效检索直至到叶子链表节点
// 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表一般数据量不大,所以效率比较高;
// 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表数据量不会很大,所以效率比较高;
if _, ok := s.handlerTree[domain]; !ok {
s.handlerTree[domain] = make(map[string]interface{})
}
@ -97,44 +97,46 @@ func (s *Server) setHandler(pattern string, item *HandlerItem) error {
lists := make([]*list.List, 0)
array := strings.Split(uri[1:], "/")
item.router.Priority = len(array)
// 键名"*fuzz"代表模糊匹配节点,其下会有一个链表;
// 键名"*list"代表链表,叶子节点和模糊匹配节点都有该属性;
for k, v := range array {
if len(v) == 0 {
continue
}
switch v[0] {
case ':':
fallthrough
case '*':
v = "/"
if v, ok := p.(map[string]interface{})["*list"]; !ok {
p.(map[string]interface{})["*list"] = list.New()
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
} else {
lists = append(lists, v.(*list.List))
}
fallthrough
default:
if _, ok := p.(map[string]interface{})[v]; !ok {
p.(map[string]interface{})[v] = make(map[string]interface{})
}
p = p.(map[string]interface{})[v]
// 到达叶子节点往list中增加匹配规则
if k == len(array) - 1 && v != "/" {
if v, ok := p.(map[string]interface{})["*list"]; !ok {
p.(map[string]interface{})["*list"] = list.New()
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
} else {
lists = append(lists, v.(*list.List))
}
}
// 判断是否模糊匹配规则
if gregex.IsMatchString(`^[:\*]|{[\w\.\-]+}`, v) {
v = "*fuzz"
// 由于是模糊规则,因此这里会有一个*list用以将后续的路由规则加进来
// 检索会从叶子节点的链表往根节点按照优先级进行检索
if v, ok := p.(map[string]interface{})["*list"]; !ok {
p.(map[string]interface{})["*list"] = list.New()
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
} else {
lists = append(lists, v.(*list.List))
}
}
// 属性层级数据写入
if _, ok := p.(map[string]interface{})[v]; !ok {
p.(map[string]interface{})[v] = make(map[string]interface{})
}
p = p.(map[string]interface{})[v]
// 到达叶子节点往list中增加匹配规则(条件 v != "*fuzz" 是因为模糊节点的话在前面已经添加了*list链表)
if k == len(array) - 1 && v != "*fuzz" {
if v, ok := p.(map[string]interface{})["*list"]; !ok {
p.(map[string]interface{})["*list"] = list.New()
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
} else {
lists = append(lists, v.(*list.List))
}
}
}
// 得到的lists是该路由规则一路匹配下来相关的模糊匹配链表(注意不是这棵树所有的链表)
// 从头开始遍历每个节点的模糊匹配链表,将该路由项插入进去(按照优先级高的放在前面)
for _, l := range lists {
for e := l.Front(); e != nil; e = e.Next() {
if s.compareHandlerItemPriority(item, e.Value.(*HandlerItem)) {
l.InsertBefore(item, e)
return nil
break
}
}
l.PushBack(item)
@ -155,6 +157,9 @@ func (s *Server) setHandler(pattern string, item *HandlerItem) error {
}
// 对比两个HandlerItem的优先级需要非常注意的是注意新老对比项的参数先后顺序
// 优先级比较规则:
// 1、层级越深优先级越高(对比/数量)
// 2、模糊规则优先级{xxx} > :xxx > *xxx
func (s *Server) compareHandlerItemPriority(newItem, oldItem *HandlerItem) bool {
if newItem.router.Priority > oldItem.router.Priority {
return true
@ -162,9 +167,18 @@ func (s *Server) compareHandlerItemPriority(newItem, oldItem *HandlerItem) bool
if newItem.router.Priority < oldItem.router.Priority {
return false
}
if strings.Count(newItem.router.Uri, "/:") > strings.Count(oldItem.router.Uri, "/:") {
// 例如:/{user}/{act} 比 /:user/:act 优先级高
if strings.Count(newItem.router.Uri, "{") > strings.Count(oldItem.router.Uri, "{") {
return true
}
// 例如: /:name/update 比 /:name/:action优先级高
if strings.Count(newItem.router.Uri, "/:") < strings.Count(oldItem.router.Uri, "/:") {
// 例如: /:name/:action 比 /:name/*any 优先级高
if strings.Count(newItem.router.Uri, "/*") < strings.Count(oldItem.router.Uri, "/*") {
return true
}
return false
}
return false
}
@ -202,7 +216,7 @@ func (s *Server) searchHandlerDynamic(r *Request) *handlerCacheItem {
if !ok {
continue
}
// 多层链表的目的是当叶子节点未有任何规则匹配时,让父级模糊匹配规则继续处理
// 多层链表(每个节点都有一个*list链表)的目的是当叶子节点未有任何规则匹配时,让父级模糊匹配规则继续处理
lists := make([]*list.List, 0)
for k, v := range array {
if _, ok := p.(map[string]interface{})["*list"]; ok {
@ -215,14 +229,18 @@ func (s *Server) searchHandlerDynamic(r *Request) *handlerCacheItem {
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
}
}
} else {
if _, ok := p.(map[string]interface{})["*fuzz"]; ok {
p = p.(map[string]interface{})["*fuzz"]
}
}
// 如果是叶子节点,同时判断当前层级的"/"键名,解决例如:/user/*action 匹配 /user 的规则
// 如果是叶子节点,同时判断当前层级的"*fuzz"键名,解决例如:/user/*action 匹配 /user 的规则
if k == len(array) - 1 {
if _, ok := p.(map[string]interface{})["/"]; ok {
p = p.(map[string]interface{})["/"]
if _, ok := p.(map[string]interface{})["*list"]; ok {
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
}
if _, ok := p.(map[string]interface{})["*fuzz"]; ok {
p = p.(map[string]interface{})["*fuzz"]
}
if _, ok := p.(map[string]interface{})["*list"]; ok {
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
}
}
}
@ -231,19 +249,26 @@ func (s *Server) searchHandlerDynamic(r *Request) *handlerCacheItem {
for i := len(lists) - 1; i >= 0; i-- {
for e := lists[i].Front(); e != nil; e = e.Next() {
item := e.Value.(*HandlerItem)
// 动态匹配规则带有gDEFAULT_METHOD的情况不会像静态规则那样直接解析为所有的HTTP METHOD存储
if strings.EqualFold(item.router.Method, gDEFAULT_METHOD) || strings.EqualFold(item.router.Method, r.Method) {
// 不管正则关键字符转义问题
//rule, names := s.patternToRegRule(gstr.ReplaceByMap(gregex.Quote(item.router.Uri), map[string]string {
// `\{` : `{`,
// `\}` : `}`,
// `\*` : `*`,
//}))
rule, names := s.patternToRegRule(item.router.Uri)
if gregex.IsMatchString(rule, r.URL.Path) {
if match, err := gregex.MatchString(rule, r.URL.Path); err == nil && len(match) > 1 {
//gutil.Dump(match)
//gutil.Dump(names)
handlerItem := &handlerCacheItem{item, nil}
// 如果需要query匹配那么需要重新解析URL
// 如果需要query匹配那么需要重新正则解析URL
if len(names) > 0 {
if match, err := gregex.MatchString(rule, r.URL.Path); err == nil {
array := strings.Split(names, ",")
if len(match) > len(array) {
handlerItem.values = make(map[string][]string)
for index, name := range array {
handlerItem.values[name] = []string{match[index + 1]}
}
array := strings.Split(names, ",")
if len(match) > len(array) {
handlerItem.values = make(map[string][]string)
for index, name := range array {
handlerItem.values[name] = []string{match[index + 1]}
}
}
}
@ -281,7 +306,18 @@ func (s *Server) patternToRegRule(rule string) (regrule string, names string) {
}
names += v[1:]
default:
regrule += "/" + v
s, _ := gregex.ReplaceStringFunc(`{[\w\.\-]+}`, v, func(s string) string {
if len(names) > 0 {
names += ","
}
names += s[1 : len(s) - 1]
return `([\w\.\-]+)`
})
if strings.EqualFold(s, v) {
regrule += "/" + v
} else {
regrule += "/" + s
}
}
}
regrule += `$`
@ -289,8 +325,8 @@ func (s *Server) patternToRegRule(rule string) (regrule string, names string) {
}
// 判断URI中是否包含动态注册规则
func (s *Server) isUriHasRule(uri string) bool {
if len(uri) > 1 && (strings.Index(uri, "/:") != -1 || strings.Index(uri, "/*") != -1) {
func (s *Server) isPatternUriHasFuzzRule(uri string) bool {
if len(uri) > 1 && gregex.IsMatchString(`^/[:\*]|{[\w\.\-]+}`, uri) {
return true
}
return false

View File

@ -34,26 +34,22 @@ func (s *Server)bindHandlerByMap(m HandlerMap) error {
return nil
}
// 将方法名称按照设定的规则合并到pattern中.
// 规则1pattern中的URI包含{method}关键字,则替换该关键字为方法名称
// 规则2如果不满足规则1那么直接将防发明附加到pattern中的URI后面
func (s *Server)mergeMethodNameToPattern(pattern string, name string) string {
// 方法名中间存在大写字母转换为小写URI地址以“-”号链接每个单词
method := ""
for i := 0; i < len(name); i++ {
if i > 0 && gstr.IsLetterUpper(name[i]) {
method += "-"
}
method += strings.ToLower(string(name[i]))
}
if strings.Index(pattern, "{method}") != -1 {
return strings.Replace(pattern, "{method}", method, -1)
// 将内置的名称按照设定的规则合并到pattern中,内置名称按照{.xxx}规则命名。
// 规则1pattern中的URI包含{.struct}关键字,则替换该关键字为结构体名称
// 规则1pattern中的URI包含{.method}关键字,则替换该关键字为方法名称;
// 规则2如果不满足规则1那么直接将防发明附加到pattern中的URI后面
func (s *Server) mergeBuildInNameToPattern(pattern string, structName, methodName string) string {
structName = s.nameToUrlPart(structName)
methodName = s.nameToUrlPart(methodName)
pattern = strings.Replace(pattern, "{.struct}", structName, -1)
if strings.Index(pattern, "{.method}") != -1 {
return strings.Replace(pattern, "{.method}", methodName, -1)
}
// 检测域名后缀
array := strings.Split(pattern, "@")
// 分离URI(其实可能包含HTTP Method)
uri := array[0]
uri = strings.TrimRight(uri, "/") + "/" + method
uri = strings.TrimRight(uri, "/") + "/" + methodName
// 加上指定域名后缀
if len(array) > 1 {
return uri + "@" + array[1]
@ -61,6 +57,20 @@ func (s *Server)mergeMethodNameToPattern(pattern string, name string) string {
return uri
}
// 将给定的名称转换为URL规范格式。
// 规范1: 全部转换为小写;
// 规范2: 方法名中间存在大写字母转换为小写URI地址以“-”号链接每个单词;
func (s *Server) nameToUrlPart(name string) string {
part := ""
for i := 0; i < len(name); i++ {
if i > 0 && gstr.IsLetterUpper(name[i]) {
part += "-"
}
part += string(name[i])
}
return strings.ToLower(part)
}
// 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
func (s *Server)BindHandler(pattern string, handler HandlerFunc) error {
return s.bindHandlerItem(pattern, &HandlerItem{
@ -76,16 +86,17 @@ func (s *Server)BindObject(pattern string, obj interface{}) error {
m := make(HandlerMap)
v := reflect.ValueOf(obj)
t := v.Type()
sname := t.Elem().Name()
for i := 0; i < v.NumMethod(); i++ {
name := t.Method(i).Name
key := s.mergeMethodNameToPattern(pattern, name)
m[key] = &HandlerItem {
method := t.Method(i).Name
key := s.mergeBuildInNameToPattern(pattern, sname, method)
m[key] = &HandlerItem {
ctype : nil,
fname : "",
faddr : v.Method(i).Interface().(func(*Request)),
}
// 如果方法中带有Index方法那么额外自动增加一个路由规则匹配主URI
if strings.EqualFold(name, "Index") {
if strings.EqualFold(method, "Index") {
m[pattern] = &HandlerItem {
ctype : nil,
fname : "",
@ -99,21 +110,24 @@ func (s *Server)BindObject(pattern string, obj interface{}) error {
// 绑定对象到URI请求处理中会自动识别方法名称并附加到对应的URI地址后面
// 第三个参数methods支持多个方法注册多个方法以英文“,”号分隔,区分大小写
func (s *Server)BindObjectMethod(pattern string, obj interface{}, methods string) error {
m := make(HandlerMap)
for _, v := range strings.Split(methods, ",") {
name := strings.TrimSpace(v)
fval := reflect.ValueOf(obj).MethodByName(name)
m := make(HandlerMap)
v := reflect.ValueOf(obj)
t := v.Type()
sname := t.Elem().Name()
for _, method := range strings.Split(methods, ",") {
mname := strings.TrimSpace(method)
fval := v.MethodByName(mname)
if !fval.IsValid() {
return errors.New("invalid method name:" + name)
return errors.New("invalid method name:" + mname)
}
key := s.mergeMethodNameToPattern(pattern, name)
key := s.mergeBuildInNameToPattern(pattern, sname, mname)
m[key] = &HandlerItem{
ctype : nil,
fname : "",
faddr : fval.Interface().(func(*Request)),
}
// 如果方法中带有Index方法那么额外自动增加一个路由规则匹配主URI
if strings.EqualFold(name, "Index") {
if strings.EqualFold(mname, "Index") {
m[pattern] = &HandlerItem {
ctype : nil,
fname : "",
@ -150,25 +164,26 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
func (s *Server)BindController(pattern string, c Controller) error {
// 遍历控制器获取方法列表并构造成uri
m := make(HandlerMap)
v := reflect.ValueOf(c)
t := v.Type()
m := make(HandlerMap)
v := reflect.ValueOf(c)
t := v.Type()
sname := t.Elem().Name()
for i := 0; i < v.NumMethod(); i++ {
name := t.Method(i).Name
if name == "Init" || name == "Shut" || name == "Exit" {
mname := t.Method(i).Name
if mname == "Init" || mname == "Shut" || mname == "Exit" {
continue
}
key := s.mergeMethodNameToPattern(pattern, name)
key := s.mergeBuildInNameToPattern(pattern, sname, mname)
m[key] = &HandlerItem {
ctype : v.Elem().Type(),
fname : name,
fname : mname,
faddr : nil,
}
// 如果方法中带有Index方法那么额外自动增加一个路由规则匹配主URI
if strings.EqualFold(name, "Index") {
if strings.EqualFold(mname, "Index") {
m[pattern] = &HandlerItem {
ctype : v.Elem().Type(),
fname : name,
fname : mname,
faddr : nil,
}
}
@ -179,25 +194,27 @@ func (s *Server)BindController(pattern string, c Controller) error {
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
// 第三个参数methods支持多个方法注册多个方法以英文“,”号分隔,不区分大小写
func (s *Server)BindControllerMethod(pattern string, c Controller, methods string) error {
m := make(HandlerMap)
cval := reflect.ValueOf(c)
for _, v := range strings.Split(methods, ",") {
name := strings.TrimSpace(v)
ctype := reflect.ValueOf(c).Elem().Type()
if !cval.MethodByName(name).IsValid() {
return errors.New("invalid method name:" + name)
m := make(HandlerMap)
v := reflect.ValueOf(c)
e := v.Type().Elem()
t := v.Elem().Type()
sname := e.Name()
for _, method := range strings.Split(methods, ",") {
mname := strings.TrimSpace(method)
if !v.MethodByName(mname).IsValid() {
return errors.New("invalid method name:" + mname)
}
key := s.mergeMethodNameToPattern(pattern, name)
key := s.mergeBuildInNameToPattern(pattern, sname, mname)
m[key] = &HandlerItem {
ctype : ctype,
fname : name,
ctype : t,
fname : mname,
faddr : nil,
}
// 如果方法中带有Index方法那么额外自动增加一个路由规则匹配主URI
if strings.EqualFold(name, "Index") {
if strings.EqualFold(mname, "Index") {
m[pattern] = &HandlerItem {
ctype : ctype,
fname : name,
ctype : t,
fname : mname,
faddr : nil,
}
}

View File

@ -28,6 +28,11 @@ func getRegexp(pattern string) (*regexp.Regexp, error) {
}
}
// 转移正则规则字符串例如Quote(`[foo]`) 返回 `\[foo\]`
func Quote(s string) string {
return regexp.QuoteMeta(s)
}
// 校验所给定的正则表达式是否符合规范
func Validate(pattern string) error {
_, err := getRegexp(pattern)
@ -76,4 +81,21 @@ func Replace(pattern string, replace, src []byte) ([]byte, error) {
func ReplaceString(pattern, replace, src string) (string, error) {
r, e := Replace(pattern, []byte(replace), []byte(src))
return string(r), e
}
// 正则替换(全部替换),给定自定义替换方法
func ReplaceFunc(pattern string, src []byte, repl func(b []byte) []byte) ([]byte, error) {
if r, err := getRegexp(pattern); err == nil {
return r.ReplaceAllFunc(src, repl), nil
} else {
return nil, err
}
}
// 正则替换(全部替换),给定自定义替换方法
func ReplaceStringFunc(pattern string, src string, repl func(s string) string) (string, error) {
bytes, err := ReplaceFunc(pattern, []byte(src), func(bytes []byte) []byte {
return []byte(repl(string(bytes)))
})
return string(bytes), err
}

View File

@ -8,11 +8,22 @@
package gutil
import (
"fmt"
"reflect"
"encoding/json"
"gitee.com/johng/gf/g/util/gstr"
"gitee.com/johng/gf/g/util/gconv"
)
// 格式化打印变量(类似于PHP-vardump)
func Dump(i interface{}) {
if b, err := json.MarshalIndent(i, "", "\t"); err == nil {
fmt.Println(string(b))
} else {
fmt.Errorf("%s\n", err.Error())
}
}
// 将map键值对映射到对应的struct对象属性上需要注意
// 1、第二个参数为struct对象指针
// 2、struct对象的公开属性才能被映射赋值

View File

@ -1,240 +0,0 @@
/*
go get -u github.com/olivere/elastic
go get -u github.com/mailru/easyjson
go get -u github.com/pkg/errors
*/
package main
import (
"context"
"encoding/json"
"fmt"
"reflect"
"time"
"github.com/olivere/elastic"
)
// Tweet is a structure used for serializing/deserializing data in Elasticsearch.
type Tweet struct {
User string `json:"user"`
Message string `json:"message"`
Retweets int `json:"retweets"`
Image string `json:"image,omitempty"`
Created time.Time `json:"created,omitempty"`
Tags []string `json:"tags,omitempty"`
Location string `json:"location,omitempty"`
Suggest *elastic.SuggestField `json:"suggest_field,omitempty"`
}
const mapping = `
{
"settings":{
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings":{
"tweet":{
"properties":{
"user":{
"type":"keyword"
},
"message":{
"type":"text",
"store": true,
"fielddata": true
},
"image":{
"type":"keyword"
},
"created":{
"type":"date"
},
"tags":{
"type":"keyword"
},
"location":{
"type":"geo_point"
},
"suggest_field":{
"type":"completion"
}
}
}
}
}`
func main() {
// Starting with elastic.v5, you must pass a context to execute each service
ctx := context.Background()
// Obtain a client and connect to the default Elasticsearch installation
// on 127.0.0.1:9200. Of course you can configure your client to connect
// to other hosts and configure it in various other ways.
client, err := elastic.NewClient()
if err != nil {
// Handle error
panic(err)
}
// Ping the Elasticsearch server to get e.g. the version number
info, code, err := client.Ping("http://127.0.0.1:9200").Do(ctx)
if err != nil {
// Handle error
panic(err)
}
fmt.Printf("Elasticsearch returned with code %d and version %s\n", code, info.Version.Number)
// Getting the ES version number is quite common, so there's a shortcut
esversion, err := client.ElasticsearchVersion("http://127.0.0.1:9200")
if err != nil {
// Handle error
panic(err)
}
fmt.Printf("Elasticsearch version %s\n", esversion)
// Use the IndexExists service to check if a specified index exists.
exists, err := client.IndexExists("twitter").Do(ctx)
if err != nil {
// Handle error
panic(err)
}
if !exists {
// Create a new index.
createIndex, err := client.CreateIndex("twitter").BodyString(mapping).Do(ctx)
if err != nil {
// Handle error
panic(err)
}
if !createIndex.Acknowledged {
// Not acknowledged
}
}
// Index a tweet (using JSON serialization)
tweet1 := Tweet{User: "olivere", Message: "Take Five", Retweets: 0}
put1, err := client.Index().
Index("twitter").
Type("tweet").
Id("1").
BodyJson(tweet1).
Do(ctx)
if err != nil {
// Handle error
panic(err)
}
fmt.Printf("Indexed tweet %s to index %s, type %s\n", put1.Id, put1.Index, put1.Type)
// Index a second tweet (by string)
tweet2 := `{"user" : "olivere", "message" : "It's a Raggy Waltz"}`
put2, err := client.Index().
Index("twitter").
Type("tweet").
Id("2").
BodyString(tweet2).
Do(ctx)
if err != nil {
// Handle error
panic(err)
}
fmt.Printf("Indexed tweet %s to index %s, type %s\n", put2.Id, put2.Index, put2.Type)
// Get tweet with specified ID
get1, err := client.Get().
Index("twitter").
Type("tweet").
Id("1").
Do(ctx)
if err != nil {
// Handle error
panic(err)
}
if get1.Found {
fmt.Printf("Got document %s in version %d from index %s, type %s\n", get1.Id, get1.Version, get1.Index, get1.Type)
}
// Flush to make sure the documents got written.
_, err = client.Flush().Index("twitter").Do(ctx)
if err != nil {
panic(err)
}
// Search with a term query
termQuery := elastic.NewTermQuery("user", "olivere")
searchResult, err := client.Search().
Index("twitter"). // search in index "twitter"
Query(termQuery). // specify the query
Sort("user", true). // sort by "user" field, ascending
From(0).Size(10). // take documents 0-9
Pretty(true). // pretty print request and response JSON
Do(ctx) // execute
if err != nil {
// Handle error
panic(err)
}
// searchResult is of type SearchResult and returns hits, suggestions,
// and all kinds of other information from Elasticsearch.
fmt.Printf("Query took %d milliseconds\n", searchResult.TookInMillis)
// Each is a convenience function that iterates over hits in a search result.
// It makes sure you don't need to check for nil values in the response.
// However, it ignores errors in serialization. If you want full control
// over iterating the hits, see below.
var ttyp Tweet
for _, item := range searchResult.Each(reflect.TypeOf(ttyp)) {
if t, ok := item.(Tweet); ok {
fmt.Printf("Tweet by %s: %s\n", t.User, t.Message)
}
}
// TotalHits is another convenience function that works even when something goes wrong.
fmt.Printf("Found a total of %d tweets\n", searchResult.TotalHits())
// Here's how you iterate through results with full control over each step.
if searchResult.Hits.TotalHits > 0 {
fmt.Printf("Found a total of %d tweets\n", searchResult.Hits.TotalHits)
// Iterate through results
for _, hit := range searchResult.Hits.Hits {
// hit.Index contains the name of the index
// Deserialize hit.Source into a Tweet (could also be just a map[string]interface{}).
var t Tweet
err := json.Unmarshal(*hit.Source, &t)
if err != nil {
// Deserialization failed
}
// Work with tweet
fmt.Printf("Tweet by %s: %s\n", t.User, t.Message)
}
} else {
// No hits
fmt.Print("Found no tweets\n")
}
// Update a tweet by the update API of Elasticsearch.
// We just increment the number of retweets.
update, err := client.Update().Index("twitter").Type("tweet").Id("1").
Script(elastic.NewScriptInline("ctx._source.retweets += params.num").Lang("painless").Param("num", 1)).
Upsert(map[string]interface{}{"retweets": 0}).
Do(ctx)
if err != nil {
// Handle error
panic(err)
}
fmt.Printf("New version of tweet %q is now %d\n", update.Id, update.Version)
// ...
// Delete an index.
//deleteIndex, err := client.DeleteIndex("twitter").Do(ctx)
//if err != nil {
// // Handle error
// panic(err)
//}
//if !deleteIndex.Acknowledged {
// // Not acknowledged
//}
}

View File

@ -1,23 +0,0 @@
package main
import "gitee.com/johng/gf/g/net/ghttp"
func main() {
s := ghttp.GetServer()
s.BindHandler("/:name", func(r *ghttp.Request){
r.Response.Writeln("pattern: /:name match")
r.Response.Writeln(r.Get("name"))
})
s.BindHandler("/:name/:action", func(r *ghttp.Request){
r.Response.Writeln("pattern: /:name/:action match")
r.Response.Writeln(r.Get("name"))
r.Response.Writeln(r.Get("action"))
})
s.BindHandler("/:name/*any", func(r *ghttp.Request){
r.Response.Writeln("pattern: /:name/*any match")
r.Response.Writeln(r.Get("name"))
r.Response.Writeln(r.Get("any"))
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,27 @@
package main
import (
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g"
)
func main() {
s := g.Server()
s.BindHandler("/:name", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/:name/update", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/:name/:action", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/:name/*any", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/user/list/{page}.html", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,30 @@
package main
import "gitee.com/johng/gf/g/net/ghttp"
func main() {
s := ghttp.GetServer()
s.BindHandler("/user/:name", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/user/member/:name/*any", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/user/member/:name/edit/*any", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/user/member/:name/edit/sex", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/user/member/:name/edit/info/*any", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/user/community/female/:name", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/admin/stats/today/:hour", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,29 @@
package main
import (
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g"
)
func main() {
s := g.Server()
// 一个简单的分页路由示例
s.BindHandler("/user/list/{page}.html", func(r *ghttp.Request){
r.Response.Writeln(r.Get("page"))
})
// {xxx} 规则与 :xxx 规则混合使用
s.BindHandler("/{obj}/:attr/{act}.php", func(r *ghttp.Request){
r.Response.Writeln(r.Get("obj"))
r.Response.Writeln(r.Get("attr"))
r.Response.Writeln(r.Get("act"))
})
// 多种模糊匹配规则混合使用
s.BindHandler("/{class}-{course}/:name/*act", func(r *ghttp.Request){
r.Response.Writeln(r.Get("class"))
r.Response.Writeln(r.Get("course"))
r.Response.Writeln(r.Get("name"))
r.Response.Writeln(r.Get("act"))
})
s.SetPort(8199)
s.Run()
}

View File

@ -1,17 +1,11 @@
package main
import (
"gitee.com/johng/gf/g/container/gpool"
"fmt"
"time"
"gitee.com/johng/gf/g/util/gregex"
)
func main() {
p := gpool.New(1000)
for i := 0 ; i < 100; i++ {
p.Put(i)
}
fmt.Println(p.Size())
time.Sleep(2*time.Second)
fmt.Println(p.Size())
fmt.Println(gregex.Quote(`/user/list/1.html`))
}