分享一个带 TTL 以及失效回调的 LRU 库. https://github.com/Akagi201/kvcache

Akagi201 发表了文章 • 0 个评论 • 108 次浏览 • 6 天前 • 来自相关话题

https://github.com/Akagi201/kvcache

完美达到我的业务需要的效果. 大家有同样的需求可以测试测试.

...

https://github.com/Akagi201/kvcache


完美达到我的业务需要的效果. 大家有同样的需求可以测试测试.


有问题一定向我反馈. akagi201@gmail.com

推荐一个简化搜索的web小工具“咸鱼SOS”【附上服务器端代码】

kaesalai 发表了文章 • 1 个评论 • 125 次浏览 • 6 天前 • 来自相关话题

咸鱼SOS 链接地址:http://xianyusos.fun

查看全部

咸鱼SOS 链接地址:http://xianyusos.fun



反正不要钱,不用白不用~ —— 周树人



使用咸鱼SOS可以简化搜索步骤,举个栗子,老师要小明同学在github中搜索“golang”关键字,查看golang的开源项目




  1. 普通小明会在浏览器的输入框中输入“github.com”,等网页显示出来后再找到搜索框搜索;




  2. 2B小明会先打开百度,在百度中搜索“github”,再选中github官网,等网页显示出来后再找到搜索框搜索;



  3. 文艺小明会把咸鱼SOS设置为首页或者加入书签,只用选中左边的GitHub,就可以直接使用中央的搜索框搜索github里的内容了。




服务端代码如下,请斧正:
server.go


package main

import (
"github.com/labstack/echo"
"xianyusos/handlers"
"io"
"html/template"
"time"
"gopkg.in/redis.v5"
"github.com/sirupsen/logrus"
"os"
"fmt"
)

const (
PERIOD = 1*time.Hour
)

type Template struct {
Templates *template.Template
}

func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
return t.Templates.ExecuteTemplate(w, name, data)
}

func main() {
if err := getEnvs(); err != nil {
panic(err)
}
e := echo.New()
defer e.Close()
h, err := handlers.New()
if err != nil {
panic(err)
}
defer h.Client.Close()
e.Renderer = &Template{
Templates: template.Must(template.ParseGlob("views/*.html")),
}
logrus.SetOutput(os.Stdout)

go func() {
for {
time.Sleep(PERIOD)
if err := h.Client.Ping().Err(); err != nil {
logrus.Errorf("Error in h.Client.Ping: [%s].", err.Error())
h.Client = redis.NewClient(&redis.Options{
Addr: handlers.REDIS_ADDRESS,
Password: handlers.REDIS_PASSWORD,
MaxRetries: 5,
})
logrus.Infof("Reconnect redis[%s].", handlers.REDIS_ADDRESS)
}
for k, v := range h.VisitorsIp {
if err := h.Client.HSet("visitors", k, v).Err(); err != nil {
logrus.Errorf("Error in h.Client.Set: [%s].", err.Error())
continue
}
}
logrus.Info("Update the value of visitors success.")
}
}()

e.GET("/", h.Home)
e.GET("/:routerName", h.Search)
e.Static("/static", "views")
e.File("/favicon.ico", "views/katongxianyu.png")
//h.RouterName["163music"] = true
h.RouterName["github"] = true
h.RouterName["baidu"] = true
h.RouterName["bilibili"] = true
h.RouterName["iqiyi"] = true
h.RouterName["jd"] = true
h.RouterName["mooc"] = true
h.RouterName["qidian"] = true
h.RouterName["runoob"] = true
h.RouterName["segmentfault"] = true
h.RouterName["stackoverflow"] = true
h.RouterName["taobao"] = true
h.RouterName["weibo"] = true
h.RouterName["youku"] = true
h.RouterName["zhihu"] = true
e.Start(":8823")
}

func getEnvs() error {
handlers.REDIS_ADDRESS = os.Getenv("REDIS_ADDRESS")
if len(handlers.REDIS_ADDRESS) == 0 {
return fmt.Errorf("Env [REDIS_ADDRESS] is nil.")
}
handlers.REDIS_PASSWORD = os.Getenv("REDIS_PASSWORD")
if len(handlers.REDIS_PASSWORD) == 0 {
return fmt.Errorf("Env [REDIS_PASSWORD] is nil.")
}
return nil
}

handler.go


package handlers

