1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/konyshe-gogo

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

GoGo Logo

Go 语言轻量 web 开发框架,特点是一行代码搞定 RESTFul,不依赖第三方 ORM,也不需要生成一堆的 controllers 和 models 文件,快速使用,性能优秀。

版本介绍

v1(已发布)

  • 基于Google的http框架, 改写了router,支持正则URL,支持restful。无需针对每张表写一个go文件。

v2(已发布)

  • 基于Google的http框架,新增了websocket,独立端口号,数据包缓冲队列,定时发送心跳包。

v3(main分支,测试中)

  • 基于epoll重写了http框架,针对低性能设备优化。自测十万并发时,相比Google的http框架,客户端响应慢了0.1秒(因为目前只用到单核),但服务端CPU消耗下降了50%。

  • gogo.StartHTTP使用Google的http,gogo.StartV3HTTP使用v3方案,使用方法一致。

注:Google的http针对每个请求,都会产生一个goroutine。如果后端业务来不及,就会积压goroutine。GoGo v3目前只用了单线程处理http请求,不积压goroutine。计划多线程和CPU绑定,减少上下文切换。

v4(开发中,即将开源)

  • 基于网卡驱动重写了epoll,利用DMA将数据从网卡直接到用户态,搭配用户态tcp/ip协议栈,零拷贝,bbr流量控制。

注:按照内核设计,数据包到网卡寄存器后,由DMA拷贝到指定地址,再由驱动拷贝到skbuf等内核处理,内核处理后拷贝进队列等协议栈处理,协议栈处理后拷贝进队列,然后发送中断等用户态处理,最后用户态通过recv函数拷贝到指定地址。除第一次DMA拷贝,之后有4次CPU拷贝,特别是最后一次内核态到用户态的拷贝,加上中断,性能消耗很大。v4只有一次DMA拷贝。

需求和规划

  • v4目前只支持e1000网卡,计划支持更多网卡

  • 支持DPDK,因为DPDK已支持很多网卡,性能顶尖

  • 无论是v4或DPDK,有个问题就是独占网卡,这对只有一个网卡的云主机而言,是无法使用的。计划支持RING方案,先通过DMA将数据从网卡拷贝到内核,再将数据从内核映射到用户态处理,一次DMA拷贝和一次CPU拷贝,不独占网卡。

快速使用

备注:由于嵌入了C代码,若遇到提示gcc无法编译的问题,可删掉UtilsBuild_linux.go文件后再尝试

package main

import (
	"net/http"
	"strconv"

	"gitcode.com/konyshe/gogo"  //二选一即可
	"gitee.com/konyshe/gogo"  //二选一即可
)

func main() {

	//初始化日志输出功能,DEBUG为日志输出级别,具体参考函数说明
	gogo.LogInit("DEBUG", 1024)

	// 初始化数据库连接
	if err := gogo.SQLInit("mysql", "数据库用户名:数据库密码@tcp(数据库地址:数据库端口)/表名?charset=utf8", 10, 1); err != nil {
		gogo.CheckErrorExit(err)
		return
	}

	// 增
	gogo.POST("/restful/:tablename", func(ctx *gogo.HTTPContext) {
		affect, err := gogo.SQLInsert(
			ctx.GetPathParam(":tablename"),
			ctx.GetPostBody())

		if err != nil {
			gogo.LogError(err)
			ctx.WriteString(err.Error())
		} else {
			ctx.WriteString(strconv.FormatInt(affect, 10))
		}
	})

	// 删
	gogo.DELETE("/restful/:tablename/:id", func(ctx *gogo.HTTPContext) {
		affect, err := gogo.SQLDelete(
			ctx.GetPathParam(":tablename"),
			"id="+ctx.GetPathParam(":id"))

		if err != nil {
			gogo.LogError(err)
			ctx.WriteString(err.Error())
		} else {
			ctx.WriteString(strconv.FormatInt(affect, 10))
		}
	})

	// 改
	gogo.PUT("/restful/:tablename/:id", func(ctx *gogo.HTTPContext) {
		affect, err := gogo.SQLUpdate(
			ctx.GetPathParam(":tablename"),
			"id="+ctx.GetPathParam(":id"),
			ctx.GetPostBody())

		if err != nil {
			gogo.LogError(err)
			ctx.WriteString(err.Error())
		} else {
			ctx.WriteString(strconv.FormatInt(affect, 10))
		}
	})

	// 查
	gogo.GET("/restful/:tablename/:id", func(ctx *gogo.HTTPContext) {
		queryData, err := gogo.SQLQueryByMap(
			"",
			ctx.GetString("feilds"),
			ctx.GetPathParam(":tablename"),
			"id="+ctx.GetPathParam(":id"),
			"", 0, 1)

		if err != nil {
			gogo.LogError(err)
			ctx.WriteString(err.Error())
		} else {
			ctx.WriteJSON(queryData)
		}
	})

	// 查
	gogo.GET("/restful/:tablename", func(ctx *gogo.HTTPContext) {
		queryData, err := gogo.SQLQueryByMap(
			ctx.GetString("columnname"),
			ctx.GetString("feilds"),
			ctx.GetPathParam(":tablename"),
			ctx.GetString("where"),
			ctx.GetString("order"),
			ctx.GetInt("offset", 0),
			ctx.GetInt("count", 10))

		if err != nil {
			gogo.LogError(err)
			ctx.WriteString(err.Error())
		} else {
			ctx.WriteJSON(queryData)
		}
	})

	// 404页面
	gogo.STATUS(http.StatusNotFound, func(ctx *gogo.HTTPContext) {
		ctx.WriteHeaderStatus(http.StatusNotFound)
		ctx.WriteString("Page Not Found !")
	})

	// 启动HTTP服务
	gogo.StartHTTP(3009)
}

