Showing preview only (362K chars total). Download the full file or copy to clipboard to get everything.
Repository: Terry-Mao/goim
Branch: master
Commit: 1800484a4533
Files: 116
Total size: 335.2 KB
Directory structure:
gitextract_ggltw923/
├── .github/
│ └── workflows/
│ └── go.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── README_cn.md
├── README_en.md
├── api/
│ ├── comet/
│ │ ├── comet.pb.go
│ │ └── comet.proto
│ ├── generate.go
│ ├── logic/
│ │ ├── logic.pb.go
│ │ └── logic.proto
│ └── protocol/
│ ├── operation.go
│ ├── protocol.go
│ ├── protocol.pb.go
│ └── protocol.proto
├── benchmarks/
│ ├── client/
│ │ └── main.go
│ ├── multi_push/
│ │ └── main.go
│ ├── push/
│ │ └── main.go
│ ├── push_room/
│ │ └── main.go
│ └── push_rooms/
│ └── main.go
├── cmd/
│ ├── comet/
│ │ ├── comet-example.toml
│ │ └── main.go
│ ├── job/
│ │ ├── job-example.toml
│ │ └── main.go
│ └── logic/
│ ├── logic-example.toml
│ └── main.go
├── codecov.sh
├── docs/
│ ├── benchmark_cn.md
│ ├── benchmark_en.md
│ ├── en/
│ │ ├── proto.md
│ │ └── push.md
│ ├── goim.graffle
│ ├── proto.md
│ └── push.md
├── examples/
│ ├── cert.pem
│ ├── javascript/
│ │ ├── client.js
│ │ ├── index.html
│ │ └── main.go
│ └── private.pem
├── go.mod
├── go.sum
├── internal/
│ ├── comet/
│ │ ├── bucket.go
│ │ ├── channel.go
│ │ ├── conf/
│ │ │ └── conf.go
│ │ ├── errors/
│ │ │ └── errors.go
│ │ ├── grpc/
│ │ │ └── server.go
│ │ ├── operation.go
│ │ ├── ring.go
│ │ ├── room.go
│ │ ├── round.go
│ │ ├── server.go
│ │ ├── server_tcp.go
│ │ ├── server_websocket.go
│ │ └── whitelist.go
│ ├── job/
│ │ ├── comet.go
│ │ ├── conf/
│ │ │ └── conf.go
│ │ ├── job.go
│ │ ├── push.go
│ │ └── room.go
│ └── logic/
│ ├── balancer.go
│ ├── balancer_test.go
│ ├── conf/
│ │ └── conf.go
│ ├── conn.go
│ ├── conn_test.go
│ ├── dao/
│ │ ├── dao.go
│ │ ├── dao_test.go
│ │ ├── kafka.go
│ │ ├── kafka_test.go
│ │ ├── redis.go
│ │ ├── redis_test.go
│ │ └── size_coverage.out
│ ├── grpc/
│ │ └── server.go
│ ├── http/
│ │ ├── middleware.go
│ │ ├── nodes.go
│ │ ├── online.go
│ │ ├── push.go
│ │ ├── result.go
│ │ └── server.go
│ ├── logic.go
│ ├── logic_test.go
│ ├── model/
│ │ ├── metadata.go
│ │ ├── online.go
│ │ └── room.go
│ ├── nodes.go
│ ├── nodes_test.go
│ ├── online.go
│ ├── online_test.go
│ ├── push.go
│ └── push_test.go
├── pkg/
│ ├── bufio/
│ │ ├── bufio.go
│ │ └── bufio_test.go
│ ├── bytes/
│ │ ├── buffer.go
│ │ ├── buffer_test.go
│ │ ├── writer.go
│ │ └── writer_test.go
│ ├── encoding/
│ │ └── binary/
│ │ ├── endian.go
│ │ └── endian_test.go
│ ├── ip/
│ │ ├── ip.go
│ │ └── ip_test.go
│ ├── strings/
│ │ ├── ints.go
│ │ └── ints_test.go
│ ├── time/
│ │ ├── debug.go
│ │ ├── duration.go
│ │ ├── duration_test.go
│ │ ├── timer.go
│ │ └── timer_test.go
│ └── websocket/
│ ├── conn.go
│ ├── request.go
│ ├── server.go
│ └── server_test.go
└── scripts/
├── README.md
├── jdk8.sh
├── kafka.sh
└── zk.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/go.yml
================================================
name: Go
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.13
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: go build -v ./...
================================================
FILE: .gitignore
================================================
# IDE ignore
.idea/
*.ipr
*.iml
*.iws
.vscode/
# temp ignore
*.log
*.cache
*.diff
*.exe
*.exe~
*.patch
*.tmp
*.swp
# system ignore
.DS_Store
Thumbs.db
# build
/cmd/comet/comet
/cmd/logic/logic
/cmd/job/job
/target
/configs
/dist
================================================
FILE: CHANGELOG.md
================================================
#### goim
##### Version 2.0.0
> 1.router has been changed to redis
> 2.Support node with redis online heartbeat maintenance
> 3.Support for gRPC and Discovery services
> 4.Support node connection number and weight scheduling
> 5.Support node scheduling by region
> 6.Support instruction subscription
> 7.Support the current connection room switch
> 8.Support multiple room types ({type}://{room_id})
> 9.Support sending messages by device_id
> 10.Support for room message aggregation
> 11.Supports IPv6
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Terry.Mao
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
# Go parameters
GOCMD=GO111MODULE=on go
GOBUILD=$(GOCMD) build
GOTEST=$(GOCMD) test
all: test build
build:
rm -rf target/
mkdir target/
cp cmd/comet/comet-example.toml target/comet.toml
cp cmd/logic/logic-example.toml target/logic.toml
cp cmd/job/job-example.toml target/job.toml
$(GOBUILD) -o target/comet cmd/comet/main.go
$(GOBUILD) -o target/logic cmd/logic/main.go
$(GOBUILD) -o target/job cmd/job/main.go
test:
$(GOTEST) -v ./...
clean:
rm -rf target/
run:
nohup target/logic -conf=target/logic.toml -region=sh -zone=sh001 -deploy.env=dev -weight=10 2>&1 > target/logic.log &
nohup target/comet -conf=target/comet.toml -region=sh -zone=sh001 -deploy.env=dev -weight=10 -addrs=127.0.0.1 -debug=true 2>&1 > target/comet.log &
nohup target/job -conf=target/job.toml -region=sh -zone=sh001 -deploy.env=dev 2>&1 > target/job.log &
stop:
pkill -f target/logic
pkill -f target/job
pkill -f target/comet
================================================
FILE: README.md
================================================
goim v2.0
==============
[](https://golang.org/)
[](https://github.com/Terry-Mao/goim/actions)
[](https://pkg.go.dev/github.com/Terry-Mao/goim)
[](https://goreportcard.com/report/github.com/Terry-Mao/goim)
goim is an im server writen in golang.
## Features
* Light weight
* High performance
* Pure Golang
* Supports single push, multiple push and broadcasting
* Supports one key to multiple subscribers (Configurable maximum subscribers count)
* Supports heartbeats (Application heartbeats, TCP, KeepAlive, HTTP long pulling)
* Supports authentication (Unauthenticated user can't subscribe)
* Supports multiple protocols (WebSocket,TCP,HTTP)
* Scalable architecture (Unlimited dynamic job and logic modules)
* Asynchronous push notification based on Kafka
## Architecture

## Quick Start
### Build
```
make build
```
### Run
```
make run
make stop
// or
nohup target/logic -conf=target/logic.toml -region=sh -zone=sh001 -deploy.env=dev -weight=10 2>&1 > target/logic.log &
nohup target/comet -conf=target/comet.toml -region=sh -zone=sh001 -deploy.env=dev -weight=10 -addrs=127.0.0.1 2>&1 > target/logic.log &
nohup target/job -conf=target/job.toml -region=sh -zone=sh001 -deploy.env=dev 2>&1 > target/logic.log &
```
### Environment
```
env:
export REGION=sh
export ZONE=sh001
export DEPLOY_ENV=dev
supervisor:
environment=REGION=sh,ZONE=sh001,DEPLOY_ENV=dev
go flag:
-region=sh -zone=sh001 deploy.env=dev
```
### Configuration
You can view the comments in target/comet.toml,logic.toml,job.toml to understand the meaning of the config.
### Dependencies
[Discovery](https://github.com/bilibili/discovery)
[Kafka](https://kafka.apache.org/quickstart)
## Document
[Protocol](./docs/protocol.png)
[English](./README_en.md)
[中文](./README_cn.md)
## Examples
Websocket: [Websocket Client Demo](https://github.com/Terry-Mao/goim/tree/master/examples/javascript)
Android: [Android](https://github.com/roamdy/goim-sdk)
iOS: [iOS](https://github.com/roamdy/goim-oc-sdk)
## Benchmark

### Benchmark Server
| CPU | Memory | OS | Instance |
| :---- | :---- | :---- | :---- |
| Intel(R) Xeon(R) CPU E5-2630 v2 @ 2.60GHz | DDR3 32GB | Debian GNU/Linux 8 | 1 |
### Benchmark Case
* Online: 1,000,000
* Duration: 15min
* Push Speed: 40/s (broadcast room)
* Push Message: {"test":1}
* Received calc mode: 1s per times, total 30 times
### Benchmark Resource
* CPU: 2000%~2300%
* Memory: 14GB
* GC Pause: 504ms
* Network: Incoming(450MBit/s), Outgoing(4.39GBit/s)
### Benchmark Result
* Received: 35,900,000/s
[中文](./docs/benchmark_cn.md)
[English](./docs/benchmark_en.md)
## LICENSE
goim is is distributed under the terms of the MIT License.
================================================
FILE: README_cn.md
================================================
goim v2.0
==============
`Terry-Mao/goim` 是一个支持集群的im及实时推送服务。
---------------------------------------
* [特性](#特性)
* [安装](#安装)
* [配置](#配置)
* [例子](#例子)
* [文档](#文档)
* [集群](#集群)
* [更多](#更多)
---------------------------------------
## 特性
* 轻量级
* 高性能
* 纯Golang实现
* 支持单个、多个、单房间以及广播消息推送
* 支持单个Key多个订阅者(可限制订阅者最大人数)
* 心跳支持(应用心跳和tcp、keepalive)
* 支持安全验证(未授权用户不能订阅)
* 多协议支持(websocket,tcp)
* 可拓扑的架构(job、logic模块可动态无限扩展)
* 基于Kafka做异步消息推送
## 安装
### 一、安装依赖
```sh
$ yum -y install java-1.7.0-openjdk
```
### 二、安装Kafka消息队列服务
kafka在官网已经描述的非常详细,在这里就不过多说明,安装、启动请查看[这里](http://kafka.apache.org/documentation.html#quickstart).
### 三、搭建golang环境
1.下载源码(根据自己的系统下载对应的[安装包](http://golang.org/dl/))
```sh
$ cd /data/programfiles
$ wget -c --no-check-certificate https://storage.googleapis.com/golang/go1.5.2.linux-amd64.tar.gz
$ tar -xvf go1.5.2.linux-amd64.tar.gz -C /usr/local
```
2.配置GO环境变量
(这里我加在/etc/profile.d/golang.sh)
```sh
$ vi /etc/profile.d/golang.sh
# 将以下环境变量添加到profile最后面
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
export GOPATH=/data/apps/go
$ source /etc/profile
```
### 四、部署goim
1.下载goim及依赖包
```sh
$ yum install hg
$ go get -u github.com/Terry-Mao/goim
$ mv $GOPATH/src/github.com/Terry-Mao/goim $GOPATH/src/goim
$ cd $GOPATH/src/goim
$ go get ./...
```
2.安装router、logic、comet、job模块(配置文件请依据实际机器环境配置)
```sh
$ cd $GOPATH/src/goim/router
$ go install
$ cp router-example.conf $GOPATH/bin/router.conf
$ cp router-log.xml $GOPATH/bin/
$ cd ../logic/
$ go install
$ cp logic-example.conf $GOPATH/bin/logic.conf
$ cp logic-log.xml $GOPATH/bin/
$ cd ../comet/
$ go install
$ cp comet-example.conf $GOPATH/bin/comet.conf
$ cp comet-log.xml $GOPATH/bin/
$ cd ../logic/job/
$ go install
$ cp job-example.conf $GOPATH/bin/job.conf
$ cp job-log.xml $GOPATH/bin/
```
到此所有的环境都搭建完成!
### 五、启动goim
```sh
$ cd /$GOPATH/bin
$ nohup $GOPATH/bin/router -c $GOPATH/bin/router.conf 2>&1 > /data/logs/goim/panic-router.log &
$ nohup $GOPATH/bin/logic -c $GOPATH/bin/logic.conf 2>&1 > /data/logs/goim/panic-logic.log &
$ nohup $GOPATH/bin/comet -c $GOPATH/bin/comet.conf 2>&1 > /data/logs/goim/panic-comet.log &
$ nohup $GOPATH/bin/job -c $GOPATH/bin/job.conf 2>&1 > /data/logs/goim/panic-job.log &
```
如果启动失败,默认配置可通过查看panic-xxx.log日志文件来排查各个模块问题.
### 六、测试
推送协议可查看[push http协议文档](./docs/push.md)
## 配置
TODO
## 例子
Websocket: [Websocket Client Demo](https://github.com/Terry-Mao/goim/tree/master/examples/javascript)
Android: [Android](https://github.com/roamdy/goim-sdk)
iOS: [iOS](https://github.com/roamdy/goim-oc-sdk)
## 文档
[push http协议文档](./docs/push.md)推送接口
## 集群
### comet
comet 属于接入层,非常容易扩展,直接开启多个comet节点,修改配置文件中的base节点下的server.id修改成不同值(注意一定要保证不同的comet进程值唯一),前端接入可以使用LVS 或者 DNS来转发
### logic
logic 属于无状态的逻辑层,可以随意增加节点,使用nginx upstream来扩展http接口,内部rpc部分,可以使用LVS四层转发
### kafka
kafka 可以使用多broker,或者多partition来扩展队列
### router
router 属于有状态节点,logic可以使用一致性hash配置节点,增加多个router节点(目前还不支持动态扩容),提前预估好在线和压力情况
### job
job 根据kafka的partition来扩展多job工作方式,具体可以参考下kafka的partition负载
## 更多
TODO
================================================
FILE: README_en.md
================================================
goim
==============
`Terry-Mao/goim` is a IM and push notification server cluster.
---------------------------------------
* [Features](#features)
* [Installing](#installing)
* [Configurations](#configurations)
* [Examples](#examples)
* [Documents](#documents)
* [More](#more)
---------------------------------------
## Features
* Light weight
* High performance
* Pure Golang
* Supports single push, multiple push, room push and broadcasting
* Supports one key to multiple subscribers (Configurable maximum subscribers count)
* Supports heartbeats (Application heartbeats, TCP, KeepAlive)
* Supports authentication (Unauthenticated user can't subscribe)
* Supports multiple protocols (WebSocket,TCP)
* Scalable architecture (Unlimited dynamic job and logic modules)
* Asynchronous push notification based on Kafka
## Installing
### Dependencies
```sh
$ yum -y install java-1.7.0-openjdk
```
### Install Kafka
Please follow the official quick start [here](http://kafka.apache.org/documentation.html#quickstart).
### Install Golang environment
Please follow the official quick start [here](https://golang.org/doc/install).
### Deploy goim
1.Download goim
```sh
$ yum install git
$ cd $GOPATH/src
$ git clone https://github.com/Terry-Mao/goim.git
$ cd $GOPATH/src/goim
$ go get ./...
```
2.Install router、logic、comet、job modules(You might need to change the configuration files based on your servers)
```sh
$ cd $GOPATH/src/goim/router
$ go install
$ cp router-example.conf $GOPATH/bin/router.conf
$ cp router-log.xml $GOPATH/bin/
$ cd ../logic/
$ go install
$ cp logic-example.conf $GOPATH/bin/logic.conf
$ cp logic-log.xml $GOPATH/bin/
$ cd ../comet/
$ go install
$ cp comet-example.conf $GOPATH/bin/comet.conf
$ cp comet-log.xml $GOPATH/bin/
$ cd ../logic/job/
$ go install
$ cp job-example.conf $GOPATH/bin/job.conf
$ cp job-log.xml $GOPATH/bin/
```
Everything is DONE!
### Run goim
You may need to change the log files location.
```sh
$ cd /$GOPATH/bin
$ nohup $GOPATH/bin/router -c $GOPATH/bin/router.conf 2>&1 > /data/logs/goim/panic-router.log &
$ nohup $GOPATH/bin/logic -c $GOPATH/bin/logic.conf 2>&1 > /data/logs/goim/panic-logic.log &
$ nohup $GOPATH/bin/comet -c $GOPATH/bin/comet.conf 2>&1 > /data/logs/goim/panic-comet.log &
$ nohup $GOPATH/bin/job -c $GOPATH/bin/job.conf 2>&1 > /data/logs/goim/panic-job.log &
```
If it fails, please check the logs for debugging.
### Testing
Check the push protocols here[push HTTP protocols](./docs/push.md)
## Configurations
TODO
## Examples
Websocket: [Websocket Client Demo](https://github.com/Terry-Mao/goim/tree/master/examples/javascript)
Android: [Android SDK](https://github.com/roamdy/goim-sdk)
iOS: [iOS](https://github.com/roamdy/goim-oc-sdk)
## Documents
[push HTTP protocols](./docs/en/push.md)
[Comet client protocols](./docs/en/proto.md)
##More
TODO
================================================
FILE: api/comet/comet.pb.go
================================================
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: comet/comet.proto
package comet
import (
context "context"
fmt "fmt"
protocol "github.com/Terry-Mao/goim/api/protocol"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type PushMsgReq struct {
Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"`
ProtoOp int32 `protobuf:"varint,3,opt,name=protoOp,proto3" json:"protoOp,omitempty"`
Proto *protocol.Proto `protobuf:"bytes,2,opt,name=proto,proto3" json:"proto,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PushMsgReq) Reset() { *m = PushMsgReq{} }
func (m *PushMsgReq) String() string { return proto.CompactTextString(m) }
func (*PushMsgReq) ProtoMessage() {}
func (*PushMsgReq) Descriptor() ([]byte, []int) {
return fileDescriptor_327b4a7d084564be, []int{0}
}
func (m *PushMsgReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PushMsgReq.Unmarshal(m, b)
}
func (m *PushMsgReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PushMsgReq.Marshal(b, m, deterministic)
}
func (m *PushMsgReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_PushMsgReq.Merge(m, src)
}
func (m *PushMsgReq) XXX_Size() int {
return xxx_messageInfo_PushMsgReq.Size(m)
}
func (m *PushMsgReq) XXX_DiscardUnknown() {
xxx_messageInfo_PushMsgReq.DiscardUnknown(m)
}
var xxx_messageInfo_PushMsgReq proto.InternalMessageInfo
func (m *PushMsgReq) GetKeys() []string {
if m != nil {
return m.Keys
}
return nil
}
func (m *PushMsgReq) GetProtoOp() int32 {
if m != nil {
return m.ProtoOp
}
return 0
}
func (m *PushMsgReq) GetProto() *protocol.Proto {
if m != nil {
return m.Proto
}
return nil
}
type PushMsgReply struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PushMsgReply) Reset() { *m = PushMsgReply{} }
func (m *PushMsgReply) String() string { return proto.CompactTextString(m) }
func (*PushMsgReply) ProtoMessage() {}
func (*PushMsgReply) Descriptor() ([]byte, []int) {
return fileDescriptor_327b4a7d084564be, []int{1}
}
func (m *PushMsgReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PushMsgReply.Unmarshal(m, b)
}
func (m *PushMsgReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PushMsgReply.Marshal(b, m, deterministic)
}
func (m *PushMsgReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_PushMsgReply.Merge(m, src)
}
func (m *PushMsgReply) XXX_Size() int {
return xxx_messageInfo_PushMsgReply.Size(m)
}
func (m *PushMsgReply) XXX_DiscardUnknown() {
xxx_messageInfo_PushMsgReply.DiscardUnknown(m)
}
var xxx_messageInfo_PushMsgReply proto.InternalMessageInfo
type BroadcastReq struct {
ProtoOp int32 `protobuf:"varint,1,opt,name=protoOp,proto3" json:"protoOp,omitempty"`
Proto *protocol.Proto `protobuf:"bytes,2,opt,name=proto,proto3" json:"proto,omitempty"`
Speed int32 `protobuf:"varint,3,opt,name=speed,proto3" json:"speed,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BroadcastReq) Reset() { *m = BroadcastReq{} }
func (m *BroadcastReq) String() string { return proto.CompactTextString(m) }
func (*BroadcastReq) ProtoMessage() {}
func (*BroadcastReq) Descriptor() ([]byte, []int) {
return fileDescriptor_327b4a7d084564be, []int{2}
}
func (m *BroadcastReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BroadcastReq.Unmarshal(m, b)
}
func (m *BroadcastReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BroadcastReq.Marshal(b, m, deterministic)
}
func (m *BroadcastReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_BroadcastReq.Merge(m, src)
}
func (m *BroadcastReq) XXX_Size() int {
return xxx_messageInfo_BroadcastReq.Size(m)
}
func (m *BroadcastReq) XXX_DiscardUnknown() {
xxx_messageInfo_BroadcastReq.DiscardUnknown(m)
}
var xxx_messageInfo_BroadcastReq proto.InternalMessageInfo
func (m *BroadcastReq) GetProtoOp() int32 {
if m != nil {
return m.ProtoOp
}
return 0
}
func (m *BroadcastReq) GetProto() *protocol.Proto {
if m != nil {
return m.Proto
}
return nil
}
func (m *BroadcastReq) GetSpeed() int32 {
if m != nil {
return m.Speed
}
return 0
}
type BroadcastReply struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BroadcastReply) Reset() { *m = BroadcastReply{} }
func (m *BroadcastReply) String() string { return proto.CompactTextString(m) }
func (*BroadcastReply) ProtoMessage() {}
func (*BroadcastReply) Descriptor() ([]byte, []int) {
return fileDescriptor_327b4a7d084564be, []int{3}
}
func (m *BroadcastReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BroadcastReply.Unmarshal(m, b)
}
func (m *BroadcastReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BroadcastReply.Marshal(b, m, deterministic)
}
func (m *BroadcastReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_BroadcastReply.Merge(m, src)
}
func (m *BroadcastReply) XXX_Size() int {
return xxx_messageInfo_BroadcastReply.Size(m)
}
func (m *BroadcastReply) XXX_DiscardUnknown() {
xxx_messageInfo_BroadcastReply.DiscardUnknown(m)
}
var xxx_messageInfo_BroadcastReply proto.InternalMessageInfo
type BroadcastRoomReq struct {
RoomID string `protobuf:"bytes,1,opt,name=roomID,proto3" json:"roomID,omitempty"`
Proto *protocol.Proto `protobuf:"bytes,2,opt,name=proto,proto3" json:"proto,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BroadcastRoomReq) Reset() { *m = BroadcastRoomReq{} }
func (m *BroadcastRoomReq) String() string { return proto.CompactTextString(m) }
func (*BroadcastRoomReq) ProtoMessage() {}
func (*BroadcastRoomReq) Descriptor() ([]byte, []int) {
return fileDescriptor_327b4a7d084564be, []int{4}
}
func (m *BroadcastRoomReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BroadcastRoomReq.Unmarshal(m, b)
}
func (m *BroadcastRoomReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BroadcastRoomReq.Marshal(b, m, deterministic)
}
func (m *BroadcastRoomReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_BroadcastRoomReq.Merge(m, src)
}
func (m *BroadcastRoomReq) XXX_Size() int {
return xxx_messageInfo_BroadcastRoomReq.Size(m)
}
func (m *BroadcastRoomReq) XXX_DiscardUnknown() {
xxx_messageInfo_BroadcastRoomReq.DiscardUnknown(m)
}
var xxx_messageInfo_BroadcastRoomReq proto.InternalMessageInfo
func (m *BroadcastRoomReq) GetRoomID() string {
if m != nil {
return m.RoomID
}
return ""
}
func (m *BroadcastRoomReq) GetProto() *protocol.Proto {
if m != nil {
return m.Proto
}
return nil
}
type BroadcastRoomReply struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BroadcastRoomReply) Reset() { *m = BroadcastRoomReply{} }
func (m *BroadcastRoomReply) String() string { return proto.CompactTextString(m) }
func (*BroadcastRoomReply) ProtoMessage() {}
func (*BroadcastRoomReply) Descriptor() ([]byte, []int) {
return fileDescriptor_327b4a7d084564be, []int{5}
}
func (m *BroadcastRoomReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BroadcastRoomReply.Unmarshal(m, b)
}
func (m *BroadcastRoomReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BroadcastRoomReply.Marshal(b, m, deterministic)
}
func (m *BroadcastRoomReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_BroadcastRoomReply.Merge(m, src)
}
func (m *BroadcastRoomReply) XXX_Size() int {
return xxx_messageInfo_BroadcastRoomReply.Size(m)
}
func (m *BroadcastRoomReply) XXX_DiscardUnknown() {
xxx_messageInfo_BroadcastRoomReply.DiscardUnknown(m)
}
var xxx_messageInfo_BroadcastRoomReply proto.InternalMessageInfo
type RoomsReq struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RoomsReq) Reset() { *m = RoomsReq{} }
func (m *RoomsReq) String() string { return proto.CompactTextString(m) }
func (*RoomsReq) ProtoMessage() {}
func (*RoomsReq) Descriptor() ([]byte, []int) {
return fileDescriptor_327b4a7d084564be, []int{6}
}
func (m *RoomsReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RoomsReq.Unmarshal(m, b)
}
func (m *RoomsReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RoomsReq.Marshal(b, m, deterministic)
}
func (m *RoomsReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_RoomsReq.Merge(m, src)
}
func (m *RoomsReq) XXX_Size() int {
return xxx_messageInfo_RoomsReq.Size(m)
}
func (m *RoomsReq) XXX_DiscardUnknown() {
xxx_messageInfo_RoomsReq.DiscardUnknown(m)
}
var xxx_messageInfo_RoomsReq proto.InternalMessageInfo
type RoomsReply struct {
Rooms map[string]bool `protobuf:"bytes,1,rep,name=rooms,proto3" json:"rooms,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RoomsReply) Reset() { *m = RoomsReply{} }
func (m *RoomsReply) String() string { return proto.CompactTextString(m) }
func (*RoomsReply) ProtoMessage() {}
func (*RoomsReply) Descriptor() ([]byte, []int) {
return fileDescriptor_327b4a7d084564be, []int{7}
}
func (m *RoomsReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RoomsReply.Unmarshal(m, b)
}
func (m *RoomsReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RoomsReply.Marshal(b, m, deterministic)
}
func (m *RoomsReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_RoomsReply.Merge(m, src)
}
func (m *RoomsReply) XXX_Size() int {
return xxx_messageInfo_RoomsReply.Size(m)
}
func (m *RoomsReply) XXX_DiscardUnknown() {
xxx_messageInfo_RoomsReply.DiscardUnknown(m)
}
var xxx_messageInfo_RoomsReply proto.InternalMessageInfo
func (m *RoomsReply) GetRooms() map[string]bool {
if m != nil {
return m.Rooms
}
return nil
}
func init() {
proto.RegisterType((*PushMsgReq)(nil), "goim.comet.PushMsgReq")
proto.RegisterType((*PushMsgReply)(nil), "goim.comet.PushMsgReply")
proto.RegisterType((*BroadcastReq)(nil), "goim.comet.BroadcastReq")
proto.RegisterType((*BroadcastReply)(nil), "goim.comet.BroadcastReply")
proto.RegisterType((*BroadcastRoomReq)(nil), "goim.comet.BroadcastRoomReq")
proto.RegisterType((*BroadcastRoomReply)(nil), "goim.comet.BroadcastRoomReply")
proto.RegisterType((*RoomsReq)(nil), "goim.comet.RoomsReq")
proto.RegisterType((*RoomsReply)(nil), "goim.comet.RoomsReply")
proto.RegisterMapType((map[string]bool)(nil), "goim.comet.RoomsReply.RoomsEntry")
}
func init() { proto.RegisterFile("comet/comet.proto", fileDescriptor_327b4a7d084564be) }
var fileDescriptor_327b4a7d084564be = []byte{
// 404 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0xcf, 0x4f, 0xe2, 0x40,
0x14, 0x4e, 0x61, 0xcb, 0x8f, 0x07, 0x4b, 0xd8, 0x49, 0x43, 0x9a, 0x66, 0xb3, 0x61, 0x7b, 0x62,
0xd7, 0xd8, 0x26, 0x18, 0x22, 0x91, 0x93, 0xa8, 0x07, 0x0f, 0x44, 0x32, 0x31, 0x1e, 0xbc, 0x15,
0x18, 0x01, 0x69, 0x9d, 0xd2, 0x16, 0x93, 0x9e, 0xfc, 0x0b, 0xfc, 0x9f, 0xcd, 0x9b, 0xa9, 0x6d,
0x31, 0x95, 0x44, 0x2f, 0xcd, 0xf7, 0xde, 0x7c, 0xf3, 0xbe, 0x6f, 0xbe, 0x07, 0xf0, 0x6b, 0xce,
0x3d, 0x16, 0xd9, 0xe2, 0x6b, 0xf9, 0x01, 0x8f, 0x38, 0x81, 0x25, 0x5f, 0x7b, 0x96, 0xe8, 0x18,
0x83, 0xe5, 0x3a, 0x5a, 0xed, 0x66, 0x58, 0xd9, 0xb7, 0x2c, 0x08, 0xe2, 0xe3, 0x89, 0xc3, 0x6d,
0x24, 0xd8, 0x8e, 0xbf, 0xb6, 0xc5, 0x85, 0x39, 0x77, 0x53, 0x20, 0x47, 0x98, 0x0f, 0x00, 0xd3,
0x5d, 0xb8, 0x9a, 0x84, 0x4b, 0xca, 0xb6, 0x84, 0xc0, 0x8f, 0x0d, 0x8b, 0x43, 0x5d, 0xe9, 0x96,
0x7b, 0x75, 0x2a, 0x30, 0xd1, 0xa1, 0x2a, 0xa8, 0x37, 0xbe, 0x5e, 0xee, 0x2a, 0x3d, 0x95, 0xbe,
0x97, 0xe4, 0x3f, 0xa8, 0x02, 0xea, 0xa5, 0xae, 0xd2, 0x6b, 0xf4, 0x35, 0x4b, 0xd8, 0x49, 0x05,
0xa6, 0x08, 0xa8, 0xa4, 0x98, 0x2d, 0x68, 0xa6, 0x3a, 0xbe, 0x1b, 0x9b, 0x8f, 0xd0, 0x1c, 0x07,
0xdc, 0x59, 0xcc, 0x9d, 0x30, 0x42, 0xe5, 0x9c, 0x8a, 0xf2, 0x6d, 0x15, 0xa2, 0x81, 0x1a, 0xfa,
0x8c, 0x2d, 0x12, 0xa7, 0xb2, 0x30, 0xdb, 0xd0, 0xca, 0x69, 0xa1, 0xfa, 0x1d, 0xb4, 0xb3, 0x0e,
0xe7, 0x1e, 0x3a, 0xe8, 0x40, 0x25, 0xe0, 0xdc, 0xbb, 0xbe, 0x14, 0x06, 0xea, 0x34, 0xa9, 0xbe,
0xf4, 0x4a, 0x0d, 0xc8, 0x87, 0xb9, 0xa8, 0x06, 0x50, 0xc3, 0x22, 0xa4, 0x6c, 0x6b, 0xbe, 0x00,
0x24, 0xd8, 0x77, 0x63, 0x72, 0x0a, 0x2a, 0xaa, 0xc8, 0xc0, 0x1b, 0xfd, 0xbf, 0x56, 0xb6, 0x50,
0x2b, 0xa3, 0x49, 0x78, 0xf5, 0x14, 0x05, 0x31, 0x95, 0x7c, 0x63, 0x98, 0x8c, 0x11, 0x4d, 0xd2,
0x86, 0xf2, 0x86, 0xc5, 0x89, 0x6f, 0x84, 0x18, 0xc4, 0xb3, 0xe3, 0xee, 0x98, 0x30, 0x5d, 0xa3,
0xb2, 0x38, 0x2b, 0x0d, 0x95, 0xfe, 0x6b, 0x09, 0xd4, 0x0b, 0x14, 0x20, 0x23, 0xa8, 0x26, 0x2b,
0x21, 0x9d, 0xbc, 0x70, 0xf6, 0x7b, 0x30, 0xf4, 0xc2, 0x3e, 0x3a, 0x3f, 0x87, 0x7a, 0xfa, 0x52,
0xb2, 0x47, 0xcb, 0xaf, 0xd5, 0x30, 0x3e, 0x39, 0xc1, 0x11, 0x13, 0xf8, 0xb9, 0x17, 0x16, 0xf9,
0x5d, 0x4c, 0x96, 0xfb, 0x31, 0xfe, 0x1c, 0x38, 0xc5, 0x71, 0x03, 0x50, 0x45, 0x24, 0x44, 0x2b,
0x48, 0x71, 0x6b, 0x74, 0x8a, 0xb3, 0x1d, 0x1f, 0xdd, 0xff, 0x3b, 0xfc, 0xcf, 0x11, 0x37, 0x46,
0xe2, 0x3b, 0xab, 0x88, 0x35, 0x9f, 0xbc, 0x05, 0x00, 0x00, 0xff, 0xff, 0xb3, 0xcb, 0xf3, 0x2a,
0x8c, 0x03, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// CometClient is the client API for Comet service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type CometClient interface {
// PushMsg push by key or mid
PushMsg(ctx context.Context, in *PushMsgReq, opts ...grpc.CallOption) (*PushMsgReply, error)
// Broadcast send to every enrity
Broadcast(ctx context.Context, in *BroadcastReq, opts ...grpc.CallOption) (*BroadcastReply, error)
// BroadcastRoom broadcast to one room
BroadcastRoom(ctx context.Context, in *BroadcastRoomReq, opts ...grpc.CallOption) (*BroadcastRoomReply, error)
// Rooms get all rooms
Rooms(ctx context.Context, in *RoomsReq, opts ...grpc.CallOption) (*RoomsReply, error)
}
type cometClient struct {
cc *grpc.ClientConn
}
func NewCometClient(cc *grpc.ClientConn) CometClient {
return &cometClient{cc}
}
func (c *cometClient) PushMsg(ctx context.Context, in *PushMsgReq, opts ...grpc.CallOption) (*PushMsgReply, error) {
out := new(PushMsgReply)
err := c.cc.Invoke(ctx, "/goim.comet.Comet/PushMsg", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *cometClient) Broadcast(ctx context.Context, in *BroadcastReq, opts ...grpc.CallOption) (*BroadcastReply, error) {
out := new(BroadcastReply)
err := c.cc.Invoke(ctx, "/goim.comet.Comet/Broadcast", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *cometClient) BroadcastRoom(ctx context.Context, in *BroadcastRoomReq, opts ...grpc.CallOption) (*BroadcastRoomReply, error) {
out := new(BroadcastRoomReply)
err := c.cc.Invoke(ctx, "/goim.comet.Comet/BroadcastRoom", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *cometClient) Rooms(ctx context.Context, in *RoomsReq, opts ...grpc.CallOption) (*RoomsReply, error) {
out := new(RoomsReply)
err := c.cc.Invoke(ctx, "/goim.comet.Comet/Rooms", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// CometServer is the server API for Comet service.
type CometServer interface {
// PushMsg push by key or mid
PushMsg(context.Context, *PushMsgReq) (*PushMsgReply, error)
// Broadcast send to every enrity
Broadcast(context.Context, *BroadcastReq) (*BroadcastReply, error)
// BroadcastRoom broadcast to one room
BroadcastRoom(context.Context, *BroadcastRoomReq) (*BroadcastRoomReply, error)
// Rooms get all rooms
Rooms(context.Context, *RoomsReq) (*RoomsReply, error)
}
// UnimplementedCometServer can be embedded to have forward compatible implementations.
type UnimplementedCometServer struct {
}
func (*UnimplementedCometServer) PushMsg(ctx context.Context, req *PushMsgReq) (*PushMsgReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method PushMsg not implemented")
}
func (*UnimplementedCometServer) Broadcast(ctx context.Context, req *BroadcastReq) (*BroadcastReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Broadcast not implemented")
}
func (*UnimplementedCometServer) BroadcastRoom(ctx context.Context, req *BroadcastRoomReq) (*BroadcastRoomReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method BroadcastRoom not implemented")
}
func (*UnimplementedCometServer) Rooms(ctx context.Context, req *RoomsReq) (*RoomsReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Rooms not implemented")
}
func RegisterCometServer(s *grpc.Server, srv CometServer) {
s.RegisterService(&_Comet_serviceDesc, srv)
}
func _Comet_PushMsg_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PushMsgReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CometServer).PushMsg(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/goim.comet.Comet/PushMsg",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CometServer).PushMsg(ctx, req.(*PushMsgReq))
}
return interceptor(ctx, in, info, handler)
}
func _Comet_Broadcast_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BroadcastReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CometServer).Broadcast(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/goim.comet.Comet/Broadcast",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CometServer).Broadcast(ctx, req.(*BroadcastReq))
}
return interceptor(ctx, in, info, handler)
}
func _Comet_BroadcastRoom_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BroadcastRoomReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CometServer).BroadcastRoom(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/goim.comet.Comet/BroadcastRoom",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CometServer).BroadcastRoom(ctx, req.(*BroadcastRoomReq))
}
return interceptor(ctx, in, info, handler)
}
func _Comet_Rooms_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RoomsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CometServer).Rooms(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/goim.comet.Comet/Rooms",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CometServer).Rooms(ctx, req.(*RoomsReq))
}
return interceptor(ctx, in, info, handler)
}
var _Comet_serviceDesc = grpc.ServiceDesc{
ServiceName: "goim.comet.Comet",
HandlerType: (*CometServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "PushMsg",
Handler: _Comet_PushMsg_Handler,
},
{
MethodName: "Broadcast",
Handler: _Comet_Broadcast_Handler,
},
{
MethodName: "BroadcastRoom",
Handler: _Comet_BroadcastRoom_Handler,
},
{
MethodName: "Rooms",
Handler: _Comet_Rooms_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "comet/comet.proto",
}
================================================
FILE: api/comet/comet.proto
================================================
syntax = "proto3";
package goim.comet;
option go_package = "github.com/Terry-Mao/goim/api/comet;comet";
import "github.com/Terry-Mao/goim/api/protocol/protocol.proto";
message PushMsgReq {
repeated string keys = 1;
int32 protoOp = 3;
goim.protocol.Proto proto = 2;
}
message PushMsgReply {}
message BroadcastReq{
int32 protoOp = 1;
goim.protocol.Proto proto = 2;
int32 speed = 3;
}
message BroadcastReply{}
message BroadcastRoomReq {
string roomID = 1;
goim.protocol.Proto proto = 2;
}
message BroadcastRoomReply{}
message RoomsReq{}
message RoomsReply {
map<string,bool> rooms = 1;
}
service Comet {
// PushMsg push by key or mid
rpc PushMsg(PushMsgReq) returns (PushMsgReply);
// Broadcast send to every enrity
rpc Broadcast(BroadcastReq) returns (BroadcastReply);
// BroadcastRoom broadcast to one room
rpc BroadcastRoom(BroadcastRoomReq) returns (BroadcastRoomReply);
// Rooms get all rooms
rpc Rooms(RoomsReq) returns (RoomsReply);
}
================================================
FILE: api/generate.go
================================================
package api
//go:generate protoc -I. -I$GOPATH/src --go_out=plugins=grpc:. --go_opt=paths=source_relative protocol/protocol.proto
//go:generate protoc -I. -I$GOPATH/src --go_out=plugins=grpc:. --go_opt=paths=source_relative comet/comet.proto
//go:generate protoc -I. -I$GOPATH/src --go_out=plugins=grpc:. --go_opt=paths=source_relative logic/logic.proto
================================================
FILE: api/logic/logic.pb.go
================================================
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: logic/logic.proto
package logic
import (
context "context"
fmt "fmt"
protocol "github.com/Terry-Mao/goim/api/protocol"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type PushMsg_Type int32
const (
PushMsg_PUSH PushMsg_Type = 0
PushMsg_ROOM PushMsg_Type = 1
PushMsg_BROADCAST PushMsg_Type = 2
)
var PushMsg_Type_name = map[int32]string{
0: "PUSH",
1: "ROOM",
2: "BROADCAST",
}
var PushMsg_Type_value = map[string]int32{
"PUSH": 0,
"ROOM": 1,
"BROADCAST": 2,
}
func (x PushMsg_Type) String() string {
return proto.EnumName(PushMsg_Type_name, int32(x))
}
func (PushMsg_Type) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{0, 0}
}
type PushMsg struct {
Type PushMsg_Type `protobuf:"varint,1,opt,name=type,proto3,enum=goim.logic.PushMsg_Type" json:"type,omitempty"`
Operation int32 `protobuf:"varint,2,opt,name=operation,proto3" json:"operation,omitempty"`
Speed int32 `protobuf:"varint,3,opt,name=speed,proto3" json:"speed,omitempty"`
Server string `protobuf:"bytes,4,opt,name=server,proto3" json:"server,omitempty"`
Room string `protobuf:"bytes,5,opt,name=room,proto3" json:"room,omitempty"`
Keys []string `protobuf:"bytes,6,rep,name=keys,proto3" json:"keys,omitempty"`
Msg []byte `protobuf:"bytes,7,opt,name=msg,proto3" json:"msg,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PushMsg) Reset() { *m = PushMsg{} }
func (m *PushMsg) String() string { return proto.CompactTextString(m) }
func (*PushMsg) ProtoMessage() {}
func (*PushMsg) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{0}
}
func (m *PushMsg) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PushMsg.Unmarshal(m, b)
}
func (m *PushMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PushMsg.Marshal(b, m, deterministic)
}
func (m *PushMsg) XXX_Merge(src proto.Message) {
xxx_messageInfo_PushMsg.Merge(m, src)
}
func (m *PushMsg) XXX_Size() int {
return xxx_messageInfo_PushMsg.Size(m)
}
func (m *PushMsg) XXX_DiscardUnknown() {
xxx_messageInfo_PushMsg.DiscardUnknown(m)
}
var xxx_messageInfo_PushMsg proto.InternalMessageInfo
func (m *PushMsg) GetType() PushMsg_Type {
if m != nil {
return m.Type
}
return PushMsg_PUSH
}
func (m *PushMsg) GetOperation() int32 {
if m != nil {
return m.Operation
}
return 0
}
func (m *PushMsg) GetSpeed() int32 {
if m != nil {
return m.Speed
}
return 0
}
func (m *PushMsg) GetServer() string {
if m != nil {
return m.Server
}
return ""
}
func (m *PushMsg) GetRoom() string {
if m != nil {
return m.Room
}
return ""
}
func (m *PushMsg) GetKeys() []string {
if m != nil {
return m.Keys
}
return nil
}
func (m *PushMsg) GetMsg() []byte {
if m != nil {
return m.Msg
}
return nil
}
type ConnectReq struct {
Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
Cookie string `protobuf:"bytes,2,opt,name=cookie,proto3" json:"cookie,omitempty"`
Token []byte `protobuf:"bytes,3,opt,name=token,proto3" json:"token,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ConnectReq) Reset() { *m = ConnectReq{} }
func (m *ConnectReq) String() string { return proto.CompactTextString(m) }
func (*ConnectReq) ProtoMessage() {}
func (*ConnectReq) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{1}
}
func (m *ConnectReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConnectReq.Unmarshal(m, b)
}
func (m *ConnectReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ConnectReq.Marshal(b, m, deterministic)
}
func (m *ConnectReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_ConnectReq.Merge(m, src)
}
func (m *ConnectReq) XXX_Size() int {
return xxx_messageInfo_ConnectReq.Size(m)
}
func (m *ConnectReq) XXX_DiscardUnknown() {
xxx_messageInfo_ConnectReq.DiscardUnknown(m)
}
var xxx_messageInfo_ConnectReq proto.InternalMessageInfo
func (m *ConnectReq) GetServer() string {
if m != nil {
return m.Server
}
return ""
}
func (m *ConnectReq) GetCookie() string {
if m != nil {
return m.Cookie
}
return ""
}
func (m *ConnectReq) GetToken() []byte {
if m != nil {
return m.Token
}
return nil
}
type ConnectReply struct {
Mid int64 `protobuf:"varint,1,opt,name=mid,proto3" json:"mid,omitempty"`
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
RoomID string `protobuf:"bytes,3,opt,name=roomID,proto3" json:"roomID,omitempty"`
Accepts []int32 `protobuf:"varint,4,rep,packed,name=accepts,proto3" json:"accepts,omitempty"`
Heartbeat int64 `protobuf:"varint,5,opt,name=heartbeat,proto3" json:"heartbeat,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ConnectReply) Reset() { *m = ConnectReply{} }
func (m *ConnectReply) String() string { return proto.CompactTextString(m) }
func (*ConnectReply) ProtoMessage() {}
func (*ConnectReply) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{2}
}
func (m *ConnectReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConnectReply.Unmarshal(m, b)
}
func (m *ConnectReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ConnectReply.Marshal(b, m, deterministic)
}
func (m *ConnectReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_ConnectReply.Merge(m, src)
}
func (m *ConnectReply) XXX_Size() int {
return xxx_messageInfo_ConnectReply.Size(m)
}
func (m *ConnectReply) XXX_DiscardUnknown() {
xxx_messageInfo_ConnectReply.DiscardUnknown(m)
}
var xxx_messageInfo_ConnectReply proto.InternalMessageInfo
func (m *ConnectReply) GetMid() int64 {
if m != nil {
return m.Mid
}
return 0
}
func (m *ConnectReply) GetKey() string {
if m != nil {
return m.Key
}
return ""
}
func (m *ConnectReply) GetRoomID() string {
if m != nil {
return m.RoomID
}
return ""
}
func (m *ConnectReply) GetAccepts() []int32 {
if m != nil {
return m.Accepts
}
return nil
}
func (m *ConnectReply) GetHeartbeat() int64 {
if m != nil {
return m.Heartbeat
}
return 0
}
type DisconnectReq struct {
Mid int64 `protobuf:"varint,1,opt,name=mid,proto3" json:"mid,omitempty"`
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
Server string `protobuf:"bytes,3,opt,name=server,proto3" json:"server,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DisconnectReq) Reset() { *m = DisconnectReq{} }
func (m *DisconnectReq) String() string { return proto.CompactTextString(m) }
func (*DisconnectReq) ProtoMessage() {}
func (*DisconnectReq) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{3}
}
func (m *DisconnectReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DisconnectReq.Unmarshal(m, b)
}
func (m *DisconnectReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DisconnectReq.Marshal(b, m, deterministic)
}
func (m *DisconnectReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_DisconnectReq.Merge(m, src)
}
func (m *DisconnectReq) XXX_Size() int {
return xxx_messageInfo_DisconnectReq.Size(m)
}
func (m *DisconnectReq) XXX_DiscardUnknown() {
xxx_messageInfo_DisconnectReq.DiscardUnknown(m)
}
var xxx_messageInfo_DisconnectReq proto.InternalMessageInfo
func (m *DisconnectReq) GetMid() int64 {
if m != nil {
return m.Mid
}
return 0
}
func (m *DisconnectReq) GetKey() string {
if m != nil {
return m.Key
}
return ""
}
func (m *DisconnectReq) GetServer() string {
if m != nil {
return m.Server
}
return ""
}
type DisconnectReply struct {
Has bool `protobuf:"varint,1,opt,name=has,proto3" json:"has,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DisconnectReply) Reset() { *m = DisconnectReply{} }
func (m *DisconnectReply) String() string { return proto.CompactTextString(m) }
func (*DisconnectReply) ProtoMessage() {}
func (*DisconnectReply) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{4}
}
func (m *DisconnectReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DisconnectReply.Unmarshal(m, b)
}
func (m *DisconnectReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DisconnectReply.Marshal(b, m, deterministic)
}
func (m *DisconnectReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_DisconnectReply.Merge(m, src)
}
func (m *DisconnectReply) XXX_Size() int {
return xxx_messageInfo_DisconnectReply.Size(m)
}
func (m *DisconnectReply) XXX_DiscardUnknown() {
xxx_messageInfo_DisconnectReply.DiscardUnknown(m)
}
var xxx_messageInfo_DisconnectReply proto.InternalMessageInfo
func (m *DisconnectReply) GetHas() bool {
if m != nil {
return m.Has
}
return false
}
type HeartbeatReq struct {
Mid int64 `protobuf:"varint,1,opt,name=mid,proto3" json:"mid,omitempty"`
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
Server string `protobuf:"bytes,3,opt,name=server,proto3" json:"server,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HeartbeatReq) Reset() { *m = HeartbeatReq{} }
func (m *HeartbeatReq) String() string { return proto.CompactTextString(m) }
func (*HeartbeatReq) ProtoMessage() {}
func (*HeartbeatReq) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{5}
}
func (m *HeartbeatReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HeartbeatReq.Unmarshal(m, b)
}
func (m *HeartbeatReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HeartbeatReq.Marshal(b, m, deterministic)
}
func (m *HeartbeatReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_HeartbeatReq.Merge(m, src)
}
func (m *HeartbeatReq) XXX_Size() int {
return xxx_messageInfo_HeartbeatReq.Size(m)
}
func (m *HeartbeatReq) XXX_DiscardUnknown() {
xxx_messageInfo_HeartbeatReq.DiscardUnknown(m)
}
var xxx_messageInfo_HeartbeatReq proto.InternalMessageInfo
func (m *HeartbeatReq) GetMid() int64 {
if m != nil {
return m.Mid
}
return 0
}
func (m *HeartbeatReq) GetKey() string {
if m != nil {
return m.Key
}
return ""
}
func (m *HeartbeatReq) GetServer() string {
if m != nil {
return m.Server
}
return ""
}
type HeartbeatReply struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HeartbeatReply) Reset() { *m = HeartbeatReply{} }
func (m *HeartbeatReply) String() string { return proto.CompactTextString(m) }
func (*HeartbeatReply) ProtoMessage() {}
func (*HeartbeatReply) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{6}
}
func (m *HeartbeatReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HeartbeatReply.Unmarshal(m, b)
}
func (m *HeartbeatReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HeartbeatReply.Marshal(b, m, deterministic)
}
func (m *HeartbeatReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_HeartbeatReply.Merge(m, src)
}
func (m *HeartbeatReply) XXX_Size() int {
return xxx_messageInfo_HeartbeatReply.Size(m)
}
func (m *HeartbeatReply) XXX_DiscardUnknown() {
xxx_messageInfo_HeartbeatReply.DiscardUnknown(m)
}
var xxx_messageInfo_HeartbeatReply proto.InternalMessageInfo
type OnlineReq struct {
Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
RoomCount map[string]int32 `protobuf:"bytes,2,rep,name=roomCount,proto3" json:"roomCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *OnlineReq) Reset() { *m = OnlineReq{} }
func (m *OnlineReq) String() string { return proto.CompactTextString(m) }
func (*OnlineReq) ProtoMessage() {}
func (*OnlineReq) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{7}
}
func (m *OnlineReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OnlineReq.Unmarshal(m, b)
}
func (m *OnlineReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_OnlineReq.Marshal(b, m, deterministic)
}
func (m *OnlineReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_OnlineReq.Merge(m, src)
}
func (m *OnlineReq) XXX_Size() int {
return xxx_messageInfo_OnlineReq.Size(m)
}
func (m *OnlineReq) XXX_DiscardUnknown() {
xxx_messageInfo_OnlineReq.DiscardUnknown(m)
}
var xxx_messageInfo_OnlineReq proto.InternalMessageInfo
func (m *OnlineReq) GetServer() string {
if m != nil {
return m.Server
}
return ""
}
func (m *OnlineReq) GetRoomCount() map[string]int32 {
if m != nil {
return m.RoomCount
}
return nil
}
type OnlineReply struct {
AllRoomCount map[string]int32 `protobuf:"bytes,1,rep,name=allRoomCount,proto3" json:"allRoomCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *OnlineReply) Reset() { *m = OnlineReply{} }
func (m *OnlineReply) String() string { return proto.CompactTextString(m) }
func (*OnlineReply) ProtoMessage() {}
func (*OnlineReply) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{8}
}
func (m *OnlineReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OnlineReply.Unmarshal(m, b)
}
func (m *OnlineReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_OnlineReply.Marshal(b, m, deterministic)
}
func (m *OnlineReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_OnlineReply.Merge(m, src)
}
func (m *OnlineReply) XXX_Size() int {
return xxx_messageInfo_OnlineReply.Size(m)
}
func (m *OnlineReply) XXX_DiscardUnknown() {
xxx_messageInfo_OnlineReply.DiscardUnknown(m)
}
var xxx_messageInfo_OnlineReply proto.InternalMessageInfo
func (m *OnlineReply) GetAllRoomCount() map[string]int32 {
if m != nil {
return m.AllRoomCount
}
return nil
}
type ReceiveReq struct {
Mid int64 `protobuf:"varint,1,opt,name=mid,proto3" json:"mid,omitempty"`
Proto *protocol.Proto `protobuf:"bytes,2,opt,name=proto,proto3" json:"proto,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReceiveReq) Reset() { *m = ReceiveReq{} }
func (m *ReceiveReq) String() string { return proto.CompactTextString(m) }
func (*ReceiveReq) ProtoMessage() {}
func (*ReceiveReq) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{9}
}
func (m *ReceiveReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReceiveReq.Unmarshal(m, b)
}
func (m *ReceiveReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReceiveReq.Marshal(b, m, deterministic)
}
func (m *ReceiveReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReceiveReq.Merge(m, src)
}
func (m *ReceiveReq) XXX_Size() int {
return xxx_messageInfo_ReceiveReq.Size(m)
}
func (m *ReceiveReq) XXX_DiscardUnknown() {
xxx_messageInfo_ReceiveReq.DiscardUnknown(m)
}
var xxx_messageInfo_ReceiveReq proto.InternalMessageInfo
func (m *ReceiveReq) GetMid() int64 {
if m != nil {
return m.Mid
}
return 0
}
func (m *ReceiveReq) GetProto() *protocol.Proto {
if m != nil {
return m.Proto
}
return nil
}
type ReceiveReply struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReceiveReply) Reset() { *m = ReceiveReply{} }
func (m *ReceiveReply) String() string { return proto.CompactTextString(m) }
func (*ReceiveReply) ProtoMessage() {}
func (*ReceiveReply) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{10}
}
func (m *ReceiveReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReceiveReply.Unmarshal(m, b)
}
func (m *ReceiveReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReceiveReply.Marshal(b, m, deterministic)
}
func (m *ReceiveReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReceiveReply.Merge(m, src)
}
func (m *ReceiveReply) XXX_Size() int {
return xxx_messageInfo_ReceiveReply.Size(m)
}
func (m *ReceiveReply) XXX_DiscardUnknown() {
xxx_messageInfo_ReceiveReply.DiscardUnknown(m)
}
var xxx_messageInfo_ReceiveReply proto.InternalMessageInfo
type NodesReq struct {
Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"`
ClientIP string `protobuf:"bytes,2,opt,name=clientIP,proto3" json:"clientIP,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NodesReq) Reset() { *m = NodesReq{} }
func (m *NodesReq) String() string { return proto.CompactTextString(m) }
func (*NodesReq) ProtoMessage() {}
func (*NodesReq) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{11}
}
func (m *NodesReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodesReq.Unmarshal(m, b)
}
func (m *NodesReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NodesReq.Marshal(b, m, deterministic)
}
func (m *NodesReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_NodesReq.Merge(m, src)
}
func (m *NodesReq) XXX_Size() int {
return xxx_messageInfo_NodesReq.Size(m)
}
func (m *NodesReq) XXX_DiscardUnknown() {
xxx_messageInfo_NodesReq.DiscardUnknown(m)
}
var xxx_messageInfo_NodesReq proto.InternalMessageInfo
func (m *NodesReq) GetPlatform() string {
if m != nil {
return m.Platform
}
return ""
}
func (m *NodesReq) GetClientIP() string {
if m != nil {
return m.ClientIP
}
return ""
}
type NodesReply struct {
Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"`
TcpPort int32 `protobuf:"varint,2,opt,name=tcp_port,json=tcpPort,proto3" json:"tcp_port,omitempty"`
WsPort int32 `protobuf:"varint,3,opt,name=ws_port,json=wsPort,proto3" json:"ws_port,omitempty"`
WssPort int32 `protobuf:"varint,4,opt,name=wss_port,json=wssPort,proto3" json:"wss_port,omitempty"`
Heartbeat int32 `protobuf:"varint,5,opt,name=heartbeat,proto3" json:"heartbeat,omitempty"`
Nodes []string `protobuf:"bytes,6,rep,name=nodes,proto3" json:"nodes,omitempty"`
Backoff *Backoff `protobuf:"bytes,7,opt,name=backoff,proto3" json:"backoff,omitempty"`
HeartbeatMax int32 `protobuf:"varint,8,opt,name=heartbeat_max,json=heartbeatMax,proto3" json:"heartbeat_max,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NodesReply) Reset() { *m = NodesReply{} }
func (m *NodesReply) String() string { return proto.CompactTextString(m) }
func (*NodesReply) ProtoMessage() {}
func (*NodesReply) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{12}
}
func (m *NodesReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodesReply.Unmarshal(m, b)
}
func (m *NodesReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NodesReply.Marshal(b, m, deterministic)
}
func (m *NodesReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_NodesReply.Merge(m, src)
}
func (m *NodesReply) XXX_Size() int {
return xxx_messageInfo_NodesReply.Size(m)
}
func (m *NodesReply) XXX_DiscardUnknown() {
xxx_messageInfo_NodesReply.DiscardUnknown(m)
}
var xxx_messageInfo_NodesReply proto.InternalMessageInfo
func (m *NodesReply) GetDomain() string {
if m != nil {
return m.Domain
}
return ""
}
func (m *NodesReply) GetTcpPort() int32 {
if m != nil {
return m.TcpPort
}
return 0
}
func (m *NodesReply) GetWsPort() int32 {
if m != nil {
return m.WsPort
}
return 0
}
func (m *NodesReply) GetWssPort() int32 {
if m != nil {
return m.WssPort
}
return 0
}
func (m *NodesReply) GetHeartbeat() int32 {
if m != nil {
return m.Heartbeat
}
return 0
}
func (m *NodesReply) GetNodes() []string {
if m != nil {
return m.Nodes
}
return nil
}
func (m *NodesReply) GetBackoff() *Backoff {
if m != nil {
return m.Backoff
}
return nil
}
func (m *NodesReply) GetHeartbeatMax() int32 {
if m != nil {
return m.HeartbeatMax
}
return 0
}
type Backoff struct {
MaxDelay int32 `protobuf:"varint,1,opt,name=max_delay,json=maxDelay,proto3" json:"max_delay,omitempty"`
BaseDelay int32 `protobuf:"varint,2,opt,name=base_delay,json=baseDelay,proto3" json:"base_delay,omitempty"`
Factor float32 `protobuf:"fixed32,3,opt,name=factor,proto3" json:"factor,omitempty"`
Jitter float32 `protobuf:"fixed32,4,opt,name=jitter,proto3" json:"jitter,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Backoff) Reset() { *m = Backoff{} }
func (m *Backoff) String() string { return proto.CompactTextString(m) }
func (*Backoff) ProtoMessage() {}
func (*Backoff) Descriptor() ([]byte, []int) {
return fileDescriptor_2dfb3aef05fe3328, []int{13}
}
func (m *Backoff) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Backoff.Unmarshal(m, b)
}
func (m *Backoff) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Backoff.Marshal(b, m, deterministic)
}
func (m *Backoff) XXX_Merge(src proto.Message) {
xxx_messageInfo_Backoff.Merge(m, src)
}
func (m *Backoff) XXX_Size() int {
return xxx_messageInfo_Backoff.Size(m)
}
func (m *Backoff) XXX_DiscardUnknown() {
xxx_messageInfo_Backoff.DiscardUnknown(m)
}
var xxx_messageInfo_Backoff proto.InternalMessageInfo
func (m *Backoff) GetMaxDelay() int32 {
if m != nil {
return m.MaxDelay
}
return 0
}
func (m *Backoff) GetBaseDelay() int32 {
if m != nil {
return m.BaseDelay
}
return 0
}
func (m *Backoff) GetFactor() float32 {
if m != nil {
return m.Factor
}
return 0
}
func (m *Backoff) GetJitter() float32 {
if m != nil {
return m.Jitter
}
return 0
}
func init() {
proto.RegisterEnum("goim.logic.PushMsg_Type", PushMsg_Type_name, PushMsg_Type_value)
proto.RegisterType((*PushMsg)(nil), "goim.logic.PushMsg")
proto.RegisterType((*ConnectReq)(nil), "goim.logic.ConnectReq")
proto.RegisterType((*ConnectReply)(nil), "goim.logic.ConnectReply")
proto.RegisterType((*DisconnectReq)(nil), "goim.logic.DisconnectReq")
proto.RegisterType((*DisconnectReply)(nil), "goim.logic.DisconnectReply")
proto.RegisterType((*HeartbeatReq)(nil), "goim.logic.HeartbeatReq")
proto.RegisterType((*HeartbeatReply)(nil), "goim.logic.HeartbeatReply")
proto.RegisterType((*OnlineReq)(nil), "goim.logic.OnlineReq")
proto.RegisterMapType((map[string]int32)(nil), "goim.logic.OnlineReq.RoomCountEntry")
proto.RegisterType((*OnlineReply)(nil), "goim.logic.OnlineReply")
proto.RegisterMapType((map[string]int32)(nil), "goim.logic.OnlineReply.AllRoomCountEntry")
proto.RegisterType((*ReceiveReq)(nil), "goim.logic.ReceiveReq")
proto.RegisterType((*ReceiveReply)(nil), "goim.logic.ReceiveReply")
proto.RegisterType((*NodesReq)(nil), "goim.logic.NodesReq")
proto.RegisterType((*NodesReply)(nil), "goim.logic.NodesReply")
proto.RegisterType((*Backoff)(nil), "goim.logic.Backoff")
}
func init() { proto.RegisterFile("logic/logic.proto", fileDescriptor_2dfb3aef05fe3328) }
var fileDescriptor_2dfb3aef05fe3328 = []byte{
// 893 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0x4f, 0x6f, 0xe3, 0x44,
0x14, 0xc7, 0x71, 0x9c, 0xc4, 0x2f, 0x69, 0xc9, 0x0e, 0x25, 0xeb, 0x7a, 0x41, 0x8a, 0xbc, 0x1c,
0x52, 0x60, 0x13, 0x29, 0x68, 0x25, 0xc4, 0x82, 0x50, 0xd3, 0x20, 0xed, 0x2e, 0x84, 0x46, 0xb3,
0xe5, 0xc2, 0xa5, 0x9a, 0x38, 0xd3, 0xd4, 0xc4, 0xf6, 0x18, 0x7b, 0xd2, 0xd4, 0x37, 0xc4, 0xf7,
0xe0, 0xc8, 0x77, 0xe3, 0x3b, 0x70, 0x41, 0xf3, 0x27, 0xb6, 0xa3, 0x4d, 0x57, 0x20, 0x2e, 0xd6,
0xfb, 0x37, 0xbf, 0xf9, 0xbd, 0xf7, 0xe6, 0x3d, 0xc3, 0xa3, 0x90, 0xad, 0x02, 0x7f, 0x24, 0xbf,
0xc3, 0x24, 0x65, 0x9c, 0x21, 0x58, 0xb1, 0x20, 0x1a, 0x4a, 0x8b, 0xfb, 0x7c, 0x15, 0xf0, 0xdb,
0xcd, 0x62, 0xe8, 0xb3, 0x68, 0x74, 0x45, 0xd3, 0x34, 0x7f, 0x36, 0x23, 0x6c, 0x24, 0x02, 0x46,
0x24, 0x09, 0x46, 0xf2, 0x80, 0xcf, 0xc2, 0x42, 0x50, 0x10, 0xde, 0x5f, 0x06, 0x34, 0xe7, 0x9b,
0xec, 0x76, 0x96, 0xad, 0xd0, 0xe7, 0x50, 0xe7, 0x79, 0x42, 0x1d, 0xa3, 0x6f, 0x0c, 0x8e, 0xc7,
0xce, 0xb0, 0x44, 0x1f, 0xea, 0x90, 0xe1, 0x55, 0x9e, 0x50, 0x2c, 0xa3, 0xd0, 0x47, 0x60, 0xb3,
0x84, 0xa6, 0x84, 0x07, 0x2c, 0x76, 0x6a, 0x7d, 0x63, 0x60, 0xe1, 0xd2, 0x80, 0x4e, 0xc0, 0xca,
0x12, 0x4a, 0x97, 0x8e, 0x29, 0x3d, 0x4a, 0x41, 0x3d, 0x68, 0x64, 0x34, 0xbd, 0xa3, 0xa9, 0x53,
0xef, 0x1b, 0x03, 0x1b, 0x6b, 0x0d, 0x21, 0xa8, 0xa7, 0x8c, 0x45, 0x8e, 0x25, 0xad, 0x52, 0x16,
0xb6, 0x35, 0xcd, 0x33, 0xa7, 0xd1, 0x37, 0x85, 0x4d, 0xc8, 0xa8, 0x0b, 0x66, 0x94, 0xad, 0x9c,
0x66, 0xdf, 0x18, 0x74, 0xb0, 0x10, 0xbd, 0x33, 0xa8, 0x0b, 0x4e, 0xa8, 0x05, 0xf5, 0xf9, 0x4f,
0x6f, 0x5e, 0x76, 0xdf, 0x13, 0x12, 0xbe, 0xbc, 0x9c, 0x75, 0x0d, 0x74, 0x04, 0xf6, 0x04, 0x5f,
0x9e, 0x4f, 0x2f, 0xce, 0xdf, 0x5c, 0x75, 0x6b, 0x1e, 0x06, 0xb8, 0x60, 0x71, 0x4c, 0x7d, 0x8e,
0xe9, 0xaf, 0x15, 0x2a, 0xc6, 0x1e, 0x95, 0x1e, 0x34, 0x7c, 0xc6, 0xd6, 0x01, 0x95, 0x39, 0xd9,
0x58, 0x6b, 0x22, 0x21, 0xce, 0xd6, 0x34, 0x96, 0x09, 0x75, 0xb0, 0x52, 0xbc, 0xdf, 0x0d, 0xe8,
0x14, 0xa0, 0x49, 0x98, 0x4b, 0x86, 0xc1, 0x52, 0x62, 0x9a, 0x58, 0x88, 0xc2, 0xb2, 0xa6, 0xb9,
0x46, 0x13, 0xa2, 0xb8, 0x42, 0x64, 0xf8, 0x6a, 0x2a, 0xb1, 0x6c, 0xac, 0x35, 0xe4, 0x40, 0x93,
0xf8, 0x3e, 0x4d, 0x78, 0xe6, 0xd4, 0xfb, 0xe6, 0xc0, 0xc2, 0x3b, 0x55, 0xd4, 0xfa, 0x96, 0x92,
0x94, 0x2f, 0x28, 0xe1, 0xb2, 0x48, 0x26, 0x2e, 0x0d, 0xde, 0xf7, 0x70, 0x34, 0x0d, 0x32, 0xbf,
0xcc, 0xed, 0x5f, 0x92, 0xd0, 0xf9, 0x9b, 0xd5, 0xfc, 0xbd, 0xa7, 0xf0, 0x7e, 0x15, 0x4c, 0xe7,
0x74, 0x4b, 0x32, 0x09, 0xd7, 0xc2, 0x42, 0xf4, 0x5e, 0x43, 0xe7, 0xe5, 0xee, 0xfa, 0xff, 0x7b,
0x61, 0x17, 0x8e, 0x2b, 0x58, 0x49, 0x98, 0x7b, 0x7f, 0x1a, 0x60, 0x5f, 0xc6, 0x61, 0x10, 0xd3,
0x77, 0x35, 0x6a, 0x02, 0xb6, 0xa8, 0xdb, 0x05, 0xdb, 0xc4, 0xdc, 0xa9, 0xf5, 0xcd, 0x41, 0x7b,
0xfc, 0x49, 0xf5, 0xc9, 0x16, 0x08, 0x43, 0xbc, 0x0b, 0xfb, 0x2e, 0xe6, 0x69, 0x8e, 0xcb, 0x63,
0xee, 0xd7, 0x70, 0xbc, 0xef, 0xdc, 0xf1, 0x36, 0x4a, 0xde, 0x27, 0x60, 0xdd, 0x91, 0x70, 0x43,
0xf5, 0x1b, 0x57, 0xca, 0x57, 0xb5, 0x2f, 0x0d, 0xef, 0x0f, 0x03, 0xda, 0xbb, 0x5b, 0x44, 0x9d,
0x66, 0xd0, 0x21, 0x61, 0x58, 0x00, 0x3a, 0x86, 0x24, 0x75, 0x76, 0x88, 0x54, 0x12, 0xe6, 0xc3,
0xf3, 0x4a, 0xac, 0x62, 0xb6, 0x77, 0xdc, 0xfd, 0x16, 0x1e, 0xbd, 0x15, 0xf2, 0x9f, 0xf8, 0xbd,
0x06, 0xc0, 0xd4, 0xa7, 0xc1, 0x1d, 0x3d, 0xdc, 0xa3, 0x4f, 0xc1, 0x92, 0x4b, 0x40, 0x9e, 0x6c,
0x8f, 0x4f, 0x14, 0xd1, 0x62, 0x41, 0xcc, 0x85, 0x80, 0x55, 0x88, 0x77, 0x0c, 0x9d, 0x02, 0x4b,
0xf4, 0x68, 0x02, 0xad, 0x1f, 0xd9, 0x92, 0x66, 0x02, 0xd9, 0x85, 0x56, 0x12, 0x12, 0x7e, 0xc3,
0xd2, 0x48, 0x13, 0x2b, 0x74, 0xe1, 0xf3, 0xc3, 0x80, 0xc6, 0xfc, 0xd5, 0x5c, 0x3f, 0x86, 0x42,
0xf7, 0xfe, 0x36, 0x00, 0x34, 0x88, 0x28, 0x5f, 0x0f, 0x1a, 0x4b, 0x16, 0x91, 0x20, 0xde, 0x35,
0x5a, 0x69, 0xe8, 0x14, 0x5a, 0xdc, 0x4f, 0xae, 0x13, 0x96, 0x72, 0x9d, 0x63, 0x93, 0xfb, 0xc9,
0x9c, 0xa5, 0x1c, 0x3d, 0x86, 0xe6, 0x36, 0x53, 0x1e, 0xb5, 0x67, 0x1a, 0xdb, 0x4c, 0x3a, 0x4e,
0xa1, 0xb5, 0xcd, 0xb4, 0xa7, 0xae, 0xce, 0x6c, 0x33, 0xe5, 0x7a, 0x6b, 0x96, 0xac, 0xca, 0x2c,
0x89, 0x6a, 0xc6, 0x82, 0x92, 0x5e, 0x3b, 0x4a, 0x41, 0xcf, 0xa0, 0xb9, 0x20, 0xfe, 0x9a, 0xdd,
0xdc, 0xc8, 0xdd, 0xd3, 0x1e, 0x7f, 0x50, 0x6d, 0xea, 0x44, 0xb9, 0xf0, 0x2e, 0x06, 0x3d, 0x85,
0xa3, 0x02, 0xf1, 0x3a, 0x22, 0xf7, 0x4e, 0x4b, 0x5e, 0xd3, 0x29, 0x8c, 0x33, 0x72, 0xef, 0x6d,
0xa0, 0xa9, 0x0f, 0xa2, 0x27, 0x60, 0x47, 0xe4, 0xfe, 0x7a, 0x49, 0x43, 0xa2, 0x5a, 0x6b, 0xe1,
0x56, 0x44, 0xee, 0xa7, 0x42, 0x47, 0x1f, 0x03, 0x2c, 0x48, 0x46, 0xb5, 0x57, 0x2f, 0x5a, 0x61,
0x51, 0xee, 0x1e, 0x34, 0x6e, 0x88, 0xcf, 0x99, 0x1a, 0xab, 0x1a, 0xd6, 0x9a, 0xb0, 0xff, 0x12,
0x70, 0xae, 0x57, 0x6d, 0x0d, 0x6b, 0x6d, 0xfc, 0x9b, 0x09, 0xd6, 0x0f, 0x82, 0x36, 0x7a, 0x01,
0x4d, 0xbd, 0xba, 0x50, 0xaf, 0x9a, 0x4e, 0xb9, 0x24, 0x5d, 0xe7, 0xa0, 0x5d, 0x34, 0x6b, 0x0a,
0x50, 0xae, 0x09, 0x74, 0x5a, 0x8d, 0xdb, 0xdb, 0x45, 0xee, 0x93, 0x87, 0x5c, 0x02, 0xe5, 0x1c,
0xec, 0x62, 0xf6, 0xd1, 0xde, 0x65, 0xd5, 0xf5, 0xe2, 0xba, 0x0f, 0x78, 0x04, 0xc4, 0x37, 0xd0,
0xc6, 0x34, 0xa6, 0x5b, 0x35, 0x59, 0xe8, 0xc3, 0x83, 0x2b, 0xc0, 0x7d, 0xfc, 0xc0, 0x10, 0x8a,
0x22, 0xe8, 0x77, 0xbd, 0x5f, 0x84, 0x72, 0x70, 0xf6, 0x8b, 0x50, 0x1d, 0x02, 0xf4, 0x1c, 0x2c,
0xf9, 0x7e, 0xd1, 0x49, 0x35, 0x64, 0x37, 0x17, 0x6e, 0xef, 0x80, 0x35, 0x09, 0xf3, 0xc9, 0x67,
0x3f, 0x9f, 0xbd, 0xfb, 0x67, 0x2d, 0x4f, 0xbc, 0x90, 0xdf, 0x45, 0x43, 0xce, 0xdf, 0x17, 0xff,
0x04, 0x00, 0x00, 0xff, 0xff, 0x6f, 0x34, 0x54, 0x90, 0xff, 0x07, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// LogicClient is the client API for Logic service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type LogicClient interface {
// Connect
Connect(ctx context.Context, in *ConnectReq, opts ...grpc.CallOption) (*ConnectReply, error)
// Disconnect
Disconnect(ctx context.Context, in *DisconnectReq, opts ...grpc.CallOption) (*DisconnectReply, error)
// Heartbeat
Heartbeat(ctx context.Context, in *HeartbeatReq, opts ...grpc.CallOption) (*HeartbeatReply, error)
// RenewOnline
RenewOnline(ctx context.Context, in *OnlineReq, opts ...grpc.CallOption) (*OnlineReply, error)
// Receive
Receive(ctx context.Context, in *ReceiveReq, opts ...grpc.CallOption) (*ReceiveReply, error)
//ServerList
Nodes(ctx context.Context, in *NodesReq, opts ...grpc.CallOption) (*NodesReply, error)
}
type logicClient struct {
cc *grpc.ClientConn
}
func NewLogicClient(cc *grpc.ClientConn) LogicClient {
return &logicClient{cc}
}
func (c *logicClient) Connect(ctx context.Context, in *ConnectReq, opts ...grpc.CallOption) (*ConnectReply, error) {
out := new(ConnectReply)
err := c.cc.Invoke(ctx, "/goim.logic.Logic/Connect", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *logicClient) Disconnect(ctx context.Context, in *DisconnectReq, opts ...grpc.CallOption) (*DisconnectReply, error) {
out := new(DisconnectReply)
err := c.cc.Invoke(ctx, "/goim.logic.Logic/Disconnect", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *logicClient) Heartbeat(ctx context.Context, in *HeartbeatReq, opts ...grpc.CallOption) (*HeartbeatReply, error) {
out := new(HeartbeatReply)
err := c.cc.Invoke(ctx, "/goim.logic.Logic/Heartbeat", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *logicClient) RenewOnline(ctx context.Context, in *OnlineReq, opts ...grpc.CallOption) (*OnlineReply, error) {
out := new(OnlineReply)
err := c.cc.Invoke(ctx, "/goim.logic.Logic/RenewOnline", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *logicClient) Receive(ctx context.Context, in *ReceiveReq, opts ...grpc.CallOption) (*ReceiveReply, error) {
out := new(ReceiveReply)
err := c.cc.Invoke(ctx, "/goim.logic.Logic/Receive", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *logicClient) Nodes(ctx context.Context, in *NodesReq, opts ...grpc.CallOption) (*NodesReply, error) {
out := new(NodesReply)
err := c.cc.Invoke(ctx, "/goim.logic.Logic/Nodes", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// LogicServer is the server API for Logic service.
type LogicServer interface {
// Connect
Connect(context.Context, *ConnectReq) (*ConnectReply, error)
// Disconnect
Disconnect(context.Context, *DisconnectReq) (*DisconnectReply, error)
// Heartbeat
Heartbeat(context.Context, *HeartbeatReq) (*HeartbeatReply, error)
// RenewOnline
RenewOnline(context.Context, *OnlineReq) (*OnlineReply, error)
// Receive
Receive(context.Context, *ReceiveReq) (*ReceiveReply, error)
//ServerList
Nodes(context.Context, *NodesReq) (*NodesReply, error)
}
// UnimplementedLogicServer can be embedded to have forward compatible implementations.
type UnimplementedLogicServer struct {
}
func (*UnimplementedLogicServer) Connect(ctx context.Context, req *ConnectReq) (*ConnectReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Connect not implemented")
}
func (*UnimplementedLogicServer) Disconnect(ctx context.Context, req *DisconnectReq) (*DisconnectReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Disconnect not implemented")
}
func (*UnimplementedLogicServer) Heartbeat(ctx context.Context, req *HeartbeatReq) (*HeartbeatReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Heartbeat not implemented")
}
func (*UnimplementedLogicServer) RenewOnline(ctx context.Context, req *OnlineReq) (*OnlineReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method RenewOnline not implemented")
}
func (*UnimplementedLogicServer) Receive(ctx context.Context, req *ReceiveReq) (*ReceiveReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Receive not implemented")
}
func (*UnimplementedLogicServer) Nodes(ctx context.Context, req *NodesReq) (*NodesReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Nodes not implemented")
}
func RegisterLogicServer(s *grpc.Server, srv LogicServer) {
s.RegisterService(&_Logic_serviceDesc, srv)
}
func _Logic_Connect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ConnectReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LogicServer).Connect(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/goim.logic.Logic/Connect",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LogicServer).Connect(ctx, req.(*ConnectReq))
}
return interceptor(ctx, in, info, handler)
}
func _Logic_Disconnect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DisconnectReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LogicServer).Disconnect(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/goim.logic.Logic/Disconnect",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LogicServer).Disconnect(ctx, req.(*DisconnectReq))
}
return interceptor(ctx, in, info, handler)
}
func _Logic_Heartbeat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HeartbeatReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LogicServer).Heartbeat(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/goim.logic.Logic/Heartbeat",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LogicServer).Heartbeat(ctx, req.(*HeartbeatReq))
}
return interceptor(ctx, in, info, handler)
}
func _Logic_RenewOnline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(OnlineReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LogicServer).RenewOnline(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/goim.logic.Logic/RenewOnline",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LogicServer).RenewOnline(ctx, req.(*OnlineReq))
}
return interceptor(ctx, in, info, handler)
}
func _Logic_Receive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReceiveReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LogicServer).Receive(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/goim.logic.Logic/Receive",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LogicServer).Receive(ctx, req.(*ReceiveReq))
}
return interceptor(ctx, in, info, handler)
}
func _Logic_Nodes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(NodesReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LogicServer).Nodes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/goim.logic.Logic/Nodes",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LogicServer).Nodes(ctx, req.(*NodesReq))
}
return interceptor(ctx, in, info, handler)
}
var _Logic_serviceDesc = grpc.ServiceDesc{
ServiceName: "goim.logic.Logic",
HandlerType: (*LogicServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Connect",
Handler: _Logic_Connect_Handler,
},
{
MethodName: "Disconnect",
Handler: _Logic_Disconnect_Handler,
},
{
MethodName: "Heartbeat",
Handler: _Logic_Heartbeat_Handler,
},
{
MethodName: "RenewOnline",
Handler: _Logic_RenewOnline_Handler,
},
{
MethodName: "Receive",
Handler: _Logic_Receive_Handler,
},
{
MethodName: "Nodes",
Handler: _Logic_Nodes_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "logic/logic.proto",
}
================================================
FILE: api/logic/logic.proto
================================================
syntax = "proto3";
package goim.logic;
option go_package = "github.com/Terry-Mao/goim/api/logic;logic";
import "github.com/Terry-Mao/goim/api/protocol/protocol.proto";
message PushMsg {
enum Type {
PUSH = 0;
ROOM = 1;
BROADCAST = 2;
}
Type type = 1;
int32 operation = 2;
int32 speed = 3;
string server = 4;
string room = 5;
repeated string keys = 6;
bytes msg = 7;
}
message ConnectReq {
string server = 1;
string cookie = 2;
bytes token = 3;
}
message ConnectReply {
int64 mid = 1;
string key = 2;
string roomID = 3;
repeated int32 accepts = 4;
int64 heartbeat = 5;
}
message DisconnectReq {
int64 mid = 1;
string key = 2;
string server = 3;
}
message DisconnectReply {
bool has = 1;
}
message HeartbeatReq {
int64 mid = 1;
string key = 2;
string server = 3;
}
message HeartbeatReply {
}
message OnlineReq {
string server = 1;
map<string, int32> roomCount = 2;
}
message OnlineReply {
map<string, int32> allRoomCount = 1;
}
message ReceiveReq {
int64 mid = 1;
goim.protocol.Proto proto = 2;
}
message ReceiveReply {
}
message NodesReq {
string platform = 1;
string clientIP = 2;
}
message NodesReply {
string domain = 1;
int32 tcp_port = 2;
int32 ws_port = 3;
int32 wss_port = 4;
int32 heartbeat = 5;
repeated string nodes = 6;
Backoff backoff = 7;
int32 heartbeat_max = 8;
}
message Backoff {
int32 max_delay = 1;
int32 base_delay = 2;
float factor = 3;
float jitter = 4;
}
service Logic {
// Connect
rpc Connect(ConnectReq) returns (ConnectReply);
// Disconnect
rpc Disconnect(DisconnectReq) returns (DisconnectReply);
// Heartbeat
rpc Heartbeat(HeartbeatReq) returns (HeartbeatReply);
// RenewOnline
rpc RenewOnline(OnlineReq) returns (OnlineReply);
// Receive
rpc Receive(ReceiveReq) returns (ReceiveReply);
//ServerList
rpc Nodes(NodesReq) returns (NodesReply);
}
================================================
FILE: api/protocol/operation.go
================================================
package protocol
const (
// OpHandshake handshake
OpHandshake = int32(0)
// OpHandshakeReply handshake reply
OpHandshakeReply = int32(1)
// OpHeartbeat heartbeat
OpHeartbeat = int32(2)
// OpHeartbeatReply heartbeat reply
OpHeartbeatReply = int32(3)
// OpSendMsg send message.
OpSendMsg = int32(4)
// OpSendMsgReply send message reply
OpSendMsgReply = int32(5)
// OpDisconnectReply disconnect reply
OpDisconnectReply = int32(6)
// OpAuth auth connnect
OpAuth = int32(7)
// OpAuthReply auth connect reply
OpAuthReply = int32(8)
// OpRaw raw message
OpRaw = int32(9)
// OpProtoReady proto ready
OpProtoReady = int32(10)
// OpProtoFinish proto finish
OpProtoFinish = int32(11)
// OpChangeRoom change room
OpChangeRoom = int32(12)
// OpChangeRoomReply change room reply
OpChangeRoomReply = int32(13)
// OpSub subscribe operation
OpSub = int32(14)
// OpSubReply subscribe operation
OpSubReply = int32(15)
// OpUnsub unsubscribe operation
OpUnsub = int32(16)
// OpUnsubReply unsubscribe operation reply
OpUnsubReply = int32(17)
)
================================================
FILE: api/protocol/protocol.go
================================================
package protocol
import (
"errors"
"github.com/Terry-Mao/goim/pkg/bufio"
"github.com/Terry-Mao/goim/pkg/bytes"
"github.com/Terry-Mao/goim/pkg/encoding/binary"
"github.com/Terry-Mao/goim/pkg/websocket"
)
const (
// MaxBodySize max proto body size
MaxBodySize = int32(1 << 12)
)
const (
// size
_packSize = 4
_headerSize = 2
_verSize = 2
_opSize = 4
_seqSize = 4
_heartSize = 4
_rawHeaderSize = _packSize + _headerSize + _verSize + _opSize + _seqSize
_maxPackSize = MaxBodySize + int32(_rawHeaderSize)
// offset
_packOffset = 0
_headerOffset = _packOffset + _packSize
_verOffset = _headerOffset + _headerSize
_opOffset = _verOffset + _verSize
_seqOffset = _opOffset + _opSize
_heartOffset = _seqOffset + _seqSize
)
var (
// ErrProtoPackLen proto packet len error
ErrProtoPackLen = errors.New("default server codec pack length error")
// ErrProtoHeaderLen proto header len error
ErrProtoHeaderLen = errors.New("default server codec header length error")
)
var (
// ProtoReady proto ready
ProtoReady = &Proto{Op: OpProtoReady}
// ProtoFinish proto finish
ProtoFinish = &Proto{Op: OpProtoFinish}
)
// WriteTo write a proto to bytes writer.
func (p *Proto) WriteTo(b *bytes.Writer) {
var (
packLen = _rawHeaderSize + int32(len(p.Body))
buf = b.Peek(_rawHeaderSize)
)
binary.BigEndian.PutInt32(buf[_packOffset:], packLen)
binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize))
binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver))
binary.BigEndian.PutInt32(buf[_opOffset:], p.Op)
binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq)
if p.Body != nil {
b.Write(p.Body)
}
}
// ReadTCP read a proto from TCP reader.
func (p *Proto) ReadTCP(rr *bufio.Reader) (err error) {
var (
bodyLen int
headerLen int16
packLen int32
buf []byte
)
if buf, err = rr.Pop(_rawHeaderSize); err != nil {
return
}
packLen = binary.BigEndian.Int32(buf[_packOffset:_headerOffset])
headerLen = binary.BigEndian.Int16(buf[_headerOffset:_verOffset])
p.Ver = int32(binary.BigEndian.Int16(buf[_verOffset:_opOffset]))
p.Op = binary.BigEndian.Int32(buf[_opOffset:_seqOffset])
p.Seq = binary.BigEndian.Int32(buf[_seqOffset:])
if packLen > _maxPackSize {
return ErrProtoPackLen
}
if headerLen != _rawHeaderSize {
return ErrProtoHeaderLen
}
if bodyLen = int(packLen - int32(headerLen)); bodyLen > 0 {
p.Body, err = rr.Pop(bodyLen)
} else {
p.Body = nil
}
return
}
// WriteTCP write a proto to TCP writer.
func (p *Proto) WriteTCP(wr *bufio.Writer) (err error) {
var (
buf []byte
packLen int32
)
if p.Op == OpRaw {
// write without buffer, job concact proto into raw buffer
_, err = wr.WriteRaw(p.Body)
return
}
packLen = _rawHeaderSize + int32(len(p.Body))
if buf, err = wr.Peek(_rawHeaderSize); err != nil {
return
}
binary.BigEndian.PutInt32(buf[_packOffset:], packLen)
binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize))
binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver))
binary.BigEndian.PutInt32(buf[_opOffset:], p.Op)
binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq)
if p.Body != nil {
_, err = wr.Write(p.Body)
}
return
}
// WriteTCPHeart write TCP heartbeat with room online.
func (p *Proto) WriteTCPHeart(wr *bufio.Writer, online int32) (err error) {
var (
buf []byte
packLen int
)
packLen = _rawHeaderSize + _heartSize
if buf, err = wr.Peek(packLen); err != nil {
return
}
// header
binary.BigEndian.PutInt32(buf[_packOffset:], int32(packLen))
binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize))
binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver))
binary.BigEndian.PutInt32(buf[_opOffset:], p.Op)
binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq)
// body
binary.BigEndian.PutInt32(buf[_heartOffset:], online)
return
}
// ReadWebsocket read a proto from websocket connection.
func (p *Proto) ReadWebsocket(ws *websocket.Conn) (err error) {
var (
bodyLen int
headerLen int16
packLen int32
buf []byte
)
if _, buf, err = ws.ReadMessage(); err != nil {
return
}
if len(buf) < _rawHeaderSize {
return ErrProtoPackLen
}
packLen = binary.BigEndian.Int32(buf[_packOffset:_headerOffset])
headerLen = binary.BigEndian.Int16(buf[_headerOffset:_verOffset])
p.Ver = int32(binary.BigEndian.Int16(buf[_verOffset:_opOffset]))
p.Op = binary.BigEndian.Int32(buf[_opOffset:_seqOffset])
p.Seq = binary.BigEndian.Int32(buf[_seqOffset:])
if packLen < 0 || packLen > _maxPackSize {
return ErrProtoPackLen
}
if headerLen != _rawHeaderSize {
return ErrProtoHeaderLen
}
if bodyLen = int(packLen - int32(headerLen)); bodyLen > 0 {
p.Body = buf[headerLen:packLen]
} else {
p.Body = nil
}
return
}
// WriteWebsocket write a proto to websocket connection.
func (p *Proto) WriteWebsocket(ws *websocket.Conn) (err error) {
var (
buf []byte
packLen int
)
packLen = _rawHeaderSize + len(p.Body)
if err = ws.WriteHeader(websocket.BinaryMessage, packLen); err != nil {
return
}
if buf, err = ws.Peek(_rawHeaderSize); err != nil {
return
}
binary.BigEndian.PutInt32(buf[_packOffset:], int32(packLen))
binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize))
binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver))
binary.BigEndian.PutInt32(buf[_opOffset:], p.Op)
binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq)
if p.Body != nil {
err = ws.WriteBody(p.Body)
}
return
}
// WriteWebsocketHeart write websocket heartbeat with room online.
func (p *Proto) WriteWebsocketHeart(wr *websocket.Conn, online int32) (err error) {
var (
buf []byte
packLen int
)
packLen = _rawHeaderSize + _heartSize
// websocket header
if err = wr.WriteHeader(websocket.BinaryMessage, packLen); err != nil {
return
}
if buf, err = wr.Peek(packLen); err != nil {
return
}
// proto header
binary.BigEndian.PutInt32(buf[_packOffset:], int32(packLen))
binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize))
binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver))
binary.BigEndian.PutInt32(buf[_opOffset:], p.Op)
binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq)
// proto body
binary.BigEndian.PutInt32(buf[_heartOffset:], online)
return
}
================================================
FILE: api/protocol/protocol.pb.go
================================================
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: protocol/protocol.proto
package protocol
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
//
// v1.0.0
// protocol
type Proto struct {
Ver int32 `protobuf:"varint,1,opt,name=ver,proto3" json:"ver,omitempty"`
Op int32 `protobuf:"varint,2,opt,name=op,proto3" json:"op,omitempty"`
Seq int32 `protobuf:"varint,3,opt,name=seq,proto3" json:"seq,omitempty"`
Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Proto) Reset() { *m = Proto{} }
func (m *Proto) String() string { return proto.CompactTextString(m) }
func (*Proto) ProtoMessage() {}
func (*Proto) Descriptor() ([]byte, []int) {
return fileDescriptor_87968d26f3046c60, []int{0}
}
func (m *Proto) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Proto.Unmarshal(m, b)
}
func (m *Proto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Proto.Marshal(b, m, deterministic)
}
func (m *Proto) XXX_Merge(src proto.Message) {
xxx_messageInfo_Proto.Merge(m, src)
}
func (m *Proto) XXX_Size() int {
return xxx_messageInfo_Proto.Size(m)
}
func (m *Proto) XXX_DiscardUnknown() {
xxx_messageInfo_Proto.DiscardUnknown(m)
}
var xxx_messageInfo_Proto proto.InternalMessageInfo
func (m *Proto) GetVer() int32 {
if m != nil {
return m.Ver
}
return 0
}
func (m *Proto) GetOp() int32 {
if m != nil {
return m.Op
}
return 0
}
func (m *Proto) GetSeq() int32 {
if m != nil {
return m.Seq
}
return 0
}
func (m *Proto) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
func init() {
proto.RegisterType((*Proto)(nil), "goim.protocol.Proto")
}
func init() { proto.RegisterFile("protocol/protocol.proto", fileDescriptor_87968d26f3046c60) }
var fileDescriptor_87968d26f3046c60 = []byte{
// 153 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2f, 0x28, 0xca, 0x2f,
0xc9, 0x4f, 0xce, 0xcf, 0xd1, 0x87, 0x31, 0xf4, 0xc0, 0x0c, 0x21, 0xde, 0xf4, 0xfc, 0xcc, 0x5c,
0x3d, 0x98, 0xa0, 0x92, 0x3f, 0x17, 0x6b, 0x00, 0x58, 0x5c, 0x80, 0x8b, 0xb9, 0x2c, 0xb5, 0x48,
0x82, 0x51, 0x81, 0x51, 0x83, 0x35, 0x08, 0xc4, 0x14, 0xe2, 0xe3, 0x62, 0xca, 0x2f, 0x90, 0x60,
0x02, 0x0b, 0x30, 0xe5, 0x17, 0x80, 0x54, 0x14, 0xa7, 0x16, 0x4a, 0x30, 0x43, 0x54, 0x14, 0xa7,
0x16, 0x0a, 0x09, 0x71, 0xb1, 0x24, 0xe5, 0xa7, 0x54, 0x4a, 0xb0, 0x28, 0x30, 0x6a, 0xf0, 0x04,
0x81, 0xd9, 0x4e, 0x86, 0x51, 0xfa, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9,
0xfa, 0x21, 0xa9, 0x45, 0x45, 0x95, 0xba, 0xbe, 0x89, 0xf9, 0xfa, 0x20, 0x6b, 0xf5, 0x13, 0x0b,
0x32, 0xe1, 0xee, 0xb1, 0x86, 0x31, 0x92, 0xd8, 0xc0, 0x2c, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff,
0xff, 0x8d, 0xc7, 0xbf, 0x64, 0xb4, 0x00, 0x00, 0x00,
}
================================================
FILE: api/protocol/protocol.proto
================================================
syntax = "proto3";
package goim.protocol;
option go_package = "github.com/Terry-Mao/goim/api/protocol;protocol";
/*
* v1.0.0
* protocol
*/
message Proto {
int32 ver = 1;
int32 op = 2;
int32 seq = 3;
bytes body = 4;
}
================================================
FILE: benchmarks/client/main.go
================================================
package main
// Start Commond eg: ./client 1 1000 localhost:3101
// first parameter:beginning userId
// second parameter: amount of clients
// third parameter: comet server ip
import (
"bufio"
"encoding/binary"
"encoding/json"
"flag"
"fmt"
"math/rand"
"net"
"os"
"runtime"
"strconv"
"sync/atomic"
"time"
log "github.com/golang/glog"
)
const (
opHeartbeat = int32(2)
opHeartbeatReply = int32(3)
opAuth = int32(7)
opAuthReply = int32(8)
)
const (
rawHeaderLen = uint16(16)
heart = 240 * time.Second
)
// Proto proto.
type Proto struct {
PackLen int32 // package length
HeaderLen int16 // header length
Ver int16 // protocol version
Operation int32 // operation for request
Seq int32 // sequence number chosen by client
Body []byte // body
}
// AuthToken auth token.
type AuthToken struct {
Mid int64 `json:"mid"`
Key string `json:"key"`
RoomID string `json:"room_id"`
Platform string `json:"platform"`
Accepts []int32 `json:"accepts"`
}
var (
countDown int64
aliveCount int64
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
flag.Parse()
begin, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
num, err := strconv.Atoi(os.Args[2])
if err != nil {
panic(err)
}
go result()
for i := begin; i < begin+num; i++ {
go client(int64(i))
}
// signal
var exit chan bool
<-exit
}
func result() {
var (
lastTimes int64
interval = int64(5)
)
for {
nowCount := atomic.LoadInt64(&countDown)
nowAlive := atomic.LoadInt64(&aliveCount)
diff := nowCount - lastTimes
lastTimes = nowCount
fmt.Println(fmt.Sprintf("%s alive:%d down:%d down/s:%d", time.Now().Format("2006-01-02 15:04:05"), nowAlive, nowCount, diff/interval))
time.Sleep(time.Second * time.Duration(interval))
}
}
func client(mid int64) {
for {
startClient(mid)
time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
}
}
func startClient(key int64) {
time.Sleep(time.Duration(rand.Intn(120)) * time.Second)
atomic.AddInt64(&aliveCount, 1)
quit := make(chan bool, 1)
defer func() {
close(quit)
atomic.AddInt64(&aliveCount, -1)
}()
// connnect to server
conn, err := net.Dial("tcp", os.Args[3])
if err != nil {
log.Errorf("net.Dial(%s) error(%v)", os.Args[3], err)
return
}
seq := int32(0)
wr := bufio.NewWriter(conn)
rd := bufio.NewReader(conn)
authToken := &AuthToken{
key,
"",
"test://1",
"ios",
[]int32{1000, 1001, 1002},
}
proto := new(Proto)
proto.Ver = 1
proto.Operation = opAuth
proto.Seq = seq
proto.Body, _ = json.Marshal(authToken)
if err = tcpWriteProto(wr, proto); err != nil {
log.Errorf("tcpWriteProto() error(%v)", err)
return
}
if err = tcpReadProto(rd, proto); err != nil {
log.Errorf("tcpReadProto() error(%v)", err)
return
}
log.Infof("key:%d auth ok, proto: %v", key, proto)
seq++
// writer
go func() {
hbProto := new(Proto)
for {
// heartbeat
hbProto.Operation = opHeartbeat
hbProto.Seq = seq
hbProto.Body = nil
if err = tcpWriteProto(wr, hbProto); err != nil {
log.Errorf("key:%d tcpWriteProto() error(%v)", key, err)
return
}
log.Infof("key:%d Write heartbeat", key)
time.Sleep(heart)
seq++
select {
case <-quit:
return
default:
}
}
}()
// reader
for {
if err = tcpReadProto(rd, proto); err != nil {
log.Errorf("key:%d tcpReadProto() error(%v)", key, err)
quit <- true
return
}
if proto.Operation == opAuthReply {
log.Infof("key:%d auth success", key)
} else if proto.Operation == opHeartbeatReply {
log.Infof("key:%d receive heartbeat", key)
if err = conn.SetReadDeadline(time.Now().Add(heart + 60*time.Second)); err != nil {
log.Errorf("conn.SetReadDeadline() error(%v)", err)
quit <- true
return
}
} else {
log.Infof("key:%d op:%d msg: %s", key, proto.Operation, string(proto.Body))
atomic.AddInt64(&countDown, 1)
}
}
}
func tcpWriteProto(wr *bufio.Writer, proto *Proto) (err error) {
// write
if err = binary.Write(wr, binary.BigEndian, uint32(rawHeaderLen)+uint32(len(proto.Body))); err != nil {
return
}
if err = binary.Write(wr, binary.BigEndian, rawHeaderLen); err != nil {
return
}
if err = binary.Write(wr, binary.BigEndian, proto.Ver); err != nil {
return
}
if err = binary.Write(wr, binary.BigEndian, proto.Operation); err != nil {
return
}
if err = binary.Write(wr, binary.BigEndian, proto.Seq); err != nil {
return
}
if proto.Body != nil {
if err = binary.Write(wr, binary.BigEndian, proto.Body); err != nil {
return
}
}
err = wr.Flush()
return
}
func tcpReadProto(rd *bufio.Reader, proto *Proto) (err error) {
var (
packLen int32
headerLen int16
)
// read
if err = binary.Read(rd, binary.BigEndian, &packLen); err != nil {
return
}
if err = binary.Read(rd, binary.BigEndian, &headerLen); err != nil {
return
}
if err = binary.Read(rd, binary.BigEndian, &proto.Ver); err != nil {
return
}
if err = binary.Read(rd, binary.BigEndian, &proto.Operation); err != nil {
return
}
if err = binary.Read(rd, binary.BigEndian, &proto.Seq); err != nil {
return
}
var (
n, t int
bodyLen = int(packLen - int32(headerLen))
)
if bodyLen > 0 {
proto.Body = make([]byte, bodyLen)
for {
if t, err = rd.Read(proto.Body[n:]); err != nil {
return
}
if n += t; n == bodyLen {
break
}
}
} else {
proto.Body = nil
}
return
}
================================================
FILE: benchmarks/multi_push/main.go
================================================
package main
// Start Command eg : ./multi_push 0 20000 localhost:7172 60
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"runtime"
"strconv"
"time"
)
var (
lg *log.Logger
httpClient *http.Client
t int
)
const testContent = "{\"test\":1}"
type pushsBodyMsg struct {
Msg json.RawMessage `json:"m"`
UserIds []int64 `json:"u"`
}
func init() {
httpTransport := &http.Transport{
Dial: func(netw, addr string) (net.Conn, error) {
deadline := time.Now().Add(30 * time.Second)
c, err := net.DialTimeout(netw, addr, 20*time.Second)
if err != nil {
return nil, err
}
_ = c.SetDeadline(deadline)
return c, nil
},
DisableKeepAlives: false,
}
httpClient = &http.Client{
Transport: httpTransport,
}
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
infoLogfi, err := os.OpenFile("./multi_push.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
panic(err)
}
lg = log.New(infoLogfi, "", log.LstdFlags|log.Lshortfile)
begin, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
length, err := strconv.Atoi(os.Args[2])
if err != nil {
panic(err)
}
t, err = strconv.Atoi(os.Args[4])
if err != nil {
panic(err)
}
num := runtime.NumCPU() * 8
l := length / num
b, e := begin, begin+l
time.AfterFunc(time.Duration(t)*time.Second, stop)
for i := 0; i < num; i++ {
go startPush(b, e)
b += l
e += l
}
if b < begin+length {
go startPush(b, begin+length)
}
time.Sleep(9999 * time.Hour)
}
func stop() {
os.Exit(-1)
}
func startPush(b, e int) {
l := make([]int64, 0, e-b)
for i := b; i < e; i++ {
l = append(l, int64(i))
}
msg := &pushsBodyMsg{Msg: json.RawMessage(testContent), UserIds: l}
body, err := json.Marshal(msg)
if err != nil {
panic(err)
}
for {
resp, err := httpPost(fmt.Sprintf("http://%s/goim/push/mids=%d", os.Args[3], b), "application/x-www-form-urlencoded", bytes.NewBuffer(body))
if err != nil {
lg.Printf("post error (%v)", err)
continue
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
lg.Printf("post error (%v)", err)
return
}
resp.Body.Close()
lg.Printf("response %s", string(body))
}
}
func httpPost(url string, contentType string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest("POST", url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", contentType)
resp, err := httpClient.Do(req)
if err != nil {
return nil, err
}
return resp, nil
}
================================================
FILE: benchmarks/push/main.go
================================================
package main
// Start Command eg : ./push 0 20000 localhost:7172 60
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"runtime"
"strconv"
"time"
)
var (
httpClient *http.Client
t int
)
const testContent = "{\"test\":1}"
type pushBodyMsg struct {
Msg json.RawMessage `json:"m"`
UserID int64 `json:"u"`
}
func init() {
httpTransport := &http.Transport{
Dial: func(netw, addr string) (net.Conn, error) {
deadline := time.Now().Add(30 * time.Second)
c, err := net.DialTimeout(netw, addr, 20*time.Second)
if err != nil {
return nil, err
}
_ = c.SetDeadline(deadline)
return c, nil
},
DisableKeepAlives: false,
}
httpClient = &http.Client{
Transport: httpTransport,
}
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
begin, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
length, err := strconv.Atoi(os.Args[2])
if err != nil {
panic(err)
}
t, err = strconv.Atoi(os.Args[4])
if err != nil {
panic(err)
}
num := runtime.NumCPU() * 2
log.Printf("start routine num:%d", num)
l := length / num
b, e := begin, begin+l
time.AfterFunc(time.Duration(t)*time.Second, stop)
for i := 0; i < num; i++ {
go startPush(b, e)
b += l
e += l
}
if b < begin+length {
go startPush(b, begin+length)
}
time.Sleep(9999 * time.Hour)
}
func stop() {
os.Exit(-1)
}
func startPush(b, e int) {
log.Printf("start Push from %d to %d", b, e)
bodys := make([][]byte, e-b)
for i := 0; i < e-b; i++ {
msg := &pushBodyMsg{Msg: json.RawMessage(testContent), UserID: int64(b)}
body, err := json.Marshal(msg)
if err != nil {
panic(err)
}
bodys[i] = body
}
for {
for i := 0; i < len(bodys); i++ {
resp, err := httpPost(fmt.Sprintf("http://%s/goim/push/mids?operation=1000&mids=%d", os.Args[3], b), "application/x-www-form-urlencoded", bytes.NewBuffer(bodys[i]))
if err != nil {
log.Printf("post error (%v)", err)
continue
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("post error (%v)", err)
return
}
resp.Body.Close()
log.Printf("response %s", string(body))
//time.Sleep(50 * time.Millisecond)
}
}
}
func httpPost(url string, contentType string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest("POST", url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", contentType)
resp, err := httpClient.Do(req)
if err != nil {
return nil, err
}
return resp, nil
}
================================================
FILE: benchmarks/push_room/main.go
================================================
package main
// Start Commond eg: ./push_room 1 20 localhost:3111
// first parameter: room id
// second parameter: num per seconds
// third parameter: logic server ip
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
"time"
)
func main() {
rountineNum, err := strconv.Atoi(os.Args[2])
if err != nil {
panic(err)
}
addr := os.Args[3]
gap := time.Second / time.Duration(rountineNum)
delay := time.Duration(0)
go run(addr, time.Duration(0)*time.Second)
for i := 0; i < rountineNum-1; i++ {
go run(addr, delay)
delay += gap
fmt.Println("delay:", delay)
}
time.Sleep(9999 * time.Hour)
}
func run(addr string, delay time.Duration) {
time.Sleep(delay)
i := int64(0)
for {
go post(addr, i)
time.Sleep(time.Second)
i++
}
}
func post(addr string, i int64) {
resp, err := http.Post("http://"+addr+"/goim/push/room?operation=1000&type=test&room="+os.Args[1], "application/json", bytes.NewBufferString(fmt.Sprintf("{\"test\":%d}", i)))
if err != nil {
fmt.Printf("Error: http.post() error(%v)\n", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error: http.post() error(%v)\n", err)
return
}
fmt.Printf("%s postId:%d, response:%s\n", time.Now().Format("2006-01-02 15:04:05"), i, string(body))
}
================================================
FILE: benchmarks/push_rooms/main.go
================================================
package main
// Start Command eg : ./push_rooms 0 20000 localhost:7172 40
// param 1 : the start of room number
// param 2 : the end of room number
// param 3 : comet server tcp address
// param 4 : push amount each goroutines per second
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"runtime"
"strconv"
"time"
)
var (
httpClient *http.Client
)
const testContent = "{\"test\":1}"
func init() {
httpTransport := &http.Transport{
Dial: func(netw, addr string) (net.Conn, error) {
deadline := time.Now().Add(30 * time.Second)
c, err := net.DialTimeout(netw, addr, 20*time.Second)
if err != nil {
return nil, err
}
_ = c.SetDeadline(deadline)
return c, nil
},
DisableKeepAlives: false,
}
httpClient = &http.Client{
Transport: httpTransport,
}
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
begin, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
length, err := strconv.Atoi(os.Args[2])
if err != nil {
panic(err)
}
num, err := strconv.Atoi(os.Args[4])
if err != nil {
panic(err)
}
delay := (1000 * time.Millisecond) / time.Duration(num)
routines := runtime.NumCPU() * 2
log.Printf("start routine num:%d", routines)
l := length / routines
b, e := begin, begin+l
for i := 0; i < routines; i++ {
go startPush(b, e, delay)
b += l
e += l
}
if b < begin+length {
go startPush(b, begin+length, delay)
}
time.Sleep(9999 * time.Hour)
}
func startPush(b, e int, delay time.Duration) {
log.Printf("start Push from %d to %d", b, e)
for {
for i := b; i < e; i++ {
resp, err := http.Post(fmt.Sprintf("http://%s/goim/push/room?operation=1000&type=test&room=%d", os.Args[3], i), "application/json", bytes.NewBufferString(testContent))
if err != nil {
log.Printf("post error (%v)", err)
continue
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("post error (%v)", err)
return
}
resp.Body.Close()
log.Printf("push room:%d response %s", i, string(body))
time.Sleep(delay)
}
}
}
================================================
FILE: cmd/comet/comet-example.toml
================================================
# This is a TOML document. Boom
[discovery]
nodes = ["127.0.0.1:7171"]
[rpcServer]
addr = ":3109"
timeout = "1s"
[rpcClient]
dial = "1s"
timeout = "1s"
[tcp]
bind = [":3101"]
sndbuf = 4096
rcvbuf = 4096
keepalive = false
reader = 32
readBuf = 1024
readBufSize = 8192
writer = 32
writeBuf = 1024
writeBufSize = 8192
[websocket]
bind = [":3102"]
tlsOpen = false
tlsBind = [":3103"]
certFile = "../../cert.pem"
privateFile = "../../private.pem"
[protocol]
timer = 32
timerSize = 2048
svrProto = 10
cliProto = 5
handshakeTimeout = "8s"
[whitelist]
Whitelist = [123]
WhiteLog = "/tmp/white_list.log"
[bucket]
size = 32
channel = 1024
room = 1024
routineAmount = 32
routineSize = 1024
================================================
FILE: cmd/comet/main.go
================================================
package main
import (
"context"
"flag"
"fmt"
"math/rand"
"net"
"os"
"os/signal"
"runtime"
"strconv"
"strings"
"syscall"
"time"
"github.com/bilibili/discovery/naming"
resolver "github.com/bilibili/discovery/naming/grpc"
"github.com/Terry-Mao/goim/internal/comet"
"github.com/Terry-Mao/goim/internal/comet/conf"
"github.com/Terry-Mao/goim/internal/comet/grpc"
md "github.com/Terry-Mao/goim/internal/logic/model"
"github.com/Terry-Mao/goim/pkg/ip"
log "github.com/golang/glog"
)
const (
ver = "2.0.0"
appid = "goim.comet"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
rand.Seed(time.Now().UTC().UnixNano())
runtime.GOMAXPROCS(runtime.NumCPU())
println(conf.Conf.Debug)
log.Infof("goim-comet [version: %s env: %+v] start", ver, conf.Conf.Env)
// register discovery
dis := naming.New(conf.Conf.Discovery)
resolver.Register(dis)
// new comet server
srv := comet.NewServer(conf.Conf)
if err := comet.InitWhitelist(conf.Conf.Whitelist); err != nil {
panic(err)
}
if err := comet.InitTCP(srv, conf.Conf.TCP.Bind, runtime.NumCPU()); err != nil {
panic(err)
}
if err := comet.InitWebsocket(srv, conf.Conf.Websocket.Bind, runtime.NumCPU()); err != nil {
panic(err)
}
if conf.Conf.Websocket.TLSOpen {
if err := comet.InitWebsocketWithTLS(srv, conf.Conf.Websocket.TLSBind, conf.Conf.Websocket.CertFile, conf.Conf.Websocket.PrivateFile, runtime.NumCPU()); err != nil {
panic(err)
}
}
// new grpc server
rpcSrv := grpc.New(conf.Conf.RPCServer, srv)
cancel := register(dis, srv)
// signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Infof("goim-comet get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
if cancel != nil {
cancel()
}
rpcSrv.GracefulStop()
srv.Close()
log.Infof("goim-comet [version: %s] exit", ver)
log.Flush()
return
case syscall.SIGHUP:
default:
return
}
}
}
func register(dis *naming.Discovery, srv *comet.Server) context.CancelFunc {
env := conf.Conf.Env
addr := ip.InternalIP()
_, port, _ := net.SplitHostPort(conf.Conf.RPCServer.Addr)
ins := &naming.Instance{
Region: env.Region,
Zone: env.Zone,
Env: env.DeployEnv,
Hostname: env.Host,
AppID: appid,
Addrs: []string{
"grpc://" + addr + ":" + port,
},
Metadata: map[string]string{
md.MetaWeight: strconv.FormatInt(env.Weight, 10),
md.MetaOffline: strconv.FormatBool(env.Offline),
md.MetaAddrs: strings.Join(env.Addrs, ","),
},
}
cancel, err := dis.Register(ins)
if err != nil {
panic(err)
}
// renew discovery metadata
go func() {
for {
var (
err error
conns int
ips = make(map[string]struct{})
)
for _, bucket := range srv.Buckets() {
for ip := range bucket.IPCount() {
ips[ip] = struct{}{}
}
conns += bucket.ChannelCount()
}
ins.Metadata[md.MetaConnCount] = fmt.Sprint(conns)
ins.Metadata[md.MetaIPCount] = fmt.Sprint(len(ips))
if err = dis.Set(ins); err != nil {
log.Errorf("dis.Set(%+v) error(%v)", ins, err)
time.Sleep(time.Second)
continue
}
time.Sleep(time.Second * 10)
}
}()
return cancel
}
================================================
FILE: cmd/job/job-example.toml
================================================
# This is a TOML document. Boom
[discovery]
nodes = ["127.0.0.1:7171"]
[kafka]
topic = "goim-push-topic"
group = "goim-push-group-job"
brokers = ["127.0.0.1:9092"]
================================================
FILE: cmd/job/main.go
================================================
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"github.com/bilibili/discovery/naming"
"github.com/Terry-Mao/goim/internal/job"
"github.com/Terry-Mao/goim/internal/job/conf"
resolver "github.com/bilibili/discovery/naming/grpc"
log "github.com/golang/glog"
)
var (
ver = "2.0.0"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
log.Infof("goim-job [version: %s env: %+v] start", ver, conf.Conf.Env)
// grpc register naming
dis := naming.New(conf.Conf.Discovery)
resolver.Register(dis)
// job
j := job.New(conf.Conf)
go j.Consume()
// signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Infof("goim-job get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
j.Close()
log.Infof("goim-job [version: %s] exit", ver)
log.Flush()
return
case syscall.SIGHUP:
default:
return
}
}
}
================================================
FILE: cmd/logic/logic-example.toml
================================================
# This is a TOML document. Boom
[discovery]
nodes = ["127.0.0.1:7171"]
[regions]
"bj" = ["北京","天津","河北","山东","山西","内蒙古","辽宁","吉林","黑龙江","甘肃","宁夏","新疆"]
"sh" = ["上海","江苏","浙江","安徽","江西","湖北","重庆","陕西","青海","河南","台湾"]
"gz" = ["广东","福建","广西","海南","湖南","四川","贵州","云南","西藏","香港","澳门"]
[node]
defaultDomain = "conn.goim.io"
hostDomain = ".goim.io"
heartbeat = "4m"
heartbeatMax = 2
tcpPort = 3101
wsPort = 3102
wssPort = 3103
regionWeight = 1.6
[backoff]
maxDelay = 300
baseDelay = 3
factor = 1.8
jitter = 0.3
[rpcServer]
network = "tcp"
addr = ":3119"
timeout = "1s"
[rpcClient]
dial = "1s"
timeout = "1s"
[httpServer]
network = "tcp"
addr = ":3111"
readTimeout = "1s"
writeTimeout = "1s"
[kafka]
topic = "goim-push-topic"
brokers = ["127.0.0.1:9092"]
[redis]
network = "tcp"
addr = "127.0.0.1:6379"
active = 60000
idle = 1024
dialTimeout = "200ms"
readTimeout = "500ms"
writeTimeout = "500ms"
idleTimeout = "120s"
expire = "30m"
================================================
FILE: cmd/logic/main.go
================================================
package main
import (
"context"
"flag"
"net"
"os"
"os/signal"
"strconv"
"syscall"
"github.com/bilibili/discovery/naming"
resolver "github.com/bilibili/discovery/naming/grpc"
"github.com/Terry-Mao/goim/internal/logic"
"github.com/Terry-Mao/goim/internal/logic/conf"
"github.com/Terry-Mao/goim/internal/logic/grpc"
"github.com/Terry-Mao/goim/internal/logic/http"
"github.com/Terry-Mao/goim/internal/logic/model"
"github.com/Terry-Mao/goim/pkg/ip"
log "github.com/golang/glog"
)
const (
ver = "2.0.0"
appid = "goim.logic"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
log.Infof("goim-logic [version: %s env: %+v] start", ver, conf.Conf.Env)
// grpc register naming
dis := naming.New(conf.Conf.Discovery)
resolver.Register(dis)
// logic
srv := logic.New(conf.Conf)
httpSrv := http.New(conf.Conf.HTTPServer, srv)
rpcSrv := grpc.New(conf.Conf.RPCServer, srv)
cancel := register(dis, srv)
// signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Infof("goim-logic get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
if cancel != nil {
cancel()
}
srv.Close()
httpSrv.Close()
rpcSrv.GracefulStop()
log.Infof("goim-logic [version: %s] exit", ver)
log.Flush()
return
case syscall.SIGHUP:
default:
return
}
}
}
func register(dis *naming.Discovery, srv *logic.Logic) context.CancelFunc {
env := conf.Conf.Env
addr := ip.InternalIP()
_, port, _ := net.SplitHostPort(conf.Conf.RPCServer.Addr)
ins := &naming.Instance{
Region: env.Region,
Zone: env.Zone,
Env: env.DeployEnv,
Hostname: env.Host,
AppID: appid,
Addrs: []string{
"grpc://" + addr + ":" + port,
},
Metadata: map[string]string{
model.MetaWeight: strconv.FormatInt(env.Weight, 10),
},
}
cancel, err := dis.Register(ins)
if err != nil {
panic(err)
}
return cancel
}
================================================
FILE: codecov.sh
================================================
#!/usr/bin/env bash
set -e
echo "" > coverage.txt
for d in $(go list ./... | grep -v cmd | grep -v docs | grep -v srcipts | grep -v benchmarks | grep -v examples); do
echo "testing for $d ..."
go test -coverprofile=profile.out -covermode=atomic $d
if [ -f profile.out ]; then
cat profile.out >> coverage.txt
rm profile.out
fi
done
================================================
FILE: docs/benchmark_cn.md
================================================
## 压测图表