import (
"github.com/labstack/echo"
"net/http"
"gopkg.in/redis.v5"
"sync"
"strings"
)

var (
REDIS_ADDRESS = "na.huanyu0w0.cn:2333"
REDIS_PASSWORD = "3.1415926"
)

type (
handler struct {
Client *redis.Client
Mux sync.Mutex
RouterName map[string]bool
VisitorsIp map[string]string
}
)

func New() (*handler, error) {
h := new(handler)
h.Client = redis.NewClient(&redis.Options{
Addr: REDIS_ADDRESS,
Password: REDIS_PASSWORD,
MaxRetries: 5,
})
var err error
h.VisitorsIp, err = h.Client.HGetAll("visitors").Result()
if err != nil {
return nil, err
}
h.Mux = sync.Mutex{}
h.RouterName = make(map[string]bool)
return h, nil
}

func (h *handler) Home(c echo.Context) error {
routerName := ""
for routerName = range h.RouterName {
break
}
return c.Redirect(http.StatusFound, "/"+routerName)
}

func (h *handler) Search(c echo.Context) error {
h.Mux.Lock()
ip := strings.Split(c.Request().RemoteAddr, ":")[0]
h.VisitorsIp[ip] = ip
h.Mux.Unlock()
routerName := c.Param("routerName")
if len(routerName) == 0 {
return echo.ErrNotFound
}
if b, ok := h.RouterName[routerName]; !ok && b == true {
return echo.ErrNotFound
}
return c.Render(http.StatusOK, routerName, len(h.VisitorsIp))
}

零拷贝读取文件成 Go 对象

taowen 发表了文章 • 2 个评论 • 173 次浏览 • 6 天前 • 来自相关话题

我们观察到从文件读取到go对象,需要两次拷贝:

  1. 从文件拷贝到内存,成为[]byte
  2. 从[]byte,按照格式进行读取,拷贝到go对象上

怎么样优化这个读取速度呢?

    ... 查看全部

我们观察到从文件读取到go对象,需要两次拷贝:



  1. 从文件拷贝到内存,成为[]byte

  2. 从[]byte,按照格式进行读取,拷贝到go对象上


怎么样优化这个读取速度呢?



  1. 利用mmap,把文件直接映射到内存,go允许把这片内存已经转化成[]byte来使用

  2. 直接在这个[]byte上“展开”go对象


所谓”展开“就是一个reinterpret cast,对一个指针的类型重新解读。


var bytes = []byte{
16, 0, 0, 0, 0, 0, 0, 0,
5, 0, 0, 0, 0, 0, 0, 0,
'h', 'e', 'l', 'l', 'o'}

假设有这样一个[]byte数组。这个是直接用mmap读取出来的。


var ptr = &bytes[0]

这个ptr就是这片内存区域的指针,指向了开头的第一个元素


type stringHeader struct {
Data uintptr
Len int
}

header := (*stringHeader)(unsafe.Pointer(ptr))

这样我们就把这个内存重新解读为了一个stringHeader了。利用stringHeader就可以构造出string来。


header.Data = uintptr(unsafe.Pointer(&bytes[16]))

把stringHeader的指针指向实际的hello数据部分。


str := (*string)(unsafe.Pointer(ptr))
fmt.Println(str) // "hello"

最后再把同一片内存区域解读为string类型,就得到了"hello"字符串了。整个解码过程只做了一次header.Data的更新,没有做任何内存分配。


相比Java来说,go允许我们使用go自己的heap外的内存。甚至允许把go的对象直接在这片内存上构造出来。这使得我们的应用可以和文件系统的缓存共享一片内存,达到内存利用率的最大化。同时相比protobuf/thrift来说,gocodec就是把cpu对值的内存表示(little endian的integer等),以及go语言对象的内存表示(stringHeader,sliceHeader)直接拷贝了,减少了编解码的计算成本。


完整的代码,欢迎star:bloomfilter_test.go


设计了一个编解码格式叫 github.com/esdb/gocodec


和protobuf的对比还没有测,和json相比,毫无悬念地不在一个量级上。


gocodec 200000 10893 ns/op 288 B/op 2 allocs/op
json 300 3746169 ns/op 910434 B/op 27 allocs/op

fx 已经支持 Go, Node, Python, Ruby, Java, PHP, Julia 这些语言了

回复

metrue 发起了问题 • 1 人关注 • 0 个回复 • 238 次浏览 • 4 天前 • 来自相关话题

