vscode问题

回复

有问必答loplop 发起了问题 • 1 人关注 • 0 个回复 • 29 次浏览 • 12 小时前 • 来自相关话题

redis 使用GETRANGE 来获取一组bitmap状态

技术讨论luw2007 发表了文章 • 0 个评论 • 21 次浏览 • 12 小时前 • 来自相关话题

在使用redis 的 bitmap 来存储业务状态。经常需要顺序获取一个范围内bitmap。 业务代码里,一般会使用pipeline来优化查询逻辑。伪代码如下

查看全部
					

在使用redis 的 bitmap 来存储业务状态。经常需要顺序获取一个范围内bitmap。
业务代码里,一般会使用pipeline来优化查询逻辑。伪代码如下


def get_bits_pipe(pipe, key, cur, num=10):
""" 使用pipeline gitbit"""
for i in xrange(num + 1):
pipe.getbit(key, cur + i)
return [cur + i for i, v in enumerate(pipe.execute()) if v]

这里减少了和redis的数据交换。提高了查询性能,但是随着查询数量的增加,性能急剧下降。


其实 redis 有更高效的方式来获取顺序的bitmap。就是通过 getrange来获取bitmap所在的字符串,然后计算出每位的值。需要注意:由于redis 的 bit 并非按照自然二进制位增加,
比如:'\x01' 对应的ascii 为1。其二进制表示'1', 在redis中表示offset为 7。感兴趣可以看看redis的实现逻辑。


以下提供golang 和python版本的样例。
实现代码: https://gist.github.com/luw2007/692d4a615dd71aa2bfa42190ad6a12e3

beego orm目前貌似没有软删除,谁已经造了轮子呢?

回复

有问必答kouga 发起了问题 • 1 人关注 • 0 个回复 • 54 次浏览 • 15 小时前 • 来自相关话题

宕机,找不到原因,请各位指点下解题思路

回复

有问必答xw_heng 发起了问题 • 1 人关注 • 0 个回复 • 73 次浏览 • 17 小时前 • 来自相关话题

GoCN每日新闻(2017-05-22)

回复

文章分享astaxie 发起了问题 • 1 人关注 • 0 个回复 • 150 次浏览 • 18 小时前 • 来自相关话题

Kubernetes指南(中文版)

文章分享lei 发表了文章 • 0 个评论 • 49 次浏览 • 19 小时前 • 来自相关话题

GoCN每日新闻(2017-05-21)

回复

文章分享astaxie 发起了问题 • 2 人关注 • 0 个回复 • 227 次浏览 • 1 天前 • 来自相关话题

GoCN每日新闻(2017-05-20)

回复

文章分享astaxie 发起了问题 • 1 人关注 • 0 个回复 • 206 次浏览 • 2 天前 • 来自相关话题

GOLANG使用嵌入结构实现接口

技术讨论winlin 发表了文章 • 0 个评论 • 97 次浏览 • 3 天前 • 来自相关话题

考虑一个Packet接口,一般会返回一个Header,例如:

type PacketHeader struct {
    ID uint32
    Timesta... 			查看全部
					

考虑一个Packet接口,一般会返回一个Header,例如:


type PacketHeader struct {
ID uint32
Timestamp uint64
}

type Packet interface {
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
Header() *PacketHeader
}

如果是OO的语言,一般会有一个基类,里面包含了Header和实现这个Header:


class BasePacket : public Packet {
protected:
PacketHeader h;
public:
virtual Header() *PacketHeader;
};

class HandshakePacket : public BasePacket {
};

在子类中就都实现了这个Header()方法了,在GOLANG同样可以做到,通过在Header中定义方法,在Packet中包含Header就可以。


func (v *PacketHeader) Header() *PakcetHeader {
return v
}

type HandshakePacket struct {
PacketHeader
}

看起来还差不多的,都可以实现,golang只是代码少一点,清晰一点点而已。考虑要添加一些辅助函数,譬如给Packet添加是否是紧急类型的包,那OO语言得做一次代理:


type Packet interface {
IsErgency() bool
}

class BasePacketHeader {
public:
bool IsErgency() {
return realtime < 3;
}
}

class BasePacket {
public:
bool IsErgency() {
return h.IsErgency();
}
}

而在GOLANG中,只需要在Header实现就好了:


func (v *PacketHeader) IsErgency() bool {
return v.realtime < 3
}

更高级的可以直接嵌入接口。譬如context.Context的实现,cancelCtx直接嵌入了一个接口:


type cancelCtx struct {
Context

通过指定类型,或者初始化的顺序初始化struct


func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{
Context: parent,
done: make(chan struct{}),
}
}

结构嵌套的方式,让组合实现起来非常便捷,避免频繁的代理。

GoCN每日新闻(2017-05-19)

回复

文章分享xieyanke 发起了问题 • 1 人关注 • 0 个回复 • 208 次浏览 • 3 天前 • 来自相关话题

使用两个context实现CLOSE包的超时等待

技术讨论winlin 发表了文章 • 0 个评论 • 90 次浏览 • 4 天前 • 来自相关话题

在UDP中,一般发送者发送包后,如果一定的时间对方没有收到,就需要重传。例如UDP实现握手的过程,如果握手的包,比如RTMFP协议的IHELLO,发送给对方后,如果一定1秒没有收到,就应该重发一次,然后等3秒、6秒、9秒,如果最后没有收到就是超时了。查看全部

在UDP中,一般发送者发送包后,如果一定的时间对方没有收到,就需要重传。例如UDP实现握手的过程,如果握手的包,比如RTMFP协议的IHELLO,发送给对方后,如果一定1秒没有收到,就应该重发一次,然后等3秒、6秒、9秒,如果最后没有收到就是超时了。


最后一个Close包,发送者不能等待这么长的时间,所以需要设置一个较短的时间做超时退出。一般收发都是一个context,在最后这个Close包时,收到ctx.Done也不能立刻退出,因为还需要稍微等待,譬如600毫秒如果没有收到响应才能退出。


一个可能的实现是这样:


in := make(chan []byte)

func Close(ctx context.Context) (err error) {
timeous := ... // 1s,3s,6s,9s...
for _, to := range timeouts {
// 发送给对方WriteToUDP("CLOSE", peer)
// 另外一个goroutine读取UDP包到in

select {
case <- time.After(to):
case <- in:
fmt.Println("Close ok")
return
case <- ctx.Done():
fmt.Println("Program quit")
return
}
}
return
}

但是这个问题在于,在程序退出时,一般都会cancel ctx然后调用Close方法,这个地方就不会等待任何的超时,就打印"Program quit"然后返回了。解决方案是用另外一个context。但是如何处理之前的ctx的done呢?可以再起一个goroutine做同步:


in := make(chan []byte)

func Close(ctx context.Context) (err error) {
ctxRead,cancelRead := context.WithCancel(context.Background())
go func(){ // sync ctx with ctxRead
select {
case <-ctxRead.Done():
case <-ctx.Done():
select {
case <-ctxRead.Done():
case <-time.After(600*time.Milliseconds):
cancelRead()
}
}
}()

ctx = ctxRead // 下面直接用ctxRead。
timeous := ... // 1s,3s,6s,9s...
for _, to := range timeouts {
// 发送给对方WriteToUDP("CLOSE", peer)
// 另外一个goroutine读取UDP包到in

select {
case <- time.After(to):
case <- in:
fmt.Println("Close ok")
return
case <- ctx.Done():
fmt.Println("Program quit")
return
}
}
return
}

这样在主要的逻辑中,还是只需要处理ctx,但是这个ctx已经是新的context了。不过在实际的过程中,这个sync的goroutine需要确定起来后,才能继续,否则会造成执行顺序不确定:


sc := make(chan bool, 1)
go func(){ // sync ctx with ctxRead
sc <- true
select {
......
}
<- sc

使用context,来控制多个goroutine的执行和取消,是非常好用的,关键可以完全关注业务的逻辑,而不会引入因为ctx取消或者超时机制而造成的特殊逻辑。

上海某土豪公司在招一个3年以上的golang薪资15k-30k

回复

招聘应聘pleas 发起了问题 • 1 人关注 • 0 个回复 • 175 次浏览 • 4 天前 • 来自相关话题

GoCN每日新闻(2017-05-17)

回复

文章分享astaxie 发起了问题 • 2 人关注 • 0 个回复 • 274 次浏览 • 5 天前 • 来自相关话题

GOLANG实现超时对象检测的最好理解的方式

技术讨论winlin 发表了文章 • 0 个评论 • 139 次浏览 • 6 天前 • 来自相关话题

依赖于心跳的系统,都需要超时检测。比如P2P系统中客户端每隔120秒向数据服务器发送一次数据汇总,服务器就需要维护一个超时时间。比如一个UDP服务器,在和客户端之间创建Session之后,如果没有数据包,一般会有Ping包,说明这个Session是存活的... 查看全部

依赖于心跳的系统,都需要超时检测。比如P2P系统中客户端每隔120秒向数据服务器发送一次数据汇总,服务器就需要维护一个超时时间。比如一个UDP服务器,在和客户端之间创建Session之后,如果没有数据包,一般会有Ping包,说明这个Session是存活的,服务器在发现Session超时后也需要清理。


首先,服务器一般需要维护一个列表,以Peer为例:


type Peer struct {
id uint64
heartbeat time.Time
}

type Server struct {
peers map[uint64]*Peer
lock sync.Mutex
}

创建Peer,同时在收到Ping消息后,更新Peer的心跳时间:


func (v *Server) Create(id uint64) *Peer {
v.lock.Lock()
defer v.lock.UnLock()

p = &Peer { id:id, heartbeat: time.Now(), }
v.peers[id] = p
return p
}

func (v *Server) OnPing(id uint64) {
v.lock.Lock()
defer v.lock.UnLock()

if p,ok := v.peers[id]; ok {
p.heatbeat = time.Now()
}
}

当然,需要起一个goroutine定期扫描这个列表, 假设300秒超时:


go func(v *Server) {
for {
func(){
v.lock.Lock()
defer v.lock.UnLock()

now := time.Now()
for id,p := range v.peers {
if p.heartbeat.Add(300 * time.Second).Before(now) {
delete(v.peers, id)
}
}
}()
time.Sleep(30 * time.Second)
}
}(server)

如果Peers的数目非常多,那么扫描时每次都需要锁定v.peers,会导致其他的业务都无法进行。特别是清理Peer这个过程如果比较复杂,譬如需要发起io请求,是一个费时的操作时,就会造成系统的等待。


一般来说,超时的Peer不会很多,因此可以用chan放一个超时的peer,每个peer专门起一个goroutine来看什么时候超时,这样就可以在检测超时时避免用锁了:


timeout := make(chan *Peer)

func (v *Server) Create(id uint64) *Peer {
v.lock.Lock()
defer v.lock.UnLock()

p = &Peer { id:id, heartbeat: time.Now(), }
v.peers[id] = p
return p

go func(p *Peer) {
for {
tm := p.heartbeat
<- time.After(300 * time.Second)
if tm.Equal(p.heartbeat) {
timeout <- p
break
}
}
}(p)
}

go func(v *Server){
for gw := range timeout {
func(){
lgateways.Lock()
defer lgateways.Unlock()

delete(gateways, gw.port)
}()

// Do something cleanup about the gateway.
}
}(server)

这样就只有在有Peer超时时,才真正锁住Server.peers

GoCN每日新闻(2017-05-16)

回复

文章分享astaxie 发起了问题 • 1 人关注 • 0 个回复 • 269 次浏览 • 6 天前 • 来自相关话题