### 服务端配置
| CPU | 内存 | 操作系统 | 数量 |
| :---- | :---- | :---- | :---- |
| Intel(R) Xeon(R) CPU E5-2630 v2 @ 2.60GHz | DDR3 32GB | Debian GNU/Linux 8 | 1 |
### 压测参数
* 不同UID同房间在线人数: 1,000,000
* 持续推送时长: 15分钟
* 持续推送数量: 40条/秒
* 推送内容: {"test":1}
* 推送类型: 单房间推送
* 到达计算方式: 1秒统计一次,共30次
### 资源使用
* 每台服务端CPU使用: 2000%~2300%(刚好满负载)
* 每台服务端内存使用: 14GB左右
* GC耗时: 504毫秒左右
* 流量使用: Incoming(450MBit/s), Outgoing(4.39GBit/s)
### 压测结果
* 推送到达: 3590万/秒左右;
## comet模块

## 流量

## heap信息(包含GC)

================================================
FILE: docs/benchmark_en.md
================================================
## Benchmark Chart

### Benchmark Server
| CPU | Memory | OS | Instance |
| :---- | :---- | :---- | :---- |
| Intel(R) Xeon(R) CPU E5-2630 v2 @ 2.60GHz | DDR3 32GB | Debian GNU/Linux 8 | 1 |
### Benchmark Case
* Online: 1,000,000
* Duration: 15min
* Push Speed: 40/s (broadcast room)
* Push Message: {"test":1}
* Received calc mode: 1s per times, total 30 times
### Benchmark Resource
* CPU: 2000%~2300%
* Memory: 14GB
* GC Pause: 504ms
* Network: Incoming(450MBit/s), Outgoing(4.39GBit/s)
### Benchmark Result
* Received: 35,900,000/s
## Comet