数据库表结构

mysql> desc dede_flink;
+----------+----------------------+------+-----+---------+----------------+
| Field    | Type                 | Null | Key | Default | Extra          |
+----------+----------------------+------+-----+---------+----------------+
| id       | smallint(5) unsigned | NO   | PRI | NULL    | auto_increment |
| sortrank | smallint(6)          | NO   |     | 0       |                |
| url      | char(60)             | NO   |     |         |                |
| webname  | char(30)             | NO   |     |         |                |
| msg      | char(200)            | NO   |     |         |                |
| email    | char(50)             | NO   |     |         |                |
| logo     | char(60)             | NO   |     |         |                |
| dtime    | int(10) unsigned     | NO   |     | 0       |                |
| typeid   | smallint(5) unsigned | NO   |     | 0       |                |
| ischeck  | smallint(6)          | NO   |     | 1       |                |
+----------+----------------------+------+-----+---------+----------------+
10 rows in set

Windows

go build -o app.exe
app.exe

Linux/MacOS

go build -o app
./app

浏览器访问测试

POST http://localhost:3009/restful/dede_flink

以下是 application/json 内容,可以批量添加多条数据

[
	{
    	"id": 20,
    	"ischeck": 1,
    	"webname": "hxyw",
    	"url":"http://www.hxyw.org"
	},
	{
    	"id": 21,
    	"ischeck": 1,
    	"webname": "bejson",
    	"url":"http://www.bejson.com"
	}
]
PUT http://localhost:3009/restful/dede_flink/21

以下是 application/json 内容,这里只将 id=21 的数据,webname 字段修改为"hello"

{
    "webname": "hello"
}
GET http://localhost:3009/restful/dede_flink/21
GET http://localhost:3009/restful/dede_flink?order=-id
GET http://localhost:3009/restful/dede_flink?offset=10&&count=100&&columnname=webname
DELETE http://localhost:3009/restful/dede_flink/1
  • 404
GET http://localhost:3009/weqwe

Start using it

  1. Download and install it:
# 二选一即可
go get -u gitee.com/konyshe/gogo 
go get -u gitcode.com/konyshe/gogo
  1. Import it in your code:
// 二选一即可
import "gitee.com/konyshe/gogo"
import "gitcode.com/konyshe/gogo"
  1. (Optional) Import net/http. This is required for example if using constants such as http.StatusNotFound.
import "net/http"

示例中的方法介绍,更全的请看代码中的注释

// LogInit 初始化日志输出功能
// level 日志输出级别
// 		配置文件指定日志级别  ALL,DEBUG,INFO,WARN,ERROR,FATAL,OFF 级别由低到高
// 		其中 ALL表示所有调用打印日志的方法都会打出,而OFF则表示都不会打出。
// 		一般习惯是测试阶段为debug,生成环境为info以上
// maxSize 日志文件最大体积,单位MB
gogo.LogInit(level string, maxSize int64)

