thrift 的几个需要改进的地方

taowen 发表了文章 • 0 个评论 • 76 次浏览 • 19 小时前 • 来自相关话题

两次拷贝

因为 thrift IDL 生成出来的 struct 不是很好用,比如:

  • 用 *int 表示 optional
  • 无法增加不在 IDL 里的业务字段

所以为... 查看全部

两次拷贝


因为 thrift IDL 生成出来的 struct 不是很好用,比如:



  • 用 *int 表示 optional

  • 无法增加不在 IDL 里的业务字段


所以为了避免和 thrift 的 codegen 做搏斗,业务就自己再定义了一个struct。然后解析的过程就变成了


[]byte => thrift struct => 业务的 struct

这就多了一次内存的拷贝,同时增加了GC负担。这个问题最佳的解决办法就是直接对象绑定到业务 struct 上,也就是 Go 的 struct 不用和 IDL 字段一一对应。这种映射关系和常见 JSON 绑定是类似的。


没 IDL 无法编解码


不是所有人都喜欢代码生成的。有的时候为了开发方便,会希望能不能用反射之类的方式直接绑定到对象上。


即便排除掉口味偏好。仍然有一些场景下无法用Codegen来解决。比如,我们需要一个 thrift proxy。这个代理可以在网络传输过程中修改 thrift message 的第0个参数的内容。我们不能提前知道所有可能的 thrift message 的 IDL,当然也无法对这些 IDL 提前代码生成。使用官方的 thrift lib 是很难优雅地解决这个问题地。


官方的库有性能问题


根据第三方 benchmark: https://github.com/smallnest/gosercomp


这个是 protobuf 的结果


BenchmarkMarshalByGogoProtoBuf-4 20000000   109 ns/op   48 B/op 1 allocs/op
BenchmarkUnmarshalByGogoProtoBuf-4 3000000 408 ns/op 144 B/op 8 allocs/op

这个是 thrift 的结果


BenchmarkMarshalByThrift-4 3000000  462 ns/op   64 B/op 1 allocs/op
BenchmarkUnmarshalByThrift-4 1000000 1356 ns/op 656 B/op 11 allocs/op

性能差距还是非常大的。到底有多慢呢?我们看一下 JSON + 反射的速度


BenchmarkMarshalByJsoniter-4 2000000    721 ns/op   800 B/op    5 allocs/op
BenchmarkUnmarshalByJsoniter-4 3000000 473 ns/op 112 B/op 6 allocs/op

也就是说,在 Go 里面使用 thrift 未必比 JSON 要快。再加上前面提到的第一个问题。事实上,如果不改进官方的Go版本的库(其实Java版本也很糟糕),还不如选择JSON。


为什么会慢呢?主要是这么两点



  • 完全无buffer的解析。所有的数据都假设从 io.Reader 里读取出来。

  • 中途大量的 check err 的代码


thrift 改进计划


thrift 这个协议本身没啥问题。主要是库实现得不好。我们给 Go 写一个新的库吧: https://github.com/thrift-iterator/go



  • 通过静态代码生成支持对象绑定,基于纯 Go 实现的代码生成框架 https://github.com/v2pro/wombat ,从 Go struct 直接生成代码。

  • 通过 jsoniter 类似的反射支持对象绑定,实现性能不受太大影响的情况下,不用代码生成也能使用 thrift协议

  • 支持无 IDL 解析,移植 jsoniter 的 api

    • 支持 iterator api,支持不同字段用不同的解析模式

    • 能 skip 字段并返回 []byte

    • 支持把thrift解析成map


  • 重写解析代码,达到和Protobuf一样的性能水平

goweb,基于go语言的API框架

Alber 发表了文章 • 0 个评论 • 208 次浏览 • 2 天前 • 来自相关话题

goweb

一个基于go语言的快速开发WebAPI的工具,这个工具受到了SpringMVC的启发,结合了go语言本身的特性,整体比较简单,接下来,看看如何使用它。

下载安装:

查看全部
					

goweb


一个基于go语言的快速开发WebAPI的工具,这个工具受到了SpringMVC的启发,结合了go语言本身的特性,整体比较简单,接下来,看看如何使用它。


下载安装:


go get github.com/alberliu/goweb

1.核心功能


请求体参数注入


package main

import "github.com/alberliu/goweb"

type User struct {
Id int `json:"id"`
Name string `json:"name"`
}

func handler(user User) User {
return user
}

func main() {
goweb.HandlePost("/test", handler)
goweb.ListenAndServe(":8000")
}

请求体:


{
"id": 1,
"name": "alber"
}

响应体:


{
"id": 1,
"name": "alber"
}

上面的代码是一个最简的例子,HandlePost(string, interface{})会将一个handler注册到一个全局的内置的goweb实例defultGoWeb,ListenAndServe(":8000")会将defultGoWeb赋给Server的handler变量,然后启动这个Server。(是不是和内置的ServerMux有点像)


goweb会自动解析注册到它本身的handler,当请求到来时,会将请求体的json数据反序列化并注入到handler的参数,handler处理完逻辑返回时,会将handler的返回值序列化为json数据返回。goweb默认使用json的序列化和反序列化方式,当然你可以定义自己的序列化方式,这个在后面你可以看到。


例子给出的handler的参数和返回都是结构体类型,当然你也可以使用指针类型。


结构体goweb其实本质上就是一个路由,它实现了Handler接口。上面的例子都是默认的defultGoWeb,你也可以自己实例化一个goweb。


func main() {
goweb:=goweb.NewGoWeb();
goweb.HandlePost("/test", handler)
server := &http.Server{Addr: ":8000", Handler: goweb}
server.ListenAndServe()
}

url参数注入


package main

import "github.com/alberliu/goweb"

type User struct {
Id int64 `json:"id"`
Name string `json:"name"`
}

func handler(id int64, name string) User {
return User{id, name}
}

func main() {
goweb := goweb.NewGoWeb();
goweb.HandleGet("/test/{id}/{name}", handler)
goweb.ListenAndServe(":8000")
}

执行上面的代码,然后访问url:http://localhost:8000/test/123456/alber


就可以返回下面的json数据


{
"id": 123456,
"name": "alber"
}

handler可以获取到url中的参数,并且注入到handler参数中。handler的第一个参数对应url中的第一个参数,第二个参数对应url中的的第二个参数,依次类推。不过暂时还有个限制,在url中使用参数时,handler中的参数必须与url中的参数个数一致,且类型必须为string或者int64。


2.handler


goweb可以注册多种形式的handler,goweb会利用反射自动解析函数,支持多种类型,但是不能超出它可以解析的范围。以下是它所有能解析的类型。



func handler(ctx goweb.Context) {
}

func handler(ctx goweb.Context) User {
return User{}
}

func handler(user User) User {
return User{}
}

func handler(ctx goweb.Context, user User) User {
return User{}
}

func handler(name string, id int64) User {
return User{}
}

func handler(ctx goweb.Context, name string, id int64) User {
return User{}
}

Context是一个请求上下文,他只有ResponseWriter和Request两个字段,它的内部结构如下所示。你可以根据自己的需求修改源码进行扩展,例如,把它作为一个请求的会话使用。


type Context struct {
w http.ResponseWriter
r *http.Request
}

3.用Group组织你的handler


func main() {
group1:=goweb.NewGroup("/group1")
group1.HandleGet("/handler1",handler)
group1.HandleGet("/handler2",handler)
group1.HandleGet("/handler3",handler)

group2:=goweb.NewGroup("/group2")
group2.HandleGet("/handler1",handler)
group2.HandleGet("/handler2",handler)
group2.HandleGet("/handler3",handler)

group3:=goweb.NewGroup("/group3")
group3.HandleGet("/handler1",handler)
group3.HandleGet("/handler2",handler)
group3.HandleGet("/handler3",handler)

goweb.HandleGroup(group1)
goweb.HandleGroup(group2)
goweb.HandleGroup(group3)
goweb.ListenAndServe(":8000")
}

group可以帮助你分层次的组织你的handler,使你的路由结构更清晰。


4.定义自己序列化和反序列化方式


var json = jsoniter.ConfigCompatibleWithStandardLibrary

func jsonUnmarshal(data []byte, v interface{}) error {
return json.Unmarshal(data, v)
}

func jsonMarshal(v interface{}) ([]byte, error){
return json.Marshal(v)
}

func main() {
goweb:=goweb.NewGoWeb();
goweb.Unmarshal=jsonUnmarshal
goweb.Marshal=jsonMarshal

goweb.ListenAndServe(":8000")

}

goweb默认采用json(使用的是开源的jsoniter)序列化和反序列化数据,goweb的Marshal、Unmarshal变量本身是一个函数.如果你想定义自己的序列化方式,只需要覆盖掉它就行,就像上面那样。


5.拦截器



func interceptor1(http.ResponseWriter, *http.Request) bool {
return true
}
func interceptor2(http.ResponseWriter, *http.Request) bool {
return true
}
func interceptor3(http.ResponseWriter, *http.Request) bool {
return true
}

func main() {
goweb := goweb.NewGoWeb();
goweb.AddInterceptor(interceptor1)
goweb.AddInterceptor(interceptor2)
goweb.AddInterceptor(interceptor3)
goweb.ListenAndServe(":8000")
}

goweb在执行handler之前,会执行一个或者多个interceptor,并且会根据AddInterceptor的先后顺序执行,当interceptor返回true时,会接着往下执行,返回false时,会终止执行。


6.过滤器


func filter(w http.ResponseWriter, r *http.Request, f func(http.ResponseWriter, *http.Request)) {
f(w, r)
}

func main() {
goweb := goweb.NewGoWeb();
goweb.Filter = filter
goweb.ListenAndServe(":8000")
}

你可以给goweb添加一个过略器,在过滤器中,如果你想执行完自己的逻辑之后,执行handler,只需要调用f(w, r)。


7.自定义错误处理



func handler400(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(400)
w.Write([]byte("bad request"))
}
func handler404(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404)
w.Write([]byte("url not found"))
}
func handler405(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(405)
w.Write([]byte("method not found"))
}

func main() {
goWeb := goweb.NewGoWeb()
goWeb.Handler400 = handler400
goWeb.Handler404 = handler404
goWeb.Handler405 = handler405

goweb.ListenAndServe(":8000")
}

当请求执行失败时,goweb中给出了一些默认的错误处理方式,就像上面那样。当然,你也可以定义一些自己错误处理方式。


写在后面


如果你有什么好的建议,可以发我邮箱,一起交流。


alber_liu@qq.com

goweb,基于go语言API框架

回复

Alber 发起了问题 • 0 人关注 • 0 个回复 • 92 次浏览 • 2 天前 • 来自相关话题

一个辅助操作数据库的go lib[from滴滴]

caibirdme 发表了文章 • 0 个评论 • 166 次浏览 • 2 天前 • 来自相关话题

github地址: gendry

gendry是一个非常简单易用的sql builder,比如构建sql语句:

... 查看全部

github地址: gendry


gendry是一个非常简单易用的sql builder,比如构建sql语句:


where := map[string]interface{}{
"city in": []interface{}{"beijing", "shanghai"},
"score": 5,
"age >": 35,
"_orderby": "bonus desc",
"_grouoby": "department",
}
table := "some_table"
selectFields := []string{"name", "age", "sex"}
cond, values, err := builder.BuildSelect(table, where, selectFields)
rows,err := db.Query(cond, vals...) // db: *sql.DB
//cond = SELECT name,age,sex FROM g_xxx WHERE (city IN (?,?) AND score=? AND age>?) GROUP BY department ORDER BY bonus DESC
//values = []interface{}{"beijing", "shanghai", 5, 35}

读取返回:


type Person struct {
Name string `json:"name"`
Age int `json:"m_age"`
}

rows,err := db.Query("SELECT age as m_age,name from g_xxx where xxx")
defer rows.Close()

var students []Person

scanner.Scan(rows, &students)

gendry最开始是我在滴滴的一个对外接口服务中使用,经过一年多的迭代和多个线上系统大流量的验证,已经非常稳定了。gendry的一个显著特点是非侵入性,只要你的项目目前使用的是标准库,那你就可以无痛添加gendry,大多数时候它就是一个helper函数。如果你不想用,一行代码就可以去掉,不会像orm一样牵一发动全身。gendry还提供了一个cli工具可以根据数据库表结构自动生成该表对应的结构体和对该表的增删改查的dao层代码。


gendry是个很简单的lib,期待大家的使用和意见,热烈欢迎各种pr


这里简要地写了一下我们为什么会开发这个lib: https://github.com/didi/gendry/wiki" title="为什么开发gendry">为什么开发gendry

websocket封装

EddieChan 发表了文章 • 3 个评论 • 194 次浏览 • 2018-01-12 16:49 • 来自相关话题

package edd_socket

    import (
        "time"
        "log"
        "encoding/json"
    ... 			查看全部
					
package edd_socket

import (
"time"
"log"
"encoding/json"
"golang.org/x/net/websocket"
)

type ws struct {
coon *websocket.Conn
}

//消息体
type Message struct {
Data interface{} `json:"content"`
Type string `json:"type"`
TimeStamp int64 `json:"time_stamp"`
}

//用户体
type User struct {
Uid string
conn *websocket.Conn
}

var (
member = make(map[string]*User)
uidMapWs = make(map[string]*websocket.Conn)
groupMapMember = make(map[string][]*User)
)

//实例化ws
func NewWS(wss *websocket.Conn) *ws {
return &ws{coon: wss}
}

//绑定uid和coon
func (this *ws) BindUid(uid string) {
client := User{Uid: uid,conn: this.coon}

member[uid] = &client
uidMapWs[uid] = this.coon
}

//是否在线
func (this *ws) IsOnline(uid string) bool {
_, exits := member[uid]
return exits
}

//断开连接
func (this *ws) CloseUid(uid string) {
msg := Message{
Data: "离开房间",
Type: "del_user",
}
this.SendToAll(msg)

delete(member, uid)
delete(uidMapWs, uid)
this.coon.Close()
}

//群发消息
func (this *ws) SendToAll(msg Message) {
msg.TimeStamp = time.Now().Unix()
sendMess, _ := json.Marshal(msg)

for k, v := range member {
if v.conn != this.coon {
if err := websocket.Message.Send(v.conn, string(sendMess)); err != nil {
delete(member, k)
delete(uidMapWs, k)
continue
}
}
}
}

//获取当前组人数
func (this *ws) GetClientCountByGroup(groupName string) int {
return len(groupMapMember[groupName])
}

func (this *ws) GetClientByGroup(groupName string) []*User {
return groupMapMember[groupName]
}

//加入某个群
func (this *ws) JoinGroup(groupName, uid string) {
groupMapMember[groupName] = append(groupMapMember[groupName], member[uid])
}

//给指定群发消息
func (this *ws) SendToGroup(groupName string, msg Message) {
msg.TimeStamp = time.Now().Unix()
sendMess, _ := json.Marshal(msg)

for k, v := range groupMapMember[groupName] {
if v.conn != this.coon {
if err := websocket.Message.Send(v.conn, string(sendMess)); err != nil {
kk := k + 1
groupMapMember[groupName] = append(groupMapMember[groupName][:k], groupMapMember[groupName][kk:]...)
continue
}
}
}
}

//离开某个群
func (this *ws) LeaveGroup(groupName, uid string) {
for k, v := range groupMapMember[groupName] {
if v.Uid == uid {
kk := k + 1
groupMapMember[groupName] = append(groupMapMember[groupName][:k], groupMapMember[groupName][kk:] ...)
break
}
}
}

//发送给指定uid
func (this *ws) SendToUid(uid string, msg Message) {
toWsCoon := uidMapWs[uid]
msg.TimeStamp = time.Now().Unix()
sendMess, _ := json.Marshal(msg)

if err := websocket.Message.Send(toWsCoon, string(sendMess)); err != nil {
delete(member, uid)
log.Println(err)
}
}

//解析客户端消息
func (this *ws) GetMsg(msg *Message) error {
var reply string
var err error
if err = websocket.Message.Receive(this.coon, &reply); err == nil {
json.Unmarshal([]byte(reply), msg)
}
return err
}

使用:


package edd_socket

import (
"fmt"
"net/http"
"crypto/md5"
"encoding/hex"
"io"
"crypto/rand"
"encoding/base64"
"log"
"golang.org/x/net/websocket"
)

//获取MD5字符串
func getMd5String(s string) string {
h := md5.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}

//用于将当前用户信息和当前websocket.conn关联
//正式上线,可用数据库用户Id代替
func getUid() string {
b := make([]byte, 48)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return ""
}

return getMd5String(base64.URLEncoding.EncodeToString(b))
}

func Echo(ws *websocket.Conn) {
wss := NewWS(ws)
uid := getUid()

defer func() {
wss.CloseUid(uid)
}()

for {
var mess Message
if err := wss.GetMsg(&mess); err != nil {
//若当前socket接收不到消息,则已掉线,主动退出for
fmt.Println(err)
break
}

switch mess.Type {
case "connect":
//连接准备,客户端标识符name和服务端标识符绑定
wss.BindUid(uid)
msg := Message{
Data: mess.Data,
Type: "join_room",
}

wss.SendToAll(msg)
case "all":
//群发
msg := Message{
Data: mess.Data,
Type: "send_all",
}
wss.SendToAll(msg)
case "join_group":
//wss.JoinGroup("one", uid)
case "who":
}
}
}

func main() {
var port = "8022"
fmt.Println("listening port:" + port)

go func() {
http.Handle("/chat", websocket.Handler(Echo))
}()

if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatal("ListenAndServer: ", err)
}
}

客户端


  var ws =new WebSocket(websocket_domain)
ws.onopen = function (res) {
ws.send(sendMessAll(name,"进入房间",'connect'))
};

ws.onmessage = function (res) {
var data = jQuery.parseJSON(res.data);
};
ws.onclose = function (res) {
console.log(res)
};
function sendMessAll(name, data, type) {
return JSON.stringify(
{
data: JSON.stringify({
name:name,
age:123
}),
type: type
}
)
}


欢迎指正我的开源地址


分享个自己的golang项目 - bitproxy代理工具

回复

moliliang 发起了问题 • 1 人关注 • 0 个回复 • 219 次浏览 • 2018-01-12 16:03 • 来自相关话题

冲顶大会等答题类游戏快速答题程序 - 提供决策结果

silenceper 发表了文章 • 4 个评论 • 307 次浏览 • 2018-01-12 02:28 • 来自相关话题

参考之前跳一跳外挂的的思路,也整了这么一个东西: 查看全部

参考之前跳一跳外挂的的思路,也整了这么一个东西:
https://github.com/silenceper/qanswer


通过抓取手机屏幕截图经过文字识别,结合搜索引擎给出一个参考值。


快速答题


《冲顶大会》,《百万英雄》等答题类游戏答题神器,顺利吃鸡!


通过抓取手机屏幕截图经过文字识别,结合搜索引擎给出一个参考值。


题目


分析结果:


结果


结果说明:




  • 结果数:通过题目+答案的搜索形式在搜索引擎中的结果数量



  • 答案出现频率:通过搜索题目,答案在第一页结果中出现的频率


结果并不是100%的,只给出一个参考值,还需用户自己判断。理论上可支持多款APP,只需要修改config.yml中的题目和答案的截取位置即可。


具体安装使用可在github中查看:


https://github.com/silenceper/qanswer/

基于go+vue实现的golang每日新闻数据浏览与检索平台

mikemintang 发表了文章 • 6 个评论 • 883 次浏览 • 2018-01-09 16:58 • 来自相关话题

介绍

gonews是基于go+vue实现的golang每日新闻浏览与检索平台

介绍


gonews是基于go+vue实现的golang每日新闻浏览与检索平台



项目截图


gonews


部署



  • 获取新闻数据


git clone https://github.com/gocn/news /data/news


  • 获取源码


go get -u github.com/mikemintang/gonews


  • 解析数据


nohup gonews -d /data/news > /data/log/gonews.log 2>&1 


  • 启动Api


nohup gonews -a api -p 8017 > /data/log/gonews.log 2>&1 &


  • 前端部署


cd $GOPATH/src/github.com/mikemintang/gonews/web
npm install
npm run build


  • Nginx配置


server {
listen 80;
server_name gonews.idoubi.cc;
index index.html index.htm index.php;
root /data/go/src/mikemintang/gonews/web;

location /api {
rewrite ^.+api/?(.*)$ /$1 break;
proxy_pass http://127.0.0.1:8017;
}
}


  • Shell脚本


#!/bin/sh

cd /data/news
git pull origin master
nohup gonews -d /data/news/ > /data/log/gonews.log 2>&1


  • 定时任务


crontab -e
*/10 * * * * /bin/sh /data/shell/cache_news.sh

用到的技术


golang包



  • github.com/go-redis/redis

  • encoding/json

  • flag

  • net/http

  • net/url

  • strconv

  • sync

  • crypto/md5

  • fmt

  • io

  • io/ioutil

  • net/url

  • os

  • path/filepath

  • regexp

  • strconv

  • strings

  • time


npm包



  • vue

  • vuex

  • vue-router

  • axios

  • moment

  • mockjs


欢迎提交Pull Request

模仿Java Stream API 操作数组/切片

singlethread 回复了问题 • 4 人关注 • 6 个回复 • 356 次浏览 • 2018-01-08 14:00 • 来自相关话题

packetbeat必须要结合elasticsearch或者logstash来用吗?能单独使用吗

tkk 回复了问题 • 3 人关注 • 1 个回复 • 248 次浏览 • 2018-01-06 17:23 • 来自相关话题

分享一个收集 Nginx 日志的 Exporter

smallfish1 发表了文章 • 0 个评论 • 142 次浏览 • 2018-01-04 21:21 • 来自相关话题

目的: 滚动收集 Nginx 日志并生成 Prometheus 采样数据。

功能:

  1. 动态配置 Nginx 日志格式
  2. Metric 支持静态和动态的 LabelSet
  3. 支持同时收集... 查看全部

目的: 滚动收集 Nginx 日志并生成 Prometheus 采样数据。


功能:



  1. 动态配置 Nginx 日志格式

  2. Metric 支持静态和动态的 LabelSet

  3. 支持同时收集多个日志


它能做什么:



  1. 统计任意 API 请求数,以及请求错误数。

  2. 任意 API 请求时长 95 值。

  3. 任意 API 请求包的大小 95值。


地址: https://github.com/songjiayang/nginx-log-exporter


欢迎大家试用和 PR

写了个腾讯云短信的 Go SDK

qichengzx 发表了文章 • 2 个评论 • 235 次浏览 • 2018-01-04 09:55 • 来自相关话题

写了个腾讯云短信的 Go 版本 SDK,尽可能完整的覆盖了目前所有功能,其中几个需要回调处理的还没做。

前前后后改了3次,最终完成了当前的版本,也是为了将构造请求尽可能的简化和方便。

第一次尝试写 SDK ,学到了很多,还有一... 查看全部

写了个腾讯云短信的 Go 版本 SDK,尽可能完整的覆盖了目前所有功能,其中几个需要回调处理的还没做。


前前后后改了3次,最终完成了当前的版本,也是为了将构造请求尽可能的简化和方便。


第一次尝试写 SDK ,学到了很多,还有一些地方做的不够好,感觉代码组织地也太啰嗦。


希望大家提出宝贵意见和建议。


附上GitHub地址:https://github.com/qichengzx/qcloudsms_go

Golang实现的全自动玩微信跳一跳游戏

sundyli 回复了问题 • 2 人关注 • 2 个回复 • 1587 次浏览 • 2017-12-30 21:14 • 来自相关话题

一个比较完整的 Vim debug workflow

SpaceVim 发表了文章 • 0 个评论 • 244 次浏览 • 2017-12-14 22:40 • 来自相关话题

SpaceVim Layers: debug


原文: http://spacevim.org/layers/debug/


Description


This layer provide a debug workflow for SpaceVim. All of the function is based on vim-vebugger.


Install


To use this configuration layer, add call SpaceVim#layers#load('debug') to your custom configuration file.


Key bindings























































Key Binding Description
SPC d l launching debugger
SPC d c Continue the execution
SPC d b Toggle a breakpoint for the current line
SPC d B Clear all breakpoints
SPC d o step over
SPC d i step into functions
SPC d O step out of current function
SPC d e s Evaluate and print the selected text
SPC d e e Evaluate the <cword> under the cursor
SPC d e S Execute the selected text
SPC d k Terminates the debugger

Debug Transient State


key bindings is too long? use SPC d . to open the debug transient state:


Debug Transient State

我用golang重写了2300+star的开源node项目.

nilman 发表了文章 • 7 个评论 • 782 次浏览 • 2017-12-13 22:50 • 来自相关话题

我很早前用node写了这开源项目:p2pspider,因为网络流量非常巨大,再加上v8有内存限制,我不得不每隔6小... 查看全部

我很早前用node写了这开源项目:p2pspider,因为网络流量非常巨大,再加上v8有内存限制,我不得不每隔6小时重启,而且cpu占用非常之高,16核的cpu,都差不多占满了,内存也是占用极其高。


在后期维护过程中,因为javascript的原因,发现极其难以维护,让我产生了用静态语言重写的念头。在研究了一些各种主流编程语言,最后选择了golang。 选它主要看中三点:1,语法极其简单;2,goroutine;3,运行速度极其快。


花了十几天左右,就重写完成,一运行,在同样工作效率的情况下,golang版本的p2pspider的综合消耗,远远低于node版,只有node的10%,这很出乎我的意料,毕竟是第一次写golang程序,很可能写得很粗糙。


由于对golang不是很熟,中间踩了很多坑,在此不表了。总之,golang是个很赞的编程语言。