## Network traffic

## Heap (include GC)

================================================
FILE: docs/en/proto.md
================================================
# comet and clients protocols
comet supports two protocols to communicate with client: WebSocket, TCP
## websocket
**Request URL**
ws://DOMAIN/sub
**HTTP Request Method**
WebSocket (JSON Frame). Response is same as the request.
**Response Result**
```json
{
"ver": 102,
"op": 10,
"seq": 10,
"body": {"data": "xxx"}
}
```
**Request and Response Parameters**
| parameter | is required | type | comment|
| :----- | :--- | :--- | :--- |
| ver | true | int | Protocol version |
| op | true | int | Operation |
| seq | true | int | Sequence number (Server returned number maps to client sent) |
| body | json | The JSON message pushed |
## tcp
**Request URL**
tcp://DOMAIN
**Protocol**
Binary. Response is same as the request.
**Request and Response Parameters**
| parameter | is required | type | comment|
| :----- | :--- | :--- | :--- |
| package length | true | int32 bigendian | package length |
| header Length | true | int16 bigendian | header length |
| ver | true | int16 bigendian | Protocol version |
| operation | true | int32 bigendian | Operation |
| seq | true | int32 bigendian | jsonp callback |
| body | false | binary | $(package lenth) - $(header length) |
## Operations
| operation | comment |
| :----- | :--- |
| 2 | Client send heartbeat|
| 3 | Server reply heartbeat|
| 7 | authentication request |
| 8 | authentication response |
================================================
FILE: docs/en/push.md
================================================
<h3>Terry-Mao/goim push HTTP protocols</h3>
push HTTP interface protocols for pusher
<h3>Interfaces</h3>
| Name | URL | HTTP method |
| :---- | :---- | :---- |
| [single push](#single push) | /1/push | POST |
| [multiple push](#multiple push) | /1/pushs | POST |
| [room push](#room push) | /1/push/room | POST |
| [broadcasting](#broadcasting) | /1/push/all | POST |
<h3>Public response body</h3>
| response code | description |
| :---- | :---- |
| 1 | success |
| 65535 | internal error |
<h3>Response structure</h3>
<pre>
{
"ret": 1 //response code
}
</pre>
##### single push
* Example request
```sh
# uid is the user id pushing to?uid=0
curl -d "{\"test\":1}" http://127.0.0.1:7172/1/push?uid=0
```
* Response
<pre>
{
"ret": 1
}
</pre>
##### Multiple push
* Example request
```sh
curl -d "{\"u\":[1,2,3,4,5],\"m\":{\"test\":1}}" http://127.0.0.1:7172/1/pushs
```
* Response
<pre>
{
"ret": 1
}
</pre>
##### room push
* Example request
```sh
curl -d "{\"test\": 1}" http://127.0.0.1:7172/1/push/room?rid=1
```
* Response
<pre>
{
"ret": 1
}
</pre>
##### Broadcasting
* Example request
```sh
curl -d "{\"test\": 1}" http://127.0.0.1:7172/1/push/all
```
* Response
<pre>
{
"ret": 1
}
</pre>
================================================
FILE: docs/proto.md
================================================
# comet 客户端通讯协议文档
comet支持两种协议和客户端通讯 websocket, tcp。
## websocket
**请求URL**
ws://DOMAIN/sub
**HTTP请求方式**
Websocket(JSON Frame),请求和返回协议一致
**请求和返回json**
```json
{
"ver": 102,
"op": 10,
"seq": 10,
"body": {"data": "xxx"}
}
```
**请求和返回参数说明**
| 参数名 | 必选 | 类型 | 说明 |
| :----- | :--- | :--- | :--- |
| ver | true | int | 协议版本号 |
| op | true | int | 指令 |
| seq | true | int | 序列号(服务端返回和客户端发送一一对应) |
| body | true | string | 授权令牌,用于检验获取用户真实用户Id |
## tcp
**请求URL**
tcp://DOMAIN
**协议格式**
二进制,请求和返回协议一致
**请求&返回参数**
| 参数名 | 必选 | 类型 | 说明 |
| :----- | :--- | :--- | :--- |
| package length | true | int32 bigendian | 包长度 |
| header Length | true | int16 bigendian | 包头长度 |
| ver | true | int16 bigendian | 协议版本 |
| operation | true | int32 bigendian | 协议指令 |
| seq | true | int32 bigendian | 序列号 |
| body | false | binary | $(package lenth) - $(header length) |
## 指令
| 指令 | 说明 |
| :----- | :--- |
| 2 | 客户端请求心跳 |
| 3 | 服务端心跳答复 |
| 5 | 下行消息 |
| 7 | auth认证 |
| 8 | auth认证返回 |
================================================
FILE: docs/push.md
================================================
## goim push API
### error codes
```
// ok
OK = 0
// request error
RequestErr = -400
// server error
ServerErr = -500
```
### push keys
[POST] /goim/push/keys
| Name | Type | Remork |
|:----------------|:--------:|:-----------------------|
| [url]:operation | int32 | operation for response |
| [url]:keys | []string | multiple client keys |
| [Body] | []byte | http request body |
response:
```
{
"code": 0
}
```
### push mids
[POST] /goim/push/mids
| Name | Type | Remork |
|:----------------|:--------:|:-----------------------|
| [url]:operation | int32 | operation for response |
| [url]:mids | []int64 | multiple user mids |
| [Body] | []byte | http request body |
response:
```
{
"code": 0
}
```
### push room
[POST] /goim/push/room
| Name | Type | Remork |
|:----------------|:--------:|:-----------------------|
| [url]:operation | int32 | operation for response |
| [url]:type | string | room type |
| [url]:room | string | room id |
| [Body] | []byte | http request body |
response:
```
{
"code": 0
}
```
### push all
[POST] /goim/push/all
| Name | Type | Remork |
|:----------------|:--------:|:-----------------------|
| [url]:operation | int32 | operation for response |
| [url]:speed | int32 | push speed |
| [Body] | []byte | http request body |
response:
```
{
"code": 0
}
```
### online top
[GET] /goim/online/top
| Name | Type | Remork |
|:--------|:--------:|:-----------------------|
| type | string | room type |
| limit | string | online limit |
response:
```
{
"code": 0,
"message": "",
"data": [
{
"room_id": "1000",
"count": 100
},
{
"room_id": "2000",
"count": 200
},
{
"room_id": "3000",
"count": 300
}
]
}
```
### online room
[GET] /goim/online/room
| Name | Type | Remork |
|:--------|:--------:|:-----------------------|
| type | string | room type |
| rooms | []string | room ids |
response:
```
{
"code": 0,
"message": "",
"data": {
"1000": 100,
"2000": 200,
"3000": 300
}
}
```
### online total
[GET] /goim/online/total
response:
```
{
"code": 0,
"message": "",
"data": {
"conn_count": 1,
"ip_count": 1
}
}
```
### nodes weighted
[GET] /goim/nodes/weighted
| Name | Type | Remork |
|:---------|:--------:|:-----------------------|
| platform | string | web/android/ios |
response:
```
{
"code": 0,
"message": "",
"data": {
"domain": "conn.goim.io",
"tcp_port": 3101,
"ws_port": 3102,
"wss_port": 3103,
"heartbeat": 30, // heartbeat seconds
"heartbeat_max": 3 // heartbeat tries
"nodes": [
"47.89.10.97"
],
"backoff": {
"max_delay": 300,
"base_delay": 3,
"factor": 1.8,
"jitter": 0.3
},
}
}
```
### nodes instances
[GET] /nodes/instances
response:
```
{
"code": 0,
"message": "",
"data": [
{
"region": "sh",
"zone": "sh001",
"env": "dev",
"appid": "goim.comet",
"hostname": "test",
"addrs": [
"grpc://192.168.1.30:3109"
],
"version": "",
"latest_timestamp": 1545750122311688676,
"metadata": {
"addrs": "47.89.10.97",
"conn_count": "1",
"ip_count": "1",
"offline": "false",
"weight": "10"
}
}
]
}
`
================================================
FILE: examples/cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIJAKWU8wETRh4fMA0GCSqGSIb3DQEBBQUAMIGEMQswCQYD
VQQGEwJjbjERMA8GA1UECAwIc2hhbmdoYWkxETAPBgNVBAcMCHNoYW5naGFpMQ0w
CwYDVQQKDARiaWxpMQ0wCwYDVQQLDARiaWxpMREwDwYDVQQDDAhiaWxpLmNvbTEe
MBwGCSqGSIb3DQEJARYPMjI0MzAzNTRAcXEuY29tMB4XDTE1MDkwMTEyMDQxMloX
DTI1MDgyOTEyMDQxMlowgYQxCzAJBgNVBAYTAmNuMREwDwYDVQQIDAhzaGFuZ2hh
aTERMA8GA1UEBwwIc2hhbmdoYWkxDTALBgNVBAoMBGJpbGkxDTALBgNVBAsMBGJp
bGkxETAPBgNVBAMMCGJpbGkuY29tMR4wHAYJKoZIhvcNAQkBFg8yMjQzMDM1NEBx
cS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKFwSxJqYPxzMe
m5PeYA4YmcUsDCqS9Z7PsszOMZ1YsWZIHMB74D49ad2R+9PoqlfNH1L9C4NFSBrF
rhSkaLmFYxw9yeJ2EAPijASBgfxMFVrEJhu7SW86OPTVnHblU8UqQdnMFOqF49C9
mCdbiGu/99BZVCL1WmlSQCWVEIzOgX+goxqHuwXUF58YUwr6WLtF0DuBcLUai1vB
Pg+PJ2fLjSR2o0KJkPOd6+y90cgoxfyJFUHuUKyV8EU4VwHEIA9rVizprziqPx6c
9A9Ng0FpA2leSPLGYCjnDtKIOvbSOS8DMkRT55ujqoVrj0yiNWsuJlc/NbD6bS16
fJjuLOtJAgMBAAGjUDBOMB0GA1UdDgQWBBQzxdSIYIkDABh98Cj6VeYasC7/STAf
BgNVHSMEGDAWgBQzxdSIYIkDABh98Cj6VeYasC7/STAMBgNVHRMEBTADAQH/MA0G
CSqGSIb3DQEBBQUAA4IBAQA1Fhr+SU62xHWlPOBhTbjod49+mNfXn2TZz/vBp/Jl
pHZgDLAEcrhXHmi2A0G9K9+qOIEn4BvTd70jSYvYlaeUSzZ/nEpeM0oE0f2Qaxov
PhxDpsqPsSQm6pE64/los1doaiElfMVFaP56UGV01kFdI013wxwd2WCuj51Hmvi9
thsS027aqxjHMJnKXPvBm2E6EDkPfc/e+AEmwBzry+aamRizaMrk/SfSGTy9/rvd
+VbBfHiJ50kMld51SLIc6qkVaTXess7mIfcsk7kyjP4eFA0y+3wmXfRZeWadND3I
O9XNNwsDVFXlhW40GUVriy95qa1Sq3sLUfUQcCH4VFFK
-----END CERTIFICATE-----
================================================
FILE: examples/javascript/client.js
================================================
(function(win) {
const rawHeaderLen = 16;
const packetOffset = 0;
const headerOffset = 4;
const verOffset = 6;
const opOffset = 8;
const seqOffset = 12;
var Client = function(options) {
var MAX_CONNECT_TIMES = 10;
var DELAY = 15000;
this.options = options || {};
this.createConnect(MAX_CONNECT_TIMES, DELAY);
}
var appendMsg = function(text) {
var span = document.createElement("SPAN");
var text = document.createTextNode(text);
span.appendChild(text);
document.getElementById("box").appendChild(span);
}
Client.prototype.createConnect = function(max, delay) {
var self = this;
if (max === 0) {
return;
}
connect();
var textDecoder = new TextDecoder();
var textEncoder = new TextEncoder();
var heartbeatInterval;
function connect() {
var ws = new WebSocket('ws://sh.tony.wiki:3102/sub');
//var ws = new WebSocket('ws://127.0.0.1:3102/sub');
ws.binaryType = 'arraybuffer';
ws.onopen = function() {
auth();
}
ws.onmessage = function(evt) {
var data = evt.data;
var dataView = new DataView(data, 0);
var packetLen = dataView.getInt32(packetOffset);
var headerLen = dataView.getInt16(headerOffset);
var ver = dataView.getInt16(verOffset);
var op = dataView.getInt32(opOffset);
var seq = dataView.getInt32(seqOffset);
console.log("receiveHeader: packetLen=" + packetLen, "headerLen=" + headerLen, "ver=" + ver, "op=" + op, "seq=" + seq);
switch(op) {
case 8:
// auth reply ok
document.getElementById("status").innerHTML = "<color style='color:green'>ok<color>";
appendMsg("receive: auth reply");
// send a heartbeat to server
heartbeat();
heartbeatInterval = setInterval(heartbeat, 30 * 1000);
break;
case 3:
// receive a heartbeat from server
console.log("receive: heartbeat");
appendMsg("receive: heartbeat reply");
break;
case 9:
// batch message
for (var offset=rawHeaderLen; offset<data.byteLength; offset+=packetLen) {
// parse
var packetLen = dataView.getInt32(offset);
var headerLen = dataView.getInt16(offset+headerOffset);
var ver = dataView.getInt16(offset+verOffset);
var op = dataView.getInt32(offset+opOffset);
var seq = dataView.getInt32(offset+seqOffset);
var msgBody = textDecoder.decode(data.slice(offset+headerLen, offset+packetLen));
// callback
messageReceived(ver, msgBody);
appendMsg("receive: ver=" + ver + " op=" + op + " seq=" + seq + " message=" + msgBody);
}
break;
default:
var msgBody = textDecoder.decode(data.slice(headerLen, packetLen));
messageReceived(ver, msgBody);
appendMsg("receive: ver=" + ver + " op=" + op + " seq=" + seq + " message=" + msgBody);
break
}
}
ws.onclose = function() {
if (heartbeatInterval) clearInterval(heartbeatInterval);
setTimeout(reConnect, delay);
document.getElementById("status").innerHTML = "<color style='color:red'>failed<color>";
}
function heartbeat() {
var headerBuf = new ArrayBuffer(rawHeaderLen);
var headerView = new DataView(headerBuf, 0);
headerView.setInt32(packetOffset, rawHeaderLen);
headerView.setInt16(headerOffset, rawHeaderLen);
headerView.setInt16(verOffset, 1);
headerView.setInt32(opOffset, 2);
headerView.setInt32(seqOffset, 1);
ws.send(headerBuf);
console.log("send: heartbeat");
appendMsg("send: heartbeat");
}
function auth() {
var token = '{"mid":123, "room_id":"live://1000", "platform":"web", "accepts":[1000,1001,1002]}'
var headerBuf = new ArrayBuffer(rawHeaderLen);
var headerView = new DataView(headerBuf, 0);
var bodyBuf = textEncoder.encode(token);
headerView.setInt32(packetOffset, rawHeaderLen + bodyBuf.byteLength);
headerView.setInt16(headerOffset, rawHeaderLen);
headerView.setInt16(verOffset, 1);
headerView.setInt32(opOffset, 7);
headerView.setInt32(seqOffset, 1);
ws.send(mergeArrayBuffer(headerBuf, bodyBuf));
appendMsg("send: auth token: " + token);
}
function messageReceived(ver, body) {
var notify = self.options.notify;
if(notify) notify(body);
console.log("messageReceived:", "ver=" + ver, "body=" + body);
}
function mergeArrayBuffer(ab1, ab2) {
var u81 = new Uint8Array(ab1),
u82 = new Uint8Array(ab2),
res = new Uint8Array(ab1.byteLength + ab2.byteLength);
res.set(u81, 0);
res.set(u82, ab1.byteLength);
return res.buffer;
}
function char2ab(str) {
var buf = new ArrayBuffer(str.length);
var bufView = new Uint8Array(buf);
for (var i=0; i<str.length; i++) {
bufView[i] = str[i];
}
return buf;
}
}
function reConnect() {
self.createConnect(--max, delay * 2);
}
}
win['MyClient'] = Client;
})(window);
================================================
FILE: examples/javascript/index.html
================================================
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>client demo</title>
<script src="client.js"></script>
<script>
var client = new MyClient({
notify: function(data) {
console.log(data);
// alert(JSON.stringify(data));
}
});
</script>
<style type="text/css">
span{ display:block;}
</style>
</head>
<body>
<h1>websocket</h1>
<h2>status:</h2>
<span id="status"></span>
<h2>push</h2>
<div>
<p>curl -d 'mid message' 'http://api.goim.io:3111/goim/push/mids?operation=1000&mids=123'</p>
<p>curl -d 'room message' 'http://api.goim.io:3111/goim/push/room?operation=1000&type=live&room=1000'</p>
<p>curl -d 'broadcast message' 'http://api.goim.io:3111/goim/push/all?operation=1000'</p>
</div>
<h2>message:</h2>
<div id="box"></div>
</body>
</html>
================================================
FILE: examples/javascript/main.go
================================================
package main
import (
"log"
"net/http"
)
func main() {
// Simple static webserver:
log.Fatal(http.ListenAndServe(":1999", http.FileServer(http.Dir("./"))))
}
================================================
FILE: examples/private.pem
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAyhcEsSamD8czHpuT3mAOGJnFLAwqkvWez7LMzjGdWLFmSBzA
e+A+PWndkfvT6KpXzR9S/QuDRUgaxa4UpGi5hWMcPcnidhAD4owEgYH8TBVaxCYb
u0lvOjj01Zx25VPFKkHZzBTqhePQvZgnW4hrv/fQWVQi9VppUkAllRCMzoF/oKMa
h7sF1BefGFMK+li7RdA7gXC1GotbwT4Pjydny40kdqNCiZDznevsvdHIKMX8iRVB
7lCslfBFOFcBxCAPa1Ys6a84qj8enPQPTYNBaQNpXkjyxmAo5w7SiDr20jkvAzJE
U+ebo6qFa49MojVrLiZXPzWw+m0tenyY7izrSQIDAQABAoIBAQCNasAwy2/nmKjw
IUS/l44lru1oXncocdMZWvCw1c1a9IEzs1MLHKfRSBTyBDyNEy7v7pyfUQAiaku5
y5DMYDB65BkuL+lWXuypCvxYOEL6ZvMmUdiUHdZE8vh5xsz4u788S+qCQpy+5uX6
1s+r4PIt2tekuxjfgs4y7YqfHn66PmxAWXTV+jnWpUWYjpRTgDWeU6ikbk2fHjvG
ytF4AV4lEKNnGBC5F+5lk3QJKjb1IW/o5q42TCeERk1k5Ruc5kfzq72hdFYrQAF1
PmstEXMExMS/K6AerYtPfiWWFWH2gCTZMf/C8Jwr0zPVpHM1PF+Ien3IkgiOTfZE
1+/wrx4BAoGBAOnpnxFnnqhZdDRi+DALfY3mXPwOizFYnAhuuuzO87zoZsps529E
j5pEZQoYPTiww5rptjFhNjoV0gsh2GO5QHCiMxM8A76aWVc4YmK0GiHVudpAu2t+
aK8+0Xr03SA27cKJjp12NWijzEdTjSQYaFwD/8/dMrU3MCu30kdEJ6iJAoGBAN0s
JXLv4CtB/DLTvifTZ/OAGSc1/Z+X0w2EqIugw7IV9YuAm2721IFhsFFXeANRRHY6
zonLUEgsuQUc30NCz+susChs2GMEFypLem8D1c5ZZY+9sIsR1WFB97o7bTMvp5lH
2REpEDZKXVOB7UmrclLgTKRQgG3O73rMP6QXYPzBAoGBAJm8Gvi0crlQuagol9fz
5Vwa2GgtItyW0U5VgHNdfSJeWBiYxO8DT6Jja0jcL3iP7K9nBYCk1KAOcVMxtmes
fKbKY+kzW36tMSS7ASbAGiC8uH6yZru6hBERp1o5jw+6Kj/eaqYg5+9TIFKMnknn
5Mb9NecnCUnC8Nz63rBKIgqJAoGBAJHUpeyfFaPwIiYxT1RbJFN9xxf/lXdBWDu1
mJxYKDCoIfsVlWcZAQ0+KE+56LvnPcjnBX/9urWcJ3KjkuJ6jzV211gQTK0c6VlN
4zCHytYAQ+L/JATOgW9bW8hDnsD9TvjWUt3pwXLKnbaOGLNWhE747g/5tHSy2VyS
h/PeJmkBAoGABMfEaiLHXXQhUK0BPYxT3T8i9IjAYwXlrgSLlnvOGslZef/45kP5
CM1UbMSwAn+HvAziOFt2WmynFCysy/lCyTud+Fd/IZFcMThp7wvi7fSZo0NDM7ES
9JfgTmCY4Kwv6kT85poIka9bp4Nh47EVB9kDoqm/lSMkfYqcWH66DJA=
-----END RSA PRIVATE KEY-----
================================================
FILE: go.mod
================================================
module github.com/Terry-Mao/goim
go 1.13
require (
github.com/BurntSushi/toml v0.3.1
github.com/Shopify/sarama v1.19.0 // indirect
github.com/Shopify/toxiproxy v2.1.4+incompatible // indirect
github.com/bilibili/discovery v1.0.1
github.com/bsm/sarama-cluster v2.1.15+incompatible
github.com/eapache/go-resiliency v1.1.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/gin-gonic/gin v1.7.0
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/protobuf v1.4.3
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
github.com/gomodule/redigo v2.0.0+incompatible
github.com/google/uuid v1.0.0
github.com/onsi/ginkgo v1.16.0 // indirect
github.com/onsi/gomega v1.11.0 // indirect
github.com/pierrec/lz4 v2.0.5+incompatible // indirect
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect
github.com/stretchr/testify v1.5.1
github.com/zhenjl/cityhash v0.0.0-20131128155616-cdd6a94144ab
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb
google.golang.org/grpc v1.22.3
gopkg.in/Shopify/sarama.v1 v1.19.0
)
================================================
FILE: go.sum
================================================
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.0.0-20170626110600-a368813c5e64/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/bilibili/discovery v1.0.1 h1:6W9B2caxOdfBEKCMawwXU3dJ0W1TCONuprbXbkGe+s4=
github.com/bilibili/discovery v1.0.1/go.mod h1:daS5nEYEBt0scrrmuoNCxWXDHFK6gtEpjhVKG6MUxUg=
github.com/bsm/sarama-cluster v2.1.15+incompatible h1:RkV6WiNRnqEEbp81druK8zYhmnIgdOjqSVi0+9Cnl2A=
github.com/bsm/sarama-cluster v2.1.15+incompatible/go.mod h1:r7ao+4tTNXvWm+VRpRJchr2kQhqxgmAp2iEX5W96gMM=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v0.0.0-20180512030042-bf7803815b0b/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU=
github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20180424202546-8dffc02ea1cb h1:g0omhilXoAZ+6sFcF6puAzT+/MoKK3ZBQ9e7nVIRjrc=
github.com/gopherjs/gopherjs v0.0.0-20180424202546-8dffc02ea1cb/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/json-iterator/go v0.0.0-20180526014329-8744d7c5c7b4/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180511053014-58118c1ea916/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.0 h1:NBrNLB37exjJLxXtFOktx6CISBdS1aF8+7MwKlTV8U4=
github.com/onsi/ginkgo v1.16.0/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug=
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/smartystreets/assertions v0.0.0-20180301161246-7678a5452ebe/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/smartystreets/gunit v0.0.0-20180314194857-6f0d6275bdcd/go.mod h1:XUKj4gbqj2QvJk/OdLWzyZ3FYli0f+MdpngyryX0gcw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/ugorji/go v0.0.0-20180407103000-f3cacc17c85e/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zhenjl/cityhash v0.0.0-20131128155616-cdd6a94144ab h1:BWHvAOZz0pBILkGl/ebPQKZDrqbaWj/iN9RE8AvaTvg=
github.com/zhenjl/cityhash v0.0.0-20131128155616-cdd6a94144ab/go.mod h1:P6L88wrqK99Njntah9SB7AyzFpUXsXYq06LkjixxQmY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v0.0.0-20181030232906-a88340f3c899/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.22.3 h1:U4Oh9xJixJwAFqa1c5uLhNAh8ERM/lc3hNhPEJiAEhs=
google.golang.org/grpc v1.22.3/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/Shopify/sarama.v1 v1.19.0 h1:yvI/R1jfMpKvvwmX4r/AQjaI5oszWEOlvKxUdaj53OM=
gopkg.in/Shopify/sarama.v1 v1.19.0/go.mod h1:AxnvoaevB2nBjNK17cG61A3LleFcWFwVBHBt+cot4Oc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/h2non/gock.v1 v1.0.8 h1:P8Ul3tXxL84suEhp+a7Uu6f9rBszP+gLkae2D6U1gS0=
gopkg.in/h2non/gock.v1 v1.0.8/go.mod h1:KHI4Z1sxDW6P4N3DfTWSEza07YpkQP7KJBfglRMEjKY=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
================================================
FILE: internal/comet/bucket.go
================================================
package comet
import (
"sync"
"sync/atomic"
pb "github.com/Terry-Mao/goim/api/comet"
"github.com/Terry-Mao/goim/api/protocol"
"github.com/Terry-Mao/goim/internal/comet/conf"
)
// Bucket is a channel holder.
type Bucket struct {
c *conf.Bucket
cLock sync.RWMutex // protect the channels for chs
chs map[string]*Channel // map sub key to a channel
// room
rooms map[string]*Room // bucket room channels
routines []chan *pb.BroadcastRoomReq
routinesNum uint64
ipCnts map[string]int32
}
// NewBucket new a bucket struct. store the key with im channel.
func NewBucket(c *conf.Bucket) (b *Bucket) {
b = new(Bucket)
b.chs = make(map[string]*Channel, c.Channel)
b.ipCnts = make(map[string]int32)
b.c = c
b.rooms = make(map[string]*Room, c.Room)
b.routines = make([]chan *pb.BroadcastRoomReq, c.RoutineAmount)
for i := uint64(0); i < c.RoutineAmount; i++ {
c := make(chan *pb.BroadcastRoomReq, c.RoutineSize)
b.routines[i] = c
go b.roomproc(c)
}
return
}
// ChannelCount channel count in the bucket
func (b *Bucket) ChannelCount() int {
return len(b.chs)
}
// RoomCount room count in the bucket
func (b *Bucket) RoomCount() int {
return len(b.rooms)
}
// RoomsCount get all room id where online number > 0.
func (b *Bucket) RoomsCount() (res map[string]int32) {
var (
roomID string
room *Room
)
b.cLock.RLock()
res = make(map[string]int32)
for roomID, room = range b.rooms {
if room.Online > 0 {
res[roomID] = room.Online
}
}
b.cLock.RUnlock()
return
}
// ChangeRoom change ro room
func (b *Bucket) ChangeRoom(nrid string, ch *Channel) (err error) {
var (
nroom *Room
ok bool
oroom = ch.Room
)
// change to no room
if nrid == "" {
if oroom != nil && oroom.Del(ch) {
b.DelRoom(oroom)
}
ch.Room = nil
return
}
b.cLock.Lock()
if nroom, ok = b.rooms[nrid]; !ok {
nroom = NewRoom(nrid)
b.rooms[nrid] = nroom
}
b.cLock.Unlock()
if oroom != nil && oroom.Del(ch) {
b.DelRoom(oroom)
}
if err = nroom.Put(ch); err != nil {
return
}
ch.Room = nroom
return
}
// Put put a channel according with sub key.
func (b *Bucket) Put(rid string, ch *Channel) (err error) {
var (
room *Room
ok bool
)
b.cLock.Lock()
// close old channel
if dch := b.chs[ch.Key]; dch != nil {
dch.Close()
}
b.chs[ch.Key] = ch
if rid != "" {
if room, ok = b.rooms[rid]; !ok {
room = NewRoom(rid)
b.rooms[rid] = room
}
ch.Room = room
}
b.ipCnts[ch.IP]++
b.cLock.Unlock()
if room != nil {
err = room.Put(ch)
}
return
}
// Del delete the channel by sub key.
func (b *Bucket) Del(dch *Channel) {
room := dch.Room
b.cLock.Lock()
if ch, ok := b.chs[dch.Key]; ok {
if ch == dch {
delete(b.chs, ch.Key)
}
// ip counter
if b.ipCnts[ch.IP] > 1 {
b.ipCnts[ch.IP]--
} else {
delete(b.ipCnts, ch.IP)
}
}
b.cLock.Unlock()
if room != nil && room.Del(dch) {
// if empty room, must delete from bucket
b.DelRoom(room)
}
}
// Channel get a channel by sub key.
func (b *Bucket) Channel(key string) (ch *Channel) {
b.cLock.RLock()
ch = b.chs[key]
b.cLock.RUnlock()
return
}
// Broadcast push msgs to all channels in the bucket.
func (b *Bucket) Broadcast(p *protocol.Proto, op int32) {
var ch *Channel
b.cLock.RLock()
for _, ch = range b.chs {
if !ch.NeedPush(op) {
continue
}
_ = ch.Push(p)
}
b.cLock.RUnlock()
}
// Room get a room by roomid.
func (b *Bucket) Room(rid string) (room *Room) {
b.cLock.RLock()
room = b.rooms[rid]
b.cLock.RUnlock()
return
}
// DelRoom delete a room by roomid.
func (b *Bucket) DelRoom(room *Room) {
b.cLock.Lock()
delete(b.rooms, room.ID)
b.cLock.Unlock()
room.Close()
}
// BroadcastRoom broadcast a message to specified room
func (b *Bucket) BroadcastRoom(arg *pb.BroadcastRoomReq) {
num := atomic.AddUint64(&b.routinesNum, 1) % b.c.RoutineAmount
b.routines[num] <- arg
}
// Rooms get all room id where online number > 0.
func (b *Bucket) Rooms() (res map[string]struct{}) {
var (
roomID string
room *Room
)
res = make(map[string]struct{})
b.cLock.RLock()
for roomID, room = range b.rooms {
if room.Online > 0 {
res[roomID] = struct{}{}
}
}
b.cLock.RUnlock()
return
}
// IPCount get ip count.
func (b *Bucket) IPCount() (res map[string]struct{}) {
var (
ip string
)
b.cLock.RLock()
res = make(map[string]struct{}, len(b.ipCnts))
for ip = range b.ipCnts {
res[ip] = struct{}{}
}
b.cLock.RUnlock()
return
}
// UpRoomsCount update all room count
func (b *Bucket) UpRoomsCount(roomCountMap map[string]int32) {
var (
roomID string
room *Room
)
b.cLock.RLock()
for roomID, room = range b.rooms {
room.AllOnline = roomCountMap[roomID]
}
b.cLock.RUnlock()
}
// roomproc
func (b *Bucket) roomproc(c chan *pb.BroadcastRoomReq) {
for {
arg := <-c
if room := b.Room(arg.RoomID); room != nil {
room.Push(arg.Proto)
}
}
}
================================================
FILE: internal/comet/channel.go
================================================
package comet
import (
"sync"
"github.com/Terry-Mao/goim/api/protocol"
"github.com/Terry-Mao/goim/internal/comet/errors"
"github.com/Terry-Mao/goim/pkg/bufio"
)
// Channel used by message pusher send msg to write goroutine.
type Channel struct {
Room *Room
CliProto Ring
signal chan *protocol.Proto
Writer bufio.Writer
Reader bufio.Reader
Next *Channel
Prev *Channel
Mid int64
Key string
IP string
watchOps map[int32]struct{}
mutex sync.RWMutex
}
// NewChannel new a channel.
func NewChannel(cli, svr int) *Channel {
c := new(Channel)
c.CliProto.Init(cli)
c.signal = make(chan *protocol.Proto, svr)
c.watchOps = make(map[int32]struct{})
return c
}
// Watch watch a operation.
func (c *Channel) Watch(accepts ...int32) {
c.mutex.Lock()
for _, op := range accepts {
c.watchOps[op] = struct{}{}
}
c.mutex.Unlock()
}
// UnWatch unwatch an operation
func (c *Channel) UnWatch(accepts ...int32) {
c.mutex.Lock()
for _, op := range accepts {
delete(c.watchOps, op)
}
c.mutex.Unlock()
}
// NeedPush verify if in watch.
func (c *Channel) NeedPush(op int32) bool {
c.mutex.RLock()
if _, ok := c.watchOps[op]; ok {
c.mutex.RUnlock()
return true
}
c.mutex.RUnlock()
return false
}
// Push server push message.
func (c *Channel) Push(p *protocol.Proto) (err error) {
select {
case c.signal <- p:
default:
err = errors.ErrSignalFullMsgDropped
}
return
}
// Ready check the channel ready or close?
func (c *Channel) Ready() *protocol.Proto {
return <-c.signal
}
// Signal send signal to the channel, protocol ready.
func (c *Channel) Signal() {
c.signal <- protocol.ProtoReady
}
// Close close the channel.
func (c *Channel) Close() {
c.signal <- protocol.ProtoFinish
}
================================================
FILE: internal/comet/conf/conf.go
================================================
package conf
import (
"flag"
"os"
"strconv"
"strings"
"time"
"github.com/bilibili/discovery/naming"
"github.com/BurntSushi/toml"
xtime "github.com/Terry-Mao/goim/pkg/time"
)
var (
confPath string
region string
zone string
deployEnv string
host string
addrs string
weight int64
offline bool
debug bool
// Conf config
Conf *Config
)
func init() {
var (
defHost, _ = os.Hostname()
defAddrs = os.Getenv("ADDRS")
defWeight, _ = strconv.ParseInt(os.Getenv("WEIGHT"), 10, 32)
defOffline, _ = strconv.ParseBool(os.Getenv("OFFLINE"))
defDebug, _ = strconv.ParseBool(os.Getenv("DEBUG"))
)
flag.StringVar(&confPath, "conf", "comet-example.toml", "default config path.")
flag.StringVar(®ion, "region", os.Getenv("REGION"), "avaliable region. or use REGION env variable, value: sh etc.")
flag.StringVar(&zone, "zone", os.Getenv("ZONE"), "avaliable zone. or use ZONE env variable, value: sh001/sh002 etc.")
flag.StringVar(&deployEnv, "deploy.env", os.Getenv("DEPLOY_ENV"), "deploy env. or use DEPLOY_ENV env variable, value: dev/fat1/uat/pre/prod etc.")
flag.StringVar(&host, "host", defHost, "machine hostname. or use default machine hostname.")
flag.StringVar(&addrs, "addrs", defAddrs, "server public ip addrs. or use ADDRS env variable, value: 127.0.0.1 etc.")
flag.Int64Var(&weight, "weight", defWeight, "load balancing weight, or use WEIGHT env variable, value: 10 etc.")
flag.BoolVar(&offline, "offline", defOffline, "server offline. or use OFFLINE env variable, value: true/false etc.")
flag.BoolVar(&debug, "debug", defDebug, "server debug. or use DEBUG env variable, value: true/false etc.")
}
// Init init config.
func Init() (err error) {
Conf = Default()
_, err = toml.DecodeFile(confPath, &Conf)
return
}
// Default new a config with specified defualt value.
func Default() *Config {
return &Config{
Debug: debug,
Env: &Env{Region: region, Zone: zone, DeployEnv: deployEnv, Host: host, Weight: weight, Addrs: strings.Split(addrs, ","), Offline: offline},
Discovery: &naming.Config{Region: region, Zone: zone, Env: deployEnv, Host: host},
RPCClient: &RPCClient{
Dial: xtime.Duration(time.Second),
Timeout: xtime.Duration(time.Second),
},
RPCServer: &RPCServer{
Network: "tcp",
Addr: ":3109",
Timeout: xtime.Duration(time.Second),
IdleTimeout: xtime.Duration(time.Second * 60),
MaxLifeTime: xtime.Duration(time.Hour * 2),
ForceCloseWait: xtime.Duration(time.Second * 20),
KeepAliveInterval: xtime.Duration(time.Second * 60),
KeepAliveTimeout: xtime.Duration(time.Second * 20),
},
TCP: &TCP{
Bind: []string{":3101"},
Sndbuf: 4096,
Rcvbuf: 4096,
KeepAlive: false,
Reader: 32,
ReadBuf: 1024,
ReadBufSize: 8192,
Writer: 32,
WriteBuf: 1024,
WriteBufSize: 8192,
},
Websocket: &Websocket{
Bind: []string{":3102"},
},
Protocol: &Protocol{
Timer: 32,
TimerSize: 2048,
CliProto: 5,
SvrProto: 10,
HandshakeTimeout: xtime.Duration(time.Second * 5),
},
Bucket: &Bucket{
Size: 32,
Channel: 1024,
Room: 1024,
RoutineAmount: 32,
RoutineSize: 1024,
},
}
}
// Config is comet config.
type Config struct {
Debug bool
Env *Env
Discovery *naming.Config
TCP *TCP
Websocket *Websocket
Protocol *Protocol
Bucket *Bucket
RPCClient *RPCClient
RPCServer *RPCServer
Whitelist *Whitelist
}
// Env is env config.
type Env struct {
Region string
Zone string
DeployEnv string
Host string
Weight int64
Offline bool
Addrs []string
}
// RPCClient is RPC client config.
type RPCClient struct {
Dial xtime.Duration
Timeout xtime.Duration
}
// RPCServer is RPC server config.
type RPCServer struct {
Network string
Addr string
Timeout xtime.Duration
IdleTimeout xtime.Duration
MaxLifeTime xtime.Duration
ForceCloseWait xtime.Duration
KeepAliveInterval xtime.Duration
KeepAliveTimeout xtime.Duration
}
// TCP is tcp config.
type TCP struct {
Bind []string
Sndbuf int
Rcvbuf int
KeepAlive bool
Reader int
ReadBuf int
ReadBufSize int
Writer int
WriteBuf int
WriteBufSize int
}
// Websocket is websocket config.
type Websocket struct {
Bind []string
TLSOpen bool
TLSBind []string
CertFile string
PrivateFile string
}
// Protocol is protocol config.
type Protocol struct {
Timer int
TimerSize int
SvrProto int
CliProto int
HandshakeTimeout xtime.Duration
}
// Bucket is bucket config.
type Bucket struct {
Size int
Channel int
Room int
RoutineAmount uint64
RoutineSize int
}
// Whitelist is white list config.
type Whitelist struct {
Whitelist []int64
WhiteLog string
}
================================================
FILE: internal/comet/errors/errors.go
================================================
package errors
import (
"errors"
)
// .
var (
// server
ErrHandshake = errors.New("handshake failed")
ErrOperation = errors.New("request operation not valid")
// ring
ErrRingEmpty = errors.New("ring buffer empty")
ErrRingFull = errors.New("ring buffer full")
// timer
ErrTimerFull = errors.New("timer full")
ErrTimerEmpty = errors.New("timer empty")
ErrTimerNoItem = errors.New("timer item not exist")
// channel
ErrPushMsgArg = errors.New("rpc pushmsg arg error")
ErrPushMsgsArg = errors.New("rpc pushmsgs arg error")
ErrMPushMsgArg = errors.New("rpc mpushmsg arg error")
ErrMPushMsgsArg = errors.New("rpc mpushmsgs arg error")
ErrSignalFullMsgDropped = errors.New("signal channel full, msg dropped")
// bucket
ErrBroadCastArg = errors.New("rpc broadcast arg error")
ErrBroadCastRoomArg = errors.New("rpc broadcast room arg error")
// room
ErrRoomDroped = errors.New("room droped")
// rpc
ErrLogic = errors.New("logic rpc is not available")
)
================================================
FILE: internal/comet/grpc/server.go
================================================
package grpc
import (
"context"
"net"
"time"
pb "github.com/Terry-Mao/goim/api/comet"
"github.com/Terry-Mao/goim/internal/comet"
"github.com/Terry-Mao/goim/internal/comet/conf"
"github.com/Terry-Mao/goim/internal/comet/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"
)
// New comet grpc server.
func New(c *conf.RPCServer, s *comet.Server) *grpc.Server {
keepParams := grpc.KeepaliveParams(keepalive.ServerParameters{
MaxConnectionIdle: time.Duration(c.IdleTimeout),
MaxConnectionAgeGrace: time.Duration(c.ForceCloseWait),
Time: time.Duration(c.KeepAliveInterval),
Timeout: time.Duration(c.KeepAliveTimeout),
MaxConnectionAge: time.Duration(c.MaxLifeTime),
})
srv := grpc.NewServer(keepParams)
pb.RegisterCometServer(srv, &server{s})
lis, err := net.Listen(c.Network, c.Addr)
if err != nil {
panic(err)
}
go func() {
if err := srv.Serve(lis); err != nil {
panic(err)
}
}()
return srv
}
type server struct {
srv *comet.Server
}
var _ pb.CometServer = &server{}
// PushMsg push a message to specified sub keys.
func (s *server) PushMsg(ctx context.Context, req *pb.PushMsgReq) (reply *pb.PushMsgReply, err error) {
if len(req.Keys) == 0 || req.Proto == nil {
return nil, errors.ErrPushMsgArg
}
for _, key := range req.Keys {
bucket := s.srv.Bucket(key)
if bucket == nil {
continue
}
if channel := bucket.Channel(key); channel != nil {
if !channel.NeedPush(req.ProtoOp) {
continue
}
if err = channel.Push(req.Proto); err != nil {
return
}
}
}
return &pb.PushMsgReply{}, nil
}
// Broadcast broadcast msg to all user.
func (s *server) Broadcast(ctx context.Context, req *pb.BroadcastReq) (*pb.BroadcastReply, error) {
if req.Proto == nil {
return nil, errors.ErrBroadCastArg
}
// TODO use broadcast queue
go func() {
for _, bucket := range s.srv.Buckets() {
bucket.Broadcast(req.GetProto(), req.ProtoOp)
if req.Speed > 0 {
t := bucket.ChannelCount() / int(req.Speed)
time.Sleep(time.Duration(t) * time.Second)
}
}
}()
return &pb.BroadcastReply{}, nil
}
// BroadcastRoom broadcast msg to specified room.
func (s *server) BroadcastRoom(ctx context.Context, req *pb.BroadcastRoomReq) (*pb.BroadcastRoomReply, error) {
if req.Proto == nil || req.RoomID == "" {
return nil, errors.ErrBroadCastRoomArg
}
for _, bucket := range s.srv.Buckets() {
bucket.BroadcastRoom(req)
}
return &pb.BroadcastRoomReply{}, nil
}
// Rooms gets all the room ids for the server.
func (s *server) Rooms(ctx context.Context, req *pb.RoomsReq) (*pb.RoomsReply, error) {
var (
roomIds = make(map[string]bool)
)
for _, bucket := range s.srv.Buckets() {
for roomID := range bucket.Rooms() {
roomIds[roomID] = true
}
}
return &pb.RoomsReply{Rooms: roomIds}, nil
}
================================================
FILE: internal/comet/operation.go
================================================
package comet
import (
"context"
"time"
"github.com/Terry-Mao/goim/api/logic"
"github.com/Terry-Mao/goim/api/protocol"
"github.com/Terry-Mao/goim/pkg/strings"
log "github.com/golang/glog"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding/gzip"
)
// Connect connected a connection.
func (s *Server) Connect(c context.Context, p *protocol.Proto, cookie string) (mid int64, key, rid string, accepts []int32, heartbeat time.Duration, err error) {
reply, err := s.rpcClient.Connect(c, &logic.ConnectReq{
Server: s.serverID,
Cookie: cookie,
Token: p.Body,
})
if err != nil {
return
}
return reply.Mid, reply.Key, reply.RoomID, reply.Accepts, time.Duration(reply.Heartbeat), nil
}
// Disconnect disconnected a connection.
func (s *Server) Disconnect(c context.Context, mid int64, key string) (err error) {
_, err = s.rpcClient.Disconnect(context.Background(), &logic.DisconnectReq{
Server: s.serverID,
Mid: mid,
Key: key,
})
return
}
// Heartbeat heartbeat a connection session.
func (s *Server) Heartbeat(ctx context.Context, mid int64, key string) (err error) {
_, err = s.rpcClient.Heartbeat(ctx, &logic.HeartbeatReq{
Server: s.serverID,
Mid: mid,
Key: key,
})
return
}
// RenewOnline renew room online.
func (s *Server) RenewOnline(ctx context.Context, serverID string, roomCount map[string]int32) (allRoom map[string]int32, err error) {
reply, err := s.rpcClient.RenewOnline(ctx, &logic.OnlineReq{
Server: s.serverID,
RoomCount: roomCount,
}, grpc.UseCompressor(gzip.Name))
if err != nil {
return
}
return reply.AllRoomCount, nil
}
// Receive receive a message.
func (s *Server) Receive(ctx context.Context, mid int64, p *protocol.Proto) (err error) {
_, err = s.rpcClient.Receive(ctx, &logic.ReceiveReq{Mid: mid, Proto: p})
return
}
// Operate operate.
func (s *Server) Operate(ctx context.Context, p *protocol.Proto, ch *Channel, b *Bucket) error {
switch p.Op {
case protocol.OpChangeRoom:
if err := b.ChangeRoom(string(p.Body), ch); err != nil {
log.Errorf("b.ChangeRoom(%s) error(%v)", p.Body, err)
}
p.Op = protocol.OpChangeRoomReply
case protocol.OpSub:
if ops, err := strings.SplitInt32s(string(p.Body), ","); err == nil {
ch.Watch(ops...)
}
p.Op = protocol.OpSubReply
case protocol.OpUnsub:
if ops, err := strings.SplitInt32s(string(p.Body), ","); err == nil {
ch.UnWatch(ops...)
}
p.Op = protocol.OpUnsubReply
default:
// TODO ack ok&failed
if err := s.Receive(ctx, ch.Mid, p); err != nil {
log.Errorf("s.Report(%d) op:%d error(%v)", ch.Mid, p.Op, err)
}
p.Body = nil
}
return nil
}
================================================
FILE: internal/comet/ring.go
================================================
package comet
import (
"github.com/Terry-Mao/goim/api/protocol"
"github.com/Terry-Mao/goim/internal/comet/conf"
"github.com/Terry-Mao/goim/internal/comet/errors"
log "github.com/golang/glog"
)
// Ring ring proto buffer.
type Ring struct {
// read
rp uint64
num uint64
mask uint64
// TODO split cacheline, many cpu cache line size is 64
// pad [40]byte
// write
wp uint64
data []protocol.Proto
}
// NewRing new a ring buffer.
func NewRing(num int) *Ring {
r := new(Ring)
r.init(uint64(num))
return r
}
// Init init ring.
func (r *Ring) Init(num int) {
r.init(uint64(num))
}
func (r *Ring) init(num uint64) {
// 2^N
if num&(num-1) != 0 {
for num&(num-1) != 0 {
num &= num - 1
}
num <<= 1
}
r.data = make([]protocol.Proto, num)
r.num = num
r.mask = r.num - 1
}
// Get get a proto from ring.
func (r *Ring) Get() (proto *protocol.Proto, err error) {
if r.rp == r.wp {
return nil, errors.ErrRingEmpty
}
proto = &r.data[r.rp&r.mask]
return
}
// GetAdv incr read index.
func (r *Ring) GetAdv() {
r.rp++
if conf.Conf.Debug {
log.Infof("ring rp: %d, idx: %d", r.rp, r.rp&r.mask)
}
}
// Set get a proto to write.
func (r *Ring) Set() (proto *protocol.Proto, err error) {
if r.wp-r.rp >= r.num {
return nil, errors.ErrRingFull
}
proto = &r.data[r.wp&r.mask]
return
}
// SetAdv incr write index.
func (r *Ring) SetAdv() {
r.wp++
if conf.Conf.Debug {
log.Infof("ring wp: %d, idx: %d", r.wp, r.wp&r.mask)
}
}
// Reset reset ring.
func (r *Ring) Reset() {
r.rp = 0
r.wp = 0
// prevent pad compiler optimization
// r.pad = [40]byte{}
}
================================================
FILE: internal/comet/room.go
================================================
package comet
import (
"sync"
"github.com/Terry-Mao/goim/api/protocol"
"github.com/Terry-Mao/goim/internal/comet/errors"
)
// Room is a room and store channel room info.
type Room struct {
ID string
rLock sync.RWMutex
next *Channel
drop bool
Online int32 // dirty read is ok
AllOnline int32
}
// NewRoom new a room struct, store channel room info.
func NewRoom(id string) (r *Room) {
r = new(Room)
r.ID = id
r.drop = false
r.next = nil
r.Online = 0
return
}
// Put put channel into the room.
func (r *Room) Put(ch *Channel) (err error) {
r.rLock.Lock()
if !r.drop {
if r.next != nil {
r.next.Prev = ch
}
ch.Next = r.next
ch.Prev = nil
r.next = ch // insert to header
r.Online++
} else {
err = errors.ErrRoomDroped
}
r.rLock.Unlock()
return
}
// Del delete channel from the room.
func (r *Room) Del(ch *Channel) bool {
r.rLock.Lock()
if ch.Next != nil {
// if not footer
ch.Next.Prev = ch.Prev
}
if ch.Prev != nil {
// if not header
ch.Prev.Next = ch.Next
} else {
r.next = ch.Next
}
ch.Next = nil
ch.Prev = nil
r.Online--
r.drop = r.Online == 0
r.rLock.Unlock()
return r.drop
}
// Push push msg to the room, if chan full discard it.
func (r *Room) Push(p *protocol.Proto) {
r.rLock.RLock()
for ch := r.next; ch != nil; ch = ch.Next {
_ = ch.Push(p)
}
r.rLock.RUnlock()
}
// Close close the room.
func (r *Room) Close() {
r.rLock.RLock()
for ch := r.next; ch != nil; ch = ch.Next {
ch.Close()
}
r.rLock.RUnlock()
}
// OnlineNum the room all online.
func (r *Room) OnlineNum() int32 {
if r.AllOnline > 0 {
return r.AllOnline
}
return r.Online
}
================================================
FILE: internal/comet/round.go
================================================
package comet
import (
"github.com/Terry-Mao/goim/internal/comet/conf"
"github.com/Terry-Mao/goim/pkg/bytes"
"github.com/Terry-Mao/goim/pkg/time"
)
// RoundOptions round options.
type RoundOptions struct {
Timer int
TimerSize int
Reader int
ReadBuf int
ReadBufSize int
Writer int
WriteBuf int
WriteBufSize int
}
// Round used for connection round-robin get a reader/writer/timer for split big lock.
type Round struct {
readers []bytes.Pool
writers []bytes.Pool
timers []time.Timer
options RoundOptions
}
// NewRound new a round struct.
func NewRound(c *conf.Config) (r *Round) {
var i int
r = &Round{
options: RoundOptions{
Reader: c.TCP.Reader,
ReadBuf: c.TCP.ReadBuf,
ReadBufSize: c.TCP.ReadBufSize,
Writer: c.TCP.Writer,
WriteBuf: c.TCP.WriteBuf,
WriteBufSize: c.TCP.WriteBufSize,
Timer: c.Protocol.Timer,
TimerSize: c.Protocol.TimerSize,
}}
// reader
r.readers = make([]bytes.Pool, r.options.Reader)
for i = 0; i < r.options.Reader; i++ {
r.readers[i].Init(r.options.ReadBuf, r.options.ReadBufSize)
}
// writer
r.writers = make([]bytes.Pool, r.options.Writer)
for i = 0; i < r.options.Writer; i++ {
r.writers[i].Init(r.options.WriteBuf, r.options.WriteBufSize)
}
// timer
r.timers = make([]time.Timer, r.options.Timer)
for i = 0; i < r.options.Timer; i++ {
r.timers[i].Init(r.options.TimerSize)
}
return
}
// Timer get a timer.
func (r *Round) Timer(rn int) *time.Timer {
return &(r.timers[rn%r.options.Timer])
}
// Reader get a reader memory buffer.
func (r *Round) Reader(rn int) *bytes.Pool {
return &(r.readers[rn%r.options.Reader])
}
// Writer get a writer memory buffer pool.
func (r *Round) Writer(rn int) *bytes.Pool {
return &(r.writers[rn%r.options.Writer])
}
================================================
FILE: internal/comet/server.go
================================================
package comet
import (
"context"
"math/rand"
"time"
"github.com/Terry-Mao/goim/api/logic"
"github.com/Terry-Mao/goim/internal/comet/conf"
log "github.com/golang/glog"
"github.com/zhenjl/cityhash"
"google.golang.org/grpc"
"google.golang.org/grpc/balancer/roundrobin"
"google.golang.org/grpc/keepalive"
)
const (
minServerHeartbeat = time.Minute * 10
maxServerHeartbeat = time.Minute * 30
// grpc options
grpcInitialWindowSize = 1 << 24
grpcInitialConnWindowSize = 1 << 24
grpcMaxSendMsgSize = 1 << 24
grpcMaxCallMsgSize = 1 << 24
grpcKeepAliveTime = time.Second * 10
grpcKeepAliveTimeout = time.Second * 3
grpcBackoffMaxDelay = time.Second * 3
)
func newLogicClient(c *conf.RPCClient) logic.LogicClient {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(c.Dial))
defer cancel()
conn, err := grpc.DialContext(ctx, "discovery://default/goim.logic",
[]grpc.DialOption{
grpc.WithInsecure(),
grpc.WithInitialWindowSize(grpcInitialWindowSize),
grpc.WithInitialConnWindowSize(grpcInitialConnWindowSize),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(grpcMaxCallMsgSize)),
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(grpcMaxSendMsgSize)),
grpc.WithBackoffMaxDelay(grpcBackoffMaxDelay),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: grpcKeepAliveTime,
Timeout: grpcKeepAliveTimeout,
PermitWithoutStream: true,
}),
grpc.WithBalancerName(roundrobin.Name),
}...)
if err != nil {
panic(err)
}
return logic.NewLogicClient(conn)
}
// Server is comet server.
type Server struct {
c *conf.Config
round *Round // accept round store
buckets []*Bucket // subkey bucket
bucketIdx uint32
serverID string
rpcClient logic.LogicClient
}
// NewServer returns a new Server.
func NewServer(c *conf.Config) *Server {
s := &Server{
c: c,
round: NewRound(c),
rpcClient: newLogicClient(c.RPCClient),
}
// init bucket
s.buckets = make([]*Bucket, c.Bucket.Size)
s.bucketIdx = uint32(c.Bucket.Size)
for i := 0; i < c.Bucket.Size; i++ {
s.buckets[i] = NewBucket(c.Bucket)
}
s.serverID = c.Env.Host
go s.onlineproc()
return s
}
// Buckets return all buckets.
func (s *Server) Buckets() []*Bucket {
return s.buckets
}
// Bucket get the bucket by subkey.
func (s *Server) Bucket(subKey string) *Bucket {
idx := cityhash.CityHash32([]byte(subKey), uint32(len(subKey))) % s.bucketIdx
if conf.Conf.Debug {
log.Infof("%s hit channel bucket index: %d use cityhash", subKey, idx)
}
return s.buckets[idx]
}
// RandServerHearbeat rand server heartbeat.
func (s *Server) RandServerHearbeat() time.Duration {
return (minServerHeartbeat + time.Duration(rand.Int63n(int64(maxServerHeartbeat-minServerHeartbeat))))
}
// Close close the server.
func (s *Server) Close() (err error) {
return
}
func (s *Server) onlineproc() {
for {
var (
allRoomsCount map[string]int32
err error
)
roomCount := make(map[string]int32)
for _, bucket := range s.buckets {
for roomID, count := range bucket.RoomsCount() {
roomCount[roomID] += count
}
}
if allRoomsCount, err = s.RenewOnline(context.Background(), s.serverID, roomCount); err != nil {
time.Sleep(time.Second)
continue
}
for _, bucket := range s.buckets {
bucket.UpRoomsCount(allRoomsCount)
}
time.Sleep(time.Second * 10)
}
}
================================================
FILE: internal/comet/server_tcp.go
================================================
package comet
import (
"context"
"io"
"net"
"strings"
"time"
"github.com/Terry-Mao/goim/api/protocol"
"github.com/Terry-Mao/goim/internal/comet/conf"
"github.com/Terry-Mao/goim/pkg/bufio"
"github.com/Terry-Mao/goim/pkg/bytes"
xtime "github.com/Terry-Mao/goim/pkg/time"
log "github.com/golang/glog"
)
const (
maxInt = 1<<31 - 1
)
// InitTCP listen all tcp.bind and start accept connections.
func InitTCP(server *Server, addrs []string, accept int) (err error) {
var (
bind string
listener *net.TCPListener
addr *net.TCPAddr
)
for _, bind = range addrs {
if addr, err = net.ResolveTCPAddr("tcp", bind); err != nil {
log.Errorf("net.ResolveTCPAddr(tcp, %s) error(%v)", bind, err)
return
}
if listener, err = net.ListenTCP("tcp", addr); err != nil {
log.Errorf("net.ListenTCP(tcp, %s) error(%v)", bind, err)
return
}
log.Infof("start tcp listen: %s", bind)
// split N core accept
for i := 0; i < accept; i++ {
go acceptTCP(server, listener)
}
}
return
}
// Accept accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func acceptTCP(server *Server, lis *net.TCPListener) {
var (
conn *net.TCPConn
err error
r int
)
for {
if conn, err = lis.AcceptTCP(); err != nil {
// if listener close then return
log.Errorf("listener.Accept(\"%s\") error(%v)", lis.Addr().String(), err)
return
}
if err = conn.SetKeepAlive(server.c.TCP.KeepAlive); err != nil {
log.Errorf("conn.SetKeepAlive() error(%v)", err)
return
}
if err = conn.SetReadBuffer(server.c.TCP.Rcvbuf); err != nil {
log.Errorf("conn.SetReadBuffer() error(%v)", err)
return
}
if err = conn.SetWriteBuffer(server.c.TCP.Sndbuf); err != nil {
log.Errorf("conn.SetWriteBuffer() error(%v)", err)
return
}
go serveTCP(server, conn, r)
if r++; r == maxInt {
r = 0
}
}
}
func serveTCP(s *Server, conn *net.TCPConn, r int) {
var (
// timer
tr = s.round.Timer(r)
rp = s.round.Reader(r)
wp = s.round.Writer(r)
// ip addr
lAddr = conn.LocalAddr().String()
rAddr = conn.RemoteAddr().String()
)
if conf.Conf.Debug {
log.Infof("start tcp serve \"%s\" with \"%s\"", lAddr, rAddr)
}
s.ServeTCP(conn, rp, wp, tr)
}
// ServeTCP serve a tcp connection.
func (s *Server) ServeTCP(conn *net.TCPConn, rp, wp *bytes.Pool, tr *xtime.Timer) {
var (
err error
rid string
accepts []int32
hb time.Duration
white bool
p *protocol.Proto
b *Bucket
trd *xtime.TimerData
lastHb = time.Now()
rb = rp.Get()
wb = wp.Get()
ch = NewChannel(s.c.Protocol.CliProto, s.c.Protocol.SvrProto)
rr = &ch.Reader
wr = &ch.Writer
)
ch.Reader.ResetBuffer(conn, rb.Bytes())
ch.Writer.ResetBuffer(conn, wb.Bytes())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// handshake
step := 0
trd = tr.Add(time.Duration(s.c.Protocol.HandshakeTimeout), func() {
conn.Close()
log.Errorf("key: %s remoteIP: %s step: %d tcp handshake timeout", ch.Key, conn.RemoteAddr().String(), step)
})
ch.IP, _, _ = net.SplitHostPort(conn.RemoteAddr().String())
// must not setadv, only used in auth
step = 1
if p, err = ch.CliProto.Set(); err == nil {
if ch.Mid, ch.Key, rid, accepts, hb, err = s.authTCP(ctx, rr, wr, p); err == nil {
ch.Watch(accepts...)
b = s.Bucket(ch.Key)
err = b.Put(rid, ch)
if conf.Conf.Debug {
log.Infof("tcp connnected key:%s mid:%d proto:%+v", ch.Key, ch.Mid, p)
}
}
}
step = 2
if err != nil {
conn.Close()
rp.Put(rb)
wp.Put(wb)
tr.Del(trd)
log.Errorf("key: %s handshake failed error(%v)", ch.Key, err)
return
}
trd.Key = ch.Key
tr.Set(trd, hb)
white = whitelist.Contains(ch.Mid)
if white {
whitelist.Printf("key: %s[%s] auth\n", ch.Key, rid)
}
step = 3
// hanshake ok start dispatch goroutine
go s.dispatchTCP(conn, wr, wp, wb, ch)
serverHeartbeat := s.RandServerHearbeat()
for {
if p, err = ch.CliProto.Set(); err != nil {
break
}
if white {
whitelist.Printf("key: %s start read proto\n", ch.Key)
}
if err = p.ReadTCP(rr); err != nil {
break
}
if white {
whitelist.Printf("key: %s read proto:%v\n", ch.Key, p)
}
if p.Op == protocol.OpHeartbeat {
tr.Set(trd, hb)
p.Op = protocol.OpHeartbeatReply
p.Body = nil
// NOTE: send server heartbeat for a long time
if now := time.Now(); now.Sub(lastHb) > serverHeartbeat {
if err1 := s.Heartbeat(ctx, ch.Mid, ch.Key); err1 == nil {
lastHb = now
}
}
if conf.Conf.Debug {
log.Infof("tcp heartbeat receive key:%s, mid:%d", ch.Key, ch.Mid)
}
step++
} else {
if err = s.Operate(ctx, p, ch, b); err != nil {
break
}
}
if white {
whitelist.Printf("key: %s process proto:%v\n", ch.Key, p)
}
ch.CliProto.SetAdv()
ch.Signal()
if white {
whitelist.Printf("key: %s signal\n", ch.Key)
}
}
if white {
whitelist.Printf("key: %s server tcp error(%v)\n", ch.Key, err)
}
if err != nil && err != io.EOF && !strings.Contains(err.Error(), "closed") {
log.Errorf("key: %s server tcp failed error(%v)", ch.Key, err)
}
b.Del(ch)
tr.Del(trd)
rp.Put(rb)
conn.Close()
ch.Close()
if err = s.Disconnect(ctx, ch.Mid, ch.Key); err != nil {
log.Errorf("key: %s mid: %d operator do disconnect error(%v)", ch.Key, ch.Mid, err)
}
if white {
whitelist.Printf("key: %s mid: %d disconnect error(%v)\n", ch.Key, ch.Mid, err)
}
if conf.Conf.Debug {
log.Infof("tcp disconnected key: %s mid: %d", ch.Key, ch.Mid)
}
}
// dispatch accepts connections on the listener and serves requests
// for each incoming connection. dispatch blocks; the caller typically
// invokes it in a go statement.
func (s *Server) dispatchTCP(conn *net.TCPConn, wr *bufio.Writer, wp *bytes.Pool, wb *bytes.Buffer, ch *Channel) {
var (
err error
finish bool
online int32
white = whitelist.Contains(ch.Mid)
)
if conf.Conf.Debug {
log.Infof("key: %s start dispatch tcp goroutine", ch.Key)
}
for {
if white {
whitelist.Printf("key: %s wait proto ready\n", ch.Key)
}
var p = ch.Ready()
if white {
whitelist.Printf("key: %s proto ready\n", ch.Key)
}
if conf.Conf.Debug {
log.Infof("key:%s dispatch msg:%v", ch.Key, *p)
}
switch p {
case protocol.ProtoFinish:
if white {
whitelist.Printf("key: %s receive proto finish\n", ch.Key)
}
if conf.Conf.Debug {
log.Infof("key: %s wakeup exit dispatch goroutine", ch.Key)
}
finish = true
goto failed
case protocol.ProtoReady:
// fetch message from svrbox(client send)
for {
if p, err = ch.CliProto.Get(); err != nil {
break
}
if white {
whitelist.Printf("key: %s start write client proto%v\n", ch.Key, p)
}
if p.Op == protocol.OpHeartbeatReply {
if ch.Room != nil {
online = ch.Room.OnlineNum()
}
if err = p.WriteTCPHeart(wr, online); err != nil {
goto failed
}
} else {
if err = p.WriteTCP(wr); err != nil {
goto failed
}
}
if white {
whitelist.Printf("key: %s write client proto%v\n", ch.Key, p)
}
p.Body = nil // avoid memory leak
ch.CliProto.GetAdv()
}
default:
if white {
whitelist.Printf("key: %s start write server proto%v\n", ch.Key, p)
}
// server send
if err = p.WriteTCP(wr); err != nil {
goto failed
}
if white {
whitelist.Printf("key: %s write server proto%v\n", ch.Key, p)
}
if conf.Conf.Debug {
log.Infof("tcp sent a message key:%s mid:%d proto:%+v", ch.Key, ch.Mid, p)
}
}
if white {
whitelist.Printf("key: %s start flush \n", ch.Key)
}
// only hungry flush response
if err = wr.Flush(); err != nil {
break
}
if white {
whitelist.Printf("key: %s flush\n", ch.Key)
}
}
failed:
if white {
whitelist.Printf("key: %s dispatch tcp error(%v)\n", ch.Key, err)
}
if err != nil {
log.Errorf("key: %s dispatch tcp error(%v)", ch.Key, err)
}
conn.Close()
wp.Put(wb)
// must ensure all channel message discard, for reader won't blocking Signal
for !finish {
finish = (ch.Ready() == protocol.ProtoFinish)
}
if conf.Conf.Debug {
log.Infof("key: %s dispatch goroutine exit", ch.Key)
}
}
// auth for goim handshake with client, use rsa & aes.
func (s *Server) authTCP(ctx context.Context, rr *bufio.Reader, wr *bufio.Writer, p *protocol.Proto) (mid int64, key, rid string, accepts []int32, hb time.Duration, err error) {
for {
if err = p.ReadTCP(rr); err != nil {
return
}
if p.Op == protocol.OpAuth {
break
} else {
log.Errorf("tcp request operation(%d) not auth", p.Op)
}
}
if mid, key, rid, accepts, hb, err = s.Connect(ctx, p, ""); err != nil {
log.Errorf("authTCP.Connect(key:%v).err(%v)", key, err)
return
}
p.Op = protocol.OpAuthReply
p.Body = nil
if err = p.WriteTCP(wr); err != nil {
log.Errorf("authTCP.WriteTCP(key:%v).err(%v)", key, err)
return
}
err = wr.Flush()
return
}
================================================
FILE: internal/comet/server_websocket.go
================================================
package comet
import (
"context"
"crypto/tls"
"io"
"net"
"strings"
"time"
"github.com/Terry-Mao/goim/api/protocol"
"github.com/Terry-Mao/goim/internal/comet/conf"
"github.com/Terry-Mao/goim/pkg/bytes"
xtime "github.com/Terry-Mao/goim/pkg/time"
"github.com/Terry-Mao/goim/pkg/websocket"
log "github.com/golang/glog"
)
// InitWebsocket listen all tcp.bind and start accept connections.
func InitWebsocket(server *Server, addrs []string, accept int) (err error) {
var (
bind string
listener *net.TCPListener
addr *net.TCPAddr
)
for _, bind = range addrs {
if addr, err = net.ResolveTCPAddr("tcp", bind); err != nil {
log.Errorf("net.ResolveTCPAddr(tcp, %s) error(%v)", bind, err)
return
}
if listener, err = net.ListenTCP("tcp", addr); err != nil {
log.Errorf("net.ListenTCP(tcp, %s) error(%v)", bind, err)
return
}
log.Infof("start ws listen: %s", bind)
// split N core accept
for i := 0; i < accept; i++ {
go acceptWebsocket(server, listener)
}
}
return
}
// InitWebsocketWithTLS init websocket with tls.
func InitWebsocketWithTLS(server *Server, addrs []string, certFile, privateFile string, accept int) (err error) {
var (
bind string
listener net.Listener
cert tls.Certificate
certs []tls.Certificate
)
certFiles := strings.Split(certFile, ",")
privateFiles := strings.Split(privateFile, ",")
for i := range certFiles {
cert, err = tls.LoadX509KeyPair(certFiles[i], privateFiles[i])
if err != nil {
log.Errorf("Error loading certificate. error(%v)", err)
return
}
certs = append(certs, cert)
}
tlsCfg := &tls.Config{Certificates: certs}
tlsCfg.BuildNameToCertificate()
for _, bind = range addrs {
if listener, err = tls.Listen("tcp", bind, tlsCfg); err != nil {
log.Errorf("net.ListenTCP(tcp, %s) error(%v)", bind, err)
return
}
log.Infof("start wss listen: %s", bind)
// split N core accept
for i := 0; i < accept; i++ {
go acceptWebsocketWithTLS(server, listener)
}
}
return
}
// Accept accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func acceptWebsocket(server *Server, lis *net.TCPListener) {
var (
conn *net.TCPConn
err error
r int
)
for {
if conn, err = lis.AcceptTCP(); err != nil {
// if listener close then return
log.Errorf("listener.Accept(%s) error(%v)", lis.Addr().String(), err)
return
}
if err = conn.SetKeepAlive(server.c.TCP.KeepAlive); err != nil {
log.Errorf("conn.SetKeepAlive() error(%v)", err)
return
}
if err = conn.SetReadBuffer(server.c.TCP.Rcvbuf); err != nil {
log.Errorf("conn.SetReadBuffer() error(%v)", err)
return
}
if err = conn.SetWriteBuffer(server.c.TCP.Sndbuf); err != nil {
log.Errorf("conn.SetWriteBuffer() error(%v)", err)
return
}
go serveWebsocket(server, conn, r)
if r++; r == maxInt {
r = 0
}
}
}
// Accept accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func acceptWebsocketWithTLS(server *Server, lis net.Listener) {
var (
conn net.Conn
err error
r int
)
for {
if conn, err = lis.Accept(); err != nil {
// if listener close then return
log.Errorf("listener.Accept(\"%s\") error(%v)", lis.Addr().String(), err)
return
}
go serveWebsocket(server, conn, r)
if r++; r == maxInt {
r = 0
}
}
}
func serveWebsocket(s *Server, conn net.Conn, r int) {
var (
// timer
tr = s.round.Timer(r)
rp = s.round.Reader(r)
wp = s.round.Writer(r)
)
if conf.Conf.Debug {
// ip addr
lAddr := conn.LocalAddr().String()
rAddr := conn.RemoteAddr().String()
log.Infof("start tcp serve \"%s\" with \"%s\"", lAddr, rAddr)
}
s.ServeWebsocket(conn, rp, wp, tr)
}
// ServeWebsocket serve a websocket connection.
func (s *Server) ServeWebsocket(conn net.Conn, rp, wp *bytes.Pool, tr *xtime.Timer) {
var (
err error
rid string
accepts []int32
hb time.Duration
white bool
p *protocol.Proto
b *Bucket
trd *xtime.TimerData
lastHB = time.Now()
rb = rp.Get()
ch = NewChannel(s.c.Protocol.CliProto, s.c.Protocol.SvrProto)
rr = &ch.Reader
wr = &ch.Writer
ws *websocket.Conn // websocket
req *websocket.Request
)
// reader
ch.Reader.ResetBuffer(conn, rb.Bytes())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// handshake
step := 0
trd = tr.Add(time.Duration(s.c.Protocol.HandshakeTimeout), func() {
// NOTE: fix close block for tls
_ = conn.SetDeadline(time.Now().Add(time.Millisecond * 100))
_ = conn.Close()
log.Errorf("key: %s remoteIP: %s step: %d ws handshake timeout", ch.Key, conn.RemoteAddr().String(), step)
})
// websocket
ch.IP, _, _ = net.SplitHostPort(conn.RemoteAddr().String())
step = 1
if req, err = websocket.ReadRequest(rr); err != nil || req.RequestURI != "/sub" {
conn.Close()
tr.Del(trd)
rp.Put(rb)
if err != io.EOF {
log.Errorf("http.ReadRequest(rr) error(%v)", err)
}
return
}
// writer
wb := wp.Get()
ch.Writer.ResetBuffer(conn, wb.Bytes())
step = 2
if ws, err = websocket.Upgrade(conn, rr, wr, req); err != nil {
conn.Close()
tr.Del(tr
gitextract_ggltw923/
├── .github/
│ └── workflows/
│ └── go.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── README_cn.md
├── README_en.md
├── api/
│ ├── comet/
│ │ ├── comet.pb.go
│ │ └── comet.proto
│ ├── generate.go
│ ├── logic/
│ │ ├── logic.pb.go
│ │ └── logic.proto
│ └── protocol/
│ ├── operation.go
│ ├── protocol.go
│ ├── protocol.pb.go
│ └── protocol.proto
├── benchmarks/
│ ├── client/
│ │ └── main.go
│ ├── multi_push/
│ │ └── main.go
│ ├── push/
│ │ └── main.go
│ ├── push_room/
│ │ └── main.go
│ └── push_rooms/
│ └── main.go
├── cmd/
│ ├── comet/
│ │ ├── comet-example.toml
│ │ └── main.go
│ ├── job/
│ │ ├── job-example.toml
│ │ └── main.go
│ └── logic/
│ ├── logic-example.toml
│ └── main.go
├── codecov.sh
├── docs/
│ ├── benchmark_cn.md
│ ├── benchmark_en.md
│ ├── en/
│ │ ├── proto.md
│ │ └── push.md
│ ├── goim.graffle
│ ├── proto.md
│ └── push.md
├── examples/
│ ├── cert.pem
│ ├── javascript/
│ │ ├── client.js
│ │ ├── index.html
│ │ └── main.go
│ └── private.pem
├── go.mod
├── go.sum
├── internal/
│ ├── comet/
│ │ ├── bucket.go
│ │ ├── channel.go
│ │ ├── conf/
│ │ │ └── conf.go
│ │ ├── errors/
│ │ │ └── errors.go
│ │ ├── grpc/
│ │ │ └── server.go
│ │ ├── operation.go
│ │ ├── ring.go
│ │ ├── room.go
│ │ ├── round.go
│ │ ├── server.go
│ │ ├── server_tcp.go
│ │ ├── server_websocket.go
│ │ └── whitelist.go
│ ├── job/
│ │ ├── comet.go
│ │ ├── conf/
│ │ │ └── conf.go
│ │ ├── job.go
│ │ ├── push.go
│ │ └── room.go
│ └── logic/
│ ├── balancer.go
│ ├── balancer_test.go
│ ├── conf/
│ │ └── conf.go
│ ├── conn.go
│ ├── conn_test.go
│ ├── dao/
│ │ ├── dao.go
│ │ ├── dao_test.go
│ │ ├── kafka.go
│ │ ├── kafka_test.go
│ │ ├── redis.go
│ │ ├── redis_test.go
│ │ └── size_coverage.out
│ ├── grpc/
│ │ └── server.go
│ ├── http/
│ │ ├── middleware.go
│ │ ├── nodes.go
│ │ ├── online.go
│ │ ├── push.go
│ │ ├── result.go
│ │ └── server.go
│ ├── logic.go
│ ├── logic_test.go
│ ├── model/
│ │ ├── metadata.go
│ │ ├── online.go
│ │ └── room.go
│ ├── nodes.go
│ ├── nodes_test.go
│ ├── online.go
│ ├── online_test.go
│ ├── push.go
│ └── push_test.go
├── pkg/
│ ├── bufio/
│ │ ├── bufio.go
│ │ └── bufio_test.go
│ ├── bytes/
│ │ ├── buffer.go
│ │ ├── buffer_test.go
│ │ ├── writer.go
│ │ └── writer_test.go
│ ├── encoding/
│ │ └── binary/
│ │ ├── endian.go
│ │ └── endian_test.go
│ ├── ip/
│ │ ├── ip.go
│ │ └── ip_test.go
│ ├── strings/
│ │ ├── ints.go
│ │ └── ints_test.go
│ ├── time/
│ │ ├── debug.go
│ │ ├── duration.go
│ │ ├── duration_test.go
│ │ ├── timer.go
│ │ └── timer_test.go
│ └── websocket/
│ ├── conn.go
│ ├── request.go
│ ├── server.go
│ └── server_test.go
└── scripts/
├── README.md
├── jdk8.sh
├── kafka.sh
└── zk.sh
SYMBOL INDEX (904 symbols across 82 files)
FILE: api/comet/comet.pb.go
constant _ (line 26) | _ = proto.ProtoPackageIsVersion3
type PushMsgReq (line 28) | type PushMsgReq struct
method Reset (line 37) | func (m *PushMsgReq) Reset() { *m = PushMsgReq{} }
method String (line 38) | func (m *PushMsgReq) String() string { return proto.CompactTextString(...
method ProtoMessage (line 39) | func (*PushMsgReq) ProtoMessage() {}
method Descriptor (line 40) | func (*PushMsgReq) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 44) | func (m *PushMsgReq) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 47) | func (m *PushMsgReq) XXX_Marshal(b []byte, deterministic bool) ([]byte...
method XXX_Merge (line 50) | func (m *PushMsgReq) XXX_Merge(src proto.Message) {
method XXX_Size (line 53) | func (m *PushMsgReq) XXX_Size() int {
method XXX_DiscardUnknown (line 56) | func (m *PushMsgReq) XXX_DiscardUnknown() {
method GetKeys (line 62) | func (m *PushMsgReq) GetKeys() []string {
method GetProtoOp (line 69) | func (m *PushMsgReq) GetProtoOp() int32 {
method GetProto (line 76) | func (m *PushMsgReq) GetProto() *protocol.Proto {
type PushMsgReply (line 83) | type PushMsgReply struct
method Reset (line 89) | func (m *PushMsgReply) Reset() { *m = PushMsgReply{} }
method String (line 90) | func (m *PushMsgReply) String() string { return proto.CompactTextStrin...
method ProtoMessage (line 91) | func (*PushMsgReply) ProtoMessage() {}
method Descriptor (line 92) | func (*PushMsgReply) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 96) | func (m *PushMsgReply) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 99) | func (m *PushMsgReply) XXX_Marshal(b []byte, deterministic bool) ([]by...
method XXX_Merge (line 102) | func (m *PushMsgReply) XXX_Merge(src proto.Message) {
method XXX_Size (line 105) | func (m *PushMsgReply) XXX_Size() int {
method XXX_DiscardUnknown (line 108) | func (m *PushMsgReply) XXX_DiscardUnknown() {
type BroadcastReq (line 114) | type BroadcastReq struct
method Reset (line 123) | func (m *BroadcastReq) Reset() { *m = BroadcastReq{} }
method String (line 124) | func (m *BroadcastReq) String() string { return proto.CompactTextStrin...
method ProtoMessage (line 125) | func (*BroadcastReq) ProtoMessage() {}
method Descriptor (line 126) | func (*BroadcastReq) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 130) | func (m *BroadcastReq) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 133) | func (m *BroadcastReq) XXX_Marshal(b []byte, deterministic bool) ([]by...
method XXX_Merge (line 136) | func (m *BroadcastReq) XXX_Merge(src proto.Message) {
method XXX_Size (line 139) | func (m *BroadcastReq) XXX_Size() int {
method XXX_DiscardUnknown (line 142) | func (m *BroadcastReq) XXX_DiscardUnknown() {
method GetProtoOp (line 148) | func (m *BroadcastReq) GetProtoOp() int32 {
method GetProto (line 155) | func (m *BroadcastReq) GetProto() *protocol.Proto {
method GetSpeed (line 162) | func (m *BroadcastReq) GetSpeed() int32 {
type BroadcastReply (line 169) | type BroadcastReply struct
method Reset (line 175) | func (m *BroadcastReply) Reset() { *m = BroadcastReply{} }
method String (line 176) | func (m *BroadcastReply) String() string { return proto.CompactTextStr...
method ProtoMessage (line 177) | func (*BroadcastReply) ProtoMessage() {}
method Descriptor (line 178) | func (*BroadcastReply) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 182) | func (m *BroadcastReply) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 185) | func (m *BroadcastReply) XXX_Marshal(b []byte, deterministic bool) ([]...
method XXX_Merge (line 188) | func (m *BroadcastReply) XXX_Merge(src proto.Message) {
method XXX_Size (line 191) | func (m *BroadcastReply) XXX_Size() int {
method XXX_DiscardUnknown (line 194) | func (m *BroadcastReply) XXX_DiscardUnknown() {
type BroadcastRoomReq (line 200) | type BroadcastRoomReq struct
method Reset (line 208) | func (m *BroadcastRoomReq) Reset() { *m = BroadcastRoomReq{} }
method String (line 209) | func (m *BroadcastRoomReq) String() string { return proto.CompactTextS...
method ProtoMessage (line 210) | func (*BroadcastRoomReq) ProtoMessage() {}
method Descriptor (line 211) | func (*BroadcastRoomReq) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 215) | func (m *BroadcastRoomReq) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 218) | func (m *BroadcastRoomReq) XXX_Marshal(b []byte, deterministic bool) (...
method XXX_Merge (line 221) | func (m *BroadcastRoomReq) XXX_Merge(src proto.Message) {
method XXX_Size (line 224) | func (m *BroadcastRoomReq) XXX_Size() int {
method XXX_DiscardUnknown (line 227) | func (m *BroadcastRoomReq) XXX_DiscardUnknown() {
method GetRoomID (line 233) | func (m *BroadcastRoomReq) GetRoomID() string {
method GetProto (line 240) | func (m *BroadcastRoomReq) GetProto() *protocol.Proto {
type BroadcastRoomReply (line 247) | type BroadcastRoomReply struct
method Reset (line 253) | func (m *BroadcastRoomReply) Reset() { *m = BroadcastRoomReply...
method String (line 254) | func (m *BroadcastRoomReply) String() string { return proto.CompactTex...
method ProtoMessage (line 255) | func (*BroadcastRoomReply) ProtoMessage() {}
method Descriptor (line 256) | func (*BroadcastRoomReply) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 260) | func (m *BroadcastRoomReply) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 263) | func (m *BroadcastRoomReply) XXX_Marshal(b []byte, deterministic bool)...
method XXX_Merge (line 266) | func (m *BroadcastRoomReply) XXX_Merge(src proto.Message) {
method XXX_Size (line 269) | func (m *BroadcastRoomReply) XXX_Size() int {
method XXX_DiscardUnknown (line 272) | func (m *BroadcastRoomReply) XXX_DiscardUnknown() {
type RoomsReq (line 278) | type RoomsReq struct
method Reset (line 284) | func (m *RoomsReq) Reset() { *m = RoomsReq{} }
method String (line 285) | func (m *RoomsReq) String() string { return proto.CompactTextString(m) }
method ProtoMessage (line 286) | func (*RoomsReq) ProtoMessage() {}
method Descriptor (line 287) | func (*RoomsReq) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 291) | func (m *RoomsReq) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 294) | func (m *RoomsReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, ...
method XXX_Merge (line 297) | func (m *RoomsReq) XXX_Merge(src proto.Message) {
method XXX_Size (line 300) | func (m *RoomsReq) XXX_Size() int {
method XXX_DiscardUnknown (line 303) | func (m *RoomsReq) XXX_DiscardUnknown() {
type RoomsReply (line 309) | type RoomsReply struct
method Reset (line 316) | func (m *RoomsReply) Reset() { *m = RoomsReply{} }
method String (line 317) | func (m *RoomsReply) String() string { return proto.CompactTextString(...
method ProtoMessage (line 318) | func (*RoomsReply) ProtoMessage() {}
method Descriptor (line 319) | func (*RoomsReply) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 323) | func (m *RoomsReply) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 326) | func (m *RoomsReply) XXX_Marshal(b []byte, deterministic bool) ([]byte...
method XXX_Merge (line 329) | func (m *RoomsReply) XXX_Merge(src proto.Message) {
method XXX_Size (line 332) | func (m *RoomsReply) XXX_Size() int {
method XXX_DiscardUnknown (line 335) | func (m *RoomsReply) XXX_DiscardUnknown() {
method GetRooms (line 341) | func (m *RoomsReply) GetRooms() map[string]bool {
function init (line 348) | func init() {
function init (line 360) | func init() { proto.RegisterFile("comet/comet.proto", fileDescriptor_327...
constant _ (line 398) | _ = grpc.SupportPackageIsVersion4
type CometClient (line 403) | type CometClient interface
type cometClient (line 414) | type cometClient struct
method PushMsg (line 422) | func (c *cometClient) PushMsg(ctx context.Context, in *PushMsgReq, opt...
method Broadcast (line 431) | func (c *cometClient) Broadcast(ctx context.Context, in *BroadcastReq,...
method BroadcastRoom (line 440) | func (c *cometClient) BroadcastRoom(ctx context.Context, in *Broadcast...
method Rooms (line 449) | func (c *cometClient) Rooms(ctx context.Context, in *RoomsReq, opts .....
function NewCometClient (line 418) | func NewCometClient(cc *grpc.ClientConn) CometClient {
type CometServer (line 459) | type CometServer interface
type UnimplementedCometServer (line 471) | type UnimplementedCometServer struct
method PushMsg (line 474) | func (*UnimplementedCometServer) PushMsg(ctx context.Context, req *Pus...
method Broadcast (line 477) | func (*UnimplementedCometServer) Broadcast(ctx context.Context, req *B...
method BroadcastRoom (line 480) | func (*UnimplementedCometServer) BroadcastRoom(ctx context.Context, re...
method Rooms (line 483) | func (*UnimplementedCometServer) Rooms(ctx context.Context, req *Rooms...
function RegisterCometServer (line 487) | func RegisterCometServer(s *grpc.Server, srv CometServer) {
function _Comet_PushMsg_Handler (line 491) | func _Comet_PushMsg_Handler(srv interface{}, ctx context.Context, dec fu...
function _Comet_Broadcast_Handler (line 509) | func _Comet_Broadcast_Handler(srv interface{}, ctx context.Context, dec ...
function _Comet_BroadcastRoom_Handler (line 527) | func _Comet_BroadcastRoom_Handler(srv interface{}, ctx context.Context, ...
function _Comet_Rooms_Handler (line 545) | func _Comet_Rooms_Handler(srv interface{}, ctx context.Context, dec func...
FILE: api/logic/logic.pb.go
constant _ (line 26) | _ = proto.ProtoPackageIsVersion3
type PushMsg_Type (line 28) | type PushMsg_Type
method String (line 48) | func (x PushMsg_Type) String() string {
method EnumDescriptor (line 52) | func (PushMsg_Type) EnumDescriptor() ([]byte, []int) {
constant PushMsg_PUSH (line 31) | PushMsg_PUSH PushMsg_Type = 0
constant PushMsg_ROOM (line 32) | PushMsg_ROOM PushMsg_Type = 1
constant PushMsg_BROADCAST (line 33) | PushMsg_BROADCAST PushMsg_Type = 2
type PushMsg (line 56) | type PushMsg struct
method Reset (line 69) | func (m *PushMsg) Reset() { *m = PushMsg{} }
method String (line 70) | func (m *PushMsg) String() string { return proto.CompactTextString(m) }
method ProtoMessage (line 71) | func (*PushMsg) ProtoMessage() {}
method Descriptor (line 72) | func (*PushMsg) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 76) | func (m *PushMsg) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 79) | func (m *PushMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, e...
method XXX_Merge (line 82) | func (m *PushMsg) XXX_Merge(src proto.Message) {
method XXX_Size (line 85) | func (m *PushMsg) XXX_Size() int {
method XXX_DiscardUnknown (line 88) | func (m *PushMsg) XXX_DiscardUnknown() {
method GetType (line 94) | func (m *PushMsg) GetType() PushMsg_Type {
method GetOperation (line 101) | func (m *PushMsg) GetOperation() int32 {
method GetSpeed (line 108) | func (m *PushMsg) GetSpeed() int32 {
method GetServer (line 115) | func (m *PushMsg) GetServer() string {
method GetRoom (line 122) | func (m *PushMsg) GetRoom() string {
method GetKeys (line 129) | func (m *PushMsg) GetKeys() []string {
method GetMsg (line 136) | func (m *PushMsg) GetMsg() []byte {
type ConnectReq (line 143) | type ConnectReq struct
method Reset (line 152) | func (m *ConnectReq) Reset() { *m = ConnectReq{} }
method String (line 153) | func (m *ConnectReq) String() string { return proto.CompactTextString(...
method ProtoMessage (line 154) | func (*ConnectReq) ProtoMessage() {}
method Descriptor (line 155) | func (*ConnectReq) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 159) | func (m *ConnectReq) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 162) | func (m *ConnectReq) XXX_Marshal(b []byte, deterministic bool) ([]byte...
method XXX_Merge (line 165) | func (m *ConnectReq) XXX_Merge(src proto.Message) {
method XXX_Size (line 168) | func (m *ConnectReq) XXX_Size() int {
method XXX_DiscardUnknown (line 171) | func (m *ConnectReq) XXX_DiscardUnknown() {
method GetServer (line 177) | func (m *ConnectReq) GetServer() string {
method GetCookie (line 184) | func (m *ConnectReq) GetCookie() string {
method GetToken (line 191) | func (m *ConnectReq) GetToken() []byte {
type ConnectReply (line 198) | type ConnectReply struct
method Reset (line 209) | func (m *ConnectReply) Reset() { *m = ConnectReply{} }
method String (line 210) | func (m *ConnectReply) String() string { return proto.CompactTextStrin...
method ProtoMessage (line 211) | func (*ConnectReply) ProtoMessage() {}
method Descriptor (line 212) | func (*ConnectReply) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 216) | func (m *ConnectReply) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 219) | func (m *ConnectReply) XXX_Marshal(b []byte, deterministic bool) ([]by...
method XXX_Merge (line 222) | func (m *ConnectReply) XXX_Merge(src proto.Message) {
method XXX_Size (line 225) | func (m *ConnectReply) XXX_Size() int {
method XXX_DiscardUnknown (line 228) | func (m *ConnectReply) XXX_DiscardUnknown() {
method GetMid (line 234) | func (m *ConnectReply) GetMid() int64 {
method GetKey (line 241) | func (m *ConnectReply) GetKey() string {
method GetRoomID (line 248) | func (m *ConnectReply) GetRoomID() string {
method GetAccepts (line 255) | func (m *ConnectReply) GetAccepts() []int32 {
method GetHeartbeat (line 262) | func (m *ConnectReply) GetHeartbeat() int64 {
type DisconnectReq (line 269) | type DisconnectReq struct
method Reset (line 278) | func (m *DisconnectReq) Reset() { *m = DisconnectReq{} }
method String (line 279) | func (m *DisconnectReq) String() string { return proto.CompactTextStri...
method ProtoMessage (line 280) | func (*DisconnectReq) ProtoMessage() {}
method Descriptor (line 281) | func (*DisconnectReq) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 285) | func (m *DisconnectReq) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 288) | func (m *DisconnectReq) XXX_Marshal(b []byte, deterministic bool) ([]b...
method XXX_Merge (line 291) | func (m *DisconnectReq) XXX_Merge(src proto.Message) {
method XXX_Size (line 294) | func (m *DisconnectReq) XXX_Size() int {
method XXX_DiscardUnknown (line 297) | func (m *DisconnectReq) XXX_DiscardUnknown() {
method GetMid (line 303) | func (m *DisconnectReq) GetMid() int64 {
method GetKey (line 310) | func (m *DisconnectReq) GetKey() string {
method GetServer (line 317) | func (m *DisconnectReq) GetServer() string {
type DisconnectReply (line 324) | type DisconnectReply struct
method Reset (line 331) | func (m *DisconnectReply) Reset() { *m = DisconnectReply{} }
method String (line 332) | func (m *DisconnectReply) String() string { return proto.CompactTextSt...
method ProtoMessage (line 333) | func (*DisconnectReply) ProtoMessage() {}
method Descriptor (line 334) | func (*DisconnectReply) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 338) | func (m *DisconnectReply) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 341) | func (m *DisconnectReply) XXX_Marshal(b []byte, deterministic bool) ([...
method XXX_Merge (line 344) | func (m *DisconnectReply) XXX_Merge(src proto.Message) {
method XXX_Size (line 347) | func (m *DisconnectReply) XXX_Size() int {
method XXX_DiscardUnknown (line 350) | func (m *DisconnectReply) XXX_DiscardUnknown() {
method GetHas (line 356) | func (m *DisconnectReply) GetHas() bool {
type HeartbeatReq (line 363) | type HeartbeatReq struct
method Reset (line 372) | func (m *HeartbeatReq) Reset() { *m = HeartbeatReq{} }
method String (line 373) | func (m *HeartbeatReq) String() string { return proto.CompactTextStrin...
method ProtoMessage (line 374) | func (*HeartbeatReq) ProtoMessage() {}
method Descriptor (line 375) | func (*HeartbeatReq) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 379) | func (m *HeartbeatReq) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 382) | func (m *HeartbeatReq) XXX_Marshal(b []byte, deterministic bool) ([]by...
method XXX_Merge (line 385) | func (m *HeartbeatReq) XXX_Merge(src proto.Message) {
method XXX_Size (line 388) | func (m *HeartbeatReq) XXX_Size() int {
method XXX_DiscardUnknown (line 391) | func (m *HeartbeatReq) XXX_DiscardUnknown() {
method GetMid (line 397) | func (m *HeartbeatReq) GetMid() int64 {
method GetKey (line 404) | func (m *HeartbeatReq) GetKey() string {
method GetServer (line 411) | func (m *HeartbeatReq) GetServer() string {
type HeartbeatReply (line 418) | type HeartbeatReply struct
method Reset (line 424) | func (m *HeartbeatReply) Reset() { *m = HeartbeatReply{} }
method String (line 425) | func (m *HeartbeatReply) String() string { return proto.CompactTextStr...
method ProtoMessage (line 426) | func (*HeartbeatReply) ProtoMessage() {}
method Descriptor (line 427) | func (*HeartbeatReply) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 431) | func (m *HeartbeatReply) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 434) | func (m *HeartbeatReply) XXX_Marshal(b []byte, deterministic bool) ([]...
method XXX_Merge (line 437) | func (m *HeartbeatReply) XXX_Merge(src proto.Message) {
method XXX_Size (line 440) | func (m *HeartbeatReply) XXX_Size() int {
method XXX_DiscardUnknown (line 443) | func (m *HeartbeatReply) XXX_DiscardUnknown() {
type OnlineReq (line 449) | type OnlineReq struct
method Reset (line 457) | func (m *OnlineReq) Reset() { *m = OnlineReq{} }
method String (line 458) | func (m *OnlineReq) String() string { return proto.CompactTextString(m) }
method ProtoMessage (line 459) | func (*OnlineReq) ProtoMessage() {}
method Descriptor (line 460) | func (*OnlineReq) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 464) | func (m *OnlineReq) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 467) | func (m *OnlineReq) XXX_Marshal(b []byte, deterministic bool) ([]byte,...
method XXX_Merge (line 470) | func (m *OnlineReq) XXX_Merge(src proto.Message) {
method XXX_Size (line 473) | func (m *OnlineReq) XXX_Size() int {
method XXX_DiscardUnknown (line 476) | func (m *OnlineReq) XXX_DiscardUnknown() {
method GetServer (line 482) | func (m *OnlineReq) GetServer() string {
method GetRoomCount (line 489) | func (m *OnlineReq) GetRoomCount() map[string]int32 {
type OnlineReply (line 496) | type OnlineReply struct
method Reset (line 503) | func (m *OnlineReply) Reset() { *m = OnlineReply{} }
method String (line 504) | func (m *OnlineReply) String() string { return proto.CompactTextString...
method ProtoMessage (line 505) | func (*OnlineReply) ProtoMessage() {}
method Descriptor (line 506) | func (*OnlineReply) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 510) | func (m *OnlineReply) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 513) | func (m *OnlineReply) XXX_Marshal(b []byte, deterministic bool) ([]byt...
method XXX_Merge (line 516) | func (m *OnlineReply) XXX_Merge(src proto.Message) {
method XXX_Size (line 519) | func (m *OnlineReply) XXX_Size() int {
method XXX_DiscardUnknown (line 522) | func (m *OnlineReply) XXX_DiscardUnknown() {
method GetAllRoomCount (line 528) | func (m *OnlineReply) GetAllRoomCount() map[string]int32 {
type ReceiveReq (line 535) | type ReceiveReq struct
method Reset (line 543) | func (m *ReceiveReq) Reset() { *m = ReceiveReq{} }
method String (line 544) | func (m *ReceiveReq) String() string { return proto.CompactTextString(...
method ProtoMessage (line 545) | func (*ReceiveReq) ProtoMessage() {}
method Descriptor (line 546) | func (*ReceiveReq) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 550) | func (m *ReceiveReq) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 553) | func (m *ReceiveReq) XXX_Marshal(b []byte, deterministic bool) ([]byte...
method XXX_Merge (line 556) | func (m *ReceiveReq) XXX_Merge(src proto.Message) {
method XXX_Size (line 559) | func (m *ReceiveReq) XXX_Size() int {
method XXX_DiscardUnknown (line 562) | func (m *ReceiveReq) XXX_DiscardUnknown() {
method GetMid (line 568) | func (m *ReceiveReq) GetMid() int64 {
method GetProto (line 575) | func (m *ReceiveReq) GetProto() *protocol.Proto {
type ReceiveReply (line 582) | type ReceiveReply struct
method Reset (line 588) | func (m *ReceiveReply) Reset() { *m = ReceiveReply{} }
method String (line 589) | func (m *ReceiveReply) String() string { return proto.CompactTextStrin...
method ProtoMessage (line 590) | func (*ReceiveReply) ProtoMessage() {}
method Descriptor (line 591) | func (*ReceiveReply) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 595) | func (m *ReceiveReply) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 598) | func (m *ReceiveReply) XXX_Marshal(b []byte, deterministic bool) ([]by...
method XXX_Merge (line 601) | func (m *ReceiveReply) XXX_Merge(src proto.Message) {
method XXX_Size (line 604) | func (m *ReceiveReply) XXX_Size() int {
method XXX_DiscardUnknown (line 607) | func (m *ReceiveReply) XXX_DiscardUnknown() {
type NodesReq (line 613) | type NodesReq struct
method Reset (line 621) | func (m *NodesReq) Reset() { *m = NodesReq{} }
method String (line 622) | func (m *NodesReq) String() string { return proto.CompactTextString(m) }
method ProtoMessage (line 623) | func (*NodesReq) ProtoMessage() {}
method Descriptor (line 624) | func (*NodesReq) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 628) | func (m *NodesReq) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 631) | func (m *NodesReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, ...
method XXX_Merge (line 634) | func (m *NodesReq) XXX_Merge(src proto.Message) {
method XXX_Size (line 637) | func (m *NodesReq) XXX_Size() int {
method XXX_DiscardUnknown (line 640) | func (m *NodesReq) XXX_DiscardUnknown() {
method GetPlatform (line 646) | func (m *NodesReq) GetPlatform() string {
method GetClientIP (line 653) | func (m *NodesReq) GetClientIP() string {
type NodesReply (line 660) | type NodesReply struct
method Reset (line 674) | func (m *NodesReply) Reset() { *m = NodesReply{} }
method String (line 675) | func (m *NodesReply) String() string { return proto.CompactTextString(...
method ProtoMessage (line 676) | func (*NodesReply) ProtoMessage() {}
method Descriptor (line 677) | func (*NodesReply) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 681) | func (m *NodesReply) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 684) | func (m *NodesReply) XXX_Marshal(b []byte, deterministic bool) ([]byte...
method XXX_Merge (line 687) | func (m *NodesReply) XXX_Merge(src proto.Message) {
method XXX_Size (line 690) | func (m *NodesReply) XXX_Size() int {
method XXX_DiscardUnknown (line 693) | func (m *NodesReply) XXX_DiscardUnknown() {
method GetDomain (line 699) | func (m *NodesReply) GetDomain() string {
method GetTcpPort (line 706) | func (m *NodesReply) GetTcpPort() int32 {
method GetWsPort (line 713) | func (m *NodesReply) GetWsPort() int32 {
method GetWssPort (line 720) | func (m *NodesReply) GetWssPort() int32 {
method GetHeartbeat (line 727) | func (m *NodesReply) GetHeartbeat() int32 {
method GetNodes (line 734) | func (m *NodesReply) GetNodes() []string {
method GetBackoff (line 741) | func (m *NodesReply) GetBackoff() *Backoff {
method GetHeartbeatMax (line 748) | func (m *NodesReply) GetHeartbeatMax() int32 {
type Backoff (line 755) | type Backoff struct
method Reset (line 765) | func (m *Backoff) Reset() { *m = Backoff{} }
method String (line 766) | func (m *Backoff) String() string { return proto.CompactTextString(m) }
method ProtoMessage (line 767) | func (*Backoff) ProtoMessage() {}
method Descriptor (line 768) | func (*Backoff) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 772) | func (m *Backoff) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 775) | func (m *Backoff) XXX_Marshal(b []byte, deterministic bool) ([]byte, e...
method XXX_Merge (line 778) | func (m *Backoff) XXX_Merge(src proto.Message) {
method XXX_Size (line 781) | func (m *Backoff) XXX_Size() int {
method XXX_DiscardUnknown (line 784) | func (m *Backoff) XXX_DiscardUnknown() {
method GetMaxDelay (line 790) | func (m *Backoff) GetMaxDelay() int32 {
method GetBaseDelay (line 797) | func (m *Backoff) GetBaseDelay() int32 {
method GetFactor (line 804) | func (m *Backoff) GetFactor() float32 {
method GetJitter (line 811) | func (m *Backoff) GetJitter() float32 {
function init (line 818) | func init() {
function init (line 838) | func init() { proto.RegisterFile("logic/logic.proto", fileDescriptor_2df...
constant _ (line 906) | _ = grpc.SupportPackageIsVersion4
type LogicClient (line 911) | type LogicClient interface
type logicClient (line 926) | type logicClient struct
method Connect (line 934) | func (c *logicClient) Connect(ctx context.Context, in *ConnectReq, opt...
method Disconnect (line 943) | func (c *logicClient) Disconnect(ctx context.Context, in *DisconnectRe...
method Heartbeat (line 952) | func (c *logicClient) Heartbeat(ctx context.Context, in *HeartbeatReq,...
method RenewOnline (line 961) | func (c *logicClient) RenewOnline(ctx context.Context, in *OnlineReq, ...
method Receive (line 970) | func (c *logicClient) Receive(ctx context.Context, in *ReceiveReq, opt...
method Nodes (line 979) | func (c *logicClient) Nodes(ctx context.Context, in *NodesReq, opts .....
function NewLogicClient (line 930) | func NewLogicClient(cc *grpc.ClientConn) LogicClient {
type LogicServer (line 989) | type LogicServer interface
type UnimplementedLogicServer (line 1005) | type UnimplementedLogicServer struct
method Connect (line 1008) | func (*UnimplementedLogicServer) Connect(ctx context.Context, req *Con...
method Disconnect (line 1011) | func (*UnimplementedLogicServer) Disconnect(ctx context.Context, req *...
method Heartbeat (line 1014) | func (*UnimplementedLogicServer) Heartbeat(ctx context.Context, req *H...
method RenewOnline (line 1017) | func (*UnimplementedLogicServer) RenewOnline(ctx context.Context, req ...
method Receive (line 1020) | func (*UnimplementedLogicServer) Receive(ctx context.Context, req *Rec...
method Nodes (line 1023) | func (*UnimplementedLogicServer) Nodes(ctx context.Context, req *Nodes...
function RegisterLogicServer (line 1027) | func RegisterLogicServer(s *grpc.Server, srv LogicServer) {
function _Logic_Connect_Handler (line 1031) | func _Logic_Connect_Handler(srv interface{}, ctx context.Context, dec fu...
function _Logic_Disconnect_Handler (line 1049) | func _Logic_Disconnect_Handler(srv interface{}, ctx context.Context, dec...
function _Logic_Heartbeat_Handler (line 1067) | func _Logic_Heartbeat_Handler(srv interface{}, ctx context.Context, dec ...
function _Logic_RenewOnline_Handler (line 1085) | func _Logic_RenewOnline_Handler(srv interface{}, ctx context.Context, de...
function _Logic_Receive_Handler (line 1103) | func _Logic_Receive_Handler(srv interface{}, ctx context.Context, dec fu...
function _Logic_Nodes_Handler (line 1121) | func _Logic_Nodes_Handler(srv interface{}, ctx context.Context, dec func...
FILE: api/protocol/operation.go
constant OpHandshake (line 5) | OpHandshake = int32(0)
constant OpHandshakeReply (line 7) | OpHandshakeReply = int32(1)
constant OpHeartbeat (line 10) | OpHeartbeat = int32(2)
constant OpHeartbeatReply (line 12) | OpHeartbeatReply = int32(3)
constant OpSendMsg (line 15) | OpSendMsg = int32(4)
constant OpSendMsgReply (line 17) | OpSendMsgReply = int32(5)
constant OpDisconnectReply (line 20) | OpDisconnectReply = int32(6)
constant OpAuth (line 23) | OpAuth = int32(7)
constant OpAuthReply (line 25) | OpAuthReply = int32(8)
constant OpRaw (line 28) | OpRaw = int32(9)
constant OpProtoReady (line 31) | OpProtoReady = int32(10)
constant OpProtoFinish (line 33) | OpProtoFinish = int32(11)
constant OpChangeRoom (line 36) | OpChangeRoom = int32(12)
constant OpChangeRoomReply (line 38) | OpChangeRoomReply = int32(13)
constant OpSub (line 41) | OpSub = int32(14)
constant OpSubReply (line 43) | OpSubReply = int32(15)
constant OpUnsub (line 46) | OpUnsub = int32(16)
constant OpUnsubReply (line 48) | OpUnsubReply = int32(17)
FILE: api/protocol/protocol.go
constant MaxBodySize (line 14) | MaxBodySize = int32(1 << 12)
constant _packSize (line 19) | _packSize = 4
constant _headerSize (line 20) | _headerSize = 2
constant _verSize (line 21) | _verSize = 2
constant _opSize (line 22) | _opSize = 4
constant _seqSize (line 23) | _seqSize = 4
constant _heartSize (line 24) | _heartSize = 4
constant _rawHeaderSize (line 25) | _rawHeaderSize = _packSize + _headerSize + _verSize + _opSize + _seqSize
constant _maxPackSize (line 26) | _maxPackSize = MaxBodySize + int32(_rawHeaderSize)
constant _packOffset (line 28) | _packOffset = 0
constant _headerOffset (line 29) | _headerOffset = _packOffset + _packSize
constant _verOffset (line 30) | _verOffset = _headerOffset + _headerSize
constant _opOffset (line 31) | _opOffset = _verOffset + _verSize
constant _seqOffset (line 32) | _seqOffset = _opOffset + _opSize
constant _heartOffset (line 33) | _heartOffset = _seqOffset + _seqSize
method WriteTo (line 51) | func (p *Proto) WriteTo(b *bytes.Writer) {
method ReadTCP (line 67) | func (p *Proto) ReadTCP(rr *bufio.Reader) (err error) {
method WriteTCP (line 97) | func (p *Proto) WriteTCP(wr *bufio.Writer) (err error) {
method WriteTCPHeart (line 123) | func (p *Proto) WriteTCPHeart(wr *bufio.Writer, online int32) (err error) {
method ReadWebsocket (line 144) | func (p *Proto) ReadWebsocket(ws *websocket.Conn) (err error) {
method WriteWebsocket (line 177) | func (p *Proto) WriteWebsocket(ws *websocket.Conn) (err error) {
method WriteWebsocketHeart (line 201) | func (p *Proto) WriteWebsocketHeart(wr *websocket.Conn, online int32) (e...
FILE: api/protocol/protocol.pb.go
constant _ (line 21) | _ = proto.ProtoPackageIsVersion3
type Proto (line 26) | type Proto struct
method Reset (line 36) | func (m *Proto) Reset() { *m = Proto{} }
method String (line 37) | func (m *Proto) String() string { return proto.CompactTextString(m) }
method ProtoMessage (line 38) | func (*Proto) ProtoMessage() {}
method Descriptor (line 39) | func (*Proto) Descriptor() ([]byte, []int) {
method XXX_Unmarshal (line 43) | func (m *Proto) XXX_Unmarshal(b []byte) error {
method XXX_Marshal (line 46) | func (m *Proto) XXX_Marshal(b []byte, deterministic bool) ([]byte, err...
method XXX_Merge (line 49) | func (m *Proto) XXX_Merge(src proto.Message) {
method XXX_Size (line 52) | func (m *Proto) XXX_Size() int {
method XXX_DiscardUnknown (line 55) | func (m *Proto) XXX_DiscardUnknown() {
method GetVer (line 61) | func (m *Proto) GetVer() int32 {
method GetOp (line 68) | func (m *Proto) GetOp() int32 {
method GetSeq (line 75) | func (m *Proto) GetSeq() int32 {
method GetBody (line 82) | func (m *Proto) GetBody() []byte {
function init (line 89) | func init() {
function init (line 93) | func init() { proto.RegisterFile("protocol/protocol.proto", fileDescript...
FILE: benchmarks/client/main.go
constant opHeartbeat (line 26) | opHeartbeat = int32(2)
constant opHeartbeatReply (line 27) | opHeartbeatReply = int32(3)
constant opAuth (line 28) | opAuth = int32(7)
constant opAuthReply (line 29) | opAuthReply = int32(8)
constant rawHeaderLen (line 33) | rawHeaderLen = uint16(16)
constant heart (line 34) | heart = 240 * time.Second
type Proto (line 38) | type Proto struct
type AuthToken (line 48) | type AuthToken struct
function main (line 61) | func main() {
function result (line 81) | func result() {
function client (line 96) | func client(mid int64) {
function startClient (line 103) | func startClient(key int64) {
function tcpWriteProto (line 187) | func tcpWriteProto(wr *bufio.Writer, proto *Proto) (err error) {
function tcpReadProto (line 213) | func tcpReadProto(rd *bufio.Reader, proto *Proto) (err error) {
FILE: benchmarks/multi_push/main.go
constant testContent (line 26) | testContent = "{\"test\":1}"
type pushsBodyMsg (line 28) | type pushsBodyMsg struct
function init (line 33) | func init() {
function main (line 52) | func main() {
function stop (line 91) | func stop() {
function startPush (line 95) | func startPush(b, e int) {
function httpPost (line 123) | func httpPost(url string, contentType string, body io.Reader) (*http.Res...
FILE: benchmarks/push/main.go
constant testContent (line 25) | testContent = "{\"test\":1}"
type pushBodyMsg (line 27) | type pushBodyMsg struct
function init (line 32) | func init() {
function main (line 51) | func main() {
function stop (line 85) | func stop() {
function startPush (line 89) | func startPush(b, e int) {
function httpPost (line 122) | func httpPost(url string, contentType string, body io.Reader) (*http.Res...
FILE: benchmarks/push_room/main.go
function main (line 18) | func main() {
function run (line 37) | func run(addr string, delay time.Duration) {
function post (line 47) | func post(addr string, i int64) {
FILE: benchmarks/push_rooms/main.go
constant testContent (line 26) | testContent = "{\"test\":1}"
function init (line 28) | func init() {
function main (line 46) | func main() {
function startPush (line 80) | func startPush(b, e int, delay time.Duration) {
FILE: cmd/comet/main.go
constant ver (line 28) | ver = "2.0.0"
constant appid (line 29) | appid = "goim.comet"
function main (line 32) | func main() {
function register (line 86) | func register(dis *naming.Discovery, srv *comet.Server) context.CancelFu...
FILE: cmd/job/main.go
function main (line 21) | func main() {
FILE: cmd/logic/main.go
constant ver (line 24) | ver = "2.0.0"
constant appid (line 25) | appid = "goim.logic"
function main (line 28) | func main() {
function register (line 66) | func register(dis *naming.Discovery, srv *logic.Logic) context.CancelFunc {
FILE: examples/javascript/client.js
function connect (line 33) | function connect() {
function reConnect (line 150) | function reConnect() {
FILE: examples/javascript/main.go
function main (line 8) | func main() {
FILE: internal/comet/bucket.go
type Bucket (line 13) | type Bucket struct
method ChannelCount (line 42) | func (b *Bucket) ChannelCount() int {
method RoomCount (line 47) | func (b *Bucket) RoomCount() int {
method RoomsCount (line 52) | func (b *Bucket) RoomsCount() (res map[string]int32) {
method ChangeRoom (line 69) | func (b *Bucket) ChangeRoom(nrid string, ch *Channel) (err error) {
method Put (line 101) | func (b *Bucket) Put(rid string, ch *Channel) (err error) {
method Del (line 128) | func (b *Bucket) Del(dch *Channel) {
method Channel (line 150) | func (b *Bucket) Channel(key string) (ch *Channel) {
method Broadcast (line 158) | func (b *Bucket) Broadcast(p *protocol.Proto, op int32) {
method Room (line 171) | func (b *Bucket) Room(rid string) (room *Room) {
method DelRoom (line 179) | func (b *Bucket) DelRoom(room *Room) {
method BroadcastRoom (line 187) | func (b *Bucket) BroadcastRoom(arg *pb.BroadcastRoomReq) {
method Rooms (line 193) | func (b *Bucket) Rooms() (res map[string]struct{}) {
method IPCount (line 210) | func (b *Bucket) IPCount() (res map[string]struct{}) {
method UpRoomsCount (line 224) | func (b *Bucket) UpRoomsCount(roomCountMap map[string]int32) {
method roomproc (line 237) | func (b *Bucket) roomproc(c chan *pb.BroadcastRoomReq) {
function NewBucket (line 26) | func NewBucket(c *conf.Bucket) (b *Bucket) {
FILE: internal/comet/channel.go
type Channel (line 12) | type Channel struct
method Watch (line 38) | func (c *Channel) Watch(accepts ...int32) {
method UnWatch (line 47) | func (c *Channel) UnWatch(accepts ...int32) {
method NeedPush (line 56) | func (c *Channel) NeedPush(op int32) bool {
method Push (line 67) | func (c *Channel) Push(p *protocol.Proto) (err error) {
method Ready (line 77) | func (c *Channel) Ready() *protocol.Proto {
method Signal (line 82) | func (c *Channel) Signal() {
method Close (line 87) | func (c *Channel) Close() {
function NewChannel (line 29) | func NewChannel(cli, svr int) *Channel {
FILE: internal/comet/conf/conf.go
function init (line 30) | func init() {
function Init (line 50) | func Init() (err error) {
function Default (line 57) | func Default() *Config {
type Config (line 109) | type Config struct
type Env (line 123) | type Env struct
type RPCClient (line 134) | type RPCClient struct
type RPCServer (line 140) | type RPCServer struct
type TCP (line 152) | type TCP struct
type Websocket (line 166) | type Websocket struct
type Protocol (line 175) | type Protocol struct
type Bucket (line 184) | type Bucket struct
type Whitelist (line 193) | type Whitelist struct
FILE: internal/comet/grpc/server.go
function New (line 18) | func New(c *conf.RPCServer, s *comet.Server) *grpc.Server {
type server (line 40) | type server struct
method PushMsg (line 47) | func (s *server) PushMsg(ctx context.Context, req *pb.PushMsgReq) (rep...
method Broadcast (line 69) | func (s *server) Broadcast(ctx context.Context, req *pb.BroadcastReq) ...
method BroadcastRoom (line 87) | func (s *server) BroadcastRoom(ctx context.Context, req *pb.BroadcastR...
method Rooms (line 98) | func (s *server) Rooms(ctx context.Context, req *pb.RoomsReq) (*pb.Roo...
FILE: internal/comet/operation.go
method Connect (line 17) | func (s *Server) Connect(c context.Context, p *protocol.Proto, cookie st...
method Disconnect (line 30) | func (s *Server) Disconnect(c context.Context, mid int64, key string) (e...
method Heartbeat (line 40) | func (s *Server) Heartbeat(ctx context.Context, mid int64, key string) (...
method RenewOnline (line 50) | func (s *Server) RenewOnline(ctx context.Context, serverID string, roomC...
method Receive (line 62) | func (s *Server) Receive(ctx context.Context, mid int64, p *protocol.Pro...
method Operate (line 68) | func (s *Server) Operate(ctx context.Context, p *protocol.Proto, ch *Cha...
FILE: internal/comet/ring.go
type Ring (line 11) | type Ring struct
method Init (line 31) | func (r *Ring) Init(num int) {
method init (line 35) | func (r *Ring) init(num uint64) {
method Get (line 49) | func (r *Ring) Get() (proto *protocol.Proto, err error) {
method GetAdv (line 58) | func (r *Ring) GetAdv() {
method Set (line 66) | func (r *Ring) Set() (proto *protocol.Proto, err error) {
method SetAdv (line 75) | func (r *Ring) SetAdv() {
method Reset (line 83) | func (r *Ring) Reset() {
function NewRing (line 24) | func NewRing(num int) *Ring {
FILE: internal/comet/room.go
type Room (line 11) | type Room struct
method Put (line 31) | func (r *Room) Put(ch *Channel) (err error) {
method Del (line 49) | func (r *Room) Del(ch *Channel) bool {
method Push (line 70) | func (r *Room) Push(p *protocol.Proto) {
method Close (line 79) | func (r *Room) Close() {
method OnlineNum (line 88) | func (r *Room) OnlineNum() int32 {
function NewRoom (line 21) | func NewRoom(id string) (r *Room) {
FILE: internal/comet/round.go
type RoundOptions (line 10) | type RoundOptions struct
type Round (line 22) | type Round struct
method Timer (line 62) | func (r *Round) Timer(rn int) *time.Timer {
method Reader (line 67) | func (r *Round) Reader(rn int) *bytes.Pool {
method Writer (line 72) | func (r *Round) Writer(rn int) *bytes.Pool {
function NewRound (line 30) | func NewRound(c *conf.Config) (r *Round) {
FILE: internal/comet/server.go
constant minServerHeartbeat (line 18) | minServerHeartbeat = time.Minute * 10
constant maxServerHeartbeat (line 19) | maxServerHeartbeat = time.Minute * 30
constant grpcInitialWindowSize (line 21) | grpcInitialWindowSize = 1 << 24
constant grpcInitialConnWindowSize (line 22) | grpcInitialConnWindowSize = 1 << 24
constant grpcMaxSendMsgSize (line 23) | grpcMaxSendMsgSize = 1 << 24
constant grpcMaxCallMsgSize (line 24) | grpcMaxCallMsgSize = 1 << 24
constant grpcKeepAliveTime (line 25) | grpcKeepAliveTime = time.Second * 10
constant grpcKeepAliveTimeout (line 26) | grpcKeepAliveTimeout = time.Second * 3
constant grpcBackoffMaxDelay (line 27) | grpcBackoffMaxDelay = time.Second * 3
function newLogicClient (line 30) | func newLogicClient(c *conf.RPCClient) logic.LogicClient {
type Server (line 55) | type Server struct
method Buckets (line 84) | func (s *Server) Buckets() []*Bucket {
method Bucket (line 89) | func (s *Server) Bucket(subKey string) *Bucket {
method RandServerHearbeat (line 98) | func (s *Server) RandServerHearbeat() time.Duration {
method Close (line 103) | func (s *Server) Close() (err error) {
method onlineproc (line 107) | func (s *Server) onlineproc() {
function NewServer (line 66) | func NewServer(c *conf.Config) *Server {
FILE: internal/comet/server_tcp.go
constant maxInt (line 19) | maxInt = 1<<31 - 1
function InitTCP (line 23) | func InitTCP(server *Server, addrs []string, accept int) (err error) {
function acceptTCP (line 50) | func acceptTCP(server *Server, lis *net.TCPListener) {
function serveTCP (line 81) | func serveTCP(s *Server, conn *net.TCPConn, r int) {
method ServeTCP (line 98) | func (s *Server) ServeTCP(conn *net.TCPConn, rp, wp *bytes.Pool, tr *xti...
method dispatchTCP (line 223) | func (s *Server) dispatchTCP(conn *net.TCPConn, wr *bufio.Writer, wp *by...
method authTCP (line 326) | func (s *Server) authTCP(ctx context.Context, rr *bufio.Reader, wr *bufi...
FILE: internal/comet/server_websocket.go
function InitWebsocket (line 20) | func InitWebsocket(server *Server, addrs []string, accept int) (err erro...
function InitWebsocketWithTLS (line 45) | func InitWebsocketWithTLS(server *Server, addrs []string, certFile, priv...
function acceptWebsocket (line 81) | func acceptWebsocket(server *Server, lis *net.TCPListener) {
function acceptWebsocketWithTLS (line 115) | func acceptWebsocketWithTLS(server *Server, lis net.Listener) {
function serveWebsocket (line 134) | func serveWebsocket(s *Server, conn net.Conn, r int) {
method ServeWebsocket (line 151) | func (s *Server) ServeWebsocket(conn net.Conn, rp, wp *bytes.Pool, tr *x...
method dispatchWebsocket (line 306) | func (s *Server) dispatchWebsocket(ws *websocket.Conn, wp *bytes.Pool, w...
method authWebsocket (line 408) | func (s *Server) authWebsocket(ctx context.Context, ws *websocket.Conn, ...
FILE: internal/comet/whitelist.go
type Whitelist (line 13) | type Whitelist struct
method Contains (line 36) | func (w *Whitelist) Contains(mid int64) (ok bool) {
method Printf (line 44) | func (w *Whitelist) Printf(format string, v ...interface{}) {
function InitWhitelist (line 19) | func InitWhitelist(c *conf.Whitelist) (err error) {
FILE: internal/job/comet.go
constant grpcInitialWindowSize (line 30) | grpcInitialWindowSize = 1 << 24
constant grpcInitialConnWindowSize (line 31) | grpcInitialConnWindowSize = 1 << 24
function newCometClient (line 34) | func newCometClient(addr string) (comet.CometClient, error) {
type Comet (line 59) | type Comet struct
method Push (line 107) | func (c *Comet) Push(arg *comet.PushMsgReq) (err error) {
method BroadcastRoom (line 114) | func (c *Comet) BroadcastRoom(arg *comet.BroadcastRoomReq) (err error) {
method Broadcast (line 121) | func (c *Comet) Broadcast(arg *comet.BroadcastReq) (err error) {
method process (line 126) | func (c *Comet) process(pushChan chan *comet.PushMsgReq, roomChan chan...
method Close (line 162) | func (c *Comet) Close() (err error) {
function NewComet (line 74) | func NewComet(in *naming.Instance, c *conf.Comet) (*Comet, error) {
FILE: internal/job/conf/conf.go
function init (line 23) | func init() {
function Init (line 35) | func Init() (err error) {
function Default (line 42) | func Default() *Config {
type Config (line 56) | type Config struct
type Room (line 65) | type Room struct
type Comet (line 72) | type Comet struct
type Kafka (line 78) | type Kafka struct
type Env (line 85) | type Env struct
FILE: internal/job/job.go
type Job (line 19) | type Job struct
method Close (line 51) | func (j *Job) Close() error {
method Consume (line 59) | func (j *Job) Consume() {
method watchComet (line 85) | func (j *Job) watchComet(c *naming.Config) {
method newAddress (line 121) | func (j *Job) newAddress(insMap map[string][]*naming.Instance) error {
function New (line 29) | func New(c *conf.Config) *Job {
function newKafkaSub (line 39) | func newKafkaSub(c *conf.Kafka) *cluster.Consumer {
FILE: internal/job/push.go
method push (line 14) | func (j *Job) push(ctx context.Context, pushMsg *pb.PushMsg) (err error) {
method pushKeys (line 29) | func (j *Job) pushKeys(operation int32, serverID string, subKeys []strin...
method broadcast (line 54) | func (j *Job) broadcast(operation int32, body []byte, speed int32) (err ...
method broadcastRoomRawBytes (line 81) | func (j *Job) broadcastRoomRawBytes(roomID string, body []byte) (err err...
FILE: internal/job/room.go
type Room (line 25) | type Room struct
method Push (line 45) | func (r *Room) Push(op int32, msg []byte) (err error) {
method pushproc (line 60) | func (r *Room) pushproc(batch int, sigTime time.Duration) {
function NewRoom (line 33) | func NewRoom(job *Job, id string, c *conf.Room) (r *Room) {
method delRoom (line 110) | func (j *Job) delRoom(roomID string) {
method getRoom (line 116) | func (j *Job) getRoom(roomID string) *Room {
FILE: internal/logic/balancer.go
constant _minWeight (line 17) | _minWeight = 1
constant _maxWeight (line 18) | _maxWeight = 1 << 20
constant _maxNodes (line 19) | _maxNodes = 5
type weightedNode (line 22) | type weightedNode struct
method String (line 32) | func (w *weightedNode) String() string {
method chosen (line 36) | func (w *weightedNode) chosen() {
method reset (line 40) | func (w *weightedNode) reset() {
method calculateWeight (line 44) | func (w *weightedNode) calculateWeight(totalWeight, totalConns int64, ...
type LoadBalancer (line 78) | type LoadBalancer struct
method Size (line 94) | func (lb *LoadBalancer) Size() int {
method weightedNodes (line 98) | func (lb *LoadBalancer) weightedNodes(region string, regionWeight floa...
method NodeAddrs (line 118) | func (lb *LoadBalancer) NodeAddrs(region, domain string, regionWeight ...
method Update (line 133) | func (lb *LoadBalancer) Update(ins []*naming.Instance) {
function NewLoadBalancer (line 86) | func NewLoadBalancer() *LoadBalancer {
FILE: internal/logic/balancer_test.go
function TestWeightedNode (line 11) | func TestWeightedNode(t *testing.T) {
function TestLoadBalancer (line 33) | func TestLoadBalancer(t *testing.T) {
FILE: internal/logic/conf/conf.go
function init (line 27) | func init() {
function Init (line 41) | func Init() (err error) {
function Default (line 48) | func Default() *Config {
type Config (line 74) | type Config struct
type Env (line 88) | type Env struct
type Node (line 97) | type Node struct
type Backoff (line 109) | type Backoff struct
type Redis (line 117) | type Redis struct
type Kafka (line 131) | type Kafka struct
type RPCClient (line 137) | type RPCClient struct
type RPCServer (line 143) | type RPCServer struct
type HTTPServer (line 155) | type HTTPServer struct
FILE: internal/logic/conn.go
method Connect (line 15) | func (l *Logic) Connect(c context.Context, server, cookie string, token ...
method Disconnect (line 42) | func (l *Logic) Disconnect(c context.Context, mid int64, key, server str...
method Heartbeat (line 52) | func (l *Logic) Heartbeat(c context.Context, mid int64, key, server stri...
method RenewOnline (line 69) | func (l *Logic) RenewOnline(c context.Context, server string, roomCount ...
method Receive (line 82) | func (l *Logic) Receive(c context.Context, mid int64, proto *protocol.Pr...
FILE: internal/logic/conn_test.go
function TestConnect (line 11) | func TestConnect(t *testing.T) {
FILE: internal/logic/dao/dao.go
type Dao (line 13) | type Dao struct
method Close (line 64) | func (d *Dao) Close() error {
method Ping (line 69) | func (d *Dao) Ping(c context.Context) error {
function New (line 21) | func New(c *conf.Config) *Dao {
function newKafkaPub (line 31) | func newKafkaPub(c *conf.Kafka) kafka.SyncProducer {
function newRedis (line 43) | func newRedis(c *conf.Redis) *redis.Pool {
FILE: internal/logic/dao/dao_test.go
function TestMain (line 16) | func TestMain(m *testing.M) {
FILE: internal/logic/dao/kafka.go
method PushMsg (line 14) | func (d *Dao) PushMsg(c context.Context, op int32, server string, keys [...
method BroadcastRoomMsg (line 38) | func (d *Dao) BroadcastRoomMsg(c context.Context, op int32, room string,...
method BroadcastMsg (line 61) | func (d *Dao) BroadcastMsg(c context.Context, op, speed int32, msg []byt...
FILE: internal/logic/dao/kafka_test.go
function TestDaoPushMsg (line 10) | func TestDaoPushMsg(t *testing.T) {
function TestDaoBroadcastRoomMsg (line 22) | func TestDaoBroadcastRoomMsg(t *testing.T) {
function TestDaoBroadcastMsg (line 33) | func TestDaoBroadcastMsg(t *testing.T) {
FILE: internal/logic/dao/redis.go
constant _prefixMidServer (line 17) | _prefixMidServer = "mid_%d"
constant _prefixKeyServer (line 18) | _prefixKeyServer = "key_%s"
constant _prefixServerOnline (line 19) | _prefixServerOnline = "ol_%s"
function keyMidServer (line 22) | func keyMidServer(mid int64) string {
function keyKeyServer (line 26) | func keyKeyServer(key string) string {
function keyServerOnline (line 30) | func keyServerOnline(key string) string {
method pingRedis (line 35) | func (d *Dao) pingRedis(c context.Context) (err error) {
method AddMapping (line 46) | func (d *Dao) AddMapping(c context.Context, mid int64, key, server strin...
method ExpireMapping (line 83) | func (d *Dao) ExpireMapping(c context.Context, mid int64, key string) (h...
method DelMapping (line 112) | func (d *Dao) DelMapping(c context.Context, mid int64, key, server strin...
method ServersByKeys (line 141) | func (d *Dao) ServersByKeys(c context.Context, keys []string) (res []str...
method KeysByMids (line 155) | func (d *Dao) KeysByMids(c context.Context, mids []int64) (ress map[stri...
method AddServerOnline (line 188) | func (d *Dao) AddServerOnline(c context.Context, server string, online *...
method addServerOnline (line 208) | func (d *Dao) addServerOnline(c context.Context, key string, hashKey str...
method ServerOnline (line 234) | func (d *Dao) ServerOnline(c context.Context, server string) (online *mo...
method serverOnline (line 252) | func (d *Dao) serverOnline(c context.Context, key string, hashKey string...
method DelServerOnline (line 271) | func (d *Dao) DelServerOnline(c context.Context, server string) (err err...
FILE: internal/logic/dao/redis_test.go
function TestDaopingRedis (line 11) | func TestDaopingRedis(t *testing.T) {
function TestDaoAddMapping (line 16) | func TestDaoAddMapping(t *testing.T) {
function TestDaoAddServerOnline (line 52) | func TestDaoAddServerOnline(t *testing.T) {
FILE: internal/logic/grpc/server.go
function New (line 20) | func New(c *conf.RPCServer, l *logic.Logic) *grpc.Server {
type server (line 42) | type server struct
method Connect (line 49) | func (s *server) Connect(ctx context.Context, req *pb.ConnectReq) (*pb...
method Disconnect (line 58) | func (s *server) Disconnect(ctx context.Context, req *pb.DisconnectReq...
method Heartbeat (line 67) | func (s *server) Heartbeat(ctx context.Context, req *pb.HeartbeatReq) ...
method RenewOnline (line 75) | func (s *server) RenewOnline(ctx context.Context, req *pb.OnlineReq) (...
method Receive (line 84) | func (s *server) Receive(ctx context.Context, req *pb.ReceiveReq) (*pb...
method Nodes (line 92) | func (s *server) Nodes(ctx context.Context, req *pb.NodesReq) (*pb.Nod...
FILE: internal/logic/http/middleware.go
function loggerHandler (line 13) | func loggerHandler(c *gin.Context) {
function recoverHandler (line 35) | func recoverHandler(c *gin.Context) {
FILE: internal/logic/http/nodes.go
method nodesWeighted (line 9) | func (s *Server) nodesWeighted(c *gin.Context) {
method nodesInstances (line 21) | func (s *Server) nodesInstances(c *gin.Context) {
FILE: internal/logic/http/online.go
method onlineTop (line 9) | func (s *Server) onlineTop(c *gin.Context) {
method onlineRoom (line 26) | func (s *Server) onlineRoom(c *gin.Context) {
method onlineTotal (line 43) | func (s *Server) onlineTotal(c *gin.Context) {
FILE: internal/logic/http/push.go
method pushKeys (line 10) | func (s *Server) pushKeys(c *gin.Context) {
method pushMids (line 32) | func (s *Server) pushMids(c *gin.Context) {
method pushRoom (line 54) | func (s *Server) pushRoom(c *gin.Context) {
method pushAll (line 77) | func (s *Server) pushAll(c *gin.Context) {
FILE: internal/logic/http/result.go
constant OK (line 9) | OK = 0
constant RequestErr (line 11) | RequestErr = -400
constant ServerErr (line 13) | ServerErr = -500
constant contextErrCode (line 15) | contextErrCode = "context/err/code"
type resp (line 18) | type resp struct
function errors (line 24) | func errors(c *gin.Context, code int, msg string) {
function result (line 32) | func result(c *gin.Context, data interface{}, code int) {
FILE: internal/logic/http/server.go
type Server (line 11) | type Server struct
method initRouter (line 33) | func (s *Server) initRouter() {
method Close (line 47) | func (s *Server) Close() {
function New (line 17) | func New(c *conf.HTTPServer, l *logic.Logic) *Server {
FILE: internal/logic/logic.go
constant _onlineTick (line 16) | _onlineTick = time.Second * 10
constant _onlineDeadline (line 17) | _onlineDeadline = time.Minute * 5
type Logic (line 21) | type Logic struct
method Ping (line 52) | func (l *Logic) Ping(c context.Context) (err error) {
method Close (line 57) | func (l *Logic) Close() {
method initRegions (line 61) | func (l *Logic) initRegions() {
method initNodes (line 69) | func (l *Logic) initNodes() {
method newNodes (line 92) | func (l *Logic) newNodes(res naming.Resolver) {
method onlineproc (line 132) | func (l *Logic) onlineproc() {
method loadOnline (line 141) | func (l *Logic) loadOnline() (err error) {
function New (line 36) | func New(c *conf.Config) (l *Logic) {
FILE: internal/logic/logic_test.go
function TestMain (line 16) | func TestMain(m *testing.M) {
FILE: internal/logic/model/metadata.go
constant MetaWeight (line 5) | MetaWeight = "weight"
constant MetaOffline (line 7) | MetaOffline = "offline"
constant MetaAddrs (line 9) | MetaAddrs = "addrs"
constant MetaIPCount (line 11) | MetaIPCount = "ip_count"
constant MetaConnCount (line 13) | MetaConnCount = "conn_count"
constant PlatformWeb (line 16) | PlatformWeb = "web"
FILE: internal/logic/model/online.go
type Online (line 4) | type Online struct
type Top (line 11) | type Top struct
FILE: internal/logic/model/room.go
function EncodeRoomKey (line 9) | func EncodeRoomKey(typ string, room string) string {
function DecodeRoomKey (line 14) | func DecodeRoomKey(key string) (string, string, error) {
FILE: internal/logic/nodes.go
method NodesInstances (line 14) | func (l *Logic) NodesInstances(c context.Context) (res []*naming.Instanc...
method NodesWeighted (line 19) | func (l *Logic) NodesWeighted(c context.Context, platform, clientIP stri...
method nodeAddrs (line 46) | func (l *Logic) nodeAddrs(c context.Context, clientIP string) (domains, ...
method location (line 59) | func (l *Logic) location(c context.Context, clientIP string) (province s...
FILE: internal/logic/nodes_test.go
function TestNodes (line 13) | func TestNodes(t *testing.T) {
FILE: internal/logic/online.go
method OnlineTop (line 16) | func (l *Logic) OnlineTop(c context.Context, typ string, n int) (tops []...
method OnlineRoom (line 43) | func (l *Logic) OnlineRoom(c context.Context, typ string, rooms []string...
method OnlineTotal (line 52) | func (l *Logic) OnlineTotal(c context.Context) (int64, int64) {
FILE: internal/logic/online_test.go
function TestOnline (line 10) | func TestOnline(t *testing.T) {
FILE: internal/logic/push.go
method PushKeys (line 12) | func (l *Logic) PushKeys(c context.Context, op int32, keys []string, msg...
method PushMids (line 33) | func (l *Logic) PushMids(c context.Context, op int32, mids []int64, msg ...
method PushRoom (line 55) | func (l *Logic) PushRoom(c context.Context, op int32, typ, room string, ...
method PushAll (line 60) | func (l *Logic) PushAll(c context.Context, op, speed int32, msg []byte) ...
FILE: internal/logic/push_test.go
function TestPushKeys (line 10) | func TestPushKeys(t *testing.T) {
function TestPushMids (line 21) | func TestPushMids(t *testing.T) {
function TestPushRoom (line 32) | func TestPushRoom(t *testing.T) {
function TestPushAll (line 44) | func TestPushAll(t *testing.T) {
FILE: pkg/bufio/bufio.go
constant defaultBufSize (line 17) | defaultBufSize = 4096
type Reader (line 34) | type Reader struct
method Reset (line 68) | func (b *Reader) Reset(r io.Reader) {
method ResetBuffer (line 74) | func (b *Reader) ResetBuffer(r io.Reader, buf []byte) {
method reset (line 78) | func (b *Reader) reset(buf []byte, r io.Reader) {
method fill (line 88) | func (b *Reader) fill() {
method readErr (line 118) | func (b *Reader) readErr() error {
method Peek (line 128) | func (b *Reader) Peek(n int) ([]byte, error) {
method Pop (line 156) | func (b *Reader) Pop(n int) ([]byte, error) {
method Discard (line 170) | func (b *Reader) Discard(n int) (discarded int, err error) {
method Read (line 203) | func (b *Reader) Read(p []byte) (n int, err error) {
method ReadByte (line 235) | func (b *Reader) ReadByte() (c byte, err error) {
method ReadSlice (line 259) | func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
method ReadLine (line 305) | func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
method Buffered (line 341) | func (b *Reader) Buffered() int { return b.w - b.r }
constant minReadBufferSize (line 41) | minReadBufferSize = 16
constant maxConsecutiveEmptyReads (line 42) | maxConsecutiveEmptyReads = 100
function NewReaderSize (line 47) | func NewReaderSize(rd io.Reader, size int) *Reader {
function NewReader (line 62) | func NewReader(rd io.Reader) *Reader {
type Writer (line 351) | type Writer struct
method Reset (line 383) | func (b *Writer) Reset(w io.Writer) {
method ResetBuffer (line 391) | func (b *Writer) ResetBuffer(w io.Writer, buf []byte) {
method Flush (line 399) | func (b *Writer) Flush() error {
method flush (line 404) | func (b *Writer) flush() error {
method Available (line 428) | func (b *Writer) Available() int { return len(b.buf) - b.n }
method Buffered (line 431) | func (b *Writer) Buffered() int { return b.n }
method Write (line 437) | func (b *Writer) Write(p []byte) (nn int, err error) {
method WriteRaw (line 465) | func (b *Writer) WriteRaw(p []byte) (nn int, err error) {
method Peek (line 483) | func (b *Writer) Peek(n int) ([]byte, error) {
method WriteString (line 505) | func (b *Writer) WriteString(s string) (int, error) {
function NewWriterSize (line 361) | func NewWriterSize(w io.Writer, size int) *Writer {
function NewWriter (line 377) | func NewWriter(w io.Writer) *Writer {
FILE: pkg/bufio/bufio_test.go
type rot13Reader (line 22) | type rot13Reader struct
method Read (line 32) | func (r13 *rot13Reader) Read(p []byte) (int, error) {
function newRot13Reader (line 26) | func newRot13Reader(r io.Reader) *rot13Reader {
function readBytes (line 46) | func readBytes(buf *Reader) string {
function TestReaderSimple (line 64) | func TestReaderSimple(t *testing.T) {
type readMaker (line 77) | type readMaker struct
function reads (line 91) | func reads(buf *Reader, m int) string {
type bufReader (line 104) | type bufReader struct
constant minReadBufferSize (line 119) | minReadBufferSize = 16
function TestReader (line 125) | func TestReader(t *testing.T) {
type zeroReader (line 157) | type zeroReader struct
method Read (line 159) | func (zeroReader) Read(p []byte) (int, error) {
function TestZeroReader (line 163) | func TestZeroReader(t *testing.T) {
function TestWriter (line 185) | func TestWriter(t *testing.T) {
type errorWriterTest (line 230) | type errorWriterTest struct
method Write (line 236) | func (w errorWriterTest) Write(p []byte) (int, error) {
function TestWriteErrors (line 249) | func TestWriteErrors(t *testing.T) {
function TestNewReaderSizeIdempotent (line 267) | func TestNewReaderSizeIdempotent(t *testing.T) {
function TestNewWriterSizeIdempotent (line 282) | func TestNewWriterSizeIdempotent(t *testing.T) {
function TestWriteString (line 297) | func TestWriteString(t *testing.T) {
function TestBufferFull (line 315) | func TestBufferFull(t *testing.T) {
function TestPeek (line 328) | func TestPeek(t *testing.T) {
type dataAndEOFReader (line 385) | type dataAndEOFReader
method Read (line 387) | func (r dataAndEOFReader) Read(p []byte) (int, error) {
type testReader (line 396) | type testReader struct
method Read (line 401) | func (t *testReader) Read(buf []byte) (n int, err error) {
function testReadLine (line 417) | func testReadLine(t *testing.T, input []byte) {
function TestReadLine (line 448) | func TestReadLine(t *testing.T) {
function TestLineTooLong (line 453) | func TestLineTooLong(t *testing.T) {
function TestReadAfterLines (line 480) | func TestReadAfterLines(t *testing.T) {
function TestReadEmptyBuffer (line 500) | func TestReadEmptyBuffer(t *testing.T) {
function TestLinesAfterRead (line 508) | func TestLinesAfterRead(t *testing.T) {
function TestReadLineNonNilLineOrError (line 522) | func TestReadLineNonNilLineOrError(t *testing.T) {
type readLineResult (line 533) | type readLineResult struct
function TestReadLineNewlines (line 558) | func TestReadLineNewlines(t *testing.T) {
function testReadLineNewlines (line 564) | func testReadLineNewlines(t *testing.T, input string, expect []readLineR...
function TestWriterReadFromCounts (line 587) | func TestWriterReadFromCounts(t *testing.T) {
type writeCountingDiscard (line 634) | type writeCountingDiscard
method Write (line 636) | func (w *writeCountingDiscard) Write(p []byte) (int, error) {
type negativeReader (line 641) | type negativeReader
method Read (line 643) | func (r *negativeReader) Read([]byte) (int, error) { return -1, nil }
function TestNegativeRead (line 645) | func TestNegativeRead(t *testing.T) {
type errorThenGoodReader (line 666) | type errorThenGoodReader struct
method Read (line 671) | func (r *errorThenGoodReader) Read(p []byte) (int, error) {
function TestReaderClearError (line 680) | func TestReaderClearError(t *testing.T) {
function TestReaderReset (line 701) | func TestReaderReset(t *testing.T) {
function TestWriterReset (line 718) | func TestWriterReset(t *testing.T) {
function TestReaderDiscard (line 733) | func TestReaderDiscard(t *testing.T) {
type onlyReader (line 863) | type onlyReader struct
type onlyWriter (line 868) | type onlyWriter struct
type scriptedReader (line 873) | type scriptedReader
method Read (line 875) | func (sr *scriptedReader) Read(p []byte) (n int, err error) {
function newScriptedReader (line 884) | func newScriptedReader(steps ...func(p []byte) (n int, err error)) io.Re...
function BenchmarkReaderCopyOptimal (line 889) | func BenchmarkReaderCopyOptimal(b *testing.B) {
function BenchmarkReaderCopyUnoptimal (line 903) | func BenchmarkReaderCopyUnoptimal(b *testing.B) {
function BenchmarkReaderCopyNoWriteTo (line 917) | func BenchmarkReaderCopyNoWriteTo(b *testing.B) {
function BenchmarkWriterCopyOptimal (line 931) | func BenchmarkWriterCopyOptimal(b *testing.B) {
function BenchmarkWriterCopyUnoptimal (line 945) | func BenchmarkWriterCopyUnoptimal(b *testing.B) {
function BenchmarkWriterCopyNoReadFrom (line 958) | func BenchmarkWriterCopyNoReadFrom(b *testing.B) {
function BenchmarkReaderEmpty (line 972) | func BenchmarkReaderEmpty(b *testing.B) {
function BenchmarkWriterEmpty (line 987) | func BenchmarkWriterEmpty(b *testing.B) {
function BenchmarkWriterFlush (line 1002) | func BenchmarkWriterFlush(b *testing.B) {
FILE: pkg/bytes/buffer.go
type Buffer (line 8) | type Buffer struct
method Bytes (line 14) | func (b *Buffer) Bytes() []byte {
type Pool (line 19) | type Pool struct
method Init (line 35) | func (p *Pool) Init(num, size int) {
method init (line 40) | func (p *Pool) init(num, size int) {
method grow (line 48) | func (p *Pool) grow() {
method Get (line 69) | func (p *Pool) Get() (b *Buffer) {
method Put (line 81) | func (p *Pool) Put(b *Buffer) {
function NewPool (line 28) | func NewPool(num, size int) (p *Pool) {
FILE: pkg/bytes/buffer_test.go
function TestBuffer (line 7) | func TestBuffer(t *testing.T) {
FILE: pkg/bytes/writer.go
type Writer (line 4) | type Writer struct
method Len (line 15) | func (w *Writer) Len() int {
method Size (line 20) | func (w *Writer) Size() int {
method Reset (line 25) | func (w *Writer) Reset() {
method Buffer (line 30) | func (w *Writer) Buffer() []byte {
method Peek (line 35) | func (w *Writer) Peek(n int) []byte {
method Write (line 44) | func (w *Writer) Write(p []byte) {
method grow (line 49) | func (w *Writer) grow(n int) {
function NewWriterSize (line 10) | func NewWriterSize(n int) *Writer {
FILE: pkg/bytes/writer_test.go
function TestWriter (line 8) | func TestWriter(t *testing.T) {
FILE: pkg/encoding/binary/endian.go
type bigEndian (line 6) | type bigEndian struct
method Int8 (line 8) | func (bigEndian) Int8(b []byte) int8 { return int8(b[0]) }
method PutInt8 (line 10) | func (bigEndian) PutInt8(b []byte, v int8) {
method Int16 (line 14) | func (bigEndian) Int16(b []byte) int16 { return int16(b[1]) | int16(b[...
method PutInt16 (line 16) | func (bigEndian) PutInt16(b []byte, v int16) {
method Int32 (line 22) | func (bigEndian) Int32(b []byte) int32 {
method PutInt32 (line 26) | func (bigEndian) PutInt32(b []byte, v int32) {
FILE: pkg/encoding/binary/endian_test.go
function TestInt8 (line 5) | func TestInt8(t *testing.T) {
function TestInt16 (line 14) | func TestInt16(t *testing.T) {
function TestInt32 (line 23) | func TestInt32(t *testing.T) {
FILE: pkg/ip/ip.go
function InternalIP (line 9) | func InternalIP() string {
FILE: pkg/ip/ip_test.go
function TestIP (line 5) | func TestIP(t *testing.T) {
FILE: pkg/strings/ints.go
function JoinInt32s (line 19) | func JoinInt32s(is []int32, p string) string {
function SplitInt32s (line 41) | func SplitInt32s(s, p string) ([]int32, error) {
function JoinInt64s (line 58) | func JoinInt64s(is []int64, p string) string {
function SplitInt64s (line 80) | func SplitInt64s(s, p string) ([]int64, error) {
FILE: pkg/strings/ints_test.go
function TestInt32 (line 8) | func TestInt32(t *testing.T) {
function TestInt64 (line 17) | func TestInt64(t *testing.T) {
FILE: pkg/time/debug.go
constant Debug (line 5) | Debug = false
FILE: pkg/time/duration.go
type Duration (line 8) | type Duration
method UnmarshalText (line 11) | func (d *Duration) UnmarshalText(text []byte) error {
FILE: pkg/time/duration_test.go
function TestDurationText (line 8) | func TestDurationText(t *testing.T) {
FILE: pkg/time/timer.go
constant timerFormat (line 11) | timerFormat = "2006-01-02 15:04:05"
constant infiniteDuration (line 12) | infiniteDuration = itime.Duration(1<<63 - 1)
type TimerData (line 16) | type TimerData struct
method Delay (line 25) | func (td *TimerData) Delay() itime.Duration {
method ExpireString (line 30) | func (td *TimerData) ExpireString() string {
type Timer (line 35) | type Timer struct
method Init (line 56) | func (t *Timer) Init(num int) {
method init (line 60) | func (t *Timer) init(num int) {
method grow (line 68) | func (t *Timer) grow() {
method get (line 84) | func (t *Timer) get() (td *TimerData) {
method put (line 94) | func (t *Timer) put(td *TimerData) {
method Add (line 102) | func (t *Timer) Add(expire itime.Duration, fn func()) (td *TimerData) {
method Del (line 114) | func (t *Timer) Del(td *TimerData) {
method add (line 123) | func (t *Timer) add(td *TimerData) {
method del (line 142) | func (t *Timer) del(td *TimerData) {
method Set (line 168) | func (t *Timer) Set(td *TimerData, expire itime.Duration) {
method start (line 177) | func (t *Timer) start() {
method expire (line 187) | func (t *Timer) expire() {
method up (line 227) | func (t *Timer) up(j int) {
method down (line 238) | func (t *Timer) down(i, n int) {
method less (line 256) | func (t *Timer) less(i, j int) bool {
method swap (line 260) | func (t *Timer) swap(i, j int) {
function NewTimer (line 49) | func NewTimer(num int) (t *Timer) {
FILE: pkg/time/timer_test.go
function TestTimer (line 10) | func TestTimer(t *testing.T) {
function printTimer (line 37) | func printTimer(timer *Timer) {
FILE: pkg/websocket/conn.go
constant finBit (line 14) | finBit = 1 << 7
constant rsv1Bit (line 15) | rsv1Bit = 1 << 6
constant rsv2Bit (line 16) | rsv2Bit = 1 << 5
constant rsv3Bit (line 17) | rsv3Bit = 1 << 4
constant opBit (line 18) | opBit = 0x0f
constant maskBit (line 21) | maskBit = 1 << 7
constant lenBit (line 22) | lenBit = 0x7f
constant continuationFrame (line 24) | continuationFrame = 0
constant continuationFrameMaxRead (line 25) | continuationFrameMaxRead = 100
constant TextMessage (line 32) | TextMessage = 1
constant BinaryMessage (line 35) | BinaryMessage = 2
constant CloseMessage (line 40) | CloseMessage = 8
constant PingMessage (line 44) | PingMessage = 9
constant PongMessage (line 48) | PongMessage = 10
type Conn (line 59) | type Conn struct
method WriteMessage (line 72) | func (c *Conn) WriteMessage(msgType int, msg []byte) (err error) {
method WriteHeader (line 81) | func (c *Conn) WriteHeader(msgType int, length int) (err error) {
method WriteBody (line 114) | func (c *Conn) WriteBody(b []byte) (err error) {
method Peek (line 122) | func (c *Conn) Peek(n int) ([]byte, error) {
method Flush (line 127) | func (c *Conn) Flush() error {
method ReadMessage (line 132) | func (c *Conn) ReadMessage() (op int, payload []byte, err error) {
method readFrame (line 181) | func (c *Conn) readFrame() (fin bool, op int, payload []byte, err erro...
method Close (line 251) | func (c *Conn) Close() error {
function newConn (line 67) | func newConn(rwc io.ReadWriteCloser, r *bufio.Reader, w *bufio.Writer) *...
function maskBytes (line 255) | func maskBytes(key []byte, pos int, b []byte) int {
FILE: pkg/websocket/request.go
type Request (line 13) | type Request struct
method readLine (line 43) | func (r *Request) readLine() ([]byte, error) {
method readMIMEHeader (line 62) | func (r *Request) readMIMEHeader() (header http.Header, err error) {
function ReadRequest (line 24) | func ReadRequest(r *bufio.Reader) (req *Request, err error) {
function parseRequestLine (line 93) | func parseRequestLine(line string) (method, requestURI, proto string, ok...
function trim (line 105) | func trim(s []byte) []byte {
FILE: pkg/websocket/server.go
function Upgrade (line 26) | func Upgrade(rwc io.ReadWriteCloser, rr *bufio.Reader, wr *bufio.Writer,...
function computeAcceptKey (line 51) | func computeAcceptKey(challengeKey string) string {
FILE: pkg/websocket/server_test.go
function TestServer (line 14) | func TestServer(t *testing.T) {
Condensed preview — 116 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (377K chars).
[
{
"path": ".github/workflows/go.yml",
"chars": 620,
"preview": "name: Go\n\non:\n push:\n branches: [ master ]\n pull_request:\n branches: [ master ]\n\njobs:\n\n build:\n name: Build"
},
{
"path": ".gitignore",
"chars": 232,
"preview": "# IDE ignore\n.idea/\n*.ipr\n*.iml\n*.iws\n.vscode/\n\n# temp ignore\n*.log\n*.cache\n*.diff\n*.exe\n*.exe~\n*.patch\n*.tmp\n*.swp\n\n# s"
},
{
"path": "CHANGELOG.md",
"chars": 526,
"preview": "#### goim\n\n##### Version 2.0.0\n> 1.router has been changed to redis \n> 2.Support node with redis online heartbeat maint"
},
{
"path": "LICENSE",
"chars": 1077,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Terry.Mao\n\nPermission is hereby granted, free of charge, to any person obtaini"
},
{
"path": "Makefile",
"chars": 923,
"preview": "# Go parameters\nGOCMD=GO111MODULE=on go\nGOBUILD=$(GOCMD) build\nGOTEST=$(GOCMD) test\n\nall: test build\nbuild:\n\trm -rf targ"
},
{
"path": "README.md",
"chars": 3054,
"preview": "goim v2.0\n==============\n\n[](https://golang.org/)\n[\n * "
},
{
"path": "README_en.md",
"chars": 2859,
"preview": "goim\n==============\n`Terry-Mao/goim` is a IM and push notification server cluster.\n\n------------------------------------"
},
{
"path": "api/comet/comet.pb.go",
"chars": 21272,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// source: comet/comet.proto\n\npackage comet\n\nimport (\n\tcontext \"context"
},
{
"path": "api/comet/comet.proto",
"chars": 1020,
"preview": "syntax = \"proto3\";\n\npackage goim.comet;\n\noption go_package = \"github.com/Terry-Mao/goim/api/comet;comet\";\n\nimport \"githu"
},
{
"path": "api/generate.go",
"chars": 355,
"preview": "package api\n\n//go:generate protoc -I. -I$GOPATH/src --go_out=plugins=grpc:. --go_opt=paths=source_relative protocol/prot"
},
{
"path": "api/logic/logic.pb.go",
"chars": 40022,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// source: logic/logic.proto\n\npackage logic\n\nimport (\n\tcontext \"context"
},
{
"path": "api/logic/logic.proto",
"chars": 1984,
"preview": "syntax = \"proto3\";\n\npackage goim.logic;\n\noption go_package = \"github.com/Terry-Mao/goim/api/logic;logic\";\n\nimport \"githu"
},
{
"path": "api/protocol/operation.go",
"chars": 1070,
"preview": "package protocol\n\nconst (\n\t// OpHandshake handshake\n\tOpHandshake = int32(0)\n\t// OpHandshakeReply handshake reply\n\tOpHand"
},
{
"path": "api/protocol/protocol.go",
"chars": 6259,
"preview": "package protocol\n\nimport (\n\t\"errors\"\n\n\t\"github.com/Terry-Mao/goim/pkg/bufio\"\n\t\"github.com/Terry-Mao/goim/pkg/bytes\"\n\t\"gi"
},
{
"path": "api/protocol/protocol.pb.go",
"chars": 3475,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// source: protocol/protocol.proto\n\npackage protocol\n\nimport (\n\tfmt \"fm"
},
{
"path": "api/protocol/protocol.proto",
"chars": 239,
"preview": "syntax = \"proto3\";\n\npackage goim.protocol;\n\noption go_package = \"github.com/Terry-Mao/goim/api/protocol;protocol\";\n\n/*\n "
},
{
"path": "benchmarks/client/main.go",
"chars": 5423,
"preview": "package main\n\n// Start Commond eg: ./client 1 1000 localhost:3101\n// first parameter:beginning userId\n// second paramete"
},
{
"path": "benchmarks/multi_push/main.go",
"chars": 2540,
"preview": "package main\n\n// Start Command eg : ./multi_push 0 20000 localhost:7172 60\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\""
},
{
"path": "benchmarks/push/main.go",
"chars": 2532,
"preview": "package main\n\n// Start Command eg : ./push 0 20000 localhost:7172 60\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\""
},
{
"path": "benchmarks/push_room/main.go",
"chars": 1309,
"preview": "package main\n\n// Start Commond eg: ./push_room 1 20 localhost:3111\n// first parameter: room id\n// second parameter: num "
},
{
"path": "benchmarks/push_rooms/main.go",
"chars": 2052,
"preview": "package main\n\n// Start Command eg : ./push_rooms 0 20000 localhost:7172 40\n// param 1 : the start of room number\n// para"
},
{
"path": "cmd/comet/comet-example.toml",
"chars": 815,
"preview": "# This is a TOML document. Boom\n[discovery]\n nodes = [\"127.0.0.1:7171\"]\n\n[rpcServer]\n addr = \":3109\"\n timeout ="
},
{
"path": "cmd/comet/main.go",
"chars": 3267,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\""
},
{
"path": "cmd/job/job-example.toml",
"chars": 181,
"preview": "# This is a TOML document. Boom\n[discovery]\n nodes = [\"127.0.0.1:7171\"]\n\n[kafka]\n topic = \"goim-push-topic\"\n gr"
},
{
"path": "cmd/job/main.go",
"chars": 999,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/bilibili/discovery/naming\"\n\t\"github.com/Terry"
},
{
"path": "cmd/logic/logic-example.toml",
"chars": 1075,
"preview": "# This is a TOML document. Boom\n[discovery]\n nodes = [\"127.0.0.1:7171\"]\n\n[regions]\n \"bj\" = [\"北京\",\"天津\",\"河北\",\"山东\",\"山"
},
{
"path": "cmd/logic/main.go",
"chars": 2007,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strconv\"\n\t\"syscall\"\n\n\t\"github.com/bilibili/discove"
},
{
"path": "codecov.sh",
"chars": 365,
"preview": "#!/usr/bin/env bash\n\nset -e\necho \"\" > coverage.txt\n\nfor d in $(go list ./... | grep -v cmd | grep -v docs | grep -v srci"
},
{
"path": "docs/benchmark_cn.md",
"chars": 622,
"preview": "## 压测图表\n\n\n### 服务端配置\n| CPU | 内存 | 操作系统 | 数量 |\n| :---- | :---- | :---- | :---- |\n| Intel(R) Xeo"
},
{
"path": "docs/benchmark_en.md",
"chars": 737,
"preview": "## Benchmark Chart\n\n\n### Benchmark Server\n| CPU | Memory | OS | Instance |\n| :---- | :---- | "
},
{
"path": "docs/en/proto.md",
"chars": 1665,
"preview": "# comet and clients protocols\ncomet supports two protocols to communicate with client: WebSocket, TCP\n\n## websocket "
},
{
"path": "docs/en/push.md",
"chars": 1256,
"preview": "<h3>Terry-Mao/goim push HTTP protocols</h3>\npush HTTP interface protocols for pusher\n\n<h3>Interfaces</h3>\n| Name | URL |"
},
{
"path": "docs/proto.md",
"chars": 1335,
"preview": "# comet 客户端通讯协议文档 \ncomet支持两种协议和客户端通讯 websocket, tcp。\n\n## websocket "
},
{
"path": "docs/push.md",
"chars": 4034,
"preview": "## goim push API\n\n### error codes\n```\n// ok\nOK = 0\n\n// request error\nRequestErr = -400\n\n// server error\nServerErr = -500"
},
{
"path": "examples/cert.pem",
"chars": 1399,
"preview": "-----BEGIN CERTIFICATE-----\nMIID3TCCAsWgAwIBAgIJAKWU8wETRh4fMA0GCSqGSIb3DQEBBQUAMIGEMQswCQYD\nVQQGEwJjbjERMA8GA1UECAwIc2h"
},
{
"path": "examples/javascript/client.js",
"chars": 6419,
"preview": "(function(win) {\n const rawHeaderLen = 16;\n const packetOffset = 0;\n const headerOffset = 4;\n const verOffse"
},
{
"path": "examples/javascript/index.html",
"chars": 1017,
"preview": "<html lang=\"zh-CN\">\n <head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <m"
},
{
"path": "examples/javascript/main.go",
"chars": 164,
"preview": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\t// Simple static webserver:\n\tlog.Fatal(http.ListenAndServe(\""
},
{
"path": "examples/private.pem",
"chars": 1679,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAyhcEsSamD8czHpuT3mAOGJnFLAwqkvWez7LMzjGdWLFmSBzA\ne+A+PWndkfvT6KpXzR9S/Qu"
},
{
"path": "go.mod",
"chars": 1277,
"preview": "module github.com/Terry-Mao/goim\n\ngo 1.13\n\nrequire (\n\tgithub.com/BurntSushi/toml v0.3.1\n\tgithub.com/Shopify/sarama v1.19"
},
{
"path": "go.sum",
"chars": 20100,
"preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.0.0-201"
},
{
"path": "internal/comet/bucket.go",
"chars": 4861,
"preview": "package comet\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\n\tpb \"github.com/Terry-Mao/goim/api/comet\"\n\t\"github.com/Terry-Mao/goim/ap"
},
{
"path": "internal/comet/channel.go",
"chars": 1756,
"preview": "package comet\n\nimport (\n\t\"sync\"\n\n\t\"github.com/Terry-Mao/goim/api/protocol\"\n\t\"github.com/Terry-Mao/goim/internal/comet/er"
},
{
"path": "internal/comet/conf/conf.go",
"chars": 4989,
"preview": "package conf\n\nimport (\n\t\"flag\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/bilibili/discovery/naming\"\n\t\"github.com"
},
{
"path": "internal/comet/errors/errors.go",
"chars": 1016,
"preview": "package errors\n\nimport (\n\t\"errors\"\n)\n\n// .\nvar (\n\t// server\n\tErrHandshake = errors.New(\"handshake failed\")\n\tErrOperation"
},
{
"path": "internal/comet/grpc/server.go",
"chars": 2827,
"preview": "package grpc\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"time\"\n\n\tpb \"github.com/Terry-Mao/goim/api/comet\"\n\t\"github.com/Terry-Mao/goim/"
},
{
"path": "internal/comet/operation.go",
"chars": 2621,
"preview": "package comet\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/Terry-Mao/goim/api/logic\"\n\t\"github.com/Terry-Mao/goim/api/proto"
},
{
"path": "internal/comet/ring.go",
"chars": 1593,
"preview": "package comet\n\nimport (\n\t\"github.com/Terry-Mao/goim/api/protocol\"\n\t\"github.com/Terry-Mao/goim/internal/comet/conf\"\n\t\"git"
},
{
"path": "internal/comet/room.go",
"chars": 1653,
"preview": "package comet\n\nimport (\n\t\"sync\"\n\n\t\"github.com/Terry-Mao/goim/api/protocol\"\n\t\"github.com/Terry-Mao/goim/internal/comet/er"
},
{
"path": "internal/comet/round.go",
"chars": 1813,
"preview": "package comet\n\nimport (\n\t\"github.com/Terry-Mao/goim/internal/comet/conf\"\n\t\"github.com/Terry-Mao/goim/pkg/bytes\"\n\t\"github"
},
{
"path": "internal/comet/server.go",
"chars": 3430,
"preview": "package comet\n\nimport (\n\t\"context\"\n\t\"math/rand\"\n\t\"time\"\n\n\t\"github.com/Terry-Mao/goim/api/logic\"\n\t\"github.com/Terry-Mao/g"
},
{
"path": "internal/comet/server_tcp.go",
"chars": 8981,
"preview": "package comet\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/Terry-Mao/goim/api/protocol\"\n\t\"github.c"
},
{
"path": "internal/comet/server_websocket.go",
"chars": 11321,
"preview": "package comet\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/Terry-Mao/goim/api/protoc"
},
{
"path": "internal/comet/whitelist.go",
"chars": 940,
"preview": "package comet\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/Terry-Mao/goim/internal/comet/conf\"\n)\n\nvar whitelist *Whitelist\n\n// W"
},
{
"path": "internal/job/comet.go",
"chars": 5188,
"preview": "package job\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/Terry-Mao/goim/api/comet\"\n\t\"gith"
},
{
"path": "internal/job/conf/conf.go",
"chars": 2015,
"preview": "package conf\n\nimport (\n\t\"flag\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/bilibili/discovery/naming\"\n\t\"github.com/BurntSushi/toml\"\n\txti"
},
{
"path": "internal/job/job.go",
"chars": 3428,
"preview": "package job\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\tpb \"github.com/Terry-Mao/goim/api/logic\"\n\t\"github.com/Terry-Ma"
},
{
"path": "internal/job/push.go",
"chars": 2581,
"preview": "package job\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/Terry-Mao/goim/api/comet\"\n\tpb \"github.com/Terry-Mao/goim/api/logic"
},
{
"path": "internal/job/room.go",
"chars": 2720,
"preview": "package job\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/Terry-Mao/goim/api/protocol\"\n\t\"github.com/Terry-Mao/goim/internal/"
},
{
"path": "internal/logic/balancer.go",
"chars": 4487,
"preview": "package logic\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/bilibili/discovery/naming\"\n\t\""
},
{
"path": "internal/logic/balancer_test.go",
"chars": 1897,
"preview": "package logic\n\nimport (\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/bilibili/discovery/naming\"\n\t\"github.com/Terry-Mao/goim/internal"
},
{
"path": "internal/logic/conf/conf.go",
"chars": 3953,
"preview": "package conf\n\nimport (\n\t\"flag\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/bilibili/discovery/naming\"\n\txtime \"github.com/Terr"
},
{
"path": "internal/logic/conn.go",
"chars": 2620,
"preview": "package logic\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/Terry-Mao/goim/api/protocol\"\n\t\"github.com/Terr"
},
{
"path": "internal/logic/conn_test.go",
"chars": 1125,
"preview": "package logic\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/Terry-Mao/goim/api/protocol\"\n\t\"github.com/stretchr/testify/a"
},
{
"path": "internal/logic/dao/dao.go",
"chars": 1651,
"preview": "package dao\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/Terry-Mao/goim/internal/logic/conf\"\n\t\"github.com/gomodule/redigo/"
},
{
"path": "internal/logic/dao/dao_test.go",
"chars": 566,
"preview": "package dao\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/Terry-Mao/goim/internal/logic/conf\"\n)\n\nvar (\n\td "
},
{
"path": "internal/logic/dao/kafka.go",
"chars": 2020,
"preview": "package dao\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\n\tpb \"github.com/Terry-Mao/goim/api/logic\"\n\tlog \"github.com/golang/glog\"\n\t\"g"
},
{
"path": "internal/logic/dao/kafka_test.go",
"chars": 746,
"preview": "package dao\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestDaoPushMsg(t *testing.T) "
},
{
"path": "internal/logic/dao/redis.go",
"chars": 7484,
"preview": "package dao\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/Terry-Mao/goim/internal/logic/model\"\n\t"
},
{
"path": "internal/logic/dao/redis_test.go",
"chars": 1571,
"preview": "package dao\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/Terry-Mao/goim/internal/logic/model\"\n\t\"github.com/stretchr/tes"
},
{
"path": "internal/logic/dao/size_coverage.out",
"chars": 8566,
"preview": "mode: set\ngithub.com/Terry-Mao/goim/internal/logic/dao/dao.go:21.31,29.2 2 1\ngithub.com/Terry-Mao/goim/internal/logic/da"
},
{
"path": "internal/logic/grpc/server.go",
"chars": 2780,
"preview": "package grpc\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"time\"\n\n\tpb \"github.com/Terry-Mao/goim/api/logic\"\n\t\"github.com/Terry-Mao/goim/"
},
{
"path": "internal/logic/http/middleware.go",
"chars": 1139,
"preview": "package http\n\nimport (\n\t\"fmt\"\n\t\"net/http/httputil\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\tlog \"github.com/gola"
},
{
"path": "internal/logic/http/nodes.go",
"chars": 467,
"preview": "package http\n\nimport (\n\t\"context\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc (s *Server) nodesWeighted(c *gin.Context) {\n\tvar "
},
{
"path": "internal/logic/http/online.go",
"chars": 1069,
"preview": "package http\n\nimport (\n\t\"context\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc (s *Server) onlineTop(c *gin.Context) {\n\tvar arg "
},
{
"path": "internal/logic/http/push.go",
"chars": 2132,
"preview": "package http\n\nimport (\n\t\"context\"\n\t\"io/ioutil\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc (s *Server) pushKeys(c *gin.Context)"
},
{
"path": "internal/logic/http/result.go",
"chars": 634,
"preview": "package http\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst (\n\t// OK ok\n\tOK = 0\n\t// RequestErr request error\n\tRequestErr"
},
{
"path": "internal/logic/http/server.go",
"chars": 1038,
"preview": "package http\n\nimport (\n\t\"github.com/Terry-Mao/goim/internal/logic\"\n\t\"github.com/Terry-Mao/goim/internal/logic/conf\"\n\n\t\"g"
},
{
"path": "internal/logic/logic.go",
"chars": 3469,
"preview": "package logic\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/Terry-Mao/goim/internal/logic/conf\"\n\t\"github.com/Ter"
},
{
"path": "internal/logic/logic_test.go",
"chars": 432,
"preview": "package logic\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/Terry-Mao/goim/internal/logic/conf\"\n)\n\nvar (\n\t"
},
{
"path": "internal/logic/model/metadata.go",
"chars": 356,
"preview": "package model\n\nconst (\n\t// MetaWeight meta weight\n\tMetaWeight = \"weight\"\n\t// MetaOffline meta offline\n\tMetaOffline = \"of"
},
{
"path": "internal/logic/model/online.go",
"chars": 307,
"preview": "package model\n\n// Online ip and room online.\ntype Online struct {\n\tServer string `json:\"server\"`\n\tRoomCount"
},
{
"path": "internal/logic/model/room.go",
"chars": 369,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n)\n\n// EncodeRoomKey encode a room key.\nfunc EncodeRoomKey(typ string, room str"
},
{
"path": "internal/logic/nodes.go",
"chars": 1819,
"preview": "package logic\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\tpb \"github.com/Terry-Mao/goim/api/logic\"\n\t\"github.com/Terry-Mao/goim/intern"
},
{
"path": "internal/logic/nodes_test.go",
"chars": 526,
"preview": "package logic\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/bilibili/discovery/naming\"\n\t\"github.com/Terry-Mao/goim/inter"
},
{
"path": "internal/logic/online.go",
"chars": 1132,
"preview": "package logic\n\nimport (\n\t\"context\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/Terry-Mao/goim/internal/logic/model\"\n)\n\nvar (\n\t_empt"
},
{
"path": "internal/logic/online_test.go",
"chars": 795,
"preview": "package logic\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestOnline(t *testing.T) {\n"
},
{
"path": "internal/logic/push.go",
"chars": 1591,
"preview": "package logic\n\nimport (\n\t\"context\"\n\n\t\"github.com/Terry-Mao/goim/internal/logic/model\"\n\n\tlog \"github.com/golang/glog\"\n)\n\n"
},
{
"path": "internal/logic/push_test.go",
"chars": 904,
"preview": "package logic\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestPushKeys(t *testing.T) "
},
{
"path": "pkg/bufio/bufio.go",
"chars": 13106,
"preview": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "pkg/bufio/bufio_test.go",
"chars": 26892,
"preview": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "pkg/bytes/buffer.go",
"chars": 1357,
"preview": "package bytes\n\nimport (\n\t\"sync\"\n)\n\n// Buffer buffer.\ntype Buffer struct {\n\tbuf []byte\n\tnext *Buffer // next free buffer"
},
{
"path": "pkg/bytes/buffer_test.go",
"chars": 322,
"preview": "package bytes\n\nimport (\n\t\"testing\"\n)\n\nfunc TestBuffer(t *testing.T) {\n\tp := NewPool(2, 10)\n\tb := p.Get()\n\tif b.Bytes() ="
},
{
"path": "pkg/bytes/writer.go",
"chars": 873,
"preview": "package bytes\n\n// Writer writer.\ntype Writer struct {\n\tn int\n\tbuf []byte\n}\n\n// NewWriterSize new a writer with size.\nf"
},
{
"path": "pkg/bytes/writer_test.go",
"chars": 371,
"preview": "package bytes\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestWriter(t *testing.T) {\n\tw := NewWriterSize(64)\n\tif w.Len() != "
},
{
"path": "pkg/encoding/binary/endian.go",
"chars": 649,
"preview": "package binary\n\n// BigEndian big endian.\nvar BigEndian bigEndian\n\ntype bigEndian struct{}\n\nfunc (bigEndian) Int8(b []byt"
},
{
"path": "pkg/encoding/binary/endian_test.go",
"chars": 453,
"preview": "package binary\n\nimport \"testing\"\n\nfunc TestInt8(t *testing.T) {\n\tb := make([]byte, 1)\n\tBigEndian.PutInt8(b, 100)\n\ti := B"
},
{
"path": "pkg/ip/ip.go",
"chars": 546,
"preview": "package ip\n\nimport (\n\t\"net\"\n\t\"strings\"\n)\n\n// InternalIP return internal ip.\nfunc InternalIP() string {\n\tinters, err := n"
},
{
"path": "pkg/ip/ip_test.go",
"chars": 112,
"preview": "package ip\n\nimport \"testing\"\n\nfunc TestIP(t *testing.T) {\n\tip := InternalIP()\n\tif ip == \"\" {\n\t\tt.FailNow()\n\t}\n}\n"
},
{
"path": "pkg/strings/ints.go",
"chars": 1756,
"preview": "package strings\n\nimport (\n\t\"bytes\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n)\n\nvar (\n\tbfPool = sync.Pool{\n\t\tNew: func() interface{}"
},
{
"path": "pkg/strings/ints_test.go",
"chars": 372,
"preview": "package strings\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestInt32(t *testing.T) {\n\ti := []int32{1, 2, 3}\n\ts := JoinInt32"
},
{
"path": "pkg/time/debug.go",
"chars": 62,
"preview": "package time\n\nconst (\n\t// Debug debug switch\n\tDebug = false\n)\n"
},
{
"path": "pkg/time/duration.go",
"chars": 335,
"preview": "package time\n\nimport (\n\txtime \"time\"\n)\n\n// Duration be used toml unmarshal string time, like 1s, 500ms.\ntype Duration xt"
},
{
"path": "pkg/time/duration_test.go",
"chars": 280,
"preview": "package time\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestDurationText(t *testing.T) {\n\tvar (\n\t\tinput = []byte(\"10s\")\n\t\tout"
},
{
"path": "pkg/time/timer.go",
"chars": 5152,
"preview": "package time\n\nimport (\n\t\"sync\"\n\titime \"time\"\n\n\tlog \"github.com/golang/glog\"\n)\n\nconst (\n\ttimerFormat = \"2006-01-02 1"
},
{
"path": "pkg/time/timer_test.go",
"chars": 1024,
"preview": "package time\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\tlog \"github.com/golang/glog\"\n)\n\nfunc TestTimer(t *testing.T) {\n\ttimer := New"
},
{
"path": "pkg/websocket/conn.go",
"chars": 5520,
"preview": "package websocket\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/Terry-Mao/goim/pkg/bufio\"\n)\n\nconst ("
},
{
"path": "pkg/websocket/request.go",
"chars": 2335,
"preview": "package websocket\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/Terry-Mao/goim/pkg/bufio\"\n)\n\n// Request"
},
{
"path": "pkg/websocket/server.go",
"chars": 1734,
"preview": "package websocket\n\nimport (\n\t\"crypto/sha1\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/Terry-Mao/goim/pk"
},
{
"path": "pkg/websocket/server_test.go",
"chars": 1293,
"preview": "package websocket\n\nimport (\n\t\"net\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"golang.org/x/net/websocket\"\n\n\t\"github.com/Terry-Mao/"
},
{
"path": "scripts/README.md",
"chars": 137,
"preview": "# Installing\n\n### Install JDK\n```\n ./jdk.sh\n```\n\n### Install Zookeeper\n```\n ./zk.sh\n```\n\n### Install Kafka\n```\n "
},
{
"path": "scripts/jdk8.sh",
"chars": 565,
"preview": "#!/bin/bash\n# config parameters\nJDK_HOME=/usr/local/jdk8/\n# download jdk\ncurl -L https://download.oracle.com/otn-pub/jav"
},
{
"path": "scripts/kafka.sh",
"chars": 2449,
"preview": "#!/bin/bash\n# config parameters\nJDK_HOME=/usr/local/jdk8/\nKAFKA_HOME=/data/app/kafka/\nKAFKA_CONF=/data/app/kafka/config/"
},
{
"path": "scripts/zk.sh",
"chars": 1931,
"preview": "#!/bin/bash\n# config parameters\nJDK_HOME=/usr/local/jdk8\nZK_HOME=/data/server/zookeeper\nZK_SUPERVISOR=/etc/supervisor/co"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the Terry-Mao/goim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 116 files (335.2 KB), approximately 117.8k tokens, and a symbol index with 904 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.