LiteIDE X33.1 发布,Go 语言开发工具

visualfc 发表了文章 • 2 个评论 • 128 次浏览 • 17 小时前 • 来自相关话题

Go 语言开发工具 LiteIDE X33.1 正式发布。 新版本对菜单进行调整和规范,新增了工具菜单,编辑菜单保持可见,重命名了部分工具窗口。 修复了编辑器内查找功能的全文替换错误, 对快速打开文件操作 ( ctrl+p / command+p ) 改... 查看全部

Go 语言开发工具 LiteIDE X33.1 正式发布。 新版本对菜单进行调整和规范,新增了工具菜单,编辑菜单保持可见,重命名了部分工具窗口。 修复了编辑器内查找功能的全文替换错误, 对快速打开文件操作 ( ctrl+p / command+p ) 改用线程方式重新实现,新增了快速打开命令功能 ( ctrl+shift+p / command+shift+p ),集成了 gomodifytags 并提供GUI界面实现对结构体 Tags 的快速增删功能。


LiteIDE X33.1 使用 go1.10beta1 编译, 支持 Go1.9 / Go1.10beta1 或者更低的 Go 版本。


Links





2017.12.12 Ver X33.1



  • LiteEditor

    • support quick open command

    • support gomodifytags

    • fix libpng warning on qt5 build


  • LiteApp

    • add tools menu for quick open actions

    • standard and rename tool window title

    • enable edit menu anytime, fix edit menu disable on editor lost focus.

    • fix editor load large file bad_alloc recover


  • LiteEnv

    • add select env to tools menu


  • LiteEditor

    • check and not open large file

    • fix edit hide edit sub menu 'setup' on macos


  • LiteFind

    • fix find editor replace all wrap around* GolangEdit

    • Integrated gomodifytags and gui tools support gomodifytags all options


  • QuickOpen

    • add quick open command action (ctrl+shift+p/command+shift+p)

    • quickopenfiles use thread for fast and cancel

    • fix quickopenfile cancel loading for esc or liteapp quit


RobotGo v0.47.0 发布, Go 桌面自动化, 移除 libpng 等依赖

回复

veni 发起了问题 • 1 人关注 • 0 个回复 • 113 次浏览 • 7 小时前 • 来自相关话题

go超级时间轮timewheel(一路向前)github.com/anjieych/timewheel

Anjie 发表了文章 • 0 个评论 • 49 次浏览 • 6 小时前 • 来自相关话题

go超级时间轮timewheel(一路向前)github.com/anjieych/timewheel

有传统时间轮的影子,但又超出传统时间轮:

  1. 非“盘/环”形结构: 一路向前,永... 查看全部

go超级时间轮timewheel(一路向前)github.com/anjieych/timewheel


有传统时间轮的影子,但又超出传统时间轮:



  1. 非“盘/环”形结构:
    一路向前,永不回头,每tick一次,移除一个slot;每个slot中的entity 触发OnExpired来处理到期(或过期)事件;

  2. 超级时间轮:
    他是一个轮或者轮的集合,因为他可以同时处理不同实现了Entity的业务,每个业务有各自OnExpired定义,只要可以遵循相同的时间刻度Interval就可以放入同一个轮来统一计时触发。
    最后,优点或缺点可以作者交流。


[========]


package main

import (
"fmt"
"github.com/anjieych/timewheel"
"time"
)

func main() {
tw := timewheel.NewTimewheel("tw-example", time.Second)
tw.Start()
tick := time.NewTicker(3 * time.Second)
for {
d := &Data{
eid: time.Now().UnixNano(),
data: <-tick.C,
}
d.SetSlotId(tw.Add(d, 5*time.Second))
}
}
// Data must implements timewheel.Entity
type Data struct {
eid int64
slotId int
data interface{}
}

func (d *Data) SetEId(eId int64) {
d.eid = eId
}
func (d *Data) GetEId() (eId int64) {
return d.eid
}
func (d *Data) SetSlotId(slotId int) {
d.slotId = slotId
}
func (d *Data) GetSlotId() (slotId int) {
return d.slotId
}
func (d *Data) OnExpired() {
fmt.Printf("%s\t OnExpired :{slotId: %d\t,eid: %d\t,data: %s}\n", time.Now(), d.GetSlotId(), d.GetEId(), d.data)
}