// 系统会优先判断是否命中固定URL,当固定URL无法命中的情况下,才会去判断是否命中正则URL(开发中部分支持)
gogo.GET("/restful/:tablename/:columnname", func(ctx *gogo.HTTPContext)

// 注册没有命中任何URL规则时进入的函数
gogo.STATUS(http.StatusNotFound, func(ctx *gogo.HTTPContext)

// SQLInit 初始化数据库操作句柄,这里要提供:
// driverName string: 数据库类型,例如mysql、sqlite、postgres等,参考github.com/go-sql-driver/mysql官方介绍
// dataSourceName string: 数据库地址,参考github.com/go-sql-driver/mysql官方介绍
// MaxOpenConns int: 最大缓存连接数,这个数值包含了MaxIdleConns
// MaxIdleConns int:预备的最大空闲连接数
gogo.SQLInit(driverName string, dataSourceName string, maxOpenConns int, maxIdleConns int) error

// SQLInsert 增加一条数据
// tableName string: 操作的表名
// data []byte: 需要更新的内容,用string转换后是json格式
gogo.SQLInsert(tableName string, data []byte) (int64, error)

// SQLDelete 根据where条件删除数据
// tableName string: 操作的表名
// where string: 过滤条件,就是where后面跟着的部分
gogo.SQLDelete(tableName, where string) (int64, error)

// SQLUpdate 更新一条数据
// tableName string: 操作的表名
// where string: 过滤条件,就是where后面跟着的部分
// data []byte: 需要更新的内容,用string转换后是json格式
gogo.SQLUpdate(tableName, where string, data []byte) (int64, error)

// SQLQueryByMap 将查询到的数据,按照指定字段的值做为索引构建map并返回
// columnName string: 作为索引的字段名称
// feilds string: 查询需要获取哪些字段的值,就是select后面跟着的部分,一般用"*"
// tableName string: 查询的表名
// where string: 过滤条件,就是where后面跟着的部分
// order string: 排序条件,就是order by后面跟着的部分。默认是ASC排序,除非"-"开头则DESC排序
// offset string: limit后面逗号相隔的两个数值,前者就是offset,后者就是count
// count string: limit后面逗号相隔的两个数值,前者就是offset,后者就是count
gogo.SQLQueryByMap(columname, feilds, tableName, where, order string, offset, count int) (interface{}, error)

// StartHTTP 启动HTTP服务
// port 端口号
func StartHTTP(port int)

// StartWebSocket 启动websocket服务
// pattern 服务路径
// port 端口号
// handler 注册回调函数
func StartWebSocket(pattern string, port int, handler func([]byte, *ImplConnection) ([]byte, error))

// GetResponseWriter 获取原始的http.ResponseWriter指针
(ctx *HTTPContext) GetResponseWriter() http.ResponseWriter

// GetRequest 获取原始的http.Request指针
(ctx *HTTPContext) GetRequest() *http.Request

// GetPathParam 获取GET请求路径中的String格式参数值,例如/restful/:table_name/:id
// key string: 参数名称
(ctx *HTTPContext) GetPathParam(key string) string

// GetPostBody 获取POST请求的内容
(ctx *HTTPContext) GetPostBody() []byte

// WriteExecute 将官方原始的ParseFiles和Execute接口做了合并
// data interface{}: 渲染模板需要的数据
// filenames ...string: 模板文件路径
(ctx *HTTPContext) WriteExecute(data interface{}, filenames ...string) error

// WriteByte 将[]byte格式的数据输出给HTTP客户端
// content []byte: 需要输出HTTP客户端的[]byte格式数据
(ctx *HTTPContext) WriteByte(content []byte)

// WriteString 将String格式的数据输出给HTTP客户端
// content string: 需要输出HTTP客户端的String格式数据
(ctx *HTTPContext) WriteString(content string)

// WriteJSON 将Struct结构体的数据转换成Json输出给HTTP客户端
// v interface{}: 需要输出HTTP客户端的Struct结构体数据
(ctx *HTTPContext) WriteJSON(v interface{}) error

// Log GoGo使用的第三方go_logger库,输出到日志文件的同时还会在控制台输出,并根据日志级别显示不同颜色
// 日志输出和官方fmt.Print、fmt.Printf使用一致
func LogDebug(a ...interface{})
func LogInfo(a ...interface{})
func LogWarning(a ...interface{})
func LogError(a ...interface{})

func LogDebugF(format string, a ...interface{})
func LogInfoF(format string, a ...interface{})
func LogWarning(format string, a ...interface{})
func LogErrorF(format string, a ...interface{})

// GlobalSignalWait 全局信号等待
// 需要程序等待协程运行的时候使用
func GlobalSignalWait()

// GlobalSignalRelease 全局信号继续
// 需要程序不再等待协程运行的时候使用
func GlobalSignalRelease()

欢迎贡献者加入

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Описание недоступно Развернуть Свернуть
Go
MIT
Отмена

Обновления (6)

все

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/konyshe-gogo.git
git@api.gitlife.ru:oschina-mirror/konyshe-gogo.git
oschina-mirror
konyshe-gogo
konyshe-gogo
v2