nats批量publish最后一条疑似丢失

2021年11月14日 13:24

 

问题描述

 
    写了一个测试脚本,从数据读取一组数据,然后逐条publish到nats-server中, 确定每条都publish了
 
    有另外一个程序中subscribe订阅,在该程序发现最后一条一直没收到
 
 

环境

 
* go: 1.16
 
* nats-server
 

程序摘要

 
* 发布
 
for _, record := range records{
    ...
    gNatsConn.Publish(Topic, record)
    time.Sleep(time.Second*0.1)    //间隔越小出现的概率越大. 间隔1秒好像就没有
}
 
* 订阅
 
natsConn.Subscribe(topic, func(m *nats.Msg){
    fmt.Println(m.Data)
    ....
}
 

原因以及解决办法

 
    nats在发布消息时,有应该是用了缓存通道, 大小是1。最后一条数据有可能没被读走
 
    发布频率较高,最后需要flush一下
 
 
 

Tags: nats go
评论(98) 阅读(772)

influxdb遇到时间点存储不了

2021年9月23日 14:33

 

 问题描述

 
    有一份mysql存储的历史数据,准备把它转为influxdb存储, 时间点用的是mysql表中记录的创建时间,创建时间是时许的。
 

环境描述

 
* influxdb: 2.0.8
 
* 开发语言: go
 
* influx客户端: influxdb-client-go 2.5.0
 
 

关键代码摘要

 
    使用如下代码, 创建point,发现存储不了。
 
point := influxdb2.NewPoint("table_name",
    map[string]string{
        ...
    },
    map[string]interface{}{
        ....
    },
    ts,     //对应mysql表中记录的创建时间
    )
 

解决过程

 
    开始以为数据是几年前的, 时间太长存储不了。 后来用time.Time().Add(-xxx), 把时间移到3年前, 结果能存储
 
 

解决办法

 
    原因是influx的精度是纳秒, 存入的时间精度是秒, 在Influx中无法存储
 
    解决办法是加1纳秒, ts.Add(time.Nanosecond*1)
 

评论(83) 阅读(1035)

influxdb如何把point中的多个field一起查出来

2021年8月29日 14:39

 

问题描述

 
    influx查询包含了水平拆分、垂直拆分. 查询结果的每一条记录record,只对应一个field
 
    写入一个point时包含多个field, 查询时如何将这几个field一起查出来呢?
 

写入示例

 
point := influxdb2.NewPoint("history",
    map[string]string{
        "Version": DedefaultVersion,
        "Name": sh.Name,
        "Active": fmt.Sprintf("%v", sh.Active),
        "PortIdx": fmt.Sprintf("%d", sh.PortIdx),
        "Port": fmt.Sprintf("%v", sh.Port),
    },
    map[string]interface{}{
        "MsgId": fmt.Sprintf("%d", sh.MsgId),
        "Content": sh.Content,
    },
    time.Now(),
    )
 
* 查询
 
from(bucket:"my-bucket")
|> range(start: -10m)
|> filter(fn: (r) => (r._measurement=="history") and (r.Name=="xxxx") )
 
用这个方法查询, 每个record只包含一个_field
 
 

解决方法: 分组

 
    分组之后的record,会多一个table属性。table相同,则为一组。也就是一个point的field。
 
* 按写入时间分组
 
|> group(columns: ["_time"])
 
* 增加id标签,按id分组
 
为每个point增加一个id标签, 然后按id分组
 
 
 
 

Tags: influxdb
评论(150) 阅读(1400)

golang编译-ldflags -H windowsgui被认为是病毒

2021年8月26日 17:22

 
 

介绍

 
    使用walk为程序做了一个windows界面, cmd窗口需要隐藏。使用了 go build -ldflags="-H windowsgui"
 
    结果exe程序被杀毒软件认为是病毒
 

为什么会认为是病毒?

 
    隐藏cmd窗口, 双击exe,可以直接启动,并且看不到任何窗口。这不就是病毒的喜欢干的事吗.
 
    所以有些杀毒软件, 干脆通杀, 使用了 "-H windowsgui" 直接被认为是病毒
 
    我们这些正常的需求该咋办呢?
 

解决办法

 
    在程序中,使用代码隐藏cmd窗口
 
 
import "github.com/lxn/win"

//隐藏cmd窗口
win.ShowWindow(win.GetConsoleWindow(), win.SW_HIDE)
 
    缺点:启动时会有很短暂的黑框闪现,不过问题不大
 
 

评论(211) 阅读(1349)

influxdb的docker-compose如何配置

2021年8月26日 06:27

 
influxdb的docker-compose如何配置
 

1. 新建数据目录

 
mkdir data-volumne
 

2. 生成配置文件

 
docker run --rm influxdb:2.0.8 influxd print-config > config.yml
 

3. docker-compose配置

 
docker-compose.yaml内容
 
version: "2"
services:
   influxdb:
       image: influxdb:2.0.8
       volumes:
           - ./data-volumne:/var/lib/influxdb2
           - ./config.yml:/etc/influxdb2/config.yml
           - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime
       ports:
           - 8086:8086
       environment:
           DOCKER_INFLUXDB_INIT_MODE: setup
           DOCKER_INFLUXDB_INIT_USERNAME: my-user
           DOCKER_INFLUXDB_INIT_PASSWORD: my-password
           DOCKER_INFLUXDB_INIT_ORG: my-org
           DOCKER_INFLUXDB_INIT_BUCKET: my-bucket
 

4. 启动

 
docker-compose up
 

5. 目录结构如下

 
[root@localhost influxdb]# tree
.
├── config.yml
├── data-volumne
│   ├── engine
│   │   └── data
│   └── influxd.bolt
└── docker-compose.yaml
 
 

* 注意

 
上面修改influx时区无效,好像不能修改时区,默认都是UTC
 

Tags: influxdb docker
评论(107) 阅读(1097)

以管理员身份运行cmd有哪几种方法

2021年8月26日 05:15

描述

    
    除了右键"以管理员身份运行",还有哪几种方法,能够以管理员身份启动cmd
 

方式一:右键"以管理员方式运行"

 

方式二:powershell命令

 
Start-Process cmd -Verb runas
Start-Process powershell -Verb runas
 

方式三: 第三方工具

 
http://code.kliu.org/misc/elevate/
 

方式四:编程

 
在编译程序时,设置启动时请求权限
 
 
 

评论(76) 阅读(870)

golang离线环境依赖包问题

2021年8月21日 17:06

 

问题描述

 
在一个win10虚拟机中安装了go(go1.16.4)的开发环境, 其中有自己的项目,此时正常编译。
    
然后将这个虚拟机放到内网,内网网络受限,不允许访问外网
    
如果新一个依赖包,该怎么办呢?
 
 
 

方法一: 拷贝vendor目录(推荐)

 
1. 在外网环境项目目录,生成vendor
 
go mod vendor
 
2. 将vendor拷贝进内网,放在内网项目目录
 
 
缺点:只能当前项目使用
 
优点:体积小
 
 
类似python的pip download -r requipment.txt,将包下载后放到一个目录
 
 

方法二: 拷贝$GOPATH/pkg/mod目录

 
1. 外在更新全局依赖
 
go mod download
 
2.拷贝进入内网,替换mod目录
 
优点:可以被所有项目公用
 
缺点:有可能会比较大
 
 

方法三: 使用自己的goproxy

 
详细见
https://github.com/goproxyio/goproxy
 
 
 

吐槽

 
不少资料都是介绍GO111MODULE=on/off/auto之类的查找顺序,看得云山雾绕
 
假如GO111MODULE=on,并且在内网,此时无法访问网络,难道它不会找一下当前目录中的vendor?
 
 

评论(94) 阅读(720)

nats-server系统服务只能使用sc命令注册

2021年8月19日 02:42

 

起因描述

 
windows环境下nats-server注册成系统, 官方推荐是sc命令
 
而我觉得sc命令台太麻烦, 为什么不用github.com/kardianos/service,或者更方便的工具呢?
 

方法一:使用nssm通过界面进行配置

 
1. 下载地址
 
http://nssm.cc/download
 
2. 过程省略
 
3. 结果死活不行,由于是第三方工具,出错找不到原因,放弃
 
 

方法二:通过服务外壳作为系统服务

 
* 介绍
 
使用go生成一个demo.exe,再将demo.exe注册成系统,demo.exe读取配置文件,然后demo.exe通过exec.Command启动nats-server.exe
 
go比较好用的库可以用github.com/kardianos/service
 
 
* demo.exe启动nats-server.exe关键源码
 
p.cmd = exec.Command(fullExec, p.Args...)
p.cmd.Dir = p.Dir
p.cmd.Env = append(os.Environ(), p.Env...)
 
* demo.json配置示例
 
{
"Name": "demo",
"DisplayName": "demo",
"Description": "demo for nats-server",

"Dir": "d:\\nats-server",
"Exec": "xxxx\\nats-server.exe",
"Args": ["-c ","xxx\\nats-server.conf"],
"Env": [
],

"Stderr": "C:\\log\\nats_err.log",
"Stdout": "C:\\log\\nats_out.log"
}
 
* 结果报错
 
The service process could not connect to the service controller.
 
意思是一个服务不能连接到另一个服务,啥意思呢? 也就是通过exec.Command启动的竟然是服务,不应该是exe么.
 
* nats-server启动源码分析
 
nats-server.exe的启动入口源码
 
func Run(server *Server) error {
    //入口1:docker
if dockerized {
server.Start()
return nil
}
isInteractive, err := svc.IsAnInteractiveSession()
if err != nil {
return err
}
    //入口2: 交互模式
if isInteractive {
server.Start()
return nil
}
    //入口3: 启动服务(serviceName被写死了,只能是nats-server)
return svc.Run(serviceName, &winServiceWrapper{server})
}
 
* 错误原因总结
 
当以外壳方式启动时,走的是"入口3",也是启动服务,如果没有nats-server,它就啥事也没干,结果nats-server并没有启动
 

方法三:sc命令

 
官方推荐
 

总结

 
1、nat-server只能以sc命令注册成服务。怕麻烦可以写一个bat。
 
2、nats-server服务名只能是nats-server
 
 

Tags: nats
评论(69) 阅读(1030)

zap日志写入通道被覆盖

2021年8月17日 20:00

 

描述

 
实现了一个io.Writer接口,允许zap日志写入,传递给界面,出现了日志被覆盖、重复的情况。
 

原因

 
io.Writer的接口Write传入的[]byte真实类型是slice。通道传递slice时,用的是引用传递,所以真实数据有被覆盖的情况 
 

示例

 
* zap初始化
 
...
out = COut()
zapcore.NewCore(encoder, zapcore.AddSync(out), clevel),
...
 
* 自定义日志写入Channel
 
package logs

var _GlobalChanOutput = &ChanOutput{
outs: make([]chan *[]byte, 0),
}

/*---------------------------------------
全局函数
示例
logs.COut().AddOut(xx)
---------------------------------------*/

//全局ChanOutput
func COut() *ChanOutput{
return _GlobalChanOutput
}

/*---------------------------------------
log to channel
---------------------------------------*/

type ChanOutput struct{
outs []chan *[]byte
}

//io.Writer接口
func (c *ChanOutput) Write(p []byte) (n int, err error) {
count :=0

//数据复制(防止被覆盖)
_p := make([]byte, len(p))
copy(_p, p)

for _, out :=range c.outs{
select{
case out <- &_p:
count += 1
default:
}
}
return count, nil
}

func (c *ChanOutput) AddOut(out chan *[]byte) {
c.outs = append(c.outs, out)
}
 
* 读取日志
 
logOut := make(chan *[]byte, 1024)
logs.COut().AddOut(logOut)

pctx, pctxCancel := context.WithCancel(context.Background())
go func(ctx context.Context){
    select {
    case msg := <- logOut:
        fmt.Println(string(*msg))
    case <-ctx.Done():
        break
    }
}(pctx)
 
 

评论(28) 阅读(629)

msgpack代替json防止丢失类型

2021年6月30日 15:12

 

描述

 
开发了一个通信协议, 协议对数据类型比较敏感, 根据不同数据数据类型使用不同格式。golang的数据类型能够满足需求。
 
然而,将协议转为http方式时,如果传入json,会发现数据类型不符合预期
 
例如,本来是应该int结果是float64; 本来应该是[]byte,结果是string
 

环境

 
http服务器: gin
 
开发语言: golang
 

解决方法探寻

 
什么传输协议会带上数据类型?
 
* json类型丢失
 
默认情况下gin的BindJSON,可将传入的参数绑定到具体的结构体,产生类型转换。然后,我的结果需要是动态的,不知道参数的具体内容。
 
* 自定义参数格式,将类型带上。例如 marchinery
 
Arg struct{
    Type string
    Value interface{}
}
 
缺点:太麻烦
 
* 常用协议中选择
 
xml         可以用,不理想
 
yaml        不能解决问题
 
protobuf    不能解决问题
 
messagepack 有详细的数据类型,完美
 
 

msgpack替代json

 
gin示例
 
func SendXX(c *gin.Context){
    rawData, _ := c.GetRawData()
    var param interface{}
    msgpack.Unmarshal(rawData, ¶m)

result := struct{
Name string
}{
Name: "xxx",
}
resultData, _ := msgpack.Marshal(result)
c.Data(200, "application/x-msgpack", resultData)
}
 

备注

 
* gin自带的msgpack版本是1.1.7不好用,不会自动进行类型转换。 最好改用  github.com/vmihailenco/msgpack/v5
 
* 替换gin中的部分接口就可以,不用全部改为msgpack
 
 
 
 

评论(102) 阅读(1326)