天下武功,唯快不破 go-pcurl

thimoonxy 发表了文章 • 2 个评论 • 595 次浏览 • 2017-10-27 10:59 • 来自相关话题

初衷

写这个玩意的初衷是想解决海外研发往国内节点传文件慢的问题( 当然,这里不考虑土堆拦路的问题) 一开始研发抱怨即便用了国际专线,既有的单线传输方案仍然没有充分利用带宽。 于是人家用torrent走公网对比了一下,完胜专线。虽然专线是... 查看全部

初衷


写这个玩意的初衷是想解决海外研发往国内节点传文件慢的问题( 当然,这里不考虑土堆拦路的问题)
一开始研发抱怨即便用了国际专线,既有的单线传输方案仍然没有充分利用带宽。
于是人家用torrent走公网对比了一下,完胜专线。虽然专线是有guarantee的,仍然被歪果仁严重鄙视。
可是生产环境,用torrent未免过于粗暴啊!
快的时候,入口流量窜了尖,你到底开不开清洗?是被DDOS了?业务受影响了?一脸懵。
网不好的时候,速度变成毛线咋办,何意百炼刚,化为绕指柔?


想到了大学时候一门基础课程,叫什么什么拆装实习。于是,萌生了拆range,并发download,然后重新组装的办法。听起来很土吧。


go-pcurl


cURL in parallel way, Written in golang
REPO


实际测试了一把



  • 用wget去官网下载docker.rpm,需要6分21秒

  • 同样的url,用go-pcurl下载用了47秒


后面想想怎么限制带宽,是否可控还是很重要的。


萌新练武术,各位多指教 ;P

GoReporter第三版

fiisio 发表了文章 • 0 个评论 • 471 次浏览 • 2017-10-18 15:08 • 来自相关话题

GoReporter第三版重构了展示页面,分类更清晰,展示模型更多。可以作为白盒测试,CodeReview助手或者最佳实践评估工具。

欢迎大家使用和提出改进建议或者帮助完善功能! 查看全部

GoReporter第三版重构了展示页面,分类更清晰,展示模型更多。可以作为白盒测试,CodeReview助手或者最佳实践评估工具。


欢迎大家使用和提出改进建议或者帮助完善功能!
https://github.com/360EntSecGroup-Skylar/goreporter

delete

zhqy 发表了文章 • 1 个评论 • 574 次浏览 • 2017-10-02 15:05 • 来自相关话题

delete

delete

用500行 Golang 代码实现高性能的消息回调中间件

zamia 发表了文章 • 3 个评论 • 1632 次浏览 • 2017-09-24 17:22 • 来自相关话题

用500行 Golang 代码实现高性能的消息回调中间件

本文描述了如何实现一个消息回调中间件,得益于 golang 管道和协程的编程思想,通过巧妙的设计,只需要约500行代码就可以实现高性能、优雅关闭、自动重连等特性,全部代码也已经开... 查看全部

用500行 Golang 代码实现高性能的消息回调中间件


本文描述了如何实现一个消息回调中间件,得益于 golang 管道和协程的编程思想,通过巧妙的设计,只需要约500行代码就可以实现高性能、优雅关闭、自动重连等特性,全部代码也已经开源在 github/fishtrip/watchman


问题


随着业务复杂度的增加,服务拆分后服务数量不断增加,异步消息队列的引入是必不可少的。当服务较少的时候,比如业务早期,很多时候就是一个比较大的单体应用或者少量几个服务,消息队列(之后写做 MQ,Message Queue)的使用方法如下:



  • 发送端,直接连接 MQ,根据业务需求发送消息;

  • 消费端,通过一个后台进程,通过长连接连接至 MQ,然后实时消费消息,然后进行相应的处理;


相对来说,发送端比较简单,消费端比较复杂,需要处理的逻辑比较多。比如目前我们公司使用的 sneakers 需要处理如下的逻辑:



  1. 消费端需要长连接,需要独立的进程实时消费消息(某些语言可能是一个独立的线程);

  2. 消费消息之后,需要加载业务框架(比如 Sneakers 需要加入 Rails 环境执行业务代码)调用相关代码来消费消息;

  3. MQ 无法连接时,需要自动重连,同时应用也需要能够优雅重启,不至于丢消息。

  4. 消费消息很可能处理失败,这个时候需要比较安全可靠的机制保证不能丢失消息,同时也要求能够过一段时间对消息进行重试,重试多次之后也需要能够对消息进一步做处理;


这个时候的系统架构一般如下:


而随着服务增多,如果每个需要消费消息的服务都部署一个这样的后台进程显然不够环保:



  1. 每个服务增加一个进程,增加了部署运维的成本;

  2. 对于队列的管理(创建、销毁、binding)以及消息重试机制,每个服务来自己负责的话,很容易造成标准不统一;

  3. 如果不同的服务是不同的语言、不同的框架,每个语言又都要实现一遍,会浪费不少开发资源;


那有没有更好的办法呢?


其中一般办法是打造一个全局的、高性能的消息回调中间件,中间件来负责队列的管理、消息的收发、重试以及出错处理,这样就不再需要每个服务去考虑诸如消息丢失、消息重试等问题了,基本解决了上面的缺点。具体这个消息回调中心应该具备哪些功能呢?



  1. 统一管理所有 MQ 队列的创建和消息监听;

  2. 当有消息接收到时,中间件调用相关服务的回调地址,因为回调中心负责所有的服务,该中间件必须是高性能、高并发的;

  3. 中间件应当具备消息重试的功能,同时重试消息的时候不应该丢失消息;

  4. 中间件应当具备「重连」和「优雅关闭」等基础功能,这样才能保证不丢消息;


这时候架构如下:


这样的话,每个服务的工作就变得轻量了很多。本文的目的就是来实现一版生产环境可用的消息回调中间件。当然,我们第一版的回调中心也不需要太多功能,有如下的限制:



  1. 整个重试流程需要 RabbitMQ 内置功能支持,所以暂时只支持 RabbitMQ;

  2. 目前只支持 HTTP 回调方式;


基本的需求有了,如何实现一个这样的消息回调中间件呢?


解决思路


开发语言选择


Golang 作为「系统级开发语言」,非常适合开发这类中间件。内建的 goroutine/channel 机制非常容易实现高并发。而作为 Golang 新手,这个项目也不复杂,很适合练手和进一步学习。


消息可靠性


关于重试和出错处理呢?我们从 Sneakers 的实现中借鉴了它的方法,通过利用 RabbitMQ 内置的机制,也就是通过 x-dead-letter 机制来保证消息在重试时可以做到高可靠性,具体可以参考前段时间我写的这篇文章。简单总结一下文中的思路:



  1. 消息正常被处理时,直接 ack 消息就好;

  2. 当消息处理出错,需要重试时,reject 消息,此时消息会进入到单独的 retry 队列;

  3. retry 队列配置好了 ttl 超时时间,等到超时时,消息会进入到 requeue Exchange(RabbitMQ 的概念,用来做消息的路由);

  4. 消息会再次进入工作队列,等待被下次重试;

  5. 如果消息的重试次数超过了一定的值,那么消息会进入到错误队列等待进一步处理;


这里面有两个地方利用了 RabbitMQ 的 Dead-Letter 机制:



  1. 当消息被 reject 之后,消息进入到该队列的 dead-letter Exchange ,也就是重试队列;

  2. 当重试队列的消息,在超时时(队列设置了 ttl-expires 时长),消息进入该队列的 dead-letter Exchange,也就是重新进入了工作队列。


通过这种机制,可以保证在进行消息处理的时候,不管是正常、还是出错时,消息都不会丢失。关于这里进一步的细节可以参考上面的文章。


实现高并发


对于中间件,性能的要求比较高,性能也包含两个方面:低延迟和高并发。低延迟在这个场景中我们无法解决,因为一个消息回调之后的延迟是其他业务服务决定的。所以我们更多的是追求高并发。


如何获得高并发?首先是开发语言的选择,这类底层的中间件很适合用 Golang 来实现,为什么呢?因为回调中心的主逻辑就是不断回调各个服务,而各个服务的延迟时间中间件无法控制,所以如果想获得高并发,最好是使用异步事件这种机制。而借助于 Golang 内置的 Channel ,既可以获得接近于异步事件的性能,又可以让整个开发变得简单高效,是一个比较合适的选择。


具体实现呢?其实对于一个回调中心来说,大概分成这么几个步骤:



  1. 获取消息:连接消息队列( 目前我们只需要支持 RabbitMQ 即可),消费消息;

  2. 回调业务接口:消费消息之后,根据配置信息,不同的队列可能需要调用不同的回调地址,开始调用业务接口(目前我们只需要支持 HTTP 协议即可);

  3. 根据回调结果处理消息:如果调用业务接口如果成功,则直接 ack 消息即可;如果调用失败,则 reject 此消息;如果超过最大重试次数,则进入出错处理逻辑;

  4. 出错处理逻辑:把原有消息 ack,同时转发此消息进入 error 队列,等待进一步处理(可能是报警,然后人工处理);


通过消息这么一个「实体」可以把所有上面的流程串联起来,是不是很像 pipeline ?而 pipeline 的设计模式是 Golang 非常推荐的实现高并发的方式。上面的每一个步骤可以看做一组协程(goroutine),他们之间通过管道通信,因此不存在资源竞争的情况,大大降低了开发成本。


而上面每个步骤可以通过设计不同的 Goroutine 模型来实现高并发:



  1. 获取消息:需要长连接 RabbitMQ,较好的实现方式是每个队列有独立的一组协程,这样队列之间的消息接受互相不会干扰,如果出现了繁忙队列和较闲的队列时,也不会出现消息处理不及时的情况;

  2. 回调业务接口:每个消息都会调用业务接口,但是业务接口的处理时长对于中间件来说是透明的。因此,这里最好的模型是每个消息一个协程。如果出现了较慢的接口,那么通过 goroutine 的内部调度机制,并不会影响系统的吞吐,同时 goroutine 可以支持上百万的并发,因此用这种模式最合适。

  3. 根据回调结果处理消息:这个步骤主要是要连接 RabbitMQ,发送 ack/reject 消息。默认我们认为 RabbitMQ 是可靠的,这里统一用同一组协程来处理即可。

  4. 出错处理逻辑:这里的消息量应该大大降低,因为多次失败(超过重试次数)的消息才会进入到这里。我们也采用同一组协程处理即可。


上面四个步骤,我们用了三种协程的设计模型,细化一下上面的图就是这个样子的。


实现


有了上面的设计过程,代码并不复杂,大概分为几部分:配置管理、主流程、消息对象、重试逻辑以及优雅关闭等的实现。详细的代码放在 github:fishtrip/watchman


配置管理


配置管理这部分,这个版本我们实现的比较简单,就是读取 yml 配置文件。配置文件主要包含的主要是三部分信息:



  • 消息队列定义。要根据消息队列的配置调用 RabbitMQ 接口生成相关的队列(重试队列、错误队列等);

  • 回调地址配置。不同的消息队列需要不同的回调地址;

  • 其他配置。如重试次数、超时等。


# config/queues.example.yml
projects:
- name: test
queues_default:
notify_base: "http://localhost:8080"
notify_timeout: 5
retry_times: 40
retry_duration: 300
binding_exchange: fishtrip
queues:
- queue_name: "order_processor"
notify_path: "/orders/notify"
routing_key:
- "order.state.created"
- "house.state.#"

我们使用 yaml.v2 包可以很方便的解析 yaml 配置文件到 struct 之中,比如对于 queue 的定义,struct 实现如下:


// config.go 28-38

type QueueConfig struct {
QueueName string `yaml:"queue_name"`
RoutingKey []string `yaml:"routing_key"`
NotifyPath string `yaml:"notify_path"`
NotifyTimeout int `yaml:"notify_timeout"`
RetryTimes int `yaml:"retry_times"`
RetryDuration int `yaml:"retry_duration"`
BindingExchange string `yaml:"binding_exchange"`

project *ProjectConfig
}

上面之所以需要一个 ProjectConfig 的指针,主要是为了方便读取 project的配置,因此加载的时候需要把队列指向 project。


// config.go
func loadQueuesConfig(configFileName string, allQueues []*QueueConfig) []*QueueConfig {
// ......
projects := projectsConfig.Projects
for i, project := range projects {
log.Printf("find project: %s", project.Name)

// 这里不能写作 queue := project.Queues
queues := projects[i].Queues

for j, queue := range queues {
log.Printf("find queue: %v", queue)

// 这里不能写作 queues[j].project = &queue
queues[j].project = &projects[i]
allQueues = append(allQueues, &queues[j])
}
}
// .......
}

上面代码中有个地方容易出错,就是在 for 循环内部设置指针的时候不能直接使用 queue 变量,因为此时获取的 queue 变量是一份复制出来的数据,并不是原始数据。


另外,config.go 中大部分逻辑是按照面向对象的思考方式来书写的,比如:


// config.go
func (qc QueueConfig) ErrorQueueName() string {
return fmt.Sprintf("%s-error", qc.QueueName)
}
func (qc QueueConfig) WorkerExchangeName() string {
if qc.BindingExchange == "" {
return qc.project.QueuesDefaultConfig.BindingExchange
}
return qc.BindingExchange
}

通过这种方式,可以写出更清晰可维护的代码。


消息对象封装


整个程序中,在 channel 中传递的数据都是 Message 对象,通过这种对象封装,可以非常方便的在各种类型的 Goroutine 之间传递数据。


Message 类的定义如下:


type Message struct {
queueConfig QueueConfig // 消息来自于哪个队列
amqpDelivery *amqp.Delivery // RabbitMQ 的消息封装
notifyResponse NotifyResponse // 消息回调结果
}

我们把 RabbitMQ 中的原生消息以及队列信息、回调结果封装在一起,通过这种方式把 Message 对象在管道之间传递。同时 Message 封装了众多的方法来供其他协程方便的调用。


// Message 相关方法
func (m Message) CurrentMessageRetries() int {}
func (m *Message) Notify(client *http.Client) *Message {}
func (m Message) IsMaxRetry() bool {}
func (m Message) IsNotifySuccess() bool {}
func (m Message) Ack() error {}
func (m Message) Reject() error {}
func (m Message) Republish(out chan<- Message) error {}
func (m Message) CloneAndPublish(channel *amqp.Channel) error {}

注意上面方法的接收对象,带指针的接收对象表示会修改对象的值。


主流程


主流程就是我们上面说的,通过 pipeline 的模式、把消息的整条流程串联起来。最核心的代码在这里:


// main.go
<-resendMessage(ackMessage(workMessage(receiveMessage(allQueues, done))))

上面每个函数都接收相同的管道定义,因此可以串联使用。其实每个函数的实现区别不大,不同的协程模型可能需要不同的写法。


下面是 receiveMessage 的写法,并进行了详细的注释。revceiveMessage 对每个消息队列都生成了 N 个协程,然后把读取的消息全部写入管道。


// main.go
func receiveMessage(queues []*QueueConfig, done <-chan struct{}) <-chan Message {

// 创建一个管道,这个管道会作为函数的返回值
out := make(chan Message, ChannelBufferLength)

// WaitGroup 用于同步,这里来控制协程是否结束
var wg sync.WaitGroup

// 入参是队列配置,这个见下文传入的值
receiver := func(qc QueueConfig) {
defer wg.Done()

// RECONNECT 标记用于跳出循环来重新连接 RabbitMQ
RECONNECT:
for {
_, channel, err := setupChannel()
if err != nil {
PanicOnError(err)
}

// 消费消息
msgs, err := channel.Consume(
qc.WorkerQueueName(), // queue
"", // consumer
false, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
PanicOnError(err)

for {
select {
case msg, ok := <-msgs:
if !ok {
log.Printf("receiver: channel is closed, maybe lost connection")
time.Sleep(5 * time.Second)
continue RECONNECT
}

// 这里生成消息的 UUID,用来跟踪整个消息流,稍后会解释
msg.MessageId = fmt.Sprintf("%s", uuid.NewV4())
message := Message{qc, &msg, 0}

// 这里把消息写到出管道
out <- message

message.Printf("receiver: received msg")
case <-done:

// 当主协程收到 done 信号的时候,自己也退出
log.Printf("receiver: received a done signal")
return
}
}
}
}

for _, queue := range queues {
wg.Add(ReceiverNum)
for i := 0; i < ReceiverNum; i++ {

// 每个队列生成 N 个协程共同消费
go receiver(*queue)
}
}

// 控制协程,当所有的消费协程退出时,出口管道也需要关闭,通知下游协程
go func() {
wg.Wait()
log.Printf("all receiver is done, closing channel")
close(out)
}()

return out
}

里面有几个关键点需要注意。



  1. 每个函数都是类似的结构,一组工作协程和协作协程,当全部工作协程退出时,关闭出口管道,通知下游协程。注意 golang 中,对于管道的使用,需要从写入端关闭,否则很容易出现崩溃。

  2. 我们在每个消息中,记录了一个唯一的 uuid,这个 uuid 用来打日志,来跟踪一整条信息流。

  3. 因为可能出现的网络状况,我们要进行判断,如果出现了连接失败的情况,直接 sleep 一段时间,然后重连。

  4. done 这个管道是在主协程进行控制的,主要用作优雅关闭。优雅关闭的作用是在升级配置、升级主程序的时候可以保证不丢消息(等待消息真的完成之后才会结束协程,整个程序才会退出)。


总结


得益于 Golang 的高效的表达能力,通过大约 500 行代码实现了一个稳定的消息回调中间件,同时具备下面的特性:



  • 高性能。在 macbook pro 15 上简单测试,每个队列的处理能力可以轻松达到 3000 message/second 以上,多个队列也可以做到线性的增加性能,整体应用达到几万每秒很轻松。同时,得益于 golang 的协程设计,如果下游出现了慢调用,那么也不会影响并发。

  • 优雅关闭。通过对信号的监听,整个程序可以在不丢消息的情况下优雅关闭,利于配置更改和程序重启。这个在生产环境非常重要。

  • 自动重连。当 RabbitMQ 服务无法连接的时候,应用可以自动重连。


当然,我们团队目前还都是 golang 新手,也没有做太多的单元测试和性能测试,下一步可能会继续优化,完善测试工作,并且优化配置的管理,欢迎各位去 github 围观源码。

一个自己实现的Excel as relate db读取库go-excel

yhf_szb 发表了文章 • 0 个评论 • 415 次浏览 • 2017-09-18 17:10 • 来自相关话题

在复杂的系统中(例如游戏),有时候为了便于非专业人员(策划)设置一些配置,会使用Excel作为一种轻量级的关系数据库或者配置文件,毕竟对于很多非开发人员来说,配个Excel要比写json或者yaml什么简单得多。

而且Excel可以写入各种... 查看全部

在复杂的系统中(例如游戏),有时候为了便于非专业人员(策划)设置一些配置,会使用Excel作为一种轻量级的关系数据库或者配置文件,毕竟对于很多非开发人员来说,配个Excel要比写json或者yaml什么简单得多。


而且Excel可以写入各种格式和字体标红单元格,维护成本大大降低。


这种场景下,读取特定格式(符合关系数据库特点的表格)的数据会比各种花式写入Excel的功能更重要,毕竟从编辑上来说微软提供的Excel本身功能就非常强大了,而现在我找到的Excel库的功能都过于强大了,用起来有点浪费,于是写了这个简化库。


这个库的工作参考了tealeg/xlsx的部分实现和读取逻辑。


假设有一个xlsx文件,里边有个Sheet叫“Standard”,它的数据结构如下:










































ID NameOf Age Slice UnmarshalString
1 Andy 1 1|2 {"Foo":"Andy"}
2 Leo 2 2|3|4 {"Foo":"Leo"}
3 Ben 3 3|4|5|6 {"Foo":"Ben"}
4 Ming 4 1 {"Foo":"Ming"}


  • 第0行是标题行。

  • 第1行开始是数据行。


以下是最简单的写法


// defined a struct
type Standard struct {
// use field name as default column name
ID int
// column means to map the column name
Name string `xlsx:"column(NameOf)"`
// you can map a column into more than one field
NamePtr *string `xlsx:"column(NameOf)"`
// omit `column` if only want to map to column name, it's equal to `column(AgeOf)`
Age int `xlsx:"AgeOf"`
// split means to split the string into slice by the `|`
Slice []int `xlsx:"split(|)"`
// *Temp implement the `encoding.BinaryUnmarshaler`
Temp *Temp `xlsx:"column(UnmarshalString)"`
// use '-' to ignore.
Ignored string `xlsx:"-"`
}

// func (this Standard) GetXLSXSheetName() string {
// return "Some other sheet name if need"
// }

type Temp struct {
Foo string
}

// self define a unmarshal interface to unmarshal string.
func (this *Temp) UnmarshalBinary(d []byte) error {
return json.Unmarshal(d, this)
}

func main() {
// will assume the sheet name as "Standard" from the struct name.
var stdList []Standard
err := excel.UnmarshalXLSX("./testdata/simple.xlsx", &stdList)
if err != nil {
panic(err)
}
}

提供一些更复杂的读取逻辑,详细看文档:https://github.com/szyhf/go-excel


时间关系,可能看test目录里的代码更好理解……



欢迎捉虫提bug。


图片验证码代码分享

jajijo 回复了问题 • 6 人关注 • 4 个回复 • 1401 次浏览 • 2017-09-15 17:55 • 来自相关话题

golang版本的curl请求库

mikemintang 发表了文章 • 0 个评论 • 672 次浏览 • 2017-09-15 07:55 • 来自相关话题

Github地址


https://github.com/mikemintang/go-curl


安装


go get github.com/mikemintang/go-curl

使用


package main

import (
"fmt"
"github.com/mikemintang/go-curl"
)

func main() {

url := "http://php.dev/api.php"

headers := map[string]string{
"User-Agent": "Sublime",
"Authorization": "Bearer access_token",
"Content-Type": "application/json",
}

cookies := map[string]string{
"userId": "12",
"loginTime": "15045682199",
}

queries := map[string]string{
"page": "2",
"act": "update",
}

postData := map[string]interface{}{
"name": "mike",
"age": 24,
"interests": []string{"basketball", "reading", "coding"},
"isAdmin": true,
}

// 链式操作
req := curl.NewRequest()
resp, err := req.
SetUrl(url).
SetHeaders(headers).
SetCookies(cookies).
SetQueries(queries).
SetPostData(postData).
Post()

if err != nil {
fmt.Println(err)
} else {
if resp.IsOk() {
fmt.Println(resp.Body)
} else {
fmt.Println(resp.Raw)
}
}

}

接收请求的api.php


<?php  

//echo json_encode($_GET); // 获取url地址中的查询参数
//echo json_encode(getallheaders()); // 获取请求头
//echo json_encode($_COOKIE); // 获取cookies
echo file_get_contents("php://input"); // 获取post提交的数据

function getallheaders() {
$headers = [];
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}

可导出的成员变量和方法



TodoList



  • [x] 以链式操作的方式发起请求

  • [ ] 以函数回调的方式发起请求

  • [ ] 以类Jquery Ajax的方式发起请求

  • [x] 发起GET/POST请求

  • [ ] 发起PUT/PATCH/DELETE/OPTIONS操作

  • [x] 以application/x-www-form-urlencoded形式提交post数据

  • [x] 以application/json形式提交post数据

  • [ ] 以multipart/form-data形式提交post数据

  • [ ] proxy代理设置

grpc-gateway的替代品--Turbo

zzxx513 发表了文章 • 0 个评论 • 466 次浏览 • 2017-09-15 01:06 • 来自相关话题

转载自:https://zhuanlan.zhihu.com/p/29350695

grpc-gateway是一个使用起来很便捷的工具,... 查看全部

转载自:https://zhuanlan.zhihu.com/p/29350695


grpc-gateway是一个使用起来很便捷的工具,可以很方便的把grpc接口用HTTP的方式暴露出去。


但在实际使用过程中,也在grpc-gateway里发现了一些问题,比如:


1,灵活性不够,如果有一些比较特殊的需求,在grpc-gateway中能扩展的余地不大;


2,严重依赖protocol buffer,而且必须是protobuf 3;


3,即使grpc服务的接口不变,只是修改HTTP接口定义,也必须重新生成代码,也就必须重新部署,重启服务;


4,只支持JSON格式的输入,不支持传统的kv格式的参数;


5,只支持grpc,嗯。。好吧,这不算问题,但thrift也很普及,是不是?


6,grpc-gateway在错误处理等方面都不够成熟,而且开发者似乎也不是很活跃。。。


Turbo努力解决了上面提到的问题,这是项目的地址:


vaporz/turbo


这是文档地址,很贴心很详细,中英双语哦~


Turbo Documentation


除了提供基本的与grpc-gateway类似的HTTP代理功能,Turbo还可以做到:


1,高度灵活,提供各种基于切面(不是吃的那个“切面”)思想的组件,可以在各个环节进行定制;


2,只依赖grpc,对protocol buffer没有要求,因此,你既可以使用protobuf2,也可以使用protobuf3;


3,HTTP接口的定义,以及与后端接口之间的映射,可以在运行时直接修改,并且立即生效!


4,不仅支持JSON格式的输入,也支持传统的kv格式的输入!


5,不仅支持grpc,还支持thrift!


6,自带命令行工具,一键创建可运行的项目,一键重新生成代码!


Turbo目前仍处于诞生初期,但现在的代码已经经过了认真的测试,认真细致的测试用例让测试覆盖率达到了98%。


当然,测试覆盖率说明不了多少问题,只有经过实战考验的代码才是可靠的!


因此,欢迎大家多多试用,多多吐槽,有任何建议或想法,请在GitHub上开Issue,坐等。


遇到任何问题,我愿意尽力帮助,尽力解决!


谢谢!

使用反射对绑定url参数到结构体

tanghui 发表了文章 • 1 个评论 • 428 次浏览 • 2017-09-14 00:00 • 来自相关话题

通过反射自动绑定url参数到结构体,同时支持对参数范围进行校验已经参数默认值的设置 example:

 package ma... 			查看全部
					

通过反射自动绑定url参数到结构体,同时支持对参数范围进行校验已经参数默认值的设置
example:


 package main
import (
"net/http"
"fmt"

"github.com/lintanghui/parse"
)
func main(){
type v struct {
Data16 int8 `params:"aaa;Range(1,10)" default:"10"`
Data32 int32
Data64 int64 `params:"data64;Range(1,20)" default:"20"`
Float32 float32 `params:"ccc"`
String string `params:"sss" default:"-"`
SliceInt []int64 `params:"iii"`
SliceStr []string `params:"ttt"`
Bool bool `params:"bbb"`
}
req, err := http.NewRequest("GET", "http://www.linth.top/x?aaa=11&data64=33&Data32=32&string=aaa&iii=1,2,3&ttt=a,b,c&bbb=true&ccc=1.2", nil)
req.ParseForm()
if err != nil {
t.Log(err)
}
p := parse.New()
var data = &v{}
err = p.Bind(data, req.Form)
fmt.Printf("%+v",data)
// OUTPUT:
// &{Data16:10 Data32:32 Data64:20 Float32:1.2 String: SliceInt:[1 2 3] SliceStr:[a b c] Bool:true}
}

https://github.com/lintanghui/parse

[开源]golang123 是使用 vue、nuxt、node.js 和 golang 开发的社区系统

shen100 发表了文章 • 5 个评论 • 803 次浏览 • 2017-09-13 17:42 • 来自相关话题

golang123 是一个开源的社区系统,界面优雅,功能丰富,小巧迅速。 已在golang中文社区 得到应用,你完全可以用它来搭建自己的社区。

golang... 查看全部

golang123 是一个开源的社区系统,界面优雅,功能丰富,小巧迅速。
已在golang中文社区 得到应用,你完全可以用它来搭建自己的社区。


golang123的技术架构是前后端分离的, 前端使用vueiviewnode.jsnuxt等技术来开发, 后端使用gogormiris等技术来开发。golang123的技术选型也是超前的, 我们大胆得使用nuxt来做前后端同构渲染



golang123目前正处于活跃的开发中,预计十月初发布Beta版



社区首页



Golang123线上机器配置



















CPU 1核
内存 512 MB
带宽 1Mbps

安装


依赖的软件



























软件 版本
node.js 8.4.0 (及以上)
golang 1.9 (及以上)
mysql 5.6.35 (及以上)
redis 4.0.1 (及以上)

克隆代码


golang123的代码克隆到gopath的src目录下,即your/gopath/src/golang123


前端依赖的模块


进入golang123/website目录,输入命令


npm install

如果安装失败,或速度慢,可尝试阿里的镜像


npm install --registry=https://registry.npm.taobao.org

后端依赖的库


//iris web框架
go get -u github.com/kataras/iris

//gorm 持久层框架
go get -u github.com/jinzhu/gorm

//redis客户端工具
go get github.com/garyburd/redigo/redis

//uuid生成工具
go get github.com/satori/go.uuid

//防XSS攻击
go get -u github.com/microcosm-cc/bluemonday

//markdown解析器
go get github.com/russross/blackfriday

配置


hosts


127.0.0.1 dev.golang123.com


nginx



  1. golang123/nginx/dev.golang123.com.example.conf文件改名为dev.golang123.com.conf,然后拷贝到nginx的虚拟主机目录下

  2. golang123/nginx/server.keygolang123/nginx/server.crt拷贝到某个目录下

  3. 打开nginx的虚拟主机目录下的dev.golang123.com.conf文件,然后修改访问日志和错误日志的路径,即修改access_log和error_log。

  4. 修改证书路径为server.key和server.crt所在的路径,即修改ssl_certificate和ssl_certificate_key


请参考如下配置中请修改标记的地方:


server {
listen 80;
server_name dev.golang123.com;

access_log /path/logs/golang123.access.log; #请修改
error_log /path/logs/golang123.error.log; #请修改

rewrite ^(.*) https://$server_name$1 permanent;
}

server {
listen 443;
server_name dev.golang123.com;

access_log /path/logs/golang123.access.log; #请修改
error_log /path/logs/golang123.error.log; #请修改

ssl on;
ssl_certificate /path/cert/golang123/server.crt; #请修改
ssl_certificate_key /path/cert/golang123/server.key; #请修改

...

}

前端配置


golang123/website/config/index.example.js文件重命名为index.js


后端配置


golang123/config.example.json文件重命名为config.json,然后修改以下配置:



  1. 修改mysql连接地址及端口

  2. 修改mysql的用户名及密码

  3. 修改redis的连接地址及端口

  4. 修改域名邮箱的用户名及密码(golang123使用的是QQ域名邮箱)


运行


运行前端项目


进入golang123/website目录,然后运行


npm run dev

运行后端项目


进入golang123目录,然后运行


go run main.go

访问


浏览器中访问 https://dev.golang123.com/


问题


有任何问题或建议都欢迎提 issue


技术交流


QQ群: 32550512

QQ群中的消息很可能还没被看到,就被后来的消息给冲掉,有问题还是建议大家开issue


赞助


如果你觉得golang123这个项目还不错的话,可以通过扫描下面的二维码来赞助我, 金额任意,无上限 ^v^



License


GPL

Copyright (c) 2013-present, shen100

x86/x64 汇编语言练习工具

sheepbao 回复了问题 • 7 人关注 • 6 个回复 • 826 次浏览 • 2017-09-11 15:05 • 来自相关话题

RobotGo v0.46.0 发布, 修复重要 bug

够浪 回复了问题 • 3 人关注 • 3 个回复 • 737 次浏览 • 2017-09-03 23:53 • 来自相关话题

filebeat 在源码上区分Windows 和Linux系统吗

回复

KSpeer 回复了问题 • 1 人关注 • 1 个回复 • 673 次浏览 • 2017-09-01 14:43 • 来自相关话题

高性能的 encoding/json 替代品 jsoniter 发布 1.0.0

taowen 发表了文章 • 6 个评论 • 560 次浏览 • 2017-08-31 00:08 • 来自相关话题

https://github.com/json-iterator/go 完全实现 encoding/json 的所有行为,但是速度要快很多。同时通过ext... 查看全部

https://github.com/json-iterator/go 完全实现 encoding/json 的所有行为,但是速度要快很多。同时通过extension机制提供了更强大的扩展能力。发布至今已经突破了 1000 star。在 Kubernetes 团队成员的催促下(https://github.com/json-iterator/go/issues/154 ),今天发布了 1.0.0 版本,希望能够被合并到 Kubernetes 的代码里。


jsoniter 诞生于滴滴出行平台技术部的实际业务需求之中。如果你也想加入我们,做有挑战的 Go 应用开发,请加我的微信:nctaowen。

Go 语言在命令行以表格的形式输出结构体切片

modood 发表了文章 • 3 个评论 • 548 次浏览 • 2017-08-29 15:32 • 来自相关话题

最近写的小工具,可以在命令行以表格的形式输出结构体切片

  • 没有第三方依赖
  • 支持中文汉字
  • 表格每列自动对齐
  • 支持自动适应列宽
  • 结构体的字段支持所有数据类型(字符... 查看全部

最近写的小工具,可以在命令行以表格的形式输出结构体切片



  • 没有第三方依赖

  • 支持中文汉字

  • 表格每列自动对齐

  • 支持自动适应列宽

  • 结构体的字段支持所有数据类型(字符串,切片,映射等)


例如可以很方便清晰地将数据库查询结果列表(结构体切片)在命令行以表格的形式输出。


项目 Github 主页:https://github.com/modood/table


对你有用的话,给个 star 支持一下吧~


package main

import (
"fmt"

"github.com/modood/table"
)

type House struct {
Name string
Sigil string
Motto string
}

func main() {
s := []House{
{"Stark", "direwolf", "Winter is coming"},
{"Targaryen", "dragon", "Fire and Blood"},
{"Lannister", "lion", "Hear Me Roar"},
}

table.Output(s)
}

输出结果:


┌───────────┬──────────┬──────────────────┐
│ Name │ Sigil │ Motto │
├───────────┼──────────┼──────────────────┤
│ Stark │ direwolf │ Winter is coming │
│ Targaryen │ dragon │ Fire and Blood │
│ Lannister │ lion │ Hear Me Roar │
└───────────┴──────────┴──────────────────┘