mirror of
https://github.com/duanhf2012/origin.git
synced 2026-02-14 15:54:42 +08:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afb04cac7f | ||
|
|
975cf93d58 | ||
|
|
c7e0fcbdbb | ||
|
|
5bea050f63 | ||
|
|
95b4e2f8de | ||
|
|
5601ab5ae2 | ||
|
|
d28094eefa | ||
|
|
68dfbc46f0 | ||
|
|
80c73b0bdb | ||
|
|
d9afeed6ee | ||
|
|
a32ff59676 | ||
|
|
2101c8903c | ||
|
|
5214f094bf | ||
|
|
fd364cf579 | ||
|
|
1eab31209c | ||
|
|
2da3ccae39 | ||
|
|
da18cf3158 | ||
|
|
c3484e9d5b | ||
|
|
b87a78c85b | ||
|
|
17a448f75c | ||
|
|
d87ad419c8 | ||
|
|
298a5d3721 | ||
|
|
64fb9368bf | ||
|
|
7f93aa5ff9 | ||
|
|
7a8d312aeb | ||
|
|
f931f61f7b | ||
|
|
151ed123f4 | ||
|
|
5a6a4c8a0d | ||
|
|
280c04a5d7 | ||
|
|
1520dae223 | ||
|
|
84f3429564 | ||
|
|
89fd5d273b | ||
|
|
3ce873ef04 | ||
|
|
3763f7d848 | ||
|
|
769f680b17 | ||
|
|
77988906f8 | ||
|
|
ae0ba1d966 | ||
|
|
f61fd5d1be | ||
|
|
eb1867c5fd | ||
|
|
8823d5fba4 | ||
|
|
9945c29a5c | ||
|
|
173d84f7e6 | ||
|
|
d687780517 | ||
|
|
a8a030c0f5 | ||
|
|
f8469ea10e | ||
|
|
be3daf19f9 | ||
|
|
aa91c7bf1b | ||
|
|
7fe73e55fb | ||
|
|
e5ceaa9e76 | ||
|
|
97c55ada71 | ||
|
|
776b234022 | ||
|
|
a4f425bd69 | ||
|
|
ee54862be2 | ||
|
|
13642d7402 | ||
|
|
18d620118e | ||
|
|
f02592d126 | ||
|
|
0f890887f7 | ||
|
|
c5c05b6ae9 | ||
|
|
68b891df51 | ||
|
|
63199bf862 | ||
|
|
4d5d45d555 | ||
|
|
ca48c443cd | ||
|
|
7d40a48a1b |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@
|
|||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
*.out
|
*.out
|
||||||
|
.DS_Store
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -56,7 +56,7 @@ cluster.json如下:
|
|||||||
"ListenAddr":"127.0.0.1:8001",
|
"ListenAddr":"127.0.0.1:8001",
|
||||||
"MaxRpcParamLen": 409600,
|
"MaxRpcParamLen": 409600,
|
||||||
"NodeName": "Node_Test1",
|
"NodeName": "Node_Test1",
|
||||||
"remark":"//以_打头的,表示只在本机进程,不对整个子网开发",
|
"remark":"//以_打头的,表示只在本机进程,不对整个子网公开",
|
||||||
"ServiceList": ["TestService1","TestService2","TestServiceCall","GateService","_TcpService","HttpService","WSService"]
|
"ServiceList": ["TestService1","TestService2","TestServiceCall","GateService","_TcpService","HttpService","WSService"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -65,7 +65,7 @@ cluster.json如下:
|
|||||||
"ListenAddr":"127.0.0.1:8002",
|
"ListenAddr":"127.0.0.1:8002",
|
||||||
"MaxRpcParamLen": 409600,
|
"MaxRpcParamLen": 409600,
|
||||||
"NodeName": "Node_Test1",
|
"NodeName": "Node_Test1",
|
||||||
"remark":"//以_打头的,表示只在本机进程,不对整个子网开发",
|
"remark":"//以_打头的,表示只在本机进程,不对整个子网公开",
|
||||||
"ServiceList": ["TestService1","TestService2","TestServiceCall","GateService","TcpService","HttpService","WSService"]
|
"ServiceList": ["TestService1","TestService2","TestServiceCall","GateService","TcpService","HttpService","WSService"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -87,12 +87,16 @@ service.json如下:
|
|||||||
---------------
|
---------------
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
|
"Global": {
|
||||||
|
"AreaId": 1
|
||||||
|
},
|
||||||
"Service":{
|
"Service":{
|
||||||
"HttpService":{
|
"HttpService":{
|
||||||
"ListenAddr":"0.0.0.0:9402",
|
"ListenAddr":"0.0.0.0:9402",
|
||||||
"ReadTimeout":10000,
|
"ReadTimeout":10000,
|
||||||
"WriteTimeout":10000,
|
"WriteTimeout":10000,
|
||||||
"ProcessTimeout":10000,
|
"ProcessTimeout":10000,
|
||||||
|
"ManualStart": false,
|
||||||
"CAFile":[
|
"CAFile":[
|
||||||
{
|
{
|
||||||
"Certfile":"",
|
"Certfile":"",
|
||||||
@@ -157,13 +161,14 @@ service.json如下:
|
|||||||
```
|
```
|
||||||
|
|
||||||
---------------
|
---------------
|
||||||
以上配置分为两个部分:Service与NodeService,NodeService中配置的对应结点中服务的配置,如果启动程序中根据nodeid查找该域的对应的服务,如果找不到时,从Service公共部分查找。
|
以上配置分为两个部分:Global,Service与NodeService。Global是全局配置,在任何服务中都可以通过cluster.GetCluster().GetGlobalCfg()获取,NodeService中配置的对应结点中服务的配置,如果启动程序中根据nodeid查找该域的对应的服务,如果找不到时,从Service公共部分查找。
|
||||||
|
|
||||||
**HttpService配置**
|
**HttpService配置**
|
||||||
* ListenAddr:Http监听地址
|
* ListenAddr:Http监听地址
|
||||||
* ReadTimeout:读网络超时毫秒
|
* ReadTimeout:读网络超时毫秒
|
||||||
* WriteTimeout:写网络超时毫秒
|
* WriteTimeout:写网络超时毫秒
|
||||||
* ProcessTimeout: 处理超时毫秒
|
* ProcessTimeout: 处理超时毫秒
|
||||||
|
* ManualStart: 是否手动控制开始监听,如果true,需要手动调用StartListen()函数
|
||||||
* CAFile: 证书文件,如果您的服务器通过web服务器代理配置https可以忽略该配置
|
* CAFile: 证书文件,如果您的服务器通过web服务器代理配置https可以忽略该配置
|
||||||
|
|
||||||
**TcpService配置**
|
**TcpService配置**
|
||||||
@@ -774,11 +779,11 @@ type TestHttpService struct {
|
|||||||
|
|
||||||
func (slf *TestHttpService) OnInit() error {
|
func (slf *TestHttpService) OnInit() error {
|
||||||
//获取系统httpservice服务
|
//获取系统httpservice服务
|
||||||
httpervice := node.GetService("HttpService").(*sysservice.HttpService)
|
httpservice := node.GetService("HttpService").(*sysservice.HttpService)
|
||||||
|
|
||||||
//新建并设置路由对象
|
//新建并设置路由对象
|
||||||
httpRouter := sysservice.NewHttpHttpRouter()
|
httpRouter := sysservice.NewHttpHttpRouter()
|
||||||
httpervice.SetHttpRouter(httpRouter,slf.GetEventHandler())
|
httpservice.SetHttpRouter(httpRouter,slf.GetEventHandler())
|
||||||
|
|
||||||
//GET方法,请求url:http://127.0.0.1:9402/get/query?nickname=boyce
|
//GET方法,请求url:http://127.0.0.1:9402/get/query?nickname=boyce
|
||||||
//并header中新增key为uid,value为1000的头,则用postman测试返回结果为:
|
//并header中新增key为uid,value为1000的头,则用postman测试返回结果为:
|
||||||
@@ -792,6 +797,8 @@ func (slf *TestHttpService) OnInit() error {
|
|||||||
//GET方式获取目录下的资源,http://127.0.0.1:port/img/head/a.jpg
|
//GET方式获取目录下的资源,http://127.0.0.1:port/img/head/a.jpg
|
||||||
httpRouter.SetServeFile(sysservice.METHOD_GET,"/img/head/","d:/img")
|
httpRouter.SetServeFile(sysservice.METHOD_GET,"/img/head/","d:/img")
|
||||||
|
|
||||||
|
//如果配置"ManualStart": true配置为true,则使用以下方法进行开启http监听
|
||||||
|
//httpservice.StartListen()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,20 +41,23 @@ type NodeRpcInfo struct {
|
|||||||
var cluster Cluster
|
var cluster Cluster
|
||||||
|
|
||||||
type Cluster struct {
|
type Cluster struct {
|
||||||
localNodeInfo NodeInfo //本结点配置信息
|
localNodeInfo NodeInfo //本结点配置信息
|
||||||
masterDiscoveryNodeList []NodeInfo //配置发现Master结点
|
masterDiscoveryNodeList []NodeInfo //配置发现Master结点
|
||||||
|
globalCfg interface{} //全局配置
|
||||||
|
|
||||||
localServiceCfg map[string]interface{} //map[serviceName]配置数据*
|
localServiceCfg map[string]interface{} //map[serviceName]配置数据*
|
||||||
mapRpc map[int]NodeRpcInfo //nodeId
|
|
||||||
serviceDiscovery IServiceDiscovery //服务发现接口
|
serviceDiscovery IServiceDiscovery //服务发现接口
|
||||||
|
|
||||||
|
|
||||||
locker sync.RWMutex //结点与服务关系保护锁
|
locker sync.RWMutex //结点与服务关系保护锁
|
||||||
|
mapRpc map[int]NodeRpcInfo //nodeId
|
||||||
mapIdNode map[int]NodeInfo //map[NodeId]NodeInfo
|
mapIdNode map[int]NodeInfo //map[NodeId]NodeInfo
|
||||||
mapServiceNode map[string]map[int]struct{} //map[serviceName]map[NodeId]
|
mapServiceNode map[string]map[int]struct{} //map[serviceName]map[NodeId]
|
||||||
|
|
||||||
rpcServer rpc.Server
|
rpcServer rpc.Server
|
||||||
rpcEventLocker sync.RWMutex //Rpc事件监听保护锁
|
rpcEventLocker sync.RWMutex //Rpc事件监听保护锁
|
||||||
mapServiceListenRpcEvent map[string]struct{} //ServiceName
|
mapServiceListenRpcEvent map[string]struct{} //ServiceName
|
||||||
|
mapServiceListenDiscoveryEvent map[string]struct{} //ServiceName
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCluster() *Cluster {
|
func GetCluster() *Cluster {
|
||||||
@@ -93,9 +96,10 @@ func (cls *Cluster) DelNode(nodeId int, immediately bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
cls.locker.Lock()
|
cls.locker.Lock()
|
||||||
|
defer cls.locker.Unlock()
|
||||||
|
|
||||||
nodeInfo, ok := cls.mapIdNode[nodeId]
|
nodeInfo, ok := cls.mapIdNode[nodeId]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
cls.locker.Unlock()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +115,6 @@ func (cls *Cluster) DelNode(nodeId int, immediately bool) {
|
|||||||
if rpc.client.IsConnected() {
|
if rpc.client.IsConnected() {
|
||||||
nodeInfo.status = Discard
|
nodeInfo.status = Discard
|
||||||
rpc.client.Unlock()
|
rpc.client.Unlock()
|
||||||
cls.locker.Unlock()
|
|
||||||
log.SRelease("Discard node ", nodeInfo.NodeId, " ", nodeInfo.ListenAddr)
|
log.SRelease("Discard node ", nodeInfo.NodeId, " ", nodeInfo.ListenAddr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -125,7 +128,6 @@ func (cls *Cluster) DelNode(nodeId int, immediately bool) {
|
|||||||
|
|
||||||
delete(cls.mapIdNode, nodeId)
|
delete(cls.mapIdNode, nodeId)
|
||||||
delete(cls.mapRpc, nodeId)
|
delete(cls.mapRpc, nodeId)
|
||||||
cls.locker.Unlock()
|
|
||||||
if ok == true {
|
if ok == true {
|
||||||
rpc.client.Close(false)
|
rpc.client.Close(false)
|
||||||
}
|
}
|
||||||
@@ -223,6 +225,9 @@ func (cls *Cluster) Init(localNodeId int, setupServiceFun SetupServiceFun) error
|
|||||||
//2.安装服务发现结点
|
//2.安装服务发现结点
|
||||||
cls.SetupServiceDiscovery(localNodeId, setupServiceFun)
|
cls.SetupServiceDiscovery(localNodeId, setupServiceFun)
|
||||||
service.RegRpcEventFun = cls.RegRpcEvent
|
service.RegRpcEventFun = cls.RegRpcEvent
|
||||||
|
service.UnRegRpcEventFun = cls.UnRegRpcEvent
|
||||||
|
service.RegDiscoveryServiceEventFun = cls.RegDiscoveryEvent
|
||||||
|
service.UnRegDiscoveryServiceEventFun = cls.UnReDiscoveryEvent
|
||||||
|
|
||||||
err = cls.serviceDiscovery.InitDiscovery(localNodeId, cls.serviceDiscoveryDelNode, cls.serviceDiscoverySetNodeInfo)
|
err = cls.serviceDiscovery.InitDiscovery(localNodeId, cls.serviceDiscoveryDelNode, cls.serviceDiscoverySetNodeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -363,6 +368,7 @@ func (cls *Cluster) triggerRpcEvent(bConnect bool, clientSeq uint32, nodeId int)
|
|||||||
cls.locker.Unlock()
|
cls.locker.Unlock()
|
||||||
|
|
||||||
cls.rpcEventLocker.Lock()
|
cls.rpcEventLocker.Lock()
|
||||||
|
defer cls.rpcEventLocker.Unlock()
|
||||||
for serviceName, _ := range cls.mapServiceListenRpcEvent {
|
for serviceName, _ := range cls.mapServiceListenRpcEvent {
|
||||||
ser := service.GetService(serviceName)
|
ser := service.GetService(serviceName)
|
||||||
if ser == nil {
|
if ser == nil {
|
||||||
@@ -375,7 +381,27 @@ func (cls *Cluster) triggerRpcEvent(bConnect bool, clientSeq uint32, nodeId int)
|
|||||||
eventData.NodeId = nodeId
|
eventData.NodeId = nodeId
|
||||||
ser.(service.IModule).NotifyEvent(&eventData)
|
ser.(service.IModule).NotifyEvent(&eventData)
|
||||||
}
|
}
|
||||||
cls.rpcEventLocker.Unlock()
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (cls *Cluster) TriggerDiscoveryEvent(bDiscovery bool, nodeId int, serviceName []string) {
|
||||||
|
cls.rpcEventLocker.Lock()
|
||||||
|
defer cls.rpcEventLocker.Unlock()
|
||||||
|
|
||||||
|
for sName, _ := range cls.mapServiceListenDiscoveryEvent {
|
||||||
|
ser := service.GetService(sName)
|
||||||
|
if ser == nil {
|
||||||
|
log.SError("cannot find service name ", serviceName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventData service.DiscoveryServiceEvent
|
||||||
|
eventData.IsDiscovery = bDiscovery
|
||||||
|
eventData.NodeId = nodeId
|
||||||
|
eventData.ServiceName = serviceName
|
||||||
|
ser.(service.IModule).NotifyEvent(&eventData)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls *Cluster) GetLocalNodeInfo() *NodeInfo {
|
func (cls *Cluster) GetLocalNodeInfo() *NodeInfo {
|
||||||
@@ -398,14 +424,25 @@ func (cls *Cluster) UnRegRpcEvent(serviceName string) {
|
|||||||
cls.rpcEventLocker.Unlock()
|
cls.rpcEventLocker.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls *Cluster) FetchAllNodeId(fetchFun func(nodeId int)) {
|
|
||||||
cls.locker.Lock()
|
func (cls *Cluster) RegDiscoveryEvent(serviceName string) {
|
||||||
for nodeId, _ := range cls.mapIdNode {
|
cls.rpcEventLocker.Lock()
|
||||||
fetchFun(nodeId)
|
if cls.mapServiceListenDiscoveryEvent == nil {
|
||||||
|
cls.mapServiceListenDiscoveryEvent = map[string]struct{}{}
|
||||||
}
|
}
|
||||||
cls.locker.Unlock()
|
|
||||||
|
cls.mapServiceListenDiscoveryEvent[serviceName] = struct{}{}
|
||||||
|
cls.rpcEventLocker.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cls *Cluster) UnReDiscoveryEvent(serviceName string) {
|
||||||
|
cls.rpcEventLocker.Lock()
|
||||||
|
delete(cls.mapServiceListenDiscoveryEvent, serviceName)
|
||||||
|
cls.rpcEventLocker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func HasService(nodeId int, serviceName string) bool {
|
func HasService(nodeId int, serviceName string) bool {
|
||||||
cluster.locker.RLock()
|
cluster.locker.RLock()
|
||||||
defer cluster.locker.RUnlock()
|
defer cluster.locker.RUnlock()
|
||||||
@@ -418,3 +455,33 @@ func HasService(nodeId int, serviceName string) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetNodeByServiceName(serviceName string) map[int]struct{} {
|
||||||
|
cluster.locker.RLock()
|
||||||
|
defer cluster.locker.RUnlock()
|
||||||
|
|
||||||
|
mapNode, ok := cluster.mapServiceNode[serviceName]
|
||||||
|
if ok == false {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mapNodeId := map[int]struct{}{}
|
||||||
|
for nodeId,_ := range mapNode {
|
||||||
|
mapNodeId[nodeId] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapNodeId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cls *Cluster) GetGlobalCfg() interface{} {
|
||||||
|
return cls.globalCfg
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (cls *Cluster) GetNodeInfo(nodeId int) (NodeInfo,bool) {
|
||||||
|
cls.locker.RLock()
|
||||||
|
defer cls.locker.RUnlock()
|
||||||
|
|
||||||
|
nodeInfo,ok:= cls.mapIdNode[nodeId]
|
||||||
|
return nodeInfo,ok
|
||||||
|
}
|
||||||
|
|||||||
@@ -290,6 +290,8 @@ func (dc *DynamicDiscoveryClient) RPC_SubServiceDiscover(req *rpc.SubscribeDisco
|
|||||||
|
|
||||||
//删除不必要的结点
|
//删除不必要的结点
|
||||||
for _, nodeId := range willDelNodeId {
|
for _, nodeId := range willDelNodeId {
|
||||||
|
nodeInfo,_ := cluster.GetNodeInfo(int(nodeId))
|
||||||
|
cluster.TriggerDiscoveryEvent(false,int(nodeId),nodeInfo.PublicServiceList)
|
||||||
dc.removeMasterNode(req.MasterNodeId, int32(nodeId))
|
dc.removeMasterNode(req.MasterNodeId, int32(nodeId))
|
||||||
if dc.findNodeId(nodeId) == false {
|
if dc.findNodeId(nodeId) == false {
|
||||||
dc.funDelService(int(nodeId), false)
|
dc.funDelService(int(nodeId), false)
|
||||||
@@ -300,6 +302,12 @@ func (dc *DynamicDiscoveryClient) RPC_SubServiceDiscover(req *rpc.SubscribeDisco
|
|||||||
for _, nodeInfo := range mapNodeInfo {
|
for _, nodeInfo := range mapNodeInfo {
|
||||||
dc.addMasterNode(req.MasterNodeId, nodeInfo.NodeId)
|
dc.addMasterNode(req.MasterNodeId, nodeInfo.NodeId)
|
||||||
dc.setNodeInfo(nodeInfo)
|
dc.setNodeInfo(nodeInfo)
|
||||||
|
|
||||||
|
if len(nodeInfo.PublicServiceList) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster.TriggerDiscoveryEvent(true,int(nodeInfo.NodeId),nodeInfo.PublicServiceList)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -5,19 +5,21 @@ import (
|
|||||||
"github.com/duanhf2012/origin/log"
|
"github.com/duanhf2012/origin/log"
|
||||||
"github.com/duanhf2012/origin/rpc"
|
"github.com/duanhf2012/origin/rpc"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"io/ioutil"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
|
||||||
type NodeInfoList struct {
|
type NodeInfoList struct {
|
||||||
MasterDiscoveryNode []NodeInfo //用于服务发现Node
|
MasterDiscoveryNode []NodeInfo //用于服务发现Node
|
||||||
NodeList []NodeInfo
|
NodeList []NodeInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls *Cluster) ReadClusterConfig(filepath string) (*NodeInfoList,error) {
|
func (cls *Cluster) ReadClusterConfig(filepath string) (*NodeInfoList, error) {
|
||||||
c := &NodeInfoList{}
|
c := &NodeInfoList{}
|
||||||
d, err := ioutil.ReadFile(filepath)
|
d, err := os.ReadFile(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -26,148 +28,191 @@ func (cls *Cluster) ReadClusterConfig(filepath string) (*NodeInfoList,error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c,nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls *Cluster) readServiceConfig(filepath string) (map[string]interface{},map[int]map[string]interface{},error) {
|
func (cls *Cluster) readServiceConfig(filepath string) (interface{}, map[string]interface{}, map[int]map[string]interface{}, error) {
|
||||||
c := map[string]interface{}{}
|
c := map[string]interface{}{}
|
||||||
//读取配置
|
//读取配置
|
||||||
d, err := ioutil.ReadFile(filepath)
|
d, err := os.ReadFile(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil,nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(d, &c)
|
err = json.Unmarshal(d, &c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil,nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GlobalCfg, ok := c["Global"]
|
||||||
serviceConfig := map[string]interface{}{}
|
serviceConfig := map[string]interface{}{}
|
||||||
serviceCfg,ok := c["Service"]
|
serviceCfg, ok := c["Service"]
|
||||||
if ok == true {
|
if ok == true {
|
||||||
serviceConfig = serviceCfg.(map[string]interface{})
|
serviceConfig = serviceCfg.(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
mapNodeService := map[int]map[string]interface{}{}
|
mapNodeService := map[int]map[string]interface{}{}
|
||||||
nodeServiceCfg,ok := c["NodeService"]
|
nodeServiceCfg, ok := c["NodeService"]
|
||||||
if ok == true {
|
if ok == true {
|
||||||
nodeServiceList := nodeServiceCfg.([]interface{})
|
nodeServiceList := nodeServiceCfg.([]interface{})
|
||||||
for _,v := range nodeServiceList{
|
for _, v := range nodeServiceList {
|
||||||
serviceCfg :=v.(map[string]interface{})
|
serviceCfg := v.(map[string]interface{})
|
||||||
nodeId,ok := serviceCfg["NodeId"]
|
nodeId, ok := serviceCfg["NodeId"]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
log.SFatal("NodeService list not find nodeId field")
|
log.SFatal("NodeService list not find nodeId field")
|
||||||
}
|
}
|
||||||
mapNodeService[int(nodeId.(float64))] = serviceCfg
|
mapNodeService[int(nodeId.(float64))] = serviceCfg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return serviceConfig,mapNodeService,nil
|
return GlobalCfg, serviceConfig, mapNodeService, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls *Cluster) readLocalClusterConfig(nodeId int) ([]NodeInfo,[]NodeInfo,error) {
|
func (cls *Cluster) readLocalClusterConfig(nodeId int) ([]NodeInfo, []NodeInfo, error) {
|
||||||
var nodeInfoList []NodeInfo
|
var nodeInfoList []NodeInfo
|
||||||
var masterDiscoverNodeList []NodeInfo
|
var masterDiscoverNodeList []NodeInfo
|
||||||
clusterCfgPath :=strings.TrimRight(configDir,"/") +"/cluster"
|
clusterCfgPath := strings.TrimRight(configDir, "/") + "/cluster"
|
||||||
fileInfoList,err := ioutil.ReadDir(clusterCfgPath)
|
fileInfoList, err := os.ReadDir(clusterCfgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil,nil,fmt.Errorf("Read dir %s is fail :%+v",clusterCfgPath,err)
|
return nil, nil, fmt.Errorf("Read dir %s is fail :%+v", clusterCfgPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//读取任何文件,只读符合格式的配置,目录下的文件可以自定义分文件
|
//读取任何文件,只读符合格式的配置,目录下的文件可以自定义分文件
|
||||||
for _,f := range fileInfoList{
|
for _, f := range fileInfoList {
|
||||||
if f.IsDir() == false {
|
if f.IsDir() == false {
|
||||||
filePath := strings.TrimRight(strings.TrimRight(clusterCfgPath,"/"),"\\")+"/"+f.Name()
|
filePath := strings.TrimRight(strings.TrimRight(clusterCfgPath, "/"), "\\") + "/" + f.Name()
|
||||||
localNodeInfoList,err := cls.ReadClusterConfig(filePath)
|
localNodeInfoList, err := cls.ReadClusterConfig(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil,nil,fmt.Errorf("read file path %s is error:%+v" ,filePath,err)
|
return nil, nil, fmt.Errorf("read file path %s is error:%+v", filePath, err)
|
||||||
}
|
}
|
||||||
masterDiscoverNodeList = append(masterDiscoverNodeList,localNodeInfoList.MasterDiscoveryNode...)
|
masterDiscoverNodeList = append(masterDiscoverNodeList, localNodeInfoList.MasterDiscoveryNode...)
|
||||||
for _,nodeInfo := range localNodeInfoList.NodeList {
|
for _, nodeInfo := range localNodeInfoList.NodeList {
|
||||||
if nodeInfo.NodeId == nodeId || nodeId == 0 {
|
if nodeInfo.NodeId == nodeId || nodeId == 0 {
|
||||||
nodeInfoList = append(nodeInfoList,nodeInfo)
|
nodeInfoList = append(nodeInfoList, nodeInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if nodeId != 0 && (len(nodeInfoList)!=1){
|
if nodeId != 0 && (len(nodeInfoList) != 1) {
|
||||||
return nil,nil,fmt.Errorf("%d configurations were found for the configuration with node ID %d!",len(nodeInfoList),nodeId)
|
return nil, nil, fmt.Errorf("%d configurations were found for the configuration with node ID %d!", len(nodeInfoList), nodeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i,_ := range nodeInfoList{
|
for i, _ := range nodeInfoList {
|
||||||
for j,s := range nodeInfoList[i].ServiceList{
|
for j, s := range nodeInfoList[i].ServiceList {
|
||||||
//私有结点不加入到Public服务列表中
|
//私有结点不加入到Public服务列表中
|
||||||
if strings.HasPrefix(s,"_") == false && nodeInfoList[i].Private==false {
|
if strings.HasPrefix(s, "_") == false && nodeInfoList[i].Private == false {
|
||||||
nodeInfoList[i].PublicServiceList = append(nodeInfoList[i].PublicServiceList,strings.TrimLeft(s,"_"))
|
nodeInfoList[i].PublicServiceList = append(nodeInfoList[i].PublicServiceList, strings.TrimLeft(s, "_"))
|
||||||
}else{
|
} else {
|
||||||
nodeInfoList[i].ServiceList[j] = strings.TrimLeft(s,"_")
|
nodeInfoList[i].ServiceList[j] = strings.TrimLeft(s, "_")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return masterDiscoverNodeList, nodeInfoList, nil
|
||||||
return masterDiscoverNodeList,nodeInfoList,nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls *Cluster) readLocalService(localNodeId int) error {
|
func (cls *Cluster) readLocalService(localNodeId int) error {
|
||||||
clusterCfgPath :=strings.TrimRight(configDir,"/") +"/cluster"
|
clusterCfgPath := strings.TrimRight(configDir, "/") + "/cluster"
|
||||||
fileInfoList,err := ioutil.ReadDir(clusterCfgPath)
|
fileInfoList, err := os.ReadDir(clusterCfgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Read dir %s is fail :%+v",clusterCfgPath,err)
|
return fmt.Errorf("Read dir %s is fail :%+v", clusterCfgPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var globalCfg interface{}
|
||||||
|
publicService := map[string]interface{}{}
|
||||||
|
nodeService := map[string]interface{}{}
|
||||||
|
|
||||||
//读取任何文件,只读符合格式的配置,目录下的文件可以自定义分文件
|
//读取任何文件,只读符合格式的配置,目录下的文件可以自定义分文件
|
||||||
for _,f := range fileInfoList {
|
for _, f := range fileInfoList {
|
||||||
if f.IsDir() == false {
|
if f.IsDir() == true {
|
||||||
filePath := strings.TrimRight(strings.TrimRight(clusterCfgPath, "/"), "\\") + "/" + f.Name()
|
continue
|
||||||
serviceConfig,mapNodeService,err := cls.readServiceConfig(filePath)
|
}
|
||||||
if err != nil {
|
|
||||||
continue
|
if filepath.Ext(f.Name())!= ".json" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath := strings.TrimRight(strings.TrimRight(clusterCfgPath, "/"), "\\") + "/" + f.Name()
|
||||||
|
currGlobalCfg, serviceConfig, mapNodeService, err := cls.readServiceConfig(filePath)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if currGlobalCfg != nil {
|
||||||
|
//不允许重复的配置global配置
|
||||||
|
if globalCfg != nil {
|
||||||
|
return fmt.Errorf("[Global] does not allow repeated configuration in %s.",f.Name())
|
||||||
}
|
}
|
||||||
|
globalCfg = currGlobalCfg
|
||||||
|
}
|
||||||
|
|
||||||
for _,s := range cls.localNodeInfo.ServiceList{
|
//保存公共配置
|
||||||
for{
|
for _, s := range cls.localNodeInfo.ServiceList {
|
||||||
//取公共服务配置
|
for {
|
||||||
pubCfg,ok := serviceConfig[s]
|
//取公共服务配置
|
||||||
if ok == true {
|
pubCfg, ok := serviceConfig[s]
|
||||||
cls.localServiceCfg[s] = pubCfg
|
if ok == true {
|
||||||
|
if _,publicOk := publicService[s];publicOk == true {
|
||||||
|
return fmt.Errorf("public service [%s] does not allow repeated configuration in %s.",s,f.Name())
|
||||||
}
|
}
|
||||||
|
publicService[s] = pubCfg
|
||||||
|
}
|
||||||
|
|
||||||
//如果结点也配置了该服务,则覆盖之
|
//取指定结点配置的服务
|
||||||
nodeService,ok := mapNodeService[localNodeId]
|
nodeServiceCfg,ok := mapNodeService[localNodeId]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
break
|
|
||||||
}
|
|
||||||
sCfg,ok := nodeService[s]
|
|
||||||
if ok == false{
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
cls.localServiceCfg[s] = sCfg
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
nodeCfg, ok := nodeServiceCfg[s]
|
||||||
|
if ok == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if _,nodeOK := nodeService[s];nodeOK == true {
|
||||||
|
return fmt.Errorf("NodeService NodeId[%d] Service[%s] does not allow repeated configuration in %s.",cls.localNodeInfo.NodeId,s,f.Name())
|
||||||
|
}
|
||||||
|
nodeService[s] = nodeCfg
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//组合所有的配置
|
||||||
|
for _, s := range cls.localNodeInfo.ServiceList {
|
||||||
|
//先从NodeService中找
|
||||||
|
var serviceCfg interface{}
|
||||||
|
var ok bool
|
||||||
|
serviceCfg,ok = nodeService[s]
|
||||||
|
if ok == true {
|
||||||
|
cls.localServiceCfg[s] =serviceCfg
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果找不到从PublicService中找
|
||||||
|
serviceCfg,ok = publicService[s]
|
||||||
|
if ok == true {
|
||||||
|
cls.localServiceCfg[s] =serviceCfg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cls.globalCfg = globalCfg
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls *Cluster) parseLocalCfg(){
|
func (cls *Cluster) parseLocalCfg() {
|
||||||
cls.mapIdNode[cls.localNodeInfo.NodeId] = cls.localNodeInfo
|
cls.mapIdNode[cls.localNodeInfo.NodeId] = cls.localNodeInfo
|
||||||
|
|
||||||
for _,sName := range cls.localNodeInfo.ServiceList{
|
for _, sName := range cls.localNodeInfo.ServiceList {
|
||||||
if _,ok:=cls.mapServiceNode[sName];ok==false{
|
if _, ok := cls.mapServiceNode[sName]; ok == false {
|
||||||
cls.mapServiceNode[sName] = make(map[int]struct{})
|
cls.mapServiceNode[sName] = make(map[int]struct{})
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.mapServiceNode[sName][cls.localNodeInfo.NodeId]= struct{}{}
|
cls.mapServiceNode[sName][cls.localNodeInfo.NodeId] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cls *Cluster) checkDiscoveryNodeList(discoverMasterNode []NodeInfo) bool {
|
||||||
func (cls *Cluster) checkDiscoveryNodeList(discoverMasterNode []NodeInfo) bool{
|
for i := 0; i < len(discoverMasterNode)-1; i++ {
|
||||||
for i:=0;i<len(discoverMasterNode)-1;i++{
|
for j := i + 1; j < len(discoverMasterNode); j++ {
|
||||||
for j:=i+1;j<len(discoverMasterNode);j++{
|
|
||||||
if discoverMasterNode[i].NodeId == discoverMasterNode[j].NodeId ||
|
if discoverMasterNode[i].NodeId == discoverMasterNode[j].NodeId ||
|
||||||
discoverMasterNode[i].ListenAddr == discoverMasterNode[j].ListenAddr {
|
discoverMasterNode[i].ListenAddr == discoverMasterNode[j].ListenAddr {
|
||||||
return false
|
return false
|
||||||
@@ -178,19 +223,19 @@ func (cls *Cluster) checkDiscoveryNodeList(discoverMasterNode []NodeInfo) bool{
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls *Cluster) InitCfg(localNodeId int) error{
|
func (cls *Cluster) InitCfg(localNodeId int) error {
|
||||||
cls.localServiceCfg = map[string]interface{}{}
|
cls.localServiceCfg = map[string]interface{}{}
|
||||||
cls.mapRpc = map[int] NodeRpcInfo{}
|
cls.mapRpc = map[int]NodeRpcInfo{}
|
||||||
cls.mapIdNode = map[int]NodeInfo{}
|
cls.mapIdNode = map[int]NodeInfo{}
|
||||||
cls.mapServiceNode = map[string]map[int]struct{}{}
|
cls.mapServiceNode = map[string]map[int]struct{}{}
|
||||||
|
|
||||||
//加载本地结点的NodeList配置
|
//加载本地结点的NodeList配置
|
||||||
discoveryNode,nodeInfoList,err := cls.readLocalClusterConfig(localNodeId)
|
discoveryNode, nodeInfoList, err := cls.readLocalClusterConfig(localNodeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cls.localNodeInfo = nodeInfoList[0]
|
cls.localNodeInfo = nodeInfoList[0]
|
||||||
if cls.checkDiscoveryNodeList(discoveryNode) ==false {
|
if cls.checkDiscoveryNodeList(discoveryNode) == false {
|
||||||
return fmt.Errorf("DiscoveryNode config is error!")
|
return fmt.Errorf("DiscoveryNode config is error!")
|
||||||
}
|
}
|
||||||
cls.masterDiscoveryNodeList = discoveryNode
|
cls.masterDiscoveryNodeList = discoveryNode
|
||||||
@@ -209,48 +254,39 @@ func (cls *Cluster) InitCfg(localNodeId int) error{
|
|||||||
func (cls *Cluster) IsConfigService(serviceName string) bool {
|
func (cls *Cluster) IsConfigService(serviceName string) bool {
|
||||||
cls.locker.RLock()
|
cls.locker.RLock()
|
||||||
defer cls.locker.RUnlock()
|
defer cls.locker.RUnlock()
|
||||||
mapNode,ok := cls.mapServiceNode[serviceName]
|
mapNode, ok := cls.mapServiceNode[serviceName]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
_,ok = mapNode[cls.localNodeInfo.NodeId]
|
_, ok = mapNode[cls.localNodeInfo.NodeId]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls *Cluster) GetNodeIdByService(serviceName string,rpcClientList []*rpc.Client,bAll bool) (error,int) {
|
func (cls *Cluster) GetNodeIdByService(serviceName string, rpcClientList []*rpc.Client, bAll bool) (error, int) {
|
||||||
cls.locker.RLock()
|
cls.locker.RLock()
|
||||||
defer cls.locker.RUnlock()
|
defer cls.locker.RUnlock()
|
||||||
mapNodeId,ok := cls.mapServiceNode[serviceName]
|
mapNodeId, ok := cls.mapServiceNode[serviceName]
|
||||||
count := 0
|
count := 0
|
||||||
if ok == true {
|
if ok == true {
|
||||||
for nodeId,_ := range mapNodeId {
|
for nodeId, _ := range mapNodeId {
|
||||||
pClient := GetCluster().getRpcClient(nodeId)
|
pClient := GetCluster().getRpcClient(nodeId)
|
||||||
if pClient==nil || (bAll == false && pClient.IsConnected()==false) {
|
if pClient == nil || (bAll == false && pClient.IsConnected() == false) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rpcClientList[count] = pClient
|
rpcClientList[count] = pClient
|
||||||
count++
|
count++
|
||||||
if count>=cap(rpcClientList) {
|
if count >= cap(rpcClientList) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil,count
|
return nil, count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls *Cluster) getServiceCfg(serviceName string) interface{}{
|
func (cls *Cluster) GetServiceCfg(serviceName string) interface{} {
|
||||||
v,ok := cls.localServiceCfg[serviceName]
|
serviceCfg, ok := cls.localServiceCfg[serviceName]
|
||||||
if ok == false {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cls *Cluster) GetServiceCfg(serviceName string) interface{}{
|
|
||||||
serviceCfg,ok := cls.localServiceCfg[serviceName]
|
|
||||||
if ok == false {
|
if ok == false {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ const (
|
|||||||
|
|
||||||
Sys_Event_Tcp EventType = -3
|
Sys_Event_Tcp EventType = -3
|
||||||
Sys_Event_Http_Event EventType = -4
|
Sys_Event_Http_Event EventType = -4
|
||||||
Sys_Event_WebSocket EventType = -5
|
Sys_Event_WebSocket EventType = -5
|
||||||
Sys_Event_Rpc_Event EventType = -6
|
Sys_Event_Node_Event EventType = -6
|
||||||
|
Sys_Event_DiscoverService EventType = -7
|
||||||
|
|
||||||
Sys_Event_User_Define EventType = 1
|
Sys_Event_User_Define EventType = 1
|
||||||
)
|
)
|
||||||
|
|||||||
31
go.mod
31
go.mod
@@ -1,13 +1,30 @@
|
|||||||
module github.com/duanhf2012/origin
|
module github.com/duanhf2012/origin
|
||||||
|
|
||||||
go 1.17
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.6.0
|
||||||
github.com/golang/protobuf v1.4.3
|
github.com/gogo/protobuf v1.3.2
|
||||||
github.com/gomodule/redigo v1.8.3
|
github.com/gomodule/redigo v1.8.8
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/json-iterator/go v1.1.10
|
github.com/json-iterator/go v1.1.12
|
||||||
google.golang.org/protobuf v1.25.0
|
go.mongodb.org/mongo-driver v1.9.1
|
||||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
|
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/go-stack/stack v1.8.0 // indirect
|
||||||
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
|
github.com/klauspost/compress v1.13.6 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
|
github.com/xdg-go/scram v1.0.2 // indirect
|
||||||
|
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||||
|
golang.org/x/text v0.3.6 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
)
|
||||||
|
|||||||
159
go.sum
159
go.sum
@@ -1,95 +1,102 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/gomodule/redigo v1.8.8 h1:f6cXq6RRfiyrOJEV7p3JhLDlmawGBVBBP1MggY8Mo4E=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/gomodule/redigo v1.8.8/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/gomodule/redigo v1.8.3 h1:HR0kYDX2RJZvAup8CsiJwxB4dTCSC0AaUq6S4SiLwUc=
|
|
||||||
github.com/gomodule/redigo v1.8.3/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
|
||||||
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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||||
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||||
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
|
||||||
|
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||||
|
github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
|
||||||
|
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
go.mongodb.org/mongo-driver v1.9.1 h1:m078y9v7sBItkt1aaoe2YlvWEXcD263e1a4E1fBrJ1c=
|
||||||
|
go.mongodb.org/mongo-driver v1.9.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f h1:aZp0e2vLN4MToVqnjNEYEtrEA8RH8U8FN1CU7JgqsPU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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-20190215142949-d0b11bdaac8a/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-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
|
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
|
||||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package network
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"github.com/duanhf2012/origin/log"
|
"github.com/duanhf2012/origin/log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
@@ -37,6 +38,10 @@ func (slf *HttpServer) Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpServer) startListen() error {
|
func (slf *HttpServer) startListen() error {
|
||||||
|
if slf.httpServer != nil {
|
||||||
|
return errors.New("Duplicate start not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
var tlsCaList []tls.Certificate
|
var tlsCaList []tls.Certificate
|
||||||
var tlsConfig *tls.Config
|
var tlsConfig *tls.Config
|
||||||
for _, caFile := range slf.caFileList {
|
for _, caFile := range slf.caFileList {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/duanhf2012/origin/network"
|
"github.com/duanhf2012/origin/network"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"github.com/duanhf2012/origin/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MessageJsonInfo struct {
|
type MessageJsonInfo struct {
|
||||||
@@ -44,18 +45,18 @@ func (jsonProcessor *JsonProcessor) SetByteOrder(littleEndian bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// must goroutine safe
|
// must goroutine safe
|
||||||
func (jsonProcessor *JsonProcessor ) MsgRoute(msg interface{},userdata interface{}) error{
|
func (jsonProcessor *JsonProcessor ) MsgRoute(clientId uint64,msg interface{}) error{
|
||||||
pPackInfo := msg.(*JsonPackInfo)
|
pPackInfo := msg.(*JsonPackInfo)
|
||||||
v,ok := jsonProcessor.mapMsg[pPackInfo.typ]
|
v,ok := jsonProcessor.mapMsg[pPackInfo.typ]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
return fmt.Errorf("cannot find msgtype %d is register!",pPackInfo.typ)
|
return fmt.Errorf("cannot find msgtype %d is register!",pPackInfo.typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.msgHandler(userdata.(uint64),pPackInfo.msg)
|
v.msgHandler(clientId,pPackInfo.msg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jsonProcessor *JsonProcessor) Unmarshal(data []byte) (interface{}, error) {
|
func (jsonProcessor *JsonProcessor) Unmarshal(clientId uint64,data []byte) (interface{}, error) {
|
||||||
typeStruct := struct {Type int `json:"typ"`}{}
|
typeStruct := struct {Type int `json:"typ"`}{}
|
||||||
defer jsonProcessor.ReleaseByteSlice(data)
|
defer jsonProcessor.ReleaseByteSlice(data)
|
||||||
err := json.Unmarshal(data, &typeStruct)
|
err := json.Unmarshal(data, &typeStruct)
|
||||||
@@ -78,7 +79,7 @@ func (jsonProcessor *JsonProcessor) Unmarshal(data []byte) (interface{}, error)
|
|||||||
return &JsonPackInfo{typ:msgType,msg:msgData},nil
|
return &JsonPackInfo{typ:msgType,msg:msgData},nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jsonProcessor *JsonProcessor) Marshal(msg interface{}) ([]byte, error) {
|
func (jsonProcessor *JsonProcessor) Marshal(clientId uint64,msg interface{}) ([]byte, error) {
|
||||||
rawMsg,err := json.Marshal(msg)
|
rawMsg,err := json.Marshal(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil,err
|
return nil,err
|
||||||
@@ -103,16 +104,26 @@ func (jsonProcessor *JsonProcessor) MakeRawMsg(msgType uint16,msg []byte) *JsonP
|
|||||||
return &JsonPackInfo{typ:msgType,rawMsg:msg}
|
return &JsonPackInfo{typ:msgType,rawMsg:msg}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jsonProcessor *JsonProcessor) UnknownMsgRoute(msg interface{}, userData interface{}){
|
func (jsonProcessor *JsonProcessor) UnknownMsgRoute(clientId uint64,msg interface{}){
|
||||||
jsonProcessor.unknownMessageHandler(userData.(uint64),msg.([]byte))
|
if jsonProcessor.unknownMessageHandler==nil {
|
||||||
|
log.SDebug("Unknown message received from ",clientId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonProcessor.unknownMessageHandler(clientId,msg.([]byte))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jsonProcessor *JsonProcessor) ConnectedRoute(userData interface{}){
|
func (jsonProcessor *JsonProcessor) ConnectedRoute(clientId uint64){
|
||||||
jsonProcessor.connectHandler(userData.(uint64))
|
if jsonProcessor.connectHandler != nil {
|
||||||
|
jsonProcessor.connectHandler(clientId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jsonProcessor *JsonProcessor) DisConnectedRoute(userData interface{}){
|
func (jsonProcessor *JsonProcessor) DisConnectedRoute(clientId uint64){
|
||||||
jsonProcessor.disconnectHandler(userData.(uint64))
|
if jsonProcessor.disconnectHandler != nil {
|
||||||
|
jsonProcessor.disconnectHandler(clientId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jsonProcessor *JsonProcessor) RegisterUnknownMsg(unknownMessageHandler UnknownMessageJsonHandler){
|
func (jsonProcessor *JsonProcessor) RegisterUnknownMsg(unknownMessageHandler UnknownMessageJsonHandler){
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ type TCPClient struct {
|
|||||||
ConnNum int
|
ConnNum int
|
||||||
ConnectInterval time.Duration
|
ConnectInterval time.Duration
|
||||||
PendingWriteNum int
|
PendingWriteNum int
|
||||||
|
ReadDeadline time.Duration
|
||||||
|
WriteDeadline time.Duration
|
||||||
AutoReconnect bool
|
AutoReconnect bool
|
||||||
NewAgent func(*TCPConn) Agent
|
NewAgent func(*TCPConn) Agent
|
||||||
cons ConnSet
|
cons ConnSet
|
||||||
@@ -52,6 +54,14 @@ func (client *TCPClient) init() {
|
|||||||
client.PendingWriteNum = 1000
|
client.PendingWriteNum = 1000
|
||||||
log.SRelease("invalid PendingWriteNum, reset to ", client.PendingWriteNum)
|
log.SRelease("invalid PendingWriteNum, reset to ", client.PendingWriteNum)
|
||||||
}
|
}
|
||||||
|
if client.ReadDeadline == 0 {
|
||||||
|
client.ReadDeadline = 15*time.Second
|
||||||
|
log.SRelease("invalid ReadDeadline, reset to ", int64(client.ReadDeadline.Seconds()),"s")
|
||||||
|
}
|
||||||
|
if client.WriteDeadline == 0 {
|
||||||
|
client.WriteDeadline = 15*time.Second
|
||||||
|
log.SRelease("invalid WriteDeadline, reset to ", int64(client.WriteDeadline.Seconds()),"s")
|
||||||
|
}
|
||||||
if client.NewAgent == nil {
|
if client.NewAgent == nil {
|
||||||
log.SFatal("NewAgent must not be nil")
|
log.SFatal("NewAgent must not be nil")
|
||||||
}
|
}
|
||||||
@@ -69,6 +79,13 @@ func (client *TCPClient) init() {
|
|||||||
client.msgParser = msgParser
|
client.msgParser = msgParser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (client *TCPClient) GetCloseFlag() bool{
|
||||||
|
client.Lock()
|
||||||
|
defer client.Unlock()
|
||||||
|
|
||||||
|
return client.closeFlag
|
||||||
|
}
|
||||||
|
|
||||||
func (client *TCPClient) dial() net.Conn {
|
func (client *TCPClient) dial() net.Conn {
|
||||||
for {
|
for {
|
||||||
conn, err := net.Dial("tcp", client.Addr)
|
conn, err := net.Dial("tcp", client.Addr)
|
||||||
@@ -93,7 +110,7 @@ reconnect:
|
|||||||
if conn == nil {
|
if conn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Lock()
|
client.Lock()
|
||||||
if client.closeFlag {
|
if client.closeFlag {
|
||||||
client.Unlock()
|
client.Unlock()
|
||||||
@@ -103,7 +120,7 @@ reconnect:
|
|||||||
client.cons[conn] = struct{}{}
|
client.cons[conn] = struct{}{}
|
||||||
client.Unlock()
|
client.Unlock()
|
||||||
|
|
||||||
tcpConn := newTCPConn(conn, client.PendingWriteNum, client.msgParser)
|
tcpConn := newTCPConn(conn, client.PendingWriteNum, client.msgParser,client.WriteDeadline)
|
||||||
agent := client.NewAgent(tcpConn)
|
agent := client.NewAgent(tcpConn)
|
||||||
agent.Run()
|
agent.Run()
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func freeChannel(conn *TCPConn){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTCPConn(conn net.Conn, pendingWriteNum int, msgParser *MsgParser) *TCPConn {
|
func newTCPConn(conn net.Conn, pendingWriteNum int, msgParser *MsgParser,writeDeadline time.Duration) *TCPConn {
|
||||||
tcpConn := new(TCPConn)
|
tcpConn := new(TCPConn)
|
||||||
tcpConn.conn = conn
|
tcpConn.conn = conn
|
||||||
tcpConn.writeChan = make(chan []byte, pendingWriteNum)
|
tcpConn.writeChan = make(chan []byte, pendingWriteNum)
|
||||||
@@ -37,6 +37,8 @@ func newTCPConn(conn net.Conn, pendingWriteNum int, msgParser *MsgParser) *TCPCo
|
|||||||
if b == nil {
|
if b == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn.SetWriteDeadline(time.Now().Add(writeDeadline))
|
||||||
_, err := conn.Write(b)
|
_, err := conn.Write(b)
|
||||||
tcpConn.msgParser.ReleaseByteSlice(b)
|
tcpConn.msgParser.ReleaseByteSlice(b)
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,21 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const Default_ReadDeadline = time.Second*30 //30s
|
||||||
|
const Default_WriteDeadline = time.Second*30 //30s
|
||||||
|
const Default_MaxConnNum = 3000
|
||||||
|
const Default_PendingWriteNum = 10000
|
||||||
|
const Default_LittleEndian = false
|
||||||
|
const Default_MinMsgLen = 2
|
||||||
|
const Default_MaxMsgLen = 65535
|
||||||
|
|
||||||
|
|
||||||
type TCPServer struct {
|
type TCPServer struct {
|
||||||
Addr string
|
Addr string
|
||||||
MaxConnNum int
|
MaxConnNum int
|
||||||
PendingWriteNum int
|
PendingWriteNum int
|
||||||
|
ReadDeadline time.Duration
|
||||||
|
WriteDeadline time.Duration
|
||||||
NewAgent func(*TCPConn) Agent
|
NewAgent func(*TCPConn) Agent
|
||||||
ln net.Listener
|
ln net.Listener
|
||||||
conns ConnSet
|
conns ConnSet
|
||||||
@@ -18,6 +29,7 @@ type TCPServer struct {
|
|||||||
wgLn sync.WaitGroup
|
wgLn sync.WaitGroup
|
||||||
wgConns sync.WaitGroup
|
wgConns sync.WaitGroup
|
||||||
|
|
||||||
|
|
||||||
// msg parser
|
// msg parser
|
||||||
LenMsgLen int
|
LenMsgLen int
|
||||||
MinMsgLen uint32
|
MinMsgLen uint32
|
||||||
@@ -39,13 +51,33 @@ func (server *TCPServer) init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if server.MaxConnNum <= 0 {
|
if server.MaxConnNum <= 0 {
|
||||||
server.MaxConnNum = 100
|
server.MaxConnNum = Default_MaxConnNum
|
||||||
log.SRelease("invalid MaxConnNum, reset to ", server.MaxConnNum)
|
log.SRelease("invalid MaxConnNum, reset to ", server.MaxConnNum)
|
||||||
}
|
}
|
||||||
if server.PendingWriteNum <= 0 {
|
if server.PendingWriteNum <= 0 {
|
||||||
server.PendingWriteNum = 100
|
server.PendingWriteNum = Default_PendingWriteNum
|
||||||
log.SRelease("invalid PendingWriteNum, reset to ", server.PendingWriteNum)
|
log.SRelease("invalid PendingWriteNum, reset to ", server.PendingWriteNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if server.MinMsgLen <= 0 {
|
||||||
|
server.MinMsgLen = Default_MinMsgLen
|
||||||
|
log.SRelease("invalid MinMsgLen, reset to ", server.MinMsgLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
if server.MaxMsgLen <= 0 {
|
||||||
|
server.MaxMsgLen = Default_MaxMsgLen
|
||||||
|
log.SRelease("invalid MaxMsgLen, reset to ", server.MaxMsgLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
if server.WriteDeadline == 0 {
|
||||||
|
server.WriteDeadline = Default_WriteDeadline
|
||||||
|
log.SRelease("invalid WriteDeadline, reset to ", server.WriteDeadline.Seconds(),"s")
|
||||||
|
}
|
||||||
|
if server.ReadDeadline == 0 {
|
||||||
|
server.ReadDeadline = Default_ReadDeadline
|
||||||
|
log.SRelease("invalid ReadDeadline, reset to ", server.ReadDeadline.Seconds(),"s")
|
||||||
|
}
|
||||||
|
|
||||||
if server.NewAgent == nil {
|
if server.NewAgent == nil {
|
||||||
log.SFatal("NewAgent must not be nil")
|
log.SFatal("NewAgent must not be nil")
|
||||||
}
|
}
|
||||||
@@ -110,7 +142,7 @@ func (server *TCPServer) run() {
|
|||||||
|
|
||||||
server.wgConns.Add(1)
|
server.wgConns.Add(1)
|
||||||
|
|
||||||
tcpConn := newTCPConn(conn, server.PendingWriteNum, server.msgParser)
|
tcpConn := newTCPConn(conn, server.PendingWriteNum, server.msgParser,server.WriteDeadline)
|
||||||
agent := server.NewAgent(tcpConn)
|
agent := server.NewAgent(tcpConn)
|
||||||
go func() {
|
go func() {
|
||||||
agent.Run()
|
agent.Run()
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type WSClient struct {
|
|||||||
ConnectInterval time.Duration
|
ConnectInterval time.Duration
|
||||||
PendingWriteNum int
|
PendingWriteNum int
|
||||||
MaxMsgLen uint32
|
MaxMsgLen uint32
|
||||||
|
MessageType int
|
||||||
HandshakeTimeout time.Duration
|
HandshakeTimeout time.Duration
|
||||||
AutoReconnect bool
|
AutoReconnect bool
|
||||||
NewAgent func(*WSConn) Agent
|
NewAgent func(*WSConn) Agent
|
||||||
@@ -21,6 +22,7 @@ type WSClient struct {
|
|||||||
cons WebsocketConnSet
|
cons WebsocketConnSet
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
closeFlag bool
|
closeFlag bool
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *WSClient) Start() {
|
func (client *WSClient) Start() {
|
||||||
@@ -63,6 +65,10 @@ func (client *WSClient) init() {
|
|||||||
log.SFatal("client is running")
|
log.SFatal("client is running")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if client.MessageType == 0 {
|
||||||
|
client.MessageType = websocket.TextMessage
|
||||||
|
}
|
||||||
|
|
||||||
client.cons = make(WebsocketConnSet)
|
client.cons = make(WebsocketConnSet)
|
||||||
client.closeFlag = false
|
client.closeFlag = false
|
||||||
client.dialer = websocket.Dialer{
|
client.dialer = websocket.Dialer{
|
||||||
@@ -102,7 +108,7 @@ reconnect:
|
|||||||
client.cons[conn] = struct{}{}
|
client.cons[conn] = struct{}{}
|
||||||
client.Unlock()
|
client.Unlock()
|
||||||
|
|
||||||
wsConn := newWSConn(conn, client.PendingWriteNum, client.MaxMsgLen)
|
wsConn := newWSConn(conn, client.PendingWriteNum, client.MaxMsgLen,client.MessageType)
|
||||||
agent := client.NewAgent(wsConn)
|
agent := client.NewAgent(wsConn)
|
||||||
agent.Run()
|
agent.Run()
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type WSConn struct {
|
|||||||
closeFlag bool
|
closeFlag bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWSConn(conn *websocket.Conn, pendingWriteNum int, maxMsgLen uint32) *WSConn {
|
func newWSConn(conn *websocket.Conn, pendingWriteNum int, maxMsgLen uint32,messageType int) *WSConn {
|
||||||
wsConn := new(WSConn)
|
wsConn := new(WSConn)
|
||||||
wsConn.conn = conn
|
wsConn.conn = conn
|
||||||
wsConn.writeChan = make(chan []byte, pendingWriteNum)
|
wsConn.writeChan = make(chan []byte, pendingWriteNum)
|
||||||
@@ -30,7 +30,7 @@ func newWSConn(conn *websocket.Conn, pendingWriteNum int, maxMsgLen uint32) *WSC
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err := conn.WriteMessage(websocket.BinaryMessage, b)
|
err := conn.WriteMessage(messageType, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ type WSServer struct {
|
|||||||
NewAgent func(*WSConn) Agent
|
NewAgent func(*WSConn) Agent
|
||||||
ln net.Listener
|
ln net.Listener
|
||||||
handler *WSHandler
|
handler *WSHandler
|
||||||
|
messageType int
|
||||||
}
|
}
|
||||||
|
|
||||||
type WSHandler struct {
|
type WSHandler struct {
|
||||||
@@ -32,6 +33,11 @@ type WSHandler struct {
|
|||||||
conns WebsocketConnSet
|
conns WebsocketConnSet
|
||||||
mutexConns sync.Mutex
|
mutexConns sync.Mutex
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
messageType int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *WSHandler) SetMessageType(messageType int) {
|
||||||
|
handler.messageType = messageType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (handler *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -45,6 +51,9 @@ func (handler *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
conn.SetReadLimit(int64(handler.maxMsgLen))
|
conn.SetReadLimit(int64(handler.maxMsgLen))
|
||||||
|
if handler.messageType == 0 {
|
||||||
|
handler.messageType = websocket.TextMessage
|
||||||
|
}
|
||||||
|
|
||||||
handler.wg.Add(1)
|
handler.wg.Add(1)
|
||||||
defer handler.wg.Done()
|
defer handler.wg.Done()
|
||||||
@@ -64,7 +73,7 @@ func (handler *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
handler.conns[conn] = struct{}{}
|
handler.conns[conn] = struct{}{}
|
||||||
handler.mutexConns.Unlock()
|
handler.mutexConns.Unlock()
|
||||||
|
|
||||||
wsConn := newWSConn(conn, handler.pendingWriteNum, handler.maxMsgLen)
|
wsConn := newWSConn(conn, handler.pendingWriteNum, handler.maxMsgLen, handler.messageType)
|
||||||
agent := handler.newAgent(wsConn)
|
agent := handler.newAgent(wsConn)
|
||||||
agent.Run()
|
agent.Run()
|
||||||
|
|
||||||
@@ -76,6 +85,13 @@ func (handler *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
agent.OnClose()
|
agent.OnClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *WSServer) SetMessageType(messageType int) {
|
||||||
|
server.messageType = messageType
|
||||||
|
if server.handler != nil {
|
||||||
|
server.handler.SetMessageType(messageType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (server *WSServer) Start() {
|
func (server *WSServer) Start() {
|
||||||
ln, err := net.Listen("tcp", server.Addr)
|
ln, err := net.Listen("tcp", server.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -123,6 +139,7 @@ func (server *WSServer) Start() {
|
|||||||
maxMsgLen: server.MaxMsgLen,
|
maxMsgLen: server.MaxMsgLen,
|
||||||
newAgent: server.NewAgent,
|
newAgent: server.NewAgent,
|
||||||
conns: make(WebsocketConnSet),
|
conns: make(WebsocketConnSet),
|
||||||
|
messageType:server.messageType,
|
||||||
upgrader: websocket.Upgrader{
|
upgrader: websocket.Upgrader{
|
||||||
HandshakeTimeout: server.HTTPTimeout,
|
HandshakeTimeout: server.HTTPTimeout,
|
||||||
CheckOrigin: func(_ *http.Request) bool { return true },
|
CheckOrigin: func(_ *http.Request) bool { return true },
|
||||||
|
|||||||
165
node/node.go
165
node/node.go
@@ -8,8 +8,9 @@ import (
|
|||||||
"github.com/duanhf2012/origin/log"
|
"github.com/duanhf2012/origin/log"
|
||||||
"github.com/duanhf2012/origin/profiler"
|
"github.com/duanhf2012/origin/profiler"
|
||||||
"github.com/duanhf2012/origin/service"
|
"github.com/duanhf2012/origin/service"
|
||||||
|
"github.com/duanhf2012/origin/util/buildtime"
|
||||||
"github.com/duanhf2012/origin/util/timer"
|
"github.com/duanhf2012/origin/util/timer"
|
||||||
"io/ioutil"
|
"io"
|
||||||
slog "log"
|
slog "log"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
@@ -30,60 +31,75 @@ var bValid bool
|
|||||||
var configDir = "./config/"
|
var configDir = "./config/"
|
||||||
var logLevel string = "debug"
|
var logLevel string = "debug"
|
||||||
var logPath string
|
var logPath string
|
||||||
|
type BuildOSType = int8
|
||||||
|
|
||||||
|
const(
|
||||||
|
Windows BuildOSType = 0
|
||||||
|
Linux BuildOSType = 1
|
||||||
|
Mac BuildOSType = 2
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
closeSig = make(chan bool,1)
|
closeSig = make(chan bool, 1)
|
||||||
sig = make(chan os.Signal, 3)
|
sig = make(chan os.Signal, 3)
|
||||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM,syscall.Signal(10))
|
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM, syscall.Signal(10))
|
||||||
|
|
||||||
console.RegisterCommandBool("help",false,"<-help> This help.",usage)
|
console.RegisterCommandBool("help", false, "<-help> This help.", usage)
|
||||||
console.RegisterCommandString("start","","<-start nodeid=nodeid> Run originserver.",startNode)
|
console.RegisterCommandString("name", "", "<-name nodeName> Node's name.", setName)
|
||||||
console.RegisterCommandString("stop","","<-stop nodeid=nodeid> Stop originserver process.",stopNode)
|
console.RegisterCommandString("start", "", "<-start nodeid=nodeid> Run originserver.", startNode)
|
||||||
console.RegisterCommandString("config","","<-config path> Configuration file path.",setConfigPath)
|
console.RegisterCommandString("stop", "", "<-stop nodeid=nodeid> Stop originserver process.", stopNode)
|
||||||
|
console.RegisterCommandString("config", "", "<-config path> Configuration file path.", setConfigPath)
|
||||||
console.RegisterCommandString("console", "", "<-console true|false> Turn on or off screen log output.", openConsole)
|
console.RegisterCommandString("console", "", "<-console true|false> Turn on or off screen log output.", openConsole)
|
||||||
console.RegisterCommandString("loglevel", "debug", "<-loglevel debug|release|warning|error|fatal> Set loglevel.", setLevel)
|
console.RegisterCommandString("loglevel", "debug", "<-loglevel debug|release|warning|error|fatal> Set loglevel.", setLevel)
|
||||||
console.RegisterCommandString("logpath", "", "<-logpath path> Set log file path.", setLogPath)
|
console.RegisterCommandString("logpath", "", "<-logpath path> Set log file path.", setLogPath)
|
||||||
console.RegisterCommandString("pprof","","<-pprof ip:port> Open performance analysis.",setPprof)
|
console.RegisterCommandString("pprof", "", "<-pprof ip:port> Open performance analysis.", setPprof)
|
||||||
}
|
}
|
||||||
|
|
||||||
func usage(val interface{}) error{
|
func usage(val interface{}) error {
|
||||||
ret := val.(bool)
|
ret := val.(bool)
|
||||||
if ret == false {
|
if ret == false {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, `orgin version: orgin/2.14.20201029
|
if len(buildtime.GetBuildDateTime()) > 0 {
|
||||||
Usage: originserver [-help] [-start node=1] [-stop] [-config path] [-pprof 0.0.0.0:6060]...
|
fmt.Fprintf(os.Stderr, "Welcome to Origin(build info: %s)\nUsage: originserver [-help] [-start node=1] [-stop] [-config path] [-pprof 0.0.0.0:6060]...\n", buildtime.GetBuildDateTime())
|
||||||
`)
|
} else {
|
||||||
|
fmt.Fprintf(os.Stderr, "Welcome to Origin\nUsage: originserver [-help] [-start node=1] [-stop] [-config path] [-pprof 0.0.0.0:6060]...\n")
|
||||||
|
}
|
||||||
|
|
||||||
console.PrintDefaults()
|
console.PrintDefaults()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setName(val interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func setPprof(val interface{}) error {
|
func setPprof(val interface{}) error {
|
||||||
listenAddr := val.(string)
|
listenAddr := val.(string)
|
||||||
if listenAddr==""{
|
if listenAddr == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
go func(){
|
go func() {
|
||||||
err := http.ListenAndServe(listenAddr, nil)
|
err := http.ListenAndServe(listenAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("%+v",err))
|
panic(fmt.Errorf("%+v", err))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setConfigPath(val interface{}) error{
|
func setConfigPath(val interface{}) error {
|
||||||
configPath := val.(string)
|
configPath := val.(string)
|
||||||
if configPath==""{
|
if configPath == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
_, err := os.Stat(configPath)
|
_, err := os.Stat(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot find file path %s",configPath)
|
return fmt.Errorf("Cannot find file path %s", configPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster.SetConfigDir(configPath)
|
cluster.SetConfigDir(configPath)
|
||||||
@@ -91,16 +107,16 @@ func setConfigPath(val interface{}) error{
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRunProcessPid(nodeId int) (int,error) {
|
func getRunProcessPid(nodeId int) (int, error) {
|
||||||
f, err := os.OpenFile(fmt.Sprintf("%s_%d.pid",os.Args[0],nodeId), os.O_RDONLY, 0600)
|
f, err := os.OpenFile(fmt.Sprintf("%s_%d.pid", os.Args[0], nodeId), os.O_RDONLY, 0600)
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
if err!= nil {
|
if err != nil {
|
||||||
return 0,err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pidByte,errs := ioutil.ReadAll(f)
|
pidByte, errs := io.ReadAll(f)
|
||||||
if errs!=nil {
|
if errs != nil {
|
||||||
return 0,errs
|
return 0, errs
|
||||||
}
|
}
|
||||||
|
|
||||||
return strconv.Atoi(string(pidByte))
|
return strconv.Atoi(string(pidByte))
|
||||||
@@ -108,13 +124,13 @@ func getRunProcessPid(nodeId int) (int,error) {
|
|||||||
|
|
||||||
func writeProcessPid(nodeId int) {
|
func writeProcessPid(nodeId int) {
|
||||||
//pid
|
//pid
|
||||||
f, err := os.OpenFile(fmt.Sprintf("%s_%d.pid",os.Args[0],nodeId), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
|
f, err := os.OpenFile(fmt.Sprintf("%s_%d.pid", os.Args[0], nodeId), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
} else {
|
} else {
|
||||||
_,err=f.Write([]byte(fmt.Sprintf("%d",os.Getpid())))
|
_, err = f.Write([]byte(fmt.Sprintf("%d", os.Getpid())))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
@@ -126,28 +142,28 @@ func GetNodeId() int {
|
|||||||
return nodeId
|
return nodeId
|
||||||
}
|
}
|
||||||
|
|
||||||
func initNode(id int){
|
func initNode(id int) {
|
||||||
//1.初始化集群
|
//1.初始化集群
|
||||||
nodeId = id
|
nodeId = id
|
||||||
err := cluster.GetCluster().Init(GetNodeId(),Setup)
|
err := cluster.GetCluster().Init(GetNodeId(), Setup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.SFatal("read system config is error ",err.Error())
|
log.SFatal("read system config is error ", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = initLog()
|
err = initLog()
|
||||||
if err != nil{
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//2.setup service
|
//2.setup service
|
||||||
for _,s := range preSetupService {
|
for _, s := range preSetupService {
|
||||||
//是否配置的service
|
//是否配置的service
|
||||||
if cluster.GetCluster().IsConfigService(s.GetName()) == false {
|
if cluster.GetCluster().IsConfigService(s.GetName()) == false {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pServiceCfg := cluster.GetCluster().GetServiceCfg(s.GetName())
|
pServiceCfg := cluster.GetCluster().GetServiceCfg(s.GetName())
|
||||||
s.Init(s,cluster.GetRpcClient,cluster.GetRpcServer,pServiceCfg)
|
s.Init(s, cluster.GetRpcClient, cluster.GetRpcServer, pServiceCfg)
|
||||||
|
|
||||||
service.Setup(s)
|
service.Setup(s)
|
||||||
}
|
}
|
||||||
@@ -156,14 +172,14 @@ func initNode(id int){
|
|||||||
service.Init(closeSig)
|
service.Init(closeSig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initLog() error{
|
func initLog() error {
|
||||||
if logPath == ""{
|
if logPath == "" {
|
||||||
setLogPath("./log")
|
setLogPath("./log")
|
||||||
}
|
}
|
||||||
|
|
||||||
localnodeinfo := cluster.GetCluster().GetLocalNodeInfo()
|
localnodeinfo := cluster.GetCluster().GetLocalNodeInfo()
|
||||||
filepre := fmt.Sprintf("%s_%d_", localnodeinfo.NodeName, localnodeinfo.NodeId)
|
filepre := fmt.Sprintf("%s_%d_", localnodeinfo.NodeName, localnodeinfo.NodeId)
|
||||||
logger,err := log.New(logLevel,logPath,filepre,slog.LstdFlags|slog.Lshortfile,10)
|
logger, err := log.New(logLevel, logPath, filepre, slog.LstdFlags|slog.Lshortfile, 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("cannot create log file!\n")
|
fmt.Printf("cannot create log file!\n")
|
||||||
return err
|
return err
|
||||||
@@ -174,8 +190,8 @@ func initLog() error{
|
|||||||
|
|
||||||
func Start() {
|
func Start() {
|
||||||
err := console.Run(os.Args)
|
err := console.Run(os.Args)
|
||||||
if err!=nil {
|
if err != nil {
|
||||||
fmt.Printf("%+v\n",err)
|
fmt.Printf("%+v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,19 +203,19 @@ func stopNode(args interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sParam := strings.Split(param,"=")
|
sParam := strings.Split(param, "=")
|
||||||
if len(sParam) != 2 {
|
if len(sParam) != 2 {
|
||||||
return fmt.Errorf("invalid option %s",param)
|
return fmt.Errorf("invalid option %s", param)
|
||||||
}
|
}
|
||||||
if sParam[0]!="nodeid" {
|
if sParam[0] != "nodeid" {
|
||||||
return fmt.Errorf("invalid option %s",param)
|
return fmt.Errorf("invalid option %s", param)
|
||||||
}
|
}
|
||||||
nodeId,err:= strconv.Atoi(sParam[1])
|
nodeId, err := strconv.Atoi(sParam[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid option %s",param)
|
return fmt.Errorf("invalid option %s", param)
|
||||||
}
|
}
|
||||||
|
|
||||||
processId,err := getRunProcessPid(nodeId)
|
processId, err := getRunProcessPid(nodeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -208,26 +224,26 @@ func stopNode(args interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func startNode(args interface{}) error{
|
func startNode(args interface{}) error {
|
||||||
//1.解析参数
|
//1.解析参数
|
||||||
param := args.(string)
|
param := args.(string)
|
||||||
if param == "" {
|
if param == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sParam := strings.Split(param,"=")
|
sParam := strings.Split(param, "=")
|
||||||
if len(sParam) != 2 {
|
if len(sParam) != 2 {
|
||||||
return fmt.Errorf("invalid option %s",param)
|
return fmt.Errorf("invalid option %s", param)
|
||||||
}
|
}
|
||||||
if sParam[0]!="nodeid" {
|
if sParam[0] != "nodeid" {
|
||||||
return fmt.Errorf("invalid option %s",param)
|
return fmt.Errorf("invalid option %s", param)
|
||||||
}
|
}
|
||||||
nodeId,err:= strconv.Atoi(sParam[1])
|
nodeId, err := strconv.Atoi(sParam[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid option %s",param)
|
return fmt.Errorf("invalid option %s", param)
|
||||||
}
|
}
|
||||||
|
|
||||||
timer.StartTimer(10*time.Millisecond,1000000)
|
timer.StartTimer(10*time.Millisecond, 1000000)
|
||||||
log.SRelease("Start running server.")
|
log.SRelease("Start running server.")
|
||||||
//2.初始化node
|
//2.初始化node
|
||||||
initNode(nodeId)
|
initNode(nodeId)
|
||||||
@@ -244,7 +260,7 @@ func startNode(args interface{}) error{
|
|||||||
//6.监听程序退出信号&性能报告
|
//6.监听程序退出信号&性能报告
|
||||||
bRun := true
|
bRun := true
|
||||||
var pProfilerTicker *time.Ticker = &time.Ticker{}
|
var pProfilerTicker *time.Ticker = &time.Ticker{}
|
||||||
if profilerInterval>0 {
|
if profilerInterval > 0 {
|
||||||
pProfilerTicker = time.NewTicker(profilerInterval)
|
pProfilerTicker = time.NewTicker(profilerInterval)
|
||||||
}
|
}
|
||||||
for bRun {
|
for bRun {
|
||||||
@@ -252,7 +268,7 @@ func startNode(args interface{}) error{
|
|||||||
case <-sig:
|
case <-sig:
|
||||||
log.SRelease("receipt stop signal.")
|
log.SRelease("receipt stop signal.")
|
||||||
bRun = false
|
bRun = false
|
||||||
case <- pProfilerTicker.C:
|
case <-pProfilerTicker.C:
|
||||||
profiler.Report()
|
profiler.Report()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,11 +281,10 @@ func startNode(args interface{}) error{
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Setup(s ...service.IService) {
|
||||||
func Setup(s ...service.IService) {
|
for _, sv := range s {
|
||||||
for _,sv := range s {
|
|
||||||
sv.OnSetup(sv)
|
sv.OnSetup(sv)
|
||||||
preSetupService = append(preSetupService,sv)
|
preSetupService = append(preSetupService, sv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +292,7 @@ func GetService(serviceName string) service.IService {
|
|||||||
return service.GetService(serviceName)
|
return service.GetService(serviceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetConfigDir(configDir string){
|
func SetConfigDir(configDir string) {
|
||||||
configDir = configDir
|
configDir = configDir
|
||||||
cluster.SetConfigDir(configDir)
|
cluster.SetConfigDir(configDir)
|
||||||
}
|
}
|
||||||
@@ -286,58 +301,58 @@ func GetConfigDir() string {
|
|||||||
return configDir
|
return configDir
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetSysLog(strLevel string, pathname string, flag int){
|
func SetSysLog(strLevel string, pathname string, flag int) {
|
||||||
logs,_:= log.New(strLevel,pathname, "", flag,10)
|
logs, _ := log.New(strLevel, pathname, "", flag, 10)
|
||||||
log.Export(logs)
|
log.Export(logs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenProfilerReport(interval time.Duration){
|
func OpenProfilerReport(interval time.Duration) {
|
||||||
profilerInterval = interval
|
profilerInterval = interval
|
||||||
}
|
}
|
||||||
|
|
||||||
func openConsole(args interface{}) error{
|
func openConsole(args interface{}) error {
|
||||||
if args == "" {
|
if args == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
strOpen := strings.ToLower(strings.TrimSpace(args.(string)))
|
strOpen := strings.ToLower(strings.TrimSpace(args.(string)))
|
||||||
if strOpen == "false" {
|
if strOpen == "false" {
|
||||||
log.OpenConsole = false
|
log.OpenConsole = false
|
||||||
}else if strOpen == "true" {
|
} else if strOpen == "true" {
|
||||||
log.OpenConsole = true
|
log.OpenConsole = true
|
||||||
}else{
|
} else {
|
||||||
return errors.New("Parameter console error!")
|
return errors.New("Parameter console error!")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setLevel(args interface{}) error{
|
func setLevel(args interface{}) error {
|
||||||
if args==""{
|
if args == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
logLevel = strings.TrimSpace(args.(string))
|
logLevel = strings.TrimSpace(args.(string))
|
||||||
if logLevel!= "debug" && logLevel!="release"&& logLevel!="warning"&&logLevel!="error"&&logLevel!="fatal" {
|
if logLevel != "debug" && logLevel != "release" && logLevel != "warning" && logLevel != "error" && logLevel != "fatal" {
|
||||||
return errors.New("unknown level: " + logLevel)
|
return errors.New("unknown level: " + logLevel)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setLogPath(args interface{}) error{
|
func setLogPath(args interface{}) error {
|
||||||
if args == ""{
|
if args == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
logPath = strings.TrimSpace(args.(string))
|
logPath = strings.TrimSpace(args.(string))
|
||||||
dir, err := os.Stat(logPath) //这个文件夹不存在
|
dir, err := os.Stat(logPath) //这个文件夹不存在
|
||||||
if err == nil && dir.IsDir()==false {
|
if err == nil && dir.IsDir() == false {
|
||||||
return errors.New("Not found dir "+logPath)
|
return errors.New("Not found dir " + logPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = os.Mkdir(logPath, os.ModePerm)
|
err = os.Mkdir(logPath, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Cannot create dir "+logPath)
|
return errors.New("Cannot create dir " + logPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ func KillProcess(processId int){
|
|||||||
fmt.Printf("kill processid %d is successful.\n",processId)
|
fmt.Printf("kill processid %d is successful.\n",processId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetBuildOSType() BuildOSType{
|
||||||
|
return Linux
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ func KillProcess(processId int){
|
|||||||
fmt.Printf("kill processid %d is successful.\n",processId)
|
fmt.Printf("kill processid %d is successful.\n",processId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetBuildOSType() BuildOSType{
|
||||||
|
return Mac
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,4 +4,8 @@ package node
|
|||||||
|
|
||||||
func KillProcess(processId int){
|
func KillProcess(processId int){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetBuildOSType() BuildOSType{
|
||||||
|
return Windows
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/duanhf2012/origin/log"
|
"github.com/duanhf2012/origin/log"
|
||||||
"github.com/duanhf2012/origin/network"
|
"github.com/duanhf2012/origin/network"
|
||||||
"github.com/duanhf2012/origin/util/timer"
|
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -32,6 +31,9 @@ type Client struct {
|
|||||||
TriggerRpcEvent
|
TriggerRpcEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MaxCheckCallRpcCount = 1000
|
||||||
|
const MaxPendingWriteNum = 200000
|
||||||
|
const ConnectInterval = 2*time.Second
|
||||||
var clientSeq uint32
|
var clientSeq uint32
|
||||||
|
|
||||||
func (client *Client) NewClientAgent(conn *network.TCPConn) network.Agent {
|
func (client *Client) NewClientAgent(conn *network.TCPConn) network.Agent {
|
||||||
@@ -41,18 +43,23 @@ func (client *Client) NewClientAgent(conn *network.TCPConn) network.Agent {
|
|||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (client *Client) Connect(id int, addr string, maxRpcParamLen uint32) error {
|
func (client *Client) Connect(id int, addr string, maxRpcParamLen uint32) error {
|
||||||
client.clientSeq = atomic.AddUint32(&clientSeq, 1)
|
client.clientSeq = atomic.AddUint32(&clientSeq, 1)
|
||||||
client.id = id
|
client.id = id
|
||||||
client.Addr = addr
|
client.Addr = addr
|
||||||
client.maxCheckCallRpcCount = 1000
|
client.maxCheckCallRpcCount = MaxCheckCallRpcCount
|
||||||
client.callRpcTimeout = 15 * time.Second
|
client.callRpcTimeout = 15 * time.Second
|
||||||
client.ConnNum = 1
|
client.ConnectInterval = ConnectInterval
|
||||||
client.ConnectInterval = time.Second * 2
|
client.PendingWriteNum = MaxPendingWriteNum
|
||||||
client.PendingWriteNum = 200000
|
|
||||||
client.AutoReconnect = true
|
client.AutoReconnect = true
|
||||||
|
|
||||||
|
client.ConnNum = 1
|
||||||
client.LenMsgLen = 4
|
client.LenMsgLen = 4
|
||||||
client.MinMsgLen = 2
|
client.MinMsgLen = 2
|
||||||
|
client.ReadDeadline = Default_ReadWriteDeadline
|
||||||
|
client.WriteDeadline = Default_ReadWriteDeadline
|
||||||
|
|
||||||
if maxRpcParamLen > 0 {
|
if maxRpcParamLen > 0 {
|
||||||
client.MaxMsgLen = maxRpcParamLen
|
client.MaxMsgLen = maxRpcParamLen
|
||||||
} else {
|
} else {
|
||||||
@@ -73,17 +80,10 @@ func (client *Client) Connect(id int, addr string, maxRpcParamLen uint32) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) startCheckRpcCallTimer() {
|
func (client *Client) startCheckRpcCallTimer() {
|
||||||
t := timer.NewTimer(5 * time.Second)
|
|
||||||
for {
|
for {
|
||||||
select {
|
time.Sleep(5 * time.Second)
|
||||||
case cTimer := <-t.C:
|
client.checkRpcCallTimeout()
|
||||||
cTimer.SetupTimer(time.Now())
|
|
||||||
client.checkRpcCallTimeout()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Cancel()
|
|
||||||
timer.ReleaseTimer(t)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) makeCallFail(call *Call) {
|
func (client *Client) makeCallFail(call *Call) {
|
||||||
@@ -161,6 +161,10 @@ func (client *Client) removePending(seq uint64) *Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) FindPending(seq uint64) *Call {
|
func (client *Client) FindPending(seq uint64) *Call {
|
||||||
|
if seq == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
client.pendingLock.Lock()
|
client.pendingLock.Lock()
|
||||||
v, ok := client.pending[seq]
|
v, ok := client.pending[seq]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
@@ -341,6 +345,19 @@ func (client *Client) GetId() int {
|
|||||||
|
|
||||||
func (client *Client) Close(waitDone bool) {
|
func (client *Client) Close(waitDone bool) {
|
||||||
client.TCPClient.Close(waitDone)
|
client.TCPClient.Close(waitDone)
|
||||||
|
|
||||||
|
client.pendingLock.Lock()
|
||||||
|
for {
|
||||||
|
pElem := client.pendingTimer.Front()
|
||||||
|
if pElem == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
pCall := pElem.Value.(*Call)
|
||||||
|
pCall.Err = errors.New("nodeid is disconnect ")
|
||||||
|
client.makeCallFail(pCall)
|
||||||
|
}
|
||||||
|
client.pendingLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) GetClientSeq() uint32 {
|
func (client *Client) GetClientSeq() uint32 {
|
||||||
|
|||||||
1777
rpc/messagequeue.pb.go
Normal file
1777
rpc/messagequeue.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
51
rpc/messagequeue.proto
Normal file
51
rpc/messagequeue.proto
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option go_package = ".;rpc";
|
||||||
|
|
||||||
|
|
||||||
|
message DBQueuePopReq {
|
||||||
|
string CustomerId = 1;
|
||||||
|
string QueueName = 2;
|
||||||
|
int32 PopStartPos = 3;
|
||||||
|
int32 PopNum = 4;
|
||||||
|
bytes pushData = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DBQueuePopRes {
|
||||||
|
string QueueName = 1;
|
||||||
|
repeated bytes pushData = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SubscribeType {
|
||||||
|
Subscribe = 0;
|
||||||
|
Unsubscribe = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SubscribeMethod {
|
||||||
|
Method_Custom = 0;//自定义模式,以消费者设置的StartIndex开始获取或订阅
|
||||||
|
Method_Last = 1;//Last模式,以该消费者上次记录的位置开始订阅
|
||||||
|
}
|
||||||
|
|
||||||
|
//订阅
|
||||||
|
message DBQueueSubscribeReq {
|
||||||
|
SubscribeType SubType = 1; //订阅类型
|
||||||
|
SubscribeMethod Method = 2; //订阅方法
|
||||||
|
string CustomerId = 3; //消费者Id
|
||||||
|
int32 FromNodeId = 4;
|
||||||
|
string RpcMethod = 5;
|
||||||
|
string TopicName = 6; //主题名称
|
||||||
|
uint64 StartIndex = 7; //开始位置 ,格式前4位是时间戳秒,后面是序号。如果填0时,服务自动修改成:(4bit 当前时间秒)| (0000 4bit)
|
||||||
|
int32 OneBatchQuantity = 8;//订阅一次发送的数量,不设置有默认值1000条
|
||||||
|
}
|
||||||
|
|
||||||
|
message DBQueueSubscribeRes {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
message DBQueuePublishReq {
|
||||||
|
string TopicName = 1; //主是,名称,数据
|
||||||
|
repeated bytes pushData = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DBQueuePublishRes {
|
||||||
|
}
|
||||||
3148
rpc/rank.pb.go
Normal file
3148
rpc/rank.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
79
rpc/rank.proto
Normal file
79
rpc/rank.proto
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package rpc;
|
||||||
|
option go_package = ".;rpc";
|
||||||
|
|
||||||
|
// RankData 排行数据
|
||||||
|
message RankData {
|
||||||
|
uint64 Key = 1; //数据主建
|
||||||
|
repeated int64 SortData = 2; //参与排行的数据
|
||||||
|
bytes Data = 3; //不参与排行的数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// RankPosData 排行数据——查询返回
|
||||||
|
message RankPosData {
|
||||||
|
uint64 Key = 1; //数据主建
|
||||||
|
uint64 Rank = 2; //名次
|
||||||
|
repeated int64 SortData = 3; //参与排行的数据
|
||||||
|
bytes Data = 4; //不参与排行的数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// RankList 排行榜数据
|
||||||
|
message RankList {
|
||||||
|
uint64 RankId = 1; //排行榜类型
|
||||||
|
string RankName = 2; //排行榜名称
|
||||||
|
int32 SkipListLevel = 3; //排行榜level-生成的跳表的level, 8/16/32/64等
|
||||||
|
bool IsDec = 4; //不参与排行的数据
|
||||||
|
uint64 MaxRank = 5; //最大排名
|
||||||
|
int64 ExpireMs = 6; //有效时间,0永不过期
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpsetRankData 更新排行榜数据
|
||||||
|
message UpsetRankData {
|
||||||
|
uint64 RankId = 1; //排行榜的ID
|
||||||
|
repeated RankData RankDataList = 2; //排行数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteByKey 删除排行榜数据
|
||||||
|
message DeleteByKey {
|
||||||
|
uint64 RankId = 1; //排行榜的分类ID
|
||||||
|
repeated uint64 KeyList = 2; //排行数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRankList 新增排行榜
|
||||||
|
message AddRankList {
|
||||||
|
repeated RankList AddList = 1; //添加的排行榜列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindRankDataByKey 查找排行信息
|
||||||
|
message FindRankDataByKey {
|
||||||
|
uint64 RankId = 1; //排行榜的ID
|
||||||
|
uint64 Key = 2; //排行的key
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindRankDataByRank 查找排行信息
|
||||||
|
message FindRankDataByRank {
|
||||||
|
uint64 RankId = 1; //排行榜的ID
|
||||||
|
uint64 Rank = 2; //排行名次
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindRankDataList 查找排行信息
|
||||||
|
message FindRankDataList {
|
||||||
|
uint64 RankId = 1; //排行榜的ID
|
||||||
|
uint64 StartRank = 2; //排行的位置 0开始
|
||||||
|
uint64 Count = 3; //查询格式
|
||||||
|
uint64 Key = 4; //附带一个Key查询排行信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// RankDataList
|
||||||
|
message RankDataList {
|
||||||
|
uint64 RankDataCount = 1; //排行长度
|
||||||
|
repeated RankPosData RankPosDataList = 2; //排行数据
|
||||||
|
RankPosData KeyRank = 3; //附带的Key查询排行结果信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// RankResult
|
||||||
|
message RankResult {
|
||||||
|
int32 AddCount = 1;//新增数量
|
||||||
|
int32 ModifyCount = 2; //修改数量
|
||||||
|
int32 RemoveCount = 3;//删除数量
|
||||||
|
}
|
||||||
@@ -68,11 +68,16 @@ type RpcHandler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TriggerRpcEvent func(bConnect bool, clientSeq uint32, nodeId int)
|
type TriggerRpcEvent func(bConnect bool, clientSeq uint32, nodeId int)
|
||||||
type IRpcListener interface {
|
type INodeListener interface {
|
||||||
OnNodeConnected(nodeId int)
|
OnNodeConnected(nodeId int)
|
||||||
OnNodeDisconnect(nodeId int)
|
OnNodeDisconnect(nodeId int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IDiscoveryServiceListener interface {
|
||||||
|
OnDiscoveryService(nodeId int, serviceName []string)
|
||||||
|
OnUnDiscoveryService(nodeId int, serviceName []string)
|
||||||
|
}
|
||||||
|
|
||||||
type IRpcHandler interface {
|
type IRpcHandler interface {
|
||||||
IRpcHandlerChannel
|
IRpcHandlerChannel
|
||||||
GetName() string
|
GetName() string
|
||||||
@@ -80,7 +85,7 @@ type IRpcHandler interface {
|
|||||||
GetRpcHandler() IRpcHandler
|
GetRpcHandler() IRpcHandler
|
||||||
HandlerRpcRequest(request *RpcRequest)
|
HandlerRpcRequest(request *RpcRequest)
|
||||||
HandlerRpcResponseCB(call *Call)
|
HandlerRpcResponseCB(call *Call)
|
||||||
CallMethod(ServiceMethod string, param interface{}, reply interface{}) error
|
CallMethod(client *Client,ServiceMethod string, param interface{},callBack reflect.Value, reply interface{}) error
|
||||||
AsyncCall(serviceMethod string, args interface{}, callback interface{}) error
|
AsyncCall(serviceMethod string, args interface{}, callback interface{}) error
|
||||||
Call(serviceMethod string, args interface{}, reply interface{}) error
|
Call(serviceMethod string, args interface{}, reply interface{}) error
|
||||||
Go(serviceMethod string, args interface{}) error
|
Go(serviceMethod string, args interface{}) error
|
||||||
@@ -294,7 +299,7 @@ func (handler *RpcHandler) HandlerRpcRequest(request *RpcRequest) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *RpcHandler) CallMethod(ServiceMethod string, param interface{}, reply interface{}) error {
|
func (handler *RpcHandler) CallMethod(client *Client,ServiceMethod string, param interface{},callBack reflect.Value, reply interface{}) error {
|
||||||
var err error
|
var err error
|
||||||
v, ok := handler.mapFunctions[ServiceMethod]
|
v, ok := handler.mapFunctions[ServiceMethod]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
@@ -304,14 +309,101 @@ func (handler *RpcHandler) CallMethod(ServiceMethod string, param interface{}, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
var paramList []reflect.Value
|
var paramList []reflect.Value
|
||||||
paramList = append(paramList, reflect.ValueOf(handler.GetRpcHandler())) //接受者
|
var returnValues []reflect.Value
|
||||||
paramList = append(paramList, reflect.ValueOf(param))
|
var pCall *Call
|
||||||
paramList = append(paramList, reflect.ValueOf(reply)) //输出参数
|
var callSeq uint64
|
||||||
|
if v.hasResponder == true {
|
||||||
|
paramList = append(paramList, reflect.ValueOf(handler.GetRpcHandler())) //接受者
|
||||||
|
pCall = MakeCall()
|
||||||
|
pCall.callback = &callBack
|
||||||
|
pCall.Seq = client.generateSeq()
|
||||||
|
callSeq = pCall.Seq
|
||||||
|
|
||||||
returnValues := v.method.Func.Call(paramList)
|
client.AddPending(pCall)
|
||||||
errInter := returnValues[0].Interface()
|
|
||||||
if errInter != nil {
|
//有返回值时
|
||||||
err = errInter.(error)
|
if reply != nil {
|
||||||
|
//如果是Call同步调用
|
||||||
|
hander :=func(Returns interface{}, Err RpcError) {
|
||||||
|
rpcCall := client.RemovePending(callSeq)
|
||||||
|
if rpcCall == nil {
|
||||||
|
log.SError("cannot find call seq ",callSeq)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//解析数据
|
||||||
|
if len(Err)!=0 {
|
||||||
|
rpcCall.Err = Err
|
||||||
|
}else if Returns != nil {
|
||||||
|
_, processor := GetProcessorType(Returns)
|
||||||
|
var bytes []byte
|
||||||
|
bytes,rpcCall.Err = processor.Marshal(Returns)
|
||||||
|
if rpcCall.Err == nil {
|
||||||
|
rpcCall.Err = processor.Unmarshal(bytes,reply)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果找不到,说明已经超时
|
||||||
|
rpcCall.Reply = reply
|
||||||
|
rpcCall.done<-rpcCall
|
||||||
|
}
|
||||||
|
paramList = append(paramList, reflect.ValueOf(hander))
|
||||||
|
}else{//无返回值时,是一个requestHandlerNull空回调
|
||||||
|
paramList = append(paramList, callBack)
|
||||||
|
}
|
||||||
|
paramList = append(paramList, reflect.ValueOf(param))
|
||||||
|
|
||||||
|
//rpc函数被调用
|
||||||
|
returnValues = v.method.Func.Call(paramList)
|
||||||
|
|
||||||
|
//判断返回值是否错误,有错误时则回调
|
||||||
|
errInter := returnValues[0].Interface()
|
||||||
|
if errInter != nil && callBack!=requestHandlerNull{
|
||||||
|
err = errInter.(error)
|
||||||
|
callBack.Call([]reflect.Value{reflect.ValueOf(reply), reflect.ValueOf(err)})
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
paramList = append(paramList, reflect.ValueOf(handler.GetRpcHandler())) //接受者
|
||||||
|
paramList = append(paramList, reflect.ValueOf(param))
|
||||||
|
|
||||||
|
//被调用RPC函数有返回值时
|
||||||
|
if v.outParamValue.IsValid() {
|
||||||
|
//不带返回值参数的RPC函数
|
||||||
|
if reply == nil {
|
||||||
|
paramList = append(paramList, reflect.New(v.outParamValue.Type().Elem()))
|
||||||
|
}else{
|
||||||
|
//带返回值参数的RPC函数
|
||||||
|
paramList = append(paramList, reflect.ValueOf(reply)) //输出参数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
returnValues = v.method.Func.Call(paramList)
|
||||||
|
errInter := returnValues[0].Interface()
|
||||||
|
|
||||||
|
//如果无回调
|
||||||
|
if callBack != requestHandlerNull {
|
||||||
|
valErr := nilError
|
||||||
|
if errInter != nil {
|
||||||
|
err = errInter.(error)
|
||||||
|
valErr = reflect.ValueOf(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
callBack.Call([]reflect.Value{reflect.ValueOf(reply),valErr })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcCall := client.FindPending(callSeq)
|
||||||
|
if rpcCall!=nil {
|
||||||
|
err = rpcCall.Done().Err
|
||||||
|
if rpcCall.callback!= nil {
|
||||||
|
valErr := nilError
|
||||||
|
if rpcCall.Err != nil {
|
||||||
|
valErr = reflect.ValueOf(rpcCall.Err)
|
||||||
|
}
|
||||||
|
rpcCall.callback.Call([]reflect.Value{reflect.ValueOf(rpcCall.Reply), valErr})
|
||||||
|
}
|
||||||
|
client.RemovePending(rpcCall.Seq)
|
||||||
|
ReleaseCall(rpcCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@@ -351,7 +443,7 @@ func (handler *RpcHandler) goRpc(processor IRpcProcessor, bCast bool, nodeId int
|
|||||||
serviceName := serviceMethod[:findIndex]
|
serviceName := serviceMethod[:findIndex]
|
||||||
if serviceName == handler.rpcHandler.GetName() { //自己服务调用
|
if serviceName == handler.rpcHandler.GetName() { //自己服务调用
|
||||||
//调用自己rpcHandler处理器
|
//调用自己rpcHandler处理器
|
||||||
return pLocalRpcServer.myselfRpcHandlerGo(serviceName, serviceMethod, args, nil)
|
return pLocalRpcServer.myselfRpcHandlerGo(pClientList[i],serviceName, serviceMethod, args, requestHandlerNull,nil)
|
||||||
}
|
}
|
||||||
//其他的rpcHandler的处理器
|
//其他的rpcHandler的处理器
|
||||||
pCall := pLocalRpcServer.selfNodeRpcHandlerGo(processor, pClientList[i], true, serviceName, 0, serviceMethod, args, nil, nil)
|
pCall := pLocalRpcServer.selfNodeRpcHandlerGo(processor, pClientList[i], true, serviceName, 0, serviceMethod, args, nil, nil)
|
||||||
@@ -405,7 +497,7 @@ func (handler *RpcHandler) callRpc(nodeId int, serviceMethod string, args interf
|
|||||||
serviceName := serviceMethod[:findIndex]
|
serviceName := serviceMethod[:findIndex]
|
||||||
if serviceName == handler.rpcHandler.GetName() { //自己服务调用
|
if serviceName == handler.rpcHandler.GetName() { //自己服务调用
|
||||||
//调用自己rpcHandler处理器
|
//调用自己rpcHandler处理器
|
||||||
return pLocalRpcServer.myselfRpcHandlerGo(serviceName, serviceMethod, args, reply)
|
return pLocalRpcServer.myselfRpcHandlerGo(pClient,serviceName, serviceMethod, args,requestHandlerNull, reply)
|
||||||
}
|
}
|
||||||
//其他的rpcHandler的处理器
|
//其他的rpcHandler的处理器
|
||||||
pCall := pLocalRpcServer.selfNodeRpcHandlerGo(nil, pClient, false, serviceName, 0, serviceMethod, args, reply, nil)
|
pCall := pLocalRpcServer.selfNodeRpcHandlerGo(nil, pClient, false, serviceName, 0, serviceMethod, args, reply, nil)
|
||||||
@@ -484,12 +576,7 @@ func (handler *RpcHandler) asyncCallRpc(nodeId int, serviceMethod string, args i
|
|||||||
serviceName := serviceMethod[:findIndex]
|
serviceName := serviceMethod[:findIndex]
|
||||||
//调用自己rpcHandler处理器
|
//调用自己rpcHandler处理器
|
||||||
if serviceName == handler.rpcHandler.GetName() { //自己服务调用
|
if serviceName == handler.rpcHandler.GetName() { //自己服务调用
|
||||||
err := pLocalRpcServer.myselfRpcHandlerGo(serviceName, serviceMethod, args, reply)
|
return pLocalRpcServer.myselfRpcHandlerGo(pClient,serviceName, serviceMethod, args,fVal ,reply)
|
||||||
if err == nil {
|
|
||||||
fVal.Call([]reflect.Value{reflect.ValueOf(reply), nilError})
|
|
||||||
} else {
|
|
||||||
fVal.Call([]reflect.Value{reflect.ValueOf(reply), reflect.ValueOf(err)})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//其他的rpcHandler的处理器
|
//其他的rpcHandler的处理器
|
||||||
@@ -566,7 +653,7 @@ func (handler *RpcHandler) RawGoNode(rpcProcessorType RpcProcessorType, nodeId i
|
|||||||
pLocalRpcServer := handler.funcRpcServer()
|
pLocalRpcServer := handler.funcRpcServer()
|
||||||
//调用自己rpcHandler处理器
|
//调用自己rpcHandler处理器
|
||||||
if serviceName == handler.rpcHandler.GetName() { //自己服务调用
|
if serviceName == handler.rpcHandler.GetName() { //自己服务调用
|
||||||
err := pLocalRpcServer.myselfRpcHandlerGo(serviceName, serviceName, rawArgs.GetRawData(), nil)
|
err := pLocalRpcServer.myselfRpcHandlerGo(handler.pClientList[i],serviceName, serviceName, rawArgs.GetRawData(), requestHandlerNull,nil)
|
||||||
//args.DoGc()
|
//args.DoGc()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RpcProcessorType uint8
|
type RpcProcessorType uint8
|
||||||
@@ -62,6 +63,8 @@ func (server *Server) Init(rpcHandleFinder RpcHandleFinder) {
|
|||||||
server.rpcServer = &network.TCPServer{}
|
server.rpcServer = &network.TCPServer{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Default_ReadWriteDeadline = 15*time.Second
|
||||||
|
|
||||||
func (server *Server) Start(listenAddr string, maxRpcParamLen uint32) {
|
func (server *Server) Start(listenAddr string, maxRpcParamLen uint32) {
|
||||||
splitAddr := strings.Split(listenAddr, ":")
|
splitAddr := strings.Split(listenAddr, ":")
|
||||||
if len(splitAddr) != 2 {
|
if len(splitAddr) != 2 {
|
||||||
@@ -77,10 +80,12 @@ func (server *Server) Start(listenAddr string, maxRpcParamLen uint32) {
|
|||||||
server.rpcServer.MaxMsgLen = math.MaxUint32
|
server.rpcServer.MaxMsgLen = math.MaxUint32
|
||||||
}
|
}
|
||||||
|
|
||||||
server.rpcServer.MaxConnNum = 10000
|
server.rpcServer.MaxConnNum = 100000
|
||||||
server.rpcServer.PendingWriteNum = 2000000
|
server.rpcServer.PendingWriteNum = 2000000
|
||||||
server.rpcServer.NewAgent = server.NewAgent
|
server.rpcServer.NewAgent = server.NewAgent
|
||||||
server.rpcServer.LittleEndian = LittleEndian
|
server.rpcServer.LittleEndian = LittleEndian
|
||||||
|
server.rpcServer.WriteDeadline = Default_ReadWriteDeadline
|
||||||
|
server.rpcServer.ReadDeadline = Default_ReadWriteDeadline
|
||||||
server.rpcServer.Start()
|
server.rpcServer.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +238,7 @@ func (server *Server) NewAgent(c *network.TCPConn) network.Agent {
|
|||||||
return agent
|
return agent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) myselfRpcHandlerGo(handlerName string, serviceMethod string, args interface{}, reply interface{}) error {
|
func (server *Server) myselfRpcHandlerGo(client *Client,handlerName string, serviceMethod string, args interface{},callBack reflect.Value, reply interface{}) error {
|
||||||
rpcHandler := server.rpcHandleFinder.FindRpcHandler(handlerName)
|
rpcHandler := server.rpcHandleFinder.FindRpcHandler(handlerName)
|
||||||
if rpcHandler == nil {
|
if rpcHandler == nil {
|
||||||
err := errors.New("service method " + serviceMethod + " not config!")
|
err := errors.New("service method " + serviceMethod + " not config!")
|
||||||
@@ -241,7 +246,9 @@ func (server *Server) myselfRpcHandlerGo(handlerName string, serviceMethod strin
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return rpcHandler.CallMethod(serviceMethod, args, reply)
|
|
||||||
|
|
||||||
|
return rpcHandler.CallMethod(client,serviceMethod, args,callBack, reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) selfNodeRpcHandlerGo(processor IRpcProcessor, client *Client, noReply bool, handlerName string, rpcMethodId uint32, serviceMethod string, args interface{}, reply interface{}, rawArgs []byte) *Call {
|
func (server *Server) selfNodeRpcHandlerGo(processor IRpcProcessor, client *Client, noReply bool, handlerName string, rpcMethodId uint32, serviceMethod string, args interface{}, reply interface{}, rawArgs []byte) *Call {
|
||||||
@@ -252,8 +259,8 @@ func (server *Server) selfNodeRpcHandlerGo(processor IRpcProcessor, client *Clie
|
|||||||
if rpcHandler == nil {
|
if rpcHandler == nil {
|
||||||
pCall.Seq = 0
|
pCall.Seq = 0
|
||||||
pCall.Err = errors.New("service method " + serviceMethod + " not config!")
|
pCall.Err = errors.New("service method " + serviceMethod + " not config!")
|
||||||
log.SError(pCall.Err.Error())
|
|
||||||
pCall.done <- pCall
|
pCall.done <- pCall
|
||||||
|
log.SError(pCall.Err.Error())
|
||||||
|
|
||||||
return pCall
|
return pCall
|
||||||
}
|
}
|
||||||
@@ -277,33 +284,34 @@ func (server *Server) selfNodeRpcHandlerGo(processor IRpcProcessor, client *Clie
|
|||||||
|
|
||||||
if noReply == false {
|
if noReply == false {
|
||||||
client.AddPending(pCall)
|
client.AddPending(pCall)
|
||||||
|
callSeq := pCall.Seq
|
||||||
req.requestHandle = func(Returns interface{}, Err RpcError) {
|
req.requestHandle = func(Returns interface{}, Err RpcError) {
|
||||||
if reply != nil && Returns != reply && Returns != nil {
|
if reply != nil && Returns != reply && Returns != nil {
|
||||||
byteReturns, err := req.rpcProcessor.Marshal(Returns)
|
byteReturns, err := req.rpcProcessor.Marshal(Returns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.SError("returns data cannot be marshal ", pCall.Seq)
|
log.SError("returns data cannot be marshal ", callSeq)
|
||||||
ReleaseRpcRequest(req)
|
ReleaseRpcRequest(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = req.rpcProcessor.Unmarshal(byteReturns, reply)
|
err = req.rpcProcessor.Unmarshal(byteReturns, reply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.SError("returns data cannot be Unmarshal ", pCall.Seq)
|
log.SError("returns data cannot be Unmarshal ", callSeq)
|
||||||
ReleaseRpcRequest(req)
|
ReleaseRpcRequest(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v := client.RemovePending(pCall.Seq)
|
v := client.RemovePending(callSeq)
|
||||||
if v == nil {
|
if v == nil {
|
||||||
log.SError("rpcClient cannot find seq ", pCall.Seq, " in pending")
|
log.SError("rpcClient cannot find seq ",callSeq, " in pending")
|
||||||
ReleaseRpcRequest(req)
|
ReleaseRpcRequest(req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(Err) == 0 {
|
if len(Err) == 0 {
|
||||||
pCall.Err = nil
|
v.Err = nil
|
||||||
} else {
|
} else {
|
||||||
pCall.Err = Err
|
v.Err = Err
|
||||||
}
|
}
|
||||||
pCall.done <- pCall
|
v.done <- v
|
||||||
ReleaseRpcRequest(req)
|
ReleaseRpcRequest(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +346,7 @@ func (server *Server) selfNodeRpcHandlerAsyncGo(client *Client, callerRpcHandler
|
|||||||
pCall.rpcHandler = callerRpcHandler
|
pCall.rpcHandler = callerRpcHandler
|
||||||
pCall.callback = &callback
|
pCall.callback = &callback
|
||||||
pCall.Reply = reply
|
pCall.Reply = reply
|
||||||
|
pCall.ServiceMethod = serviceMethod
|
||||||
client.AddPending(pCall)
|
client.AddPending(pCall)
|
||||||
req.requestHandle = func(Returns interface{}, Err RpcError) {
|
req.requestHandle = func(Returns interface{}, Err RpcError) {
|
||||||
v := client.RemovePending(callSeq)
|
v := client.RemovePending(callSeq)
|
||||||
|
|||||||
@@ -2,31 +2,33 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/duanhf2012/origin/event"
|
"github.com/duanhf2012/origin/event"
|
||||||
"github.com/duanhf2012/origin/log"
|
"github.com/duanhf2012/origin/log"
|
||||||
rpcHandle "github.com/duanhf2012/origin/rpc"
|
rpcHandle "github.com/duanhf2012/origin/rpc"
|
||||||
"github.com/duanhf2012/origin/util/timer"
|
"github.com/duanhf2012/origin/util/timer"
|
||||||
"reflect"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const InitModuleId = 1e9
|
const InitModuleId = 1e9
|
||||||
|
|
||||||
type IModule interface {
|
type IModule interface {
|
||||||
SetModuleId(moduleId uint32) bool
|
SetModuleId(moduleId uint32) bool
|
||||||
GetModuleId() uint32
|
GetModuleId() uint32
|
||||||
AddModule(module IModule) (uint32,error)
|
AddModule(module IModule) (uint32, error)
|
||||||
GetModule(moduleId uint32) IModule
|
GetModule(moduleId uint32) IModule
|
||||||
GetAncestor()IModule
|
GetAncestor() IModule
|
||||||
ReleaseModule(moduleId uint32)
|
ReleaseModule(moduleId uint32)
|
||||||
NewModuleId() uint32
|
NewModuleId() uint32
|
||||||
GetParent()IModule
|
GetParent() IModule
|
||||||
OnInit() error
|
OnInit() error
|
||||||
OnRelease()
|
OnRelease()
|
||||||
getBaseModule() IModule
|
getBaseModule() IModule
|
||||||
GetService() IService
|
GetService() IService
|
||||||
GetModuleName() string
|
GetModuleName() string
|
||||||
GetEventProcessor()event.IEventProcessor
|
GetEventProcessor() event.IEventProcessor
|
||||||
NotifyEvent(ev event.IEvent)
|
NotifyEvent(ev event.IEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,25 +40,25 @@ type IModuleTimer interface {
|
|||||||
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
rpcHandle.IRpcHandler
|
rpcHandle.IRpcHandler
|
||||||
moduleId uint32 //模块Id
|
moduleId uint32 //模块Id
|
||||||
moduleName string //模块名称
|
moduleName string //模块名称
|
||||||
parent IModule //父亲
|
parent IModule //父亲
|
||||||
self IModule //自己
|
self IModule //自己
|
||||||
child map[uint32]IModule //孩子们
|
child map[uint32]IModule //孩子们
|
||||||
mapActiveTimer map[timer.ITimer]struct{}
|
mapActiveTimer map[timer.ITimer]struct{}
|
||||||
mapActiveIdTimer map[uint64]timer.ITimer
|
mapActiveIdTimer map[uint64]timer.ITimer
|
||||||
dispatcher *timer.Dispatcher //timer
|
dispatcher *timer.Dispatcher //timer
|
||||||
|
|
||||||
//根结点
|
//根结点
|
||||||
ancestor IModule //始祖
|
ancestor IModule //始祖
|
||||||
seedModuleId uint32 //模块id种子
|
seedModuleId uint32 //模块id种子
|
||||||
descendants map[uint32]IModule //始祖的后裔们
|
descendants map[uint32]IModule //始祖的后裔们
|
||||||
|
|
||||||
//事件管道
|
//事件管道
|
||||||
eventHandler event.IEventHandler
|
eventHandler event.IEventHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) SetModuleId(moduleId uint32) bool{
|
func (m *Module) SetModuleId(moduleId uint32) bool {
|
||||||
if m.moduleId > 0 {
|
if m.moduleId > 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -65,35 +67,35 @@ func (m *Module) SetModuleId(moduleId uint32) bool{
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) GetModuleId() uint32{
|
func (m *Module) GetModuleId() uint32 {
|
||||||
return m.moduleId
|
return m.moduleId
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) GetModuleName() string{
|
func (m *Module) GetModuleName() string {
|
||||||
return m.moduleName
|
return m.moduleName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) OnInit() error{
|
func (m *Module) OnInit() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) AddModule(module IModule) (uint32,error){
|
func (m *Module) AddModule(module IModule) (uint32, error) {
|
||||||
//没有事件处理器不允许加入其他模块
|
//没有事件处理器不允许加入其他模块
|
||||||
if m.GetEventProcessor() == nil {
|
if m.GetEventProcessor() == nil {
|
||||||
return 0,fmt.Errorf("module %+v Event Processor is nil", m.self)
|
return 0, fmt.Errorf("module %+v Event Processor is nil", m.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pAddModule := module.getBaseModule().(*Module)
|
pAddModule := module.getBaseModule().(*Module)
|
||||||
if pAddModule.GetModuleId()==0 {
|
if pAddModule.GetModuleId() == 0 {
|
||||||
pAddModule.moduleId = m.NewModuleId()
|
pAddModule.moduleId = m.NewModuleId()
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.child == nil {
|
if m.child == nil {
|
||||||
m.child = map[uint32]IModule{}
|
m.child = map[uint32]IModule{}
|
||||||
}
|
}
|
||||||
_,ok := m.child[module.GetModuleId()]
|
_, ok := m.child[module.GetModuleId()]
|
||||||
if ok == true {
|
if ok == true {
|
||||||
return 0,fmt.Errorf("exists module id %d",module.GetModuleId())
|
return 0, fmt.Errorf("exists module id %d", module.GetModuleId())
|
||||||
}
|
}
|
||||||
pAddModule.IRpcHandler = m.IRpcHandler
|
pAddModule.IRpcHandler = m.IRpcHandler
|
||||||
pAddModule.self = module
|
pAddModule.self = module
|
||||||
@@ -105,17 +107,17 @@ func (m *Module) AddModule(module IModule) (uint32,error){
|
|||||||
pAddModule.eventHandler.Init(m.eventHandler.GetEventProcessor())
|
pAddModule.eventHandler.Init(m.eventHandler.GetEventProcessor())
|
||||||
err := module.OnInit()
|
err := module.OnInit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0,err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.child[module.GetModuleId()] = module
|
m.child[module.GetModuleId()] = module
|
||||||
m.ancestor.getBaseModule().(*Module).descendants[module.GetModuleId()] = module
|
m.ancestor.getBaseModule().(*Module).descendants[module.GetModuleId()] = module
|
||||||
|
|
||||||
log.SDebug("Add module ",module.GetModuleName()," completed")
|
log.SDebug("Add module ", module.GetModuleName(), " completed")
|
||||||
return module.GetModuleId(),nil
|
return module.GetModuleId(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) ReleaseModule(moduleId uint32){
|
func (m *Module) ReleaseModule(moduleId uint32) {
|
||||||
pModule := m.GetModule(moduleId).getBaseModule().(*Module)
|
pModule := m.GetModule(moduleId).getBaseModule().(*Module)
|
||||||
|
|
||||||
//释放子孙
|
//释放子孙
|
||||||
@@ -123,19 +125,19 @@ func (m *Module) ReleaseModule(moduleId uint32){
|
|||||||
m.ReleaseModule(id)
|
m.ReleaseModule(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pModule.GetEventHandler().Destroy()
|
|
||||||
pModule.self.OnRelease()
|
pModule.self.OnRelease()
|
||||||
|
pModule.GetEventHandler().Destroy()
|
||||||
log.SDebug("Release module ", pModule.GetModuleName())
|
log.SDebug("Release module ", pModule.GetModuleName())
|
||||||
for pTimer := range pModule.mapActiveTimer {
|
for pTimer := range pModule.mapActiveTimer {
|
||||||
pTimer.Cancel()
|
pTimer.Cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
for _,t := range pModule.mapActiveIdTimer {
|
for _, t := range pModule.mapActiveIdTimer {
|
||||||
t.Cancel()
|
t.Cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(m.child,moduleId)
|
delete(m.child, moduleId)
|
||||||
delete (m.ancestor.getBaseModule().(*Module).descendants,moduleId)
|
delete(m.ancestor.getBaseModule().(*Module).descendants, moduleId)
|
||||||
|
|
||||||
//清理被删除的Module
|
//清理被删除的Module
|
||||||
pModule.self = nil
|
pModule.self = nil
|
||||||
@@ -149,16 +151,17 @@ func (m *Module) ReleaseModule(moduleId uint32){
|
|||||||
pModule.mapActiveIdTimer = nil
|
pModule.mapActiveIdTimer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) NewModuleId() uint32{
|
func (m *Module) NewModuleId() uint32 {
|
||||||
m.ancestor.getBaseModule().(*Module).seedModuleId+=1
|
m.ancestor.getBaseModule().(*Module).seedModuleId += 1
|
||||||
return m.ancestor.getBaseModule().(*Module).seedModuleId
|
return m.ancestor.getBaseModule().(*Module).seedModuleId
|
||||||
}
|
}
|
||||||
|
|
||||||
var timerSeedId uint32
|
var timerSeedId uint32
|
||||||
func (m *Module) GenTimerId() uint64{
|
|
||||||
for{
|
func (m *Module) GenTimerId() uint64 {
|
||||||
newTimerId := (uint64(m.GetModuleId())<<32)|uint64(atomic.AddUint32(&timerSeedId,1))
|
for {
|
||||||
if _,ok := m.mapActiveIdTimer[newTimerId];ok == true {
|
newTimerId := (uint64(m.GetModuleId()) << 32) | uint64(atomic.AddUint32(&timerSeedId, 1))
|
||||||
|
if _, ok := m.mapActiveIdTimer[newTimerId]; ok == true {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,33 +169,32 @@ func (m *Module) GenTimerId() uint64{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Module) GetAncestor() IModule {
|
||||||
func (m *Module) GetAncestor()IModule{
|
|
||||||
return m.ancestor
|
return m.ancestor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) GetModule(moduleId uint32) IModule{
|
func (m *Module) GetModule(moduleId uint32) IModule {
|
||||||
iModule,ok := m.GetAncestor().getBaseModule().(*Module).descendants[moduleId]
|
iModule, ok := m.GetAncestor().getBaseModule().(*Module).descendants[moduleId]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return iModule
|
return iModule
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) getBaseModule() IModule{
|
func (m *Module) getBaseModule() IModule {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) GetParent()IModule{
|
func (m *Module) GetParent() IModule {
|
||||||
return m.parent
|
return m.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) OnCloseTimer(t timer.ITimer){
|
func (m *Module) OnCloseTimer(t timer.ITimer) {
|
||||||
delete(m.mapActiveIdTimer,t.GetId())
|
delete(m.mapActiveIdTimer, t.GetId())
|
||||||
delete(m.mapActiveTimer,t)
|
delete(m.mapActiveTimer, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) OnAddTimer(t timer.ITimer){
|
func (m *Module) OnAddTimer(t timer.ITimer) {
|
||||||
if t != nil {
|
if t != nil {
|
||||||
if m.mapActiveTimer == nil {
|
if m.mapActiveTimer == nil {
|
||||||
m.mapActiveTimer = map[timer.ITimer]struct{}{}
|
m.mapActiveTimer = map[timer.ITimer]struct{}{}
|
||||||
@@ -204,33 +206,33 @@ func (m *Module) OnAddTimer(t timer.ITimer){
|
|||||||
|
|
||||||
func (m *Module) AfterFunc(d time.Duration, cb func(*timer.Timer)) *timer.Timer {
|
func (m *Module) AfterFunc(d time.Duration, cb func(*timer.Timer)) *timer.Timer {
|
||||||
if m.mapActiveTimer == nil {
|
if m.mapActiveTimer == nil {
|
||||||
m.mapActiveTimer =map[timer.ITimer]struct{}{}
|
m.mapActiveTimer = map[timer.ITimer]struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.dispatcher.AfterFunc(d,nil,cb,m.OnCloseTimer,m.OnAddTimer)
|
return m.dispatcher.AfterFunc(d, nil, cb, m.OnCloseTimer, m.OnAddTimer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) CronFunc(cronExpr *timer.CronExpr, cb func(*timer.Cron)) *timer.Cron {
|
func (m *Module) CronFunc(cronExpr *timer.CronExpr, cb func(*timer.Cron)) *timer.Cron {
|
||||||
if m.mapActiveTimer == nil {
|
if m.mapActiveTimer == nil {
|
||||||
m.mapActiveTimer =map[timer.ITimer]struct{}{}
|
m.mapActiveTimer = map[timer.ITimer]struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.dispatcher.CronFunc(cronExpr,nil,cb,m.OnCloseTimer,m.OnAddTimer)
|
return m.dispatcher.CronFunc(cronExpr, nil, cb, m.OnCloseTimer, m.OnAddTimer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) NewTicker(d time.Duration, cb func(*timer.Ticker)) *timer.Ticker {
|
func (m *Module) NewTicker(d time.Duration, cb func(*timer.Ticker)) *timer.Ticker {
|
||||||
if m.mapActiveTimer == nil {
|
if m.mapActiveTimer == nil {
|
||||||
m.mapActiveTimer =map[timer.ITimer]struct{}{}
|
m.mapActiveTimer = map[timer.ITimer]struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.dispatcher.TickerFunc(d,nil,cb,m.OnCloseTimer,m.OnAddTimer)
|
return m.dispatcher.TickerFunc(d, nil, cb, m.OnCloseTimer, m.OnAddTimer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) cb(*timer.Timer){
|
func (m *Module) cb(*timer.Timer) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) SafeAfterFunc(timerId *uint64,d time.Duration, AdditionData interface{},cb func(uint64,interface{})) {
|
func (m *Module) SafeAfterFunc(timerId *uint64, d time.Duration, AdditionData interface{}, cb func(uint64, interface{})) {
|
||||||
if m.mapActiveIdTimer == nil {
|
if m.mapActiveIdTimer == nil {
|
||||||
m.mapActiveIdTimer = map[uint64]timer.ITimer{}
|
m.mapActiveIdTimer = map[uint64]timer.ITimer{}
|
||||||
}
|
}
|
||||||
@@ -240,45 +242,45 @@ func (m *Module) SafeAfterFunc(timerId *uint64,d time.Duration, AdditionData int
|
|||||||
}
|
}
|
||||||
|
|
||||||
*timerId = m.GenTimerId()
|
*timerId = m.GenTimerId()
|
||||||
t := m.dispatcher.AfterFunc(d,cb,nil,m.OnCloseTimer,m.OnAddTimer)
|
t := m.dispatcher.AfterFunc(d, cb, nil, m.OnCloseTimer, m.OnAddTimer)
|
||||||
t.AdditionData = AdditionData
|
t.AdditionData = AdditionData
|
||||||
t.Id = *timerId
|
t.Id = *timerId
|
||||||
m.mapActiveIdTimer[*timerId] = t
|
m.mapActiveIdTimer[*timerId] = t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) SafeCronFunc(cronId *uint64,cronExpr *timer.CronExpr, AdditionData interface{}, cb func(uint64,interface{})) {
|
func (m *Module) SafeCronFunc(cronId *uint64, cronExpr *timer.CronExpr, AdditionData interface{}, cb func(uint64, interface{})) {
|
||||||
if m.mapActiveIdTimer == nil {
|
if m.mapActiveIdTimer == nil {
|
||||||
m.mapActiveIdTimer = map[uint64]timer.ITimer{}
|
m.mapActiveIdTimer = map[uint64]timer.ITimer{}
|
||||||
}
|
}
|
||||||
|
|
||||||
*cronId = m.GenTimerId()
|
*cronId = m.GenTimerId()
|
||||||
c := m.dispatcher.CronFunc(cronExpr,cb,nil,m.OnCloseTimer,m.OnAddTimer)
|
c := m.dispatcher.CronFunc(cronExpr, cb, nil, m.OnCloseTimer, m.OnAddTimer)
|
||||||
c.AdditionData = AdditionData
|
c.AdditionData = AdditionData
|
||||||
c.Id = *cronId
|
c.Id = *cronId
|
||||||
m.mapActiveIdTimer[*cronId] = c
|
m.mapActiveIdTimer[*cronId] = c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) SafeNewTicker(tickerId *uint64,d time.Duration, AdditionData interface{}, cb func(uint64,interface{})) {
|
func (m *Module) SafeNewTicker(tickerId *uint64, d time.Duration, AdditionData interface{}, cb func(uint64, interface{})) {
|
||||||
if m.mapActiveIdTimer == nil {
|
if m.mapActiveIdTimer == nil {
|
||||||
m.mapActiveIdTimer = map[uint64]timer.ITimer{}
|
m.mapActiveIdTimer = map[uint64]timer.ITimer{}
|
||||||
}
|
}
|
||||||
|
|
||||||
*tickerId = m.GenTimerId()
|
*tickerId = m.GenTimerId()
|
||||||
t := m.dispatcher.TickerFunc(d,cb,nil,m.OnCloseTimer,m.OnAddTimer)
|
t := m.dispatcher.TickerFunc(d, cb, nil, m.OnCloseTimer, m.OnAddTimer)
|
||||||
t.AdditionData = AdditionData
|
t.AdditionData = AdditionData
|
||||||
t.Id = *tickerId
|
t.Id = *tickerId
|
||||||
m.mapActiveIdTimer[*tickerId] = t
|
m.mapActiveIdTimer[*tickerId] = t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) CancelTimerId(timerId *uint64) bool{
|
func (m *Module) CancelTimerId(timerId *uint64) bool {
|
||||||
if m.mapActiveIdTimer == nil {
|
if m.mapActiveIdTimer == nil {
|
||||||
log.SError("mapActiveIdTimer is nil")
|
log.SError("mapActiveIdTimer is nil")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
t,ok := m.mapActiveIdTimer[*timerId]
|
t, ok := m.mapActiveIdTimer[*timerId]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
log.SError("cannot find timer id ",timerId)
|
log.SError("cannot find timer id ", timerId)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,23 +289,21 @@ func (m *Module) CancelTimerId(timerId *uint64) bool{
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Module) OnRelease() {
|
||||||
|
|
||||||
func (m *Module) OnRelease(){
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) GetService() IService {
|
func (m *Module) GetService() IService {
|
||||||
return m.GetAncestor().(IService)
|
return m.GetAncestor().(IService)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) GetEventProcessor() event.IEventProcessor{
|
func (m *Module) GetEventProcessor() event.IEventProcessor {
|
||||||
return m.eventHandler.GetEventProcessor()
|
return m.eventHandler.GetEventProcessor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) NotifyEvent(ev event.IEvent){
|
func (m *Module) NotifyEvent(ev event.IEvent) {
|
||||||
m.eventHandler.NotifyEvent(ev)
|
m.eventHandler.NotifyEvent(ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) GetEventHandler() event.IEventHandler{
|
func (m *Module) GetEventHandler() event.IEventHandler {
|
||||||
return m.eventHandler
|
return m.eventHandler
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,18 +22,24 @@ var timerDispatcherLen = 100000
|
|||||||
|
|
||||||
type IService interface {
|
type IService interface {
|
||||||
Init(iService IService,getClientFun rpc.FuncRpcClient,getServerFun rpc.FuncRpcServer,serviceCfg interface{})
|
Init(iService IService,getClientFun rpc.FuncRpcClient,getServerFun rpc.FuncRpcServer,serviceCfg interface{})
|
||||||
SetName(serviceName string)
|
Wait()
|
||||||
GetName() string
|
Start()
|
||||||
|
|
||||||
OnSetup(iService IService)
|
OnSetup(iService IService)
|
||||||
OnInit() error
|
OnInit() error
|
||||||
OnStart()
|
OnStart()
|
||||||
OnRelease()
|
OnRelease()
|
||||||
Wait()
|
|
||||||
Start()
|
SetName(serviceName string)
|
||||||
|
GetName() string
|
||||||
GetRpcHandler() rpc.IRpcHandler
|
GetRpcHandler() rpc.IRpcHandler
|
||||||
GetServiceCfg()interface{}
|
GetServiceCfg()interface{}
|
||||||
OpenProfiler()
|
|
||||||
GetProfiler() *profiler.Profiler
|
GetProfiler() *profiler.Profiler
|
||||||
|
GetServiceEventChannelNum() int
|
||||||
|
GetServiceTimerChannelNum() int
|
||||||
|
|
||||||
|
SetEventChannelNum(num int)
|
||||||
|
OpenProfiler()
|
||||||
}
|
}
|
||||||
|
|
||||||
// eventPool的内存池,缓存Event
|
// eventPool的内存池,缓存Event
|
||||||
@@ -52,7 +58,8 @@ type Service struct {
|
|||||||
startStatus bool
|
startStatus bool
|
||||||
eventProcessor event.IEventProcessor
|
eventProcessor event.IEventProcessor
|
||||||
profiler *profiler.Profiler //性能分析器
|
profiler *profiler.Profiler //性能分析器
|
||||||
rpcEventLister rpc.IRpcListener
|
nodeEventLister rpc.INodeListener
|
||||||
|
discoveryServiceLister rpc.IDiscoveryServiceListener
|
||||||
chanEvent chan event.IEvent
|
chanEvent chan event.IEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +69,13 @@ type RpcConnEvent struct{
|
|||||||
NodeId int
|
NodeId int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiscoveryServiceEvent 发现服务结点
|
||||||
|
type DiscoveryServiceEvent struct{
|
||||||
|
IsDiscovery bool
|
||||||
|
ServiceName []string
|
||||||
|
NodeId int
|
||||||
|
}
|
||||||
|
|
||||||
func SetMaxServiceChannel(maxEventChannel int){
|
func SetMaxServiceChannel(maxEventChannel int){
|
||||||
maxServiceEventChannel = maxEventChannel
|
maxServiceEventChannel = maxEventChannel
|
||||||
eventPool = originSync.NewPoolEx(make(chan originSync.IPoolData, maxServiceEventChannel), func() originSync.IPoolData {
|
eventPool = originSync.NewPoolEx(make(chan originSync.IPoolData, maxServiceEventChannel), func() originSync.IPoolData {
|
||||||
@@ -69,8 +83,12 @@ func SetMaxServiceChannel(maxEventChannel int){
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rpcEventData *DiscoveryServiceEvent) GetEventType() event.EventType{
|
||||||
|
return event.Sys_Event_DiscoverService
|
||||||
|
}
|
||||||
|
|
||||||
func (rpcEventData *RpcConnEvent) GetEventType() event.EventType{
|
func (rpcEventData *RpcConnEvent) GetEventType() event.EventType{
|
||||||
return event.Sys_Event_Rpc_Event
|
return event.Sys_Event_Node_Event
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) OnSetup(iService IService){
|
func (s *Service) OnSetup(iService IService){
|
||||||
@@ -88,7 +106,10 @@ func (s *Service) OpenProfiler() {
|
|||||||
|
|
||||||
func (s *Service) Init(iService IService,getClientFun rpc.FuncRpcClient,getServerFun rpc.FuncRpcServer,serviceCfg interface{}) {
|
func (s *Service) Init(iService IService,getClientFun rpc.FuncRpcClient,getServerFun rpc.FuncRpcServer,serviceCfg interface{}) {
|
||||||
s.dispatcher =timer.NewDispatcher(timerDispatcherLen)
|
s.dispatcher =timer.NewDispatcher(timerDispatcherLen)
|
||||||
s.chanEvent = make(chan event.IEvent,maxServiceEventChannel)
|
if s.chanEvent == nil {
|
||||||
|
s.chanEvent = make(chan event.IEvent,maxServiceEventChannel)
|
||||||
|
}
|
||||||
|
|
||||||
s.rpcHandler.InitRpcHandler(iService.(rpc.IRpcHandler),getClientFun,getServerFun,iService.(rpc.IRpcHandlerChannel))
|
s.rpcHandler.InitRpcHandler(iService.(rpc.IRpcHandler),getClientFun,getServerFun,iService.(rpc.IRpcHandlerChannel))
|
||||||
s.IRpcHandler = &s.rpcHandler
|
s.IRpcHandler = &s.rpcHandler
|
||||||
s.self = iService.(IModule)
|
s.self = iService.(IModule)
|
||||||
@@ -259,24 +280,44 @@ func (s *Service) RegRawRpc(rpcMethodId uint32,rawRpcCB rpc.RawRpcCallBack){
|
|||||||
func (s *Service) OnStart(){
|
func (s *Service) OnStart(){
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) OnRpcEvent(ev event.IEvent){
|
func (s *Service) OnNodeEvent(ev event.IEvent){
|
||||||
event := ev.(*RpcConnEvent)
|
event := ev.(*RpcConnEvent)
|
||||||
if event.IsConnect {
|
if event.IsConnect {
|
||||||
s.rpcEventLister.OnNodeConnected(event.NodeId)
|
s.nodeEventLister.OnNodeConnected(event.NodeId)
|
||||||
}else{
|
}else{
|
||||||
s.rpcEventLister.OnNodeDisconnect(event.NodeId)
|
s.nodeEventLister.OnNodeDisconnect(event.NodeId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) RegRpcListener(rpcEventLister rpc.IRpcListener) {
|
func (s *Service) OnDiscoverServiceEvent(ev event.IEvent){
|
||||||
s.rpcEventLister = rpcEventLister
|
event := ev.(*DiscoveryServiceEvent)
|
||||||
s.RegEventReceiverFunc(event.Sys_Event_Rpc_Event,s.GetEventHandler(),s.OnRpcEvent)
|
if event.IsDiscovery {
|
||||||
|
s.discoveryServiceLister.OnDiscoveryService(event.NodeId,event.ServiceName)
|
||||||
|
}else{
|
||||||
|
s.discoveryServiceLister.OnUnDiscoveryService(event.NodeId,event.ServiceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) RegRpcListener(rpcEventLister rpc.INodeListener) {
|
||||||
|
s.nodeEventLister = rpcEventLister
|
||||||
|
s.RegEventReceiverFunc(event.Sys_Event_Node_Event,s.GetEventHandler(),s.OnNodeEvent)
|
||||||
RegRpcEventFun(s.GetName())
|
RegRpcEventFun(s.GetName())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) UnRegRpcListener(rpcLister rpc.IRpcListener) {
|
func (s *Service) UnRegRpcListener(rpcLister rpc.INodeListener) {
|
||||||
s.UnRegEventReceiverFunc(event.Sys_Event_Rpc_Event,s.GetEventHandler())
|
s.UnRegEventReceiverFunc(event.Sys_Event_Node_Event,s.GetEventHandler())
|
||||||
RegRpcEventFun(s.GetName())
|
UnRegRpcEventFun(s.GetName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) RegDiscoverListener(discoveryServiceListener rpc.IDiscoveryServiceListener) {
|
||||||
|
s.discoveryServiceLister = discoveryServiceListener
|
||||||
|
s.RegEventReceiverFunc(event.Sys_Event_DiscoverService,s.GetEventHandler(),s.OnDiscoverServiceEvent)
|
||||||
|
RegDiscoveryServiceEventFun(s.GetName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) UnRegDiscoverListener(rpcLister rpc.INodeListener) {
|
||||||
|
s.UnRegEventReceiverFunc(event.Sys_Event_DiscoverService,s.GetEventHandler())
|
||||||
|
UnRegDiscoveryServiceEventFun(s.GetName())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -311,6 +352,21 @@ func (s *Service) pushEvent(ev event.IEvent) error{
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetServiceEventChannelNum() int{
|
||||||
|
return len(s.chanEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetServiceTimerChannelNum() int{
|
||||||
|
return len(s.dispatcher.ChanTimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetEventChannelNum(num int){
|
||||||
|
if s.chanEvent == nil {
|
||||||
|
s.chanEvent = make(chan event.IEvent,num)
|
||||||
|
}else {
|
||||||
|
panic("this stage cannot be set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) SetGoRoutineNum(goroutineNum int32) bool {
|
func (s *Service) SetGoRoutineNum(goroutineNum int32) bool {
|
||||||
//已经开始状态不允许修改协程数量,打开性能分析器不允许开多线程
|
//已经开始状态不允许修改协程数量,打开性能分析器不允许开多线程
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
//本地所有的service
|
//本地所有的service
|
||||||
var mapServiceName map[string]IService
|
var mapServiceName map[string]IService
|
||||||
var setupServiceList []IService
|
var setupServiceList []IService
|
||||||
|
|
||||||
type RegRpcEventFunType func(serviceName string)
|
type RegRpcEventFunType func(serviceName string)
|
||||||
|
type RegDiscoveryServiceEventFunType func(serviceName string)
|
||||||
var RegRpcEventFun RegRpcEventFunType
|
var RegRpcEventFun RegRpcEventFunType
|
||||||
|
var UnRegRpcEventFun RegRpcEventFunType
|
||||||
|
|
||||||
|
var RegDiscoveryServiceEventFun RegDiscoveryServiceEventFunType
|
||||||
|
var UnRegDiscoveryServiceEventFun RegDiscoveryServiceEventFunType
|
||||||
|
|
||||||
func init(){
|
func init(){
|
||||||
mapServiceName = map[string]IService{}
|
mapServiceName = map[string]IService{}
|
||||||
@@ -18,7 +25,8 @@ func Init(chanCloseSig chan bool) {
|
|||||||
for _,s := range setupServiceList {
|
for _,s := range setupServiceList {
|
||||||
err := s.OnInit()
|
err := s.OnInit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
errs := errors.New("Failed to initialize "+s.GetName()+" service:"+err.Error())
|
||||||
|
panic(errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -64,7 +64,7 @@ func (m *HttpClientModule) Init(maxpool int, proxyUrl string) {
|
|||||||
Proxy: proxyFun,
|
Proxy: proxyFun,
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
},
|
},
|
||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ func (m *HttpClientModule) Request(method string, url string, body []byte, heade
|
|||||||
}
|
}
|
||||||
defer rsp.Body.Close()
|
defer rsp.Body.Close()
|
||||||
|
|
||||||
ret.Body, err = ioutil.ReadAll(rsp.Body)
|
ret.Body, err = io.ReadAll(rsp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ret.Err = err
|
ret.Err = err
|
||||||
return ret
|
return ret
|
||||||
|
|||||||
113
sysmodule/mongodbmodule/mongodbmodule.go
Normal file
113
sysmodule/mongodbmodule/mongodbmodule.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package mongodbmodule
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
"go.mongodb.org/mongo-driver/x/bsonx"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MongoModule struct {
|
||||||
|
client *mongo.Client
|
||||||
|
maxOperatorTimeOut time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
*mongo.Client
|
||||||
|
maxOperatorTimeOut time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mm *MongoModule) Init(uri string, maxOperatorTimeOut time.Duration) error {
|
||||||
|
var err error
|
||||||
|
mm.client, err = mongo.NewClient(options.Client().ApplyURI(uri))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mm.maxOperatorTimeOut = maxOperatorTimeOut
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mm *MongoModule) Start() error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := mm.client.Connect(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := mm.client.Ping(ctxTimeout, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mm *MongoModule) TakeSession() Session {
|
||||||
|
return Session{Client: mm.client, maxOperatorTimeOut: mm.maxOperatorTimeOut}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) CountDocument(db string, collection string, filter interface{}) (int64, error) {
|
||||||
|
ctxTimeout, cancel := s.GetDefaultContext()
|
||||||
|
defer cancel()
|
||||||
|
return s.Database(db).Collection(collection).CountDocuments(ctxTimeout, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) NextSeq(db string, collection string, id interface{}) (int, error) {
|
||||||
|
var res struct {
|
||||||
|
Seq int
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxTimeout, cancel := s.GetDefaultContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
after := options.After
|
||||||
|
updateOpts := options.FindOneAndUpdateOptions{ReturnDocument: &after}
|
||||||
|
err := s.Client.Database(db).Collection(collection).FindOneAndUpdate(ctxTimeout, bson.M{"_id": id}, bson.M{"$inc": bson.M{"Seq": 1}},&updateOpts).Decode(&res)
|
||||||
|
return res.Seq, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//indexKeys[索引][每个索引key字段]
|
||||||
|
func (s *Session) EnsureIndex(db string, collection string, indexKeys [][]string, bBackground bool,sparse bool) error {
|
||||||
|
return s.ensureIndex(db, collection, indexKeys, bBackground, false,sparse)
|
||||||
|
}
|
||||||
|
|
||||||
|
//indexKeys[索引][每个索引key字段]
|
||||||
|
func (s *Session) EnsureUniqueIndex(db string, collection string, indexKeys [][]string, bBackground bool,sparse bool) error {
|
||||||
|
return s.ensureIndex(db, collection, indexKeys, bBackground, true,sparse)
|
||||||
|
}
|
||||||
|
|
||||||
|
//keys[索引][每个索引key字段]
|
||||||
|
func (s *Session) ensureIndex(db string, collection string, indexKeys [][]string, bBackground bool, unique bool,sparse bool) error {
|
||||||
|
var indexes []mongo.IndexModel
|
||||||
|
for _, keys := range indexKeys {
|
||||||
|
keysDoc := bsonx.Doc{}
|
||||||
|
for _, key := range keys {
|
||||||
|
keysDoc = keysDoc.Append(key, bsonx.Int32(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
options:= options.Index().SetUnique(unique).SetBackground(bBackground)
|
||||||
|
if sparse == true {
|
||||||
|
options.SetSparse(true)
|
||||||
|
}
|
||||||
|
indexes = append(indexes, mongo.IndexModel{Keys: keysDoc, Options:options })
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxTimeout, cancel := context.WithTimeout(context.Background(), s.maxOperatorTimeOut)
|
||||||
|
defer cancel()
|
||||||
|
_, err := s.Database(db).Collection(collection).Indexes().CreateMany(ctxTimeout, indexes)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) GetDefaultContext() (context.Context, context.CancelFunc) {
|
||||||
|
return context.WithTimeout(context.Background(), s.maxOperatorTimeOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) Collection(db string, collection string) *mongo.Collection {
|
||||||
|
return s.Database(db).Collection(collection)
|
||||||
|
}
|
||||||
208
sysmodule/mongodbmodule/mongodbmodule_test.go
Normal file
208
sysmodule/mongodbmodule/mongodbmodule_test.go
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
package mongodbmodule
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Student struct {
|
||||||
|
ID primitive.ObjectID `bson:"_id"`
|
||||||
|
Name string `bson: "name"`
|
||||||
|
Age int `bson: "age"`
|
||||||
|
Sid string `bson: "sid"`
|
||||||
|
Status int `bson: "status"`
|
||||||
|
MapData map[int64]int64 `bson: "maptest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StudentName struct {
|
||||||
|
Name string `bson: "name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Example(t *testing.T) {
|
||||||
|
//0.初始化模块
|
||||||
|
var mongoModule MongoModule
|
||||||
|
err := mongoModule.Init("mongodb://admin:123456@192.168.2.15:27017/?authSource=admin&maxPoolSize=100&maxConnecting=2&connectTimeoutMS=10000&socketTimeoutMS=5000", time.Second*10)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mongoModule.Start()
|
||||||
|
|
||||||
|
//1.创建索引
|
||||||
|
session := mongoModule.TakeSession()
|
||||||
|
var IndexKeys [][]string
|
||||||
|
//分别建立number,name组合索引
|
||||||
|
var key1 []string
|
||||||
|
key1 = append(key1, "number", "name")
|
||||||
|
//keyId为索引
|
||||||
|
var key2 []string
|
||||||
|
key2 = append(key2, "KeyId")
|
||||||
|
|
||||||
|
IndexKeys = append(IndexKeys, key1, key2)
|
||||||
|
session.EnsureIndex("testdb", "test2", IndexKeys, true)
|
||||||
|
|
||||||
|
//2.插入数据
|
||||||
|
//插入单行
|
||||||
|
var s Student
|
||||||
|
s.ID = primitive.NewObjectID()
|
||||||
|
s.Age = 35
|
||||||
|
s.Name = "xxx"
|
||||||
|
|
||||||
|
ctx, cancel := session.GetDefaultContext()
|
||||||
|
ret, err := session.Collection("testdb", "test2").InsertOne(ctx, s)
|
||||||
|
cancel()
|
||||||
|
insertId := ret.InsertedID.(primitive.ObjectID)
|
||||||
|
log.Println(insertId.Hex(), err)
|
||||||
|
|
||||||
|
//插入多行
|
||||||
|
var ss []interface{}
|
||||||
|
ctx, cancel = session.GetDefaultContext()
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
var s Student
|
||||||
|
s.ID = primitive.NewObjectID()
|
||||||
|
s.Age = i
|
||||||
|
s.Name = fmt.Sprintf("name_%d", i)
|
||||||
|
ss = append(ss, s)
|
||||||
|
}
|
||||||
|
manyRet, err := session.Collection("testdb", "test2").InsertMany(ctx, ss)
|
||||||
|
cancel()
|
||||||
|
log.Println(manyRet, err)
|
||||||
|
|
||||||
|
//3.更新数据
|
||||||
|
//update
|
||||||
|
var sUpdate Student
|
||||||
|
sUpdate.ID, _ = primitive.ObjectIDFromHex("62429c3b32a269dcbe0cdc7b")
|
||||||
|
sUpdate.Age = 35
|
||||||
|
sUpdate.Name = "xxxx555555"
|
||||||
|
|
||||||
|
//update := bson.M{"$set": bson.M{"age": 35}}
|
||||||
|
update := bson.M{"$set": sUpdate}
|
||||||
|
ctx, cancel = session.GetDefaultContext()
|
||||||
|
objectId, _ := primitive.ObjectIDFromHex("62429c3b32a269dcbe0cdc7b")
|
||||||
|
updateResult, err := session.Collection("testdb", "test2").UpdateOne(ctx, bson.M{"_id": objectId}, update)
|
||||||
|
cancel()
|
||||||
|
log.Println("collection.UpdateOne:", updateResult, err)
|
||||||
|
|
||||||
|
//upset
|
||||||
|
var s_upset Student
|
||||||
|
s_upset.ID, _ = primitive.ObjectIDFromHex("62429c3b32a269dcbe0cdc7e")
|
||||||
|
s_upset.Name = "皇商xx"
|
||||||
|
s_upset.Age = 35099
|
||||||
|
s_upset.Sid = "Sid22"
|
||||||
|
s_upset.MapData = make(map[int64]int64)
|
||||||
|
s_upset.MapData[3434] = 13424234
|
||||||
|
s_upset.MapData[444] = 656565656
|
||||||
|
update = bson.M{"$set": s_upset}
|
||||||
|
updateOpts := options.Update().SetUpsert(true)
|
||||||
|
ctx, cancel = session.GetDefaultContext()
|
||||||
|
|
||||||
|
updateResult, err = session.Collection("testdb", "test2").UpdateOne(ctx, bson.M{"_id": s_upset.ID}, update, updateOpts)
|
||||||
|
cancel()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("collection.UpdateOne:", updateResult)
|
||||||
|
|
||||||
|
//4.删除
|
||||||
|
ctx, cancel = session.GetDefaultContext()
|
||||||
|
Id, _ := primitive.ObjectIDFromHex("62429aa71b6445d1f5bf9aee")
|
||||||
|
deleteResult, err := session.Collection("testdb", "test2").DeleteOne(ctx, bson.M{"_id": Id})
|
||||||
|
cancel()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("collection.DeleteOne:", deleteResult)
|
||||||
|
|
||||||
|
//5.查询
|
||||||
|
//查询单条
|
||||||
|
ctx, cancel = session.GetDefaultContext()
|
||||||
|
|
||||||
|
var sel_One Student
|
||||||
|
Ids, _ := primitive.ObjectIDFromHex("62429b13bbff8acf147ef8d7")
|
||||||
|
err = session.Collection("testdb", "test2").FindOne(context.Background(), bson.M{"_id": Ids}).Decode(&sel_One)
|
||||||
|
cancel()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("collection.FindOne: ", sel_One)
|
||||||
|
|
||||||
|
//查询多条1
|
||||||
|
ctx, cancel = session.GetDefaultContext()
|
||||||
|
cur, err := session.Collection("testdb", "test2").Find(ctx, bson.M{})
|
||||||
|
cancel()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := cur.Err(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sSlice []Student
|
||||||
|
ctx, cancel = session.GetDefaultContext()
|
||||||
|
err = cur.All(ctx, &sSlice)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
cur.Close(ctx)
|
||||||
|
cancel()
|
||||||
|
log.Println("collection.Find curl.All: ", sSlice)
|
||||||
|
for _, one := range sSlice {
|
||||||
|
log.Println(one)
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询多条2
|
||||||
|
cur, err = session.Collection("testdb", "test2").Find(context.Background(), bson.M{})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := cur.Err(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for cur.Next(context.Background()) {
|
||||||
|
var s Student
|
||||||
|
if err = cur.Decode(&s); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("collection.Find cur.Next:", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
cur.Close(context.Background())
|
||||||
|
|
||||||
|
//模糊查询
|
||||||
|
cur, err = session.Collection("testdb", "test2").Find(context.Background(), bson.M{"name": primitive.Regex{Pattern: "xxx"}})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := cur.Err(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sSlices []Student
|
||||||
|
err = cur.All(context.Background(), &sSlices)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
cur.Close(context.Background())
|
||||||
|
|
||||||
|
//6.获取数据总行数
|
||||||
|
count, err := session.Collection("testdb", "test2").CountDocuments(context.Background(), bson.D{})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(count)
|
||||||
|
}
|
||||||
|
log.Println("collection.CountDocuments:", count)
|
||||||
|
|
||||||
|
//7.自动序号
|
||||||
|
Id, _ = primitive.ObjectIDFromHex("62429b13bbff8acf147ef8d7")
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
seq, _ := session.NextSeq("testdb", "test2", Id)
|
||||||
|
log.Println(seq)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,17 +42,17 @@ type RedisModule struct {
|
|||||||
|
|
||||||
// ConfigRedis 服务器配置
|
// ConfigRedis 服务器配置
|
||||||
type ConfigRedis struct {
|
type ConfigRedis struct {
|
||||||
IP string
|
IP string
|
||||||
Port int
|
Port int
|
||||||
Password string
|
Password string
|
||||||
DbIndex int
|
DbIndex int
|
||||||
MaxIdle int //最大的空闲连接数,表示即使没有redis连接时依然可以保持N个空闲的连接,而不被清除,随时处于待命状态。
|
MaxIdle int //最大的空闲连接数,表示即使没有redis连接时依然可以保持N个空闲的连接,而不被清除,随时处于待命状态。
|
||||||
MaxActive int //最大的激活连接数,表示同时最多有N个连接
|
MaxActive int //最大的激活连接数,表示同时最多有N个连接
|
||||||
IdleTimeout int //最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭
|
IdleTimeout int //最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RedisModule) Init(redisCfg *ConfigRedis) {
|
func (m *RedisModule) Init(redisCfg *ConfigRedis) {
|
||||||
redisServer := fmt.Sprintf("%s:%d",redisCfg.IP, redisCfg.Port)
|
redisServer := fmt.Sprintf("%s:%d", redisCfg.IP, redisCfg.Port)
|
||||||
m.redisPool = &redis.Pool{
|
m.redisPool = &redis.Pool{
|
||||||
Wait: true,
|
Wait: true,
|
||||||
MaxIdle: redisCfg.MaxIdle,
|
MaxIdle: redisCfg.MaxIdle,
|
||||||
@@ -192,7 +192,6 @@ func (m *RedisModule) HSetStruct(key string, val interface{}) error {
|
|||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
|
||||||
_, err = conn.Do("HSET", redis.Args{}.Add(key).AddFlat(val)...)
|
_, err = conn.Do("HSET", redis.Args{}.Add(key).AddFlat(val)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -254,11 +253,11 @@ func (m *RedisModule) setMuchStringByExpire(mapInfo map[interface{}]interface{},
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if serr!=nil {
|
if serr != nil {
|
||||||
log.Error("setMuchStringByExpire fail,reason:%v", serr)
|
log.Error("setMuchStringByExpire fail,reason:%v", serr)
|
||||||
conn.Do("DISCARD")
|
conn.Do("DISCARD")
|
||||||
return serr
|
return serr
|
||||||
}else{
|
} else {
|
||||||
_, err = conn.Do("EXEC")
|
_, err = conn.Do("EXEC")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +286,7 @@ func (m *RedisModule) GetString(key interface{}) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return redis.String(ret,nil)
|
return redis.String(ret, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RedisModule) GetStringJSON(key string, st interface{}) error {
|
func (m *RedisModule) GetStringJSON(key string, st interface{}) error {
|
||||||
@@ -345,7 +344,7 @@ func (m *RedisModule) GetStringMap(keys []string) (retMap map[string]string, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetMuchString fail,reason:%v", err)
|
log.Error("GetMuchString fail,reason:%v", err)
|
||||||
conn.Do("DISCARD")
|
conn.Do("DISCARD")
|
||||||
return nil,err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,7 +441,7 @@ func (m *RedisModule) DelStringKeyList(keys []interface{}) (map[interface{}]bool
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("DelMuchString fail,reason:%v", err)
|
log.Error("DelMuchString fail,reason:%v", err)
|
||||||
conn.Do("DISCARD")
|
conn.Do("DISCARD")
|
||||||
return nil,err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 执行命令
|
// 执行命令
|
||||||
@@ -491,7 +490,7 @@ func (m *RedisModule) SetHash(redisKey, hashKey, value interface{}) error {
|
|||||||
return retErr
|
return retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
//GetRedisAllHashJSON ...
|
// GetRedisAllHashJSON ...
|
||||||
func (m *RedisModule) GetAllHashJSON(redisKey string) (map[string]string, error) {
|
func (m *RedisModule) GetAllHashJSON(redisKey string) (map[string]string, error) {
|
||||||
if redisKey == "" {
|
if redisKey == "" {
|
||||||
return nil, errors.New("Key Is Empty")
|
return nil, errors.New("Key Is Empty")
|
||||||
@@ -531,7 +530,7 @@ func (m *RedisModule) GetHash(redisKey interface{}, fieldKey interface{}) (strin
|
|||||||
return "", errors.New("Reids Get Hash nil")
|
return "", errors.New("Reids Get Hash nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
return redis.String(value,nil)
|
return redis.String(value, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RedisModule) GetMuchHash(args ...interface{}) ([]string, error) {
|
func (m *RedisModule) GetMuchHash(args ...interface{}) ([]string, error) {
|
||||||
@@ -556,7 +555,7 @@ func (m *RedisModule) GetMuchHash(args ...interface{}) ([]string, error) {
|
|||||||
|
|
||||||
valueList := value.([]interface{})
|
valueList := value.([]interface{})
|
||||||
retList := []string{}
|
retList := []string{}
|
||||||
for _, valueItem := range valueList{
|
for _, valueItem := range valueList {
|
||||||
valueByte, ok := valueItem.([]byte)
|
valueByte, ok := valueItem.([]byte)
|
||||||
if !ok {
|
if !ok {
|
||||||
retList = append(retList, "")
|
retList = append(retList, "")
|
||||||
@@ -618,8 +617,8 @@ func (m *RedisModule) SetHashMapJSON(redisKey string, mapFieldValue map[interfac
|
|||||||
for symbol, val := range mapFieldValue {
|
for symbol, val := range mapFieldValue {
|
||||||
temp, err := json.Marshal(val)
|
temp, err := json.Marshal(val)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_,err = conn.Do("HSET", redisKey, symbol, temp)
|
_, err = conn.Do("HSET", redisKey, symbol, temp)
|
||||||
if err!=nil {
|
if err != nil {
|
||||||
log.Error("SetMuchHashJSON fail,reason:%v", err)
|
log.Error("SetMuchHashJSON fail,reason:%v", err)
|
||||||
conn.Send("DISCARD")
|
conn.Send("DISCARD")
|
||||||
return err
|
return err
|
||||||
@@ -650,25 +649,25 @@ func (m *RedisModule) DelHash(args ...interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *RedisModule) LPushList(args ...interface{}) error {
|
func (m *RedisModule) LPushList(args ...interface{}) error {
|
||||||
err := m.setListPush("LPUSH",args...)
|
err := m.setListPush("LPUSH", args...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RedisModule) LPushListJSON(key interface{}, value ...interface{}) error {
|
func (m *RedisModule) LPushListJSON(key interface{}, value ...interface{}) error {
|
||||||
return m.setListJSONPush("LPUSH",key,value...)
|
return m.setListJSONPush("LPUSH", key, value...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RedisModule) RPushList(args ...interface{}) error {
|
func (m *RedisModule) RPushList(args ...interface{}) error {
|
||||||
err := m.setListPush("RPUSH",args...)
|
err := m.setListPush("RPUSH", args...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RedisModule) RPushListJSON(key interface{}, value ...interface{}) error {
|
func (m *RedisModule) RPushListJSON(key interface{}, value ...interface{}) error {
|
||||||
return m.setListJSONPush("RPUSH",key,value...)
|
return m.setListJSONPush("RPUSH", key, value...)
|
||||||
}
|
}
|
||||||
|
|
||||||
//LPUSH和RPUSH
|
// LPUSH和RPUSH
|
||||||
func (m *RedisModule) setListPush(setType string,args...interface{}) error {
|
func (m *RedisModule) setListPush(setType string, args ...interface{}) error {
|
||||||
if setType != "LPUSH" && setType != "RPUSH" {
|
if setType != "LPUSH" && setType != "RPUSH" {
|
||||||
return errors.New("Redis List Push Type Error,Must Be LPUSH or RPUSH")
|
return errors.New("Redis List Push Type Error,Must Be LPUSH or RPUSH")
|
||||||
}
|
}
|
||||||
@@ -685,17 +684,17 @@ func (m *RedisModule) setListPush(setType string,args...interface{}) error {
|
|||||||
return retErr
|
return retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RedisModule) setListJSONPush(setType string,key interface{}, value ...interface{}) error {
|
func (m *RedisModule) setListJSONPush(setType string, key interface{}, value ...interface{}) error {
|
||||||
args := []interface{}{key}
|
args := []interface{}{key}
|
||||||
for _,v := range value{
|
for _, v := range value {
|
||||||
jData, err := json.Marshal(v)
|
jData, err := json.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
args = append(args,string(jData))
|
args = append(args, string(jData))
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.setListPush(setType,args...)
|
return m.setListPush(setType, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lrange ...
|
// Lrange ...
|
||||||
@@ -715,7 +714,7 @@ func (m *RedisModule) LRangeList(key string, start, end int) ([]string, error) {
|
|||||||
return redis.Strings(reply, err)
|
return redis.Strings(reply, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取List的长度
|
// 获取List的长度
|
||||||
func (m *RedisModule) GetListLen(key string) (int, error) {
|
func (m *RedisModule) GetListLen(key string) (int, error) {
|
||||||
conn, err := m.getConn()
|
conn, err := m.getConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -731,11 +730,11 @@ func (m *RedisModule) GetListLen(key string) (int, error) {
|
|||||||
return redis.Int(reply, err)
|
return redis.Int(reply, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//弹出List最后条记录
|
// 弹出List最后条记录
|
||||||
func (m *RedisModule) RPOPListValue(key string) (string,error) {
|
func (m *RedisModule) RPOPListValue(key string) (string, error) {
|
||||||
conn, err := m.getConn()
|
conn, err := m.getConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "",err
|
return "", err
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
@@ -783,7 +782,7 @@ func (m *RedisModule) LRange(key string, start, stop int) ([]byte, error) {
|
|||||||
return makeListJson(reply.([]interface{}), false), nil
|
return makeListJson(reply.([]interface{}), false), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//弹出list(消息队列)数据,数据放入out fromLeft表示是否从左侧弹出 block表示是否阻塞 timeout表示阻塞超时
|
// 弹出list(消息队列)数据,数据放入out fromLeft表示是否从左侧弹出 block表示是否阻塞 timeout表示阻塞超时
|
||||||
func (m *RedisModule) ListPopJson(key string, fromLeft, block bool, timeout int, out interface{}) error {
|
func (m *RedisModule) ListPopJson(key string, fromLeft, block bool, timeout int, out interface{}) error {
|
||||||
b, err := m.ListPop(key, fromLeft, block, timeout)
|
b, err := m.ListPop(key, fromLeft, block, timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -796,7 +795,7 @@ func (m *RedisModule) ListPopJson(key string, fromLeft, block bool, timeout int,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//弹出list(消息队列)数据 fromLeft表示是否从左侧弹出 block表示是否阻塞 timeout表示阻塞超时
|
// 弹出list(消息队列)数据 fromLeft表示是否从左侧弹出 block表示是否阻塞 timeout表示阻塞超时
|
||||||
func (m *RedisModule) ListPop(key string, fromLeft, block bool, timeout int) ([]byte, error) {
|
func (m *RedisModule) ListPop(key string, fromLeft, block bool, timeout int) ([]byte, error) {
|
||||||
cmd := ""
|
cmd := ""
|
||||||
if fromLeft {
|
if fromLeft {
|
||||||
@@ -838,7 +837,7 @@ func (m *RedisModule) ListPop(key string, fromLeft, block bool, timeout int) ([]
|
|||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//有序集合插入Json
|
// 有序集合插入Json
|
||||||
func (m *RedisModule) ZADDInsertJson(key string, score float64, value interface{}) error {
|
func (m *RedisModule) ZADDInsertJson(key string, score float64, value interface{}) error {
|
||||||
|
|
||||||
conn, err := m.getConn()
|
conn, err := m.getConn()
|
||||||
@@ -858,7 +857,7 @@ func (m *RedisModule) ZADDInsertJson(key string, score float64, value interface{
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//有序集合插入
|
// 有序集合插入
|
||||||
func (m *RedisModule) ZADDInsert(key string, score float64, Data interface{}) error {
|
func (m *RedisModule) ZADDInsert(key string, score float64, Data interface{}) error {
|
||||||
conn, err := m.getConn()
|
conn, err := m.getConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -898,7 +897,7 @@ func (m *RedisModule) ZRangeJSON(key string, start, stop int, ascend bool, withS
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//取有序set指定排名 ascend=true表示按升序遍历 否则按降序遍历
|
// 取有序set指定排名 ascend=true表示按升序遍历 否则按降序遍历
|
||||||
func (m *RedisModule) ZRange(key string, start, stop int, ascend bool, withScores bool) ([]byte, error) {
|
func (m *RedisModule) ZRange(key string, start, stop int, ascend bool, withScores bool) ([]byte, error) {
|
||||||
conn, err := m.getConn()
|
conn, err := m.getConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -922,7 +921,7 @@ func (m *RedisModule) ZRange(key string, start, stop int, ascend bool, withScore
|
|||||||
return makeListJson(reply.([]interface{}), withScores), nil
|
return makeListJson(reply.([]interface{}), withScores), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取有序集合长度
|
// 获取有序集合长度
|
||||||
func (m *RedisModule) Zcard(key string) (int, error) {
|
func (m *RedisModule) Zcard(key string) (int, error) {
|
||||||
conn, err := m.getConn()
|
conn, err := m.getConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -937,7 +936,7 @@ func (m *RedisModule) Zcard(key string) (int, error) {
|
|||||||
return int(reply.(int64)), nil
|
return int(reply.(int64)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//["123","234"]
|
// ["123","234"]
|
||||||
func makeListJson(redisReply []interface{}, withScores bool) []byte {
|
func makeListJson(redisReply []interface{}, withScores bool) []byte {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteString("[")
|
buf.WriteString("[")
|
||||||
@@ -1006,7 +1005,7 @@ func (m *RedisModule) ZRangeByScore(key string, start, stop float64, ascend bool
|
|||||||
return makeListJson(reply.([]interface{}), withScores), nil
|
return makeListJson(reply.([]interface{}), withScores), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取指定member的排名
|
// 获取指定member的排名
|
||||||
func (m *RedisModule) ZScore(key string, member interface{}) (float64, error) {
|
func (m *RedisModule) ZScore(key string, member interface{}) (float64, error) {
|
||||||
conn, err := m.getConn()
|
conn, err := m.getConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1022,7 +1021,7 @@ func (m *RedisModule) ZScore(key string, member interface{}) (float64, error) {
|
|||||||
return redis.Float64(reply, err)
|
return redis.Float64(reply, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取指定member的排名
|
// 获取指定member的排名
|
||||||
func (m *RedisModule) ZRank(key string, member interface{}, ascend bool) (int, error) {
|
func (m *RedisModule) ZRank(key string, member interface{}, ascend bool) (int, error) {
|
||||||
conn, err := m.getConn()
|
conn, err := m.getConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1100,17 +1099,17 @@ func (m *RedisModule) HincrbyHashInt(redisKey, hashKey string, value int) error
|
|||||||
func (m *RedisModule) EXPlREInsert(key string, TTl int) error {
|
func (m *RedisModule) EXPlREInsert(key string, TTl int) error {
|
||||||
conn, err := m.getConn()
|
conn, err := m.getConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
_, err = conn.Do("expire", key, TTl)
|
_, err = conn.Do("expire", key, TTl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("expire fail,reason:%v", err)
|
log.Error("expire fail,reason:%v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RedisModule) Zremrangebyrank(redisKey string, start, end interface{}) (int, error) {
|
func (m *RedisModule) Zremrangebyrank(redisKey string, start, end interface{}) (int, error) {
|
||||||
conn, err := m.getConn()
|
conn, err := m.getConn()
|
||||||
@@ -1151,3 +1150,9 @@ func (m *RedisModule) Keys(key string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
return strs, nil
|
return strs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *RedisModule) OnRelease() {
|
||||||
|
if m.redisPool != nil {
|
||||||
|
m.redisPool.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/duanhf2012/origin/util/uuid"
|
"github.com/duanhf2012/origin/util/uuid"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -17,13 +16,13 @@ import (
|
|||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
|
||||||
var DefaultReadTimeout time.Duration = time.Second*10
|
var DefaultReadTimeout time.Duration = time.Second * 10
|
||||||
var DefaultWriteTimeout time.Duration = time.Second*10
|
var DefaultWriteTimeout time.Duration = time.Second * 10
|
||||||
var DefaultProcessTimeout time.Duration = time.Second*10
|
var DefaultProcessTimeout time.Duration = time.Second * 10
|
||||||
|
|
||||||
//http redirect
|
//http redirect
|
||||||
type HttpRedirectData struct {
|
type HttpRedirectData struct {
|
||||||
Url string
|
Url string
|
||||||
CookieList []*http.Cookie
|
CookieList []*http.Cookie
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +43,7 @@ type routerMatchData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type routerServeFileData struct {
|
type routerServeFileData struct {
|
||||||
matchUrl string
|
matchUrl string
|
||||||
localPath string
|
localPath string
|
||||||
method HTTP_METHOD
|
method HTTP_METHOD
|
||||||
}
|
}
|
||||||
@@ -56,45 +55,45 @@ type IHttpRouter interface {
|
|||||||
|
|
||||||
SetServeFile(method HTTP_METHOD, urlpath string, dirname string) error
|
SetServeFile(method HTTP_METHOD, urlpath string, dirname string) error
|
||||||
SetFormFileKey(formFileKey string)
|
SetFormFileKey(formFileKey string)
|
||||||
GetFormFileKey()string
|
GetFormFileKey() string
|
||||||
AddHttpFiltrate(FiltrateFun HttpFiltrate) bool
|
AddHttpFiltrate(FiltrateFun HttpFiltrate) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type HttpRouter struct {
|
type HttpRouter struct {
|
||||||
pathRouter map[HTTP_METHOD] map[string] routerMatchData //url地址,对应本service地址
|
pathRouter map[HTTP_METHOD]map[string]routerMatchData //url地址,对应本service地址
|
||||||
serveFileData map[string] *routerServeFileData
|
serveFileData map[string]*routerServeFileData
|
||||||
httpFiltrateList [] HttpFiltrate
|
httpFiltrateList []HttpFiltrate
|
||||||
|
|
||||||
formFileKey string
|
formFileKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
type HttpSession struct {
|
type HttpSession struct {
|
||||||
httpRouter IHttpRouter
|
httpRouter IHttpRouter
|
||||||
r *http.Request
|
r *http.Request
|
||||||
w http.ResponseWriter
|
w http.ResponseWriter
|
||||||
|
|
||||||
//parse result
|
//parse result
|
||||||
mapParam map[string]string
|
mapParam map[string]string
|
||||||
body []byte
|
body []byte
|
||||||
|
|
||||||
//processor result
|
//processor result
|
||||||
statusCode int
|
statusCode int
|
||||||
msg []byte
|
msg []byte
|
||||||
fileData *routerServeFileData
|
fileData *routerServeFileData
|
||||||
redirectData *HttpRedirectData
|
redirectData *HttpRedirectData
|
||||||
sessionDone chan *HttpSession
|
sessionDone chan *HttpSession
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type HttpService struct {
|
type HttpService struct {
|
||||||
service.Service
|
service.Service
|
||||||
|
|
||||||
httpServer network.HttpServer
|
httpServer network.HttpServer
|
||||||
postAliasUrl map[HTTP_METHOD] map[string]routerMatchData //url地址,对应本service地址
|
postAliasUrl map[HTTP_METHOD]map[string]routerMatchData //url地址,对应本service地址
|
||||||
httpRouter IHttpRouter
|
httpRouter IHttpRouter
|
||||||
listenAddr string
|
listenAddr string
|
||||||
corsHeader *CORSHeader
|
corsHeader *CORSHeader
|
||||||
processTimeout time.Duration
|
processTimeout time.Duration
|
||||||
|
manualStart bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type HttpFiltrate func(session *HttpSession) bool //true is pass
|
type HttpFiltrate func(session *HttpSession) bool //true is pass
|
||||||
@@ -109,16 +108,20 @@ func (httpService *HttpService) AddFiltrate(FiltrateFun HttpFiltrate) bool {
|
|||||||
|
|
||||||
func NewHttpHttpRouter() IHttpRouter {
|
func NewHttpHttpRouter() IHttpRouter {
|
||||||
httpRouter := &HttpRouter{}
|
httpRouter := &HttpRouter{}
|
||||||
httpRouter.pathRouter =map[HTTP_METHOD] map[string] routerMatchData{}
|
httpRouter.pathRouter = map[HTTP_METHOD]map[string]routerMatchData{}
|
||||||
httpRouter.serveFileData = map[string] *routerServeFileData{}
|
httpRouter.serveFileData = map[string]*routerServeFileData{}
|
||||||
httpRouter.formFileKey = "file"
|
httpRouter.formFileKey = "file"
|
||||||
for i:=METHOD_NONE+1;i<METHOD_INVALID;i++{
|
for i := METHOD_NONE + 1; i < METHOD_INVALID; i++ {
|
||||||
httpRouter.pathRouter[i] = map[string] routerMatchData{}
|
httpRouter.pathRouter[i] = map[string]routerMatchData{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return httpRouter
|
return httpRouter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (slf *HttpSession) GetRawQuery() string{
|
||||||
|
return slf.r.URL.RawQuery
|
||||||
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) Query(key string) (string, bool) {
|
func (slf *HttpSession) Query(key string) (string, bool) {
|
||||||
if slf.mapParam == nil {
|
if slf.mapParam == nil {
|
||||||
slf.mapParam = make(map[string]string)
|
slf.mapParam = make(map[string]string)
|
||||||
@@ -137,7 +140,7 @@ func (slf *HttpSession) Query(key string) (string, bool) {
|
|||||||
return ret, ok
|
return ret, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) GetBody() []byte{
|
func (slf *HttpSession) GetBody() []byte {
|
||||||
return slf.body
|
return slf.body
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,19 +148,19 @@ func (slf *HttpSession) GetMethod() HTTP_METHOD {
|
|||||||
return slf.getMethod(slf.r.Method)
|
return slf.getMethod(slf.r.Method)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) GetPath() string{
|
func (slf *HttpSession) GetPath() string {
|
||||||
return strings.Trim(slf.r.URL.Path,"/")
|
return strings.Trim(slf.r.URL.Path, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) SetHeader(key, value string) {
|
func (slf *HttpSession) SetHeader(key, value string) {
|
||||||
slf.w.Header().Set(key,value)
|
slf.w.Header().Set(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) AddHeader(key, value string) {
|
func (slf *HttpSession) AddHeader(key, value string) {
|
||||||
slf.w.Header().Add(key,value)
|
slf.w.Header().Add(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) GetHeader(key string) string{
|
func (slf *HttpSession) GetHeader(key string) string {
|
||||||
return slf.r.Header.Get(key)
|
return slf.r.Header.Get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +168,7 @@ func (slf *HttpSession) DelHeader(key string) {
|
|||||||
slf.r.Header.Del(key)
|
slf.r.Header.Del(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) WriteStatusCode(statusCode int){
|
func (slf *HttpSession) WriteStatusCode(statusCode int) {
|
||||||
slf.statusCode = statusCode
|
slf.statusCode = statusCode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,24 +176,26 @@ func (slf *HttpSession) Write(msg []byte) {
|
|||||||
slf.msg = msg
|
slf.msg = msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) WriteJsonDone(statusCode int,msgJson interface{}) error {
|
func (slf *HttpSession) WriteJsonDone(statusCode int, msgJson interface{}) error {
|
||||||
msg, err := json.Marshal(msgJson)
|
msg, err := json.Marshal(msgJson)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
slf.Write(msg)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slf.statusCode = statusCode
|
||||||
|
slf.Write(msg)
|
||||||
slf.Done()
|
slf.Done()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) flush() {
|
func (slf *HttpSession) flush() {
|
||||||
slf.w.WriteHeader(slf.statusCode)
|
slf.w.WriteHeader(slf.statusCode)
|
||||||
if slf.msg!=nil {
|
if slf.msg != nil {
|
||||||
slf.w.Write(slf.msg)
|
slf.w.Write(slf.msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) Done(){
|
func (slf *HttpSession) Done() {
|
||||||
slf.sessionDone <- slf
|
slf.sessionDone <- slf
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,15 +222,15 @@ func (slf *HttpRouter) analysisRouterUrl(url string) (string, error) {
|
|||||||
return strings.Trim(url, "/"), nil
|
return strings.Trim(url, "/"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpSession) Handle(){
|
func (slf *HttpSession) Handle() {
|
||||||
slf.httpRouter.Router(slf)
|
slf.httpRouter.Router(slf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpRouter) SetFormFileKey(formFileKey string){
|
func (slf *HttpRouter) SetFormFileKey(formFileKey string) {
|
||||||
slf.formFileKey = formFileKey
|
slf.formFileKey = formFileKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpRouter) GetFormFileKey()string{
|
func (slf *HttpRouter) GetFormFileKey() string {
|
||||||
return slf.formFileKey
|
return slf.formFileKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,19 +242,19 @@ func (slf *HttpRouter) POST(url string, handle HttpHandle) bool {
|
|||||||
return slf.regRouter(METHOD_POST, url, handle)
|
return slf.regRouter(METHOD_POST, url, handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpRouter) regRouter(method HTTP_METHOD, url string, handle HttpHandle) bool{
|
func (slf *HttpRouter) regRouter(method HTTP_METHOD, url string, handle HttpHandle) bool {
|
||||||
mapRouter,ok := slf.pathRouter[method]
|
mapRouter, ok := slf.pathRouter[method]
|
||||||
if ok == false{
|
if ok == false {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
mapRouter[strings.Trim(url,"/")] = routerMatchData{httpHandle:handle}
|
mapRouter[strings.Trim(url, "/")] = routerMatchData{httpHandle: handle}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpRouter) Router(session *HttpSession){
|
func (slf *HttpRouter) Router(session *HttpSession) {
|
||||||
if slf.httpFiltrateList!=nil {
|
if slf.httpFiltrateList != nil {
|
||||||
for _,fun := range slf.httpFiltrateList{
|
for _, fun := range slf.httpFiltrateList {
|
||||||
if fun(session) == false {
|
if fun(session) == false {
|
||||||
//session.done()
|
//session.done()
|
||||||
return
|
return
|
||||||
@@ -286,13 +291,13 @@ func (slf *HttpRouter) Router(session *HttpSession){
|
|||||||
session.Done()
|
session.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (httpService *HttpService) HttpEventHandler(ev event.IEvent) {
|
func (httpService *HttpService) HttpEventHandler(ev event.IEvent) {
|
||||||
ev.(*event.Event).Data.(*HttpSession).Handle()
|
ev.(*event.Event).Data.(*HttpSession).Handle()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (httpService *HttpService) SetHttpRouter(httpRouter IHttpRouter,eventHandler event.IEventHandler) {
|
func (httpService *HttpService) SetHttpRouter(httpRouter IHttpRouter, eventHandler event.IEventHandler) {
|
||||||
httpService.httpRouter = httpRouter
|
httpService.httpRouter = httpRouter
|
||||||
httpService.RegEventReceiverFunc(event.Sys_Event_Http_Event,eventHandler, httpService.HttpEventHandler)
|
httpService.RegEventReceiverFunc(event.Sys_Event_Http_Event, eventHandler, httpService.HttpEventHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *HttpRouter) SetServeFile(method HTTP_METHOD, urlpath string, dirname string) error {
|
func (slf *HttpRouter) SetServeFile(method HTTP_METHOD, urlpath string, dirname string) error {
|
||||||
@@ -347,68 +352,84 @@ func (httpService *HttpService) OnInit() error {
|
|||||||
if iConfig == nil {
|
if iConfig == nil {
|
||||||
return fmt.Errorf("%s service config is error!", httpService.GetName())
|
return fmt.Errorf("%s service config is error!", httpService.GetName())
|
||||||
}
|
}
|
||||||
tcpCfg := iConfig.(map[string]interface{})
|
httpCfg := iConfig.(map[string]interface{})
|
||||||
addr,ok := tcpCfg["ListenAddr"]
|
addr, ok := httpCfg["ListenAddr"]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
return fmt.Errorf("%s service config is error!", httpService.GetName())
|
return fmt.Errorf("%s service config is error!", httpService.GetName())
|
||||||
}
|
}
|
||||||
var readTimeout time.Duration = DefaultReadTimeout
|
var readTimeout time.Duration = DefaultReadTimeout
|
||||||
var writeTimeout time.Duration = DefaultWriteTimeout
|
var writeTimeout time.Duration = DefaultWriteTimeout
|
||||||
|
|
||||||
if cfgRead,ok := tcpCfg["ReadTimeout"];ok == true {
|
if cfgRead, ok := httpCfg["ReadTimeout"]; ok == true {
|
||||||
readTimeout = time.Duration(cfgRead.(float64))*time.Millisecond
|
readTimeout = time.Duration(cfgRead.(float64)) * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfgWrite,ok := tcpCfg["WriteTimeout"];ok == true {
|
if cfgWrite, ok := httpCfg["WriteTimeout"]; ok == true {
|
||||||
writeTimeout = time.Duration(cfgWrite.(float64))*time.Millisecond
|
writeTimeout = time.Duration(cfgWrite.(float64)) * time.Millisecond
|
||||||
|
}
|
||||||
|
|
||||||
|
if manualStart, ok := httpCfg["ManualStart"]; ok == true {
|
||||||
|
httpService.manualStart = manualStart.(bool)
|
||||||
|
}else{
|
||||||
|
manualStart =false
|
||||||
}
|
}
|
||||||
|
|
||||||
httpService.processTimeout = DefaultProcessTimeout
|
httpService.processTimeout = DefaultProcessTimeout
|
||||||
if cfgProcessTimeout,ok := tcpCfg["ProcessTimeout"];ok == true {
|
if cfgProcessTimeout, ok := httpCfg["ProcessTimeout"]; ok == true {
|
||||||
httpService.processTimeout = time.Duration(cfgProcessTimeout.(float64))*time.Millisecond
|
httpService.processTimeout = time.Duration(cfgProcessTimeout.(float64)) * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
||||||
httpService.httpServer.Init(addr.(string), httpService, readTimeout, writeTimeout)
|
httpService.httpServer.Init(addr.(string), httpService, readTimeout, writeTimeout)
|
||||||
//Set CAFile
|
//Set CAFile
|
||||||
caFileList,ok := tcpCfg["CAFile"]
|
caFileList, ok := httpCfg["CAFile"]
|
||||||
if ok == false {
|
if ok == false {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
iCaList := caFileList.([]interface{})
|
iCaList := caFileList.([]interface{})
|
||||||
var caFile [] network.CAFile
|
var caFile []network.CAFile
|
||||||
for _,i := range iCaList {
|
for _, i := range iCaList {
|
||||||
mapCAFile := i.(map[string]interface{})
|
mapCAFile := i.(map[string]interface{})
|
||||||
c,ok := mapCAFile["Certfile"]
|
c, ok := mapCAFile["Certfile"]
|
||||||
if ok == false{
|
if ok == false {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
k,ok := mapCAFile["Keyfile"]
|
k, ok := mapCAFile["Keyfile"]
|
||||||
if ok == false{
|
if ok == false {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.(string)!="" && k.(string)!="" {
|
if c.(string) != "" && k.(string) != "" {
|
||||||
caFile = append(caFile,network.CAFile{
|
caFile = append(caFile, network.CAFile{
|
||||||
CertFile: c.(string),
|
CertFile: c.(string),
|
||||||
Keyfile: k.(string),
|
Keyfile: k.(string),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
httpService.httpServer.SetCAFile(caFile)
|
httpService.httpServer.SetCAFile(caFile)
|
||||||
httpService.httpServer.Start()
|
|
||||||
|
if httpService.manualStart == false {
|
||||||
|
httpService.httpServer.Start()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (httpService *HttpService) StartListen() {
|
||||||
|
if httpService.manualStart {
|
||||||
|
httpService.httpServer.Start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (httpService *HttpService) SetAllowCORS(corsHeader *CORSHeader) {
|
func (httpService *HttpService) SetAllowCORS(corsHeader *CORSHeader) {
|
||||||
httpService.corsHeader = corsHeader
|
httpService.corsHeader = corsHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (httpService *HttpService) ProcessFile(session *HttpSession){
|
func (httpService *HttpService) ProcessFile(session *HttpSession) {
|
||||||
uPath := session.r.URL.Path
|
uPath := session.r.URL.Path
|
||||||
idx := strings.Index(uPath, session.fileData.matchUrl)
|
idx := strings.Index(uPath, session.fileData.matchUrl)
|
||||||
subPath := strings.Trim(uPath[idx+len(session.fileData.matchUrl):], "/")
|
subPath := strings.Trim(uPath[idx+len(session.fileData.matchUrl):], "/")
|
||||||
|
|
||||||
destLocalPath := session.fileData.localPath + "/"+subPath
|
destLocalPath := session.fileData.localPath + "/" + subPath
|
||||||
|
|
||||||
switch session.GetMethod() {
|
switch session.GetMethod() {
|
||||||
case METHOD_GET:
|
case METHOD_GET:
|
||||||
@@ -452,29 +473,29 @@ func (httpService *HttpService) ProcessFile(session *HttpSession){
|
|||||||
defer localFd.Close()
|
defer localFd.Close()
|
||||||
io.Copy(localFd, resourceFile)
|
io.Copy(localFd, resourceFile)
|
||||||
session.WriteStatusCode(http.StatusOK)
|
session.WriteStatusCode(http.StatusOK)
|
||||||
session.Write([]byte(uPath+"/"+fileName))
|
session.Write([]byte(uPath + "/" + fileName))
|
||||||
session.flush()
|
session.flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAllowCORSHeader() *CORSHeader{
|
func NewAllowCORSHeader() *CORSHeader {
|
||||||
header := &CORSHeader{}
|
header := &CORSHeader{}
|
||||||
header.AllowCORSHeader = map[string][]string{}
|
header.AllowCORSHeader = map[string][]string{}
|
||||||
header.AllowCORSHeader["Access-Control-Allow-Origin"] = []string{"*"}
|
header.AllowCORSHeader["Access-Control-Allow-Origin"] = []string{"*"}
|
||||||
header.AllowCORSHeader["Access-Control-Allow-Methods"] =[]string{ "POST, GET, OPTIONS, PUT, DELETE"}
|
header.AllowCORSHeader["Access-Control-Allow-Methods"] = []string{"POST, GET, OPTIONS, PUT, DELETE"}
|
||||||
header.AllowCORSHeader["Access-Control-Allow-Headers"] = []string{"Content-Type"}
|
header.AllowCORSHeader["Access-Control-Allow-Headers"] = []string{"Content-Type"}
|
||||||
|
|
||||||
return header
|
return header
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *CORSHeader) AddAllowHeader(key string,val string){
|
func (slf *CORSHeader) AddAllowHeader(key string, val string) {
|
||||||
slf.AllowCORSHeader["Access-Control-Allow-Headers"] = append(slf.AllowCORSHeader["Access-Control-Allow-Headers"],fmt.Sprintf("%s,%s",key,val))
|
slf.AllowCORSHeader["Access-Control-Allow-Headers"] = append(slf.AllowCORSHeader["Access-Control-Allow-Headers"], fmt.Sprintf("%s,%s", key, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *CORSHeader) copyTo(header http.Header){
|
func (slf *CORSHeader) copyTo(header http.Header) {
|
||||||
for k,v := range slf.AllowCORSHeader{
|
for k, v := range slf.AllowCORSHeader {
|
||||||
for _,val := range v{
|
for _, val := range v {
|
||||||
header.Add(k,val)
|
header.Add(k, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -489,12 +510,12 @@ func (httpService *HttpService) ServeHTTP(w http.ResponseWriter, r *http.Request
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
session := &HttpSession{sessionDone:make(chan *HttpSession,1),httpRouter:httpService.httpRouter,statusCode:http.StatusOK}
|
session := &HttpSession{sessionDone: make(chan *HttpSession, 1), httpRouter: httpService.httpRouter, statusCode: http.StatusOK}
|
||||||
session.r = r
|
session.r = r
|
||||||
session.w = w
|
session.w = w
|
||||||
|
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.WriteStatusCode(http.StatusGatewayTimeout)
|
session.WriteStatusCode(http.StatusGatewayTimeout)
|
||||||
session.flush()
|
session.flush()
|
||||||
@@ -502,19 +523,19 @@ func (httpService *HttpService) ServeHTTP(w http.ResponseWriter, r *http.Request
|
|||||||
}
|
}
|
||||||
session.body = body
|
session.body = body
|
||||||
|
|
||||||
httpService.GetEventHandler().NotifyEvent(&event.Event{Type:event.Sys_Event_Http_Event,Data:session})
|
httpService.GetEventHandler().NotifyEvent(&event.Event{Type: event.Sys_Event_Http_Event, Data: session})
|
||||||
ticker := time.NewTicker(httpService.processTimeout)
|
ticker := time.NewTicker(httpService.processTimeout)
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
session.WriteStatusCode(http.StatusGatewayTimeout)
|
session.WriteStatusCode(http.StatusGatewayTimeout)
|
||||||
session.flush()
|
session.flush()
|
||||||
break
|
break
|
||||||
case <- session.sessionDone:
|
case <-session.sessionDone:
|
||||||
if session.fileData!=nil {
|
if session.fileData != nil {
|
||||||
httpService.ProcessFile(session)
|
httpService.ProcessFile(session)
|
||||||
}else if session.redirectData!=nil {
|
} else if session.redirectData != nil {
|
||||||
session.redirects()
|
session.redirects()
|
||||||
}else{
|
} else {
|
||||||
session.flush()
|
session.flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
229
sysservice/messagequeueservice/CustomerSubscriber.go
Normal file
229
sysservice/messagequeueservice/CustomerSubscriber.go
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
package messagequeueservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/duanhf2012/origin/cluster"
|
||||||
|
"github.com/duanhf2012/origin/log"
|
||||||
|
"github.com/duanhf2012/origin/rpc"
|
||||||
|
"github.com/duanhf2012/origin/util/coroutine"
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomerSubscriber struct {
|
||||||
|
rpc.IRpcHandler
|
||||||
|
topic string
|
||||||
|
subscriber *Subscriber
|
||||||
|
fromNodeId int
|
||||||
|
callBackRpcMethod string
|
||||||
|
serviceName string
|
||||||
|
StartIndex uint64
|
||||||
|
oneBatchQuantity int32
|
||||||
|
subscribeMethod SubscribeMethod
|
||||||
|
customerId string
|
||||||
|
|
||||||
|
isStop int32 //退出标记
|
||||||
|
}
|
||||||
|
|
||||||
|
const DefaultOneBatchQuantity = 1000
|
||||||
|
|
||||||
|
type SubscribeMethod = int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
MethodCustom SubscribeMethod = 0 //自定义模式,以消费者设置的StartIndex开始获取或订阅
|
||||||
|
MethodLast SubscribeMethod = 1 //Last模式,以该消费者上次记录的位置开始订阅
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cs *CustomerSubscriber) trySetSubscriberBaseInfo(rpcHandler rpc.IRpcHandler, ss *Subscriber, topic string, subscribeMethod SubscribeMethod, customerId string, fromNodeId int, callBackRpcMethod string, startIndex uint64, oneBatchQuantity int32) error {
|
||||||
|
cs.subscriber = ss
|
||||||
|
cs.fromNodeId = fromNodeId
|
||||||
|
cs.callBackRpcMethod = callBackRpcMethod
|
||||||
|
//cs.StartIndex = startIndex
|
||||||
|
cs.subscribeMethod = subscribeMethod
|
||||||
|
cs.customerId = customerId
|
||||||
|
cs.StartIndex = startIndex
|
||||||
|
cs.topic = topic
|
||||||
|
cs.IRpcHandler = rpcHandler
|
||||||
|
if oneBatchQuantity == 0 {
|
||||||
|
cs.oneBatchQuantity = DefaultOneBatchQuantity
|
||||||
|
} else {
|
||||||
|
cs.oneBatchQuantity = oneBatchQuantity
|
||||||
|
}
|
||||||
|
|
||||||
|
strRpcMethod := strings.Split(callBackRpcMethod, ".")
|
||||||
|
if len(strRpcMethod) != 2 {
|
||||||
|
err := errors.New("RpcMethod " + callBackRpcMethod + " is error")
|
||||||
|
log.SError(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cs.serviceName = strRpcMethod[0]
|
||||||
|
|
||||||
|
if cluster.HasService(fromNodeId, cs.serviceName) == false {
|
||||||
|
err := fmt.Errorf("nodeId %d cannot found %s", fromNodeId, cs.serviceName)
|
||||||
|
log.SError(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cluster.GetCluster().IsNodeConnected(fromNodeId) == false {
|
||||||
|
err := fmt.Errorf("nodeId %d is disconnect", fromNodeId)
|
||||||
|
log.SError(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if startIndex == 0 {
|
||||||
|
now := time.Now()
|
||||||
|
zeroTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||||
|
//fmt.Println(zeroTime.Unix())
|
||||||
|
cs.StartIndex = uint64(zeroTime.Unix() << 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始订阅
|
||||||
|
func (cs *CustomerSubscriber) Subscribe(rpcHandler rpc.IRpcHandler, ss *Subscriber, topic string, subscribeMethod SubscribeMethod, customerId string, fromNodeId int, callBackRpcMethod string, startIndex uint64, oneBatchQuantity int32) error {
|
||||||
|
err := cs.trySetSubscriberBaseInfo(rpcHandler, ss, topic, subscribeMethod, customerId, fromNodeId, callBackRpcMethod, startIndex, oneBatchQuantity)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cs.subscriber.queueWait.Add(1)
|
||||||
|
coroutine.GoRecover(cs.SubscribeRun, -1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消订阅
|
||||||
|
func (cs *CustomerSubscriber) UnSubscribe() {
|
||||||
|
atomic.StoreInt32(&cs.isStop, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *CustomerSubscriber) LoadLastIndex() {
|
||||||
|
for {
|
||||||
|
if atomic.LoadInt32(&cs.isStop) != 0 {
|
||||||
|
log.SRelease("topic ", cs.topic, " out of subscription")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SRelease("customer ", cs.customerId, " start load last index ")
|
||||||
|
lastIndex, ret := cs.subscriber.dataPersist.LoadCustomerIndex(cs.topic, cs.customerId)
|
||||||
|
if ret == true {
|
||||||
|
if lastIndex > 0 {
|
||||||
|
cs.StartIndex = lastIndex
|
||||||
|
} else {
|
||||||
|
//否则直接使用客户端发回来的
|
||||||
|
}
|
||||||
|
log.SRelease("customer ", cs.customerId, " load finish,start index is ", cs.StartIndex)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SRelease("customer ", cs.customerId, " load last index is fail...")
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *CustomerSubscriber) SubscribeRun() {
|
||||||
|
defer cs.subscriber.queueWait.Done()
|
||||||
|
log.SRelease("topic ", cs.topic, " start subscription")
|
||||||
|
|
||||||
|
//加载之前的位置
|
||||||
|
if cs.subscribeMethod == MethodLast {
|
||||||
|
cs.LoadLastIndex()
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if atomic.LoadInt32(&cs.isStop) != 0 {
|
||||||
|
log.SRelease("topic ", cs.topic, " out of subscription")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if cs.checkCustomerIsValid() == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo 检测退出
|
||||||
|
if cs.subscribe() == false {
|
||||||
|
log.SRelease("topic ", cs.topic, " out of subscription")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除订阅关系
|
||||||
|
cs.subscriber.removeCustomer(cs.customerId, cs)
|
||||||
|
log.SRelease("topic ", cs.topic, " unsubscription")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *CustomerSubscriber) subscribe() bool {
|
||||||
|
//先从内存中查找
|
||||||
|
topicData, ret := cs.subscriber.queue.FindData(cs.StartIndex, cs.oneBatchQuantity)
|
||||||
|
if ret == true {
|
||||||
|
cs.publishToCustomer(topicData)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//从持久化数据中来找
|
||||||
|
topicData = cs.subscriber.dataPersist.FindTopicData(cs.topic, cs.StartIndex, int64(cs.oneBatchQuantity))
|
||||||
|
return cs.publishToCustomer(topicData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *CustomerSubscriber) checkCustomerIsValid() bool {
|
||||||
|
//1.检查nodeid是否在线,不在线,直接取消订阅
|
||||||
|
if cluster.GetCluster().IsNodeConnected(cs.fromNodeId) == false {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//2.验证是否有该服务,如果没有则退出
|
||||||
|
if cluster.HasService(cs.fromNodeId, cs.serviceName) == false {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *CustomerSubscriber) publishToCustomer(topicData []TopicData) bool {
|
||||||
|
if cs.checkCustomerIsValid() == false {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(topicData) == 0 {
|
||||||
|
//没有任何数据待一秒吧
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//3.发送失败重试发送
|
||||||
|
var dbQueuePublishReq rpc.DBQueuePublishReq
|
||||||
|
var dbQueuePushRes rpc.DBQueuePublishRes
|
||||||
|
dbQueuePublishReq.TopicName = cs.topic
|
||||||
|
cs.subscriber.dataPersist.OnPushTopicDataToCustomer(cs.topic, topicData)
|
||||||
|
for i := 0; i < len(topicData); i++ {
|
||||||
|
dbQueuePublishReq.PushData = append(dbQueuePublishReq.PushData, topicData[i].RawData)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if atomic.LoadInt32(&cs.isStop) != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if cs.checkCustomerIsValid() == false {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//推送数据
|
||||||
|
err := cs.CallNode(cs.fromNodeId, cs.callBackRpcMethod, &dbQueuePublishReq, &dbQueuePushRes)
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//持久化进度
|
||||||
|
endIndex := cs.subscriber.dataPersist.GetIndex(&topicData[len(topicData)-1])
|
||||||
|
cs.StartIndex = endIndex
|
||||||
|
cs.subscriber.dataPersist.PersistIndex(cs.topic, cs.customerId, endIndex)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
97
sysservice/messagequeueservice/MemoryQueue.go
Normal file
97
sysservice/messagequeueservice/MemoryQueue.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package messagequeueservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/duanhf2012/origin/util/algorithms"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemoryQueue struct {
|
||||||
|
subscriber *Subscriber
|
||||||
|
|
||||||
|
topicQueue []TopicData
|
||||||
|
head int32
|
||||||
|
tail int32
|
||||||
|
|
||||||
|
locker sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mq *MemoryQueue) Init(cap int32) {
|
||||||
|
mq.topicQueue = make([]TopicData, cap+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从队尾Push数据
|
||||||
|
func (mq *MemoryQueue) Push(topicData *TopicData) bool {
|
||||||
|
mq.locker.Lock()
|
||||||
|
defer mq.locker.Unlock()
|
||||||
|
|
||||||
|
nextPos := (mq.tail + 1) % int32(len(mq.topicQueue))
|
||||||
|
//如果队列满了
|
||||||
|
if nextPos == mq.head {
|
||||||
|
//将对首的数据删除掉
|
||||||
|
mq.head++
|
||||||
|
mq.head = mq.head % int32(len(mq.topicQueue))
|
||||||
|
}
|
||||||
|
|
||||||
|
mq.tail = nextPos
|
||||||
|
mq.topicQueue[mq.tail] = *topicData
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mq *MemoryQueue) findData(startPos int32, startIndex uint64, limit int32) ([]TopicData, bool) {
|
||||||
|
//空队列,无数据
|
||||||
|
if mq.head == mq.tail {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
var findStartPos int32
|
||||||
|
var findEndPos int32
|
||||||
|
findStartPos = startPos //(mq.head + 1) % cap(mq.topicQueue)
|
||||||
|
if findStartPos <= mq.tail {
|
||||||
|
findEndPos = mq.tail + 1
|
||||||
|
} else {
|
||||||
|
findEndPos = int32(cap(mq.topicQueue))
|
||||||
|
}
|
||||||
|
|
||||||
|
//二分查找位置
|
||||||
|
pos := int32(algorithms.BiSearch(mq.topicQueue[findStartPos:findEndPos], startIndex, 1))
|
||||||
|
if pos == -1 {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += findStartPos
|
||||||
|
//取得结束位置
|
||||||
|
endPos := limit + pos
|
||||||
|
if endPos > findEndPos {
|
||||||
|
endPos = findEndPos
|
||||||
|
}
|
||||||
|
|
||||||
|
return mq.topicQueue[pos:endPos], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindData 返回参数[]TopicData 表示查找到的数据,nil表示无数据。bool表示是否不应该在内存中来查
|
||||||
|
func (mq *MemoryQueue) FindData(startIndex uint64, limit int32) ([]TopicData, bool) {
|
||||||
|
mq.locker.RLock()
|
||||||
|
defer mq.locker.RUnlock()
|
||||||
|
|
||||||
|
//队列为空时,应该从数据库查找
|
||||||
|
if mq.head == mq.tail {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
//先判断startIndex是否比第一个元素要大
|
||||||
|
headTopic := (mq.head + 1) % int32(len(mq.topicQueue))
|
||||||
|
//此时需要从持久化数据中取
|
||||||
|
if startIndex+1 > mq.topicQueue[headTopic].Seq {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
retData, ret := mq.findData(mq.head+1, startIndex, limit)
|
||||||
|
if mq.head <= mq.tail || ret == true {
|
||||||
|
return retData, true
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果是正常head在后,尾在前,从数组0下标开始找到tail
|
||||||
|
return mq.findData(0, startIndex, limit)
|
||||||
|
}
|
||||||
36
sysservice/messagequeueservice/MemoryQueue_test.go
Normal file
36
sysservice/messagequeueservice/MemoryQueue_test.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package messagequeueservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type In int
|
||||||
|
|
||||||
|
func (i In) GetValue() int {
|
||||||
|
return int(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_BiSearch(t *testing.T) {
|
||||||
|
var memQueue MemoryQueue
|
||||||
|
memQueue.Init(5)
|
||||||
|
|
||||||
|
for i := 1; i <= 8; i++ {
|
||||||
|
memQueue.Push(&TopicData{Seq: uint64(i)})
|
||||||
|
}
|
||||||
|
|
||||||
|
startindex := uint64(0)
|
||||||
|
for {
|
||||||
|
retData, ret := memQueue.FindData(startindex+1, 10)
|
||||||
|
fmt.Println(retData, ret)
|
||||||
|
for _, d := range retData {
|
||||||
|
if d.Seq > startindex {
|
||||||
|
startindex = d.Seq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ret == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
126
sysservice/messagequeueservice/MessageQueueService.go
Normal file
126
sysservice/messagequeueservice/MessageQueueService.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package messagequeueservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/duanhf2012/origin/log"
|
||||||
|
"github.com/duanhf2012/origin/service"
|
||||||
|
"github.com/duanhf2012/origin/rpc"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QueueDataPersist interface {
|
||||||
|
service.IModule
|
||||||
|
|
||||||
|
OnExit()
|
||||||
|
OnReceiveTopicData(topic string, topicData []TopicData) //当收到推送过来的数据时
|
||||||
|
OnPushTopicDataToCustomer(topic string, topicData []TopicData) //当推送数据到Customer时回调
|
||||||
|
PersistTopicData(topic string, topicData []TopicData, retryCount int) ([]TopicData, bool) //持久化数据,失败则返回false,上层会重复尝试,直到成功,建议在函数中加入次数,超过次数则返回true
|
||||||
|
FindTopicData(topic string, startIndex uint64, limit int64) []TopicData //查找数据,参数bool代表数据库查找是否成功
|
||||||
|
LoadCustomerIndex(topic string, customerId string) (uint64, bool) //false时代表获取失败,一般是读取错误,会进行重试。如果不存在时,返回(0,true)
|
||||||
|
GetIndex(topicData *TopicData) uint64 //通过topic数据获取进度索引号
|
||||||
|
PersistIndex(topic string, customerId string, index uint64) //持久化进度索引号
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageQueueService struct {
|
||||||
|
service.Service
|
||||||
|
|
||||||
|
sync.Mutex
|
||||||
|
mapTopicRoom map[string]*TopicRoom
|
||||||
|
|
||||||
|
queueWait sync.WaitGroup
|
||||||
|
dataPersist QueueDataPersist
|
||||||
|
|
||||||
|
memoryQueueLen int32
|
||||||
|
maxProcessTopicBacklogNum int32 //最大积压的数据量,因为是写入到channel中,然后由协程取出再持久化,不设置有默认值100000
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MessageQueueService) OnInit() error {
|
||||||
|
ms.mapTopicRoom = map[string]*TopicRoom{}
|
||||||
|
errC := ms.ReadCfg()
|
||||||
|
if errC != nil {
|
||||||
|
return errC
|
||||||
|
}
|
||||||
|
|
||||||
|
if ms.dataPersist == nil {
|
||||||
|
return errors.New("not setup QueueDataPersist.")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := ms.AddModule(ms.dataPersist)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MessageQueueService) ReadCfg() error {
|
||||||
|
mapDBServiceCfg, ok := ms.GetService().GetServiceCfg().(map[string]interface{})
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("MessageQueueService config is error")
|
||||||
|
}
|
||||||
|
|
||||||
|
maxProcessTopicBacklogNum, ok := mapDBServiceCfg["MaxProcessTopicBacklogNum"]
|
||||||
|
if ok == false {
|
||||||
|
ms.maxProcessTopicBacklogNum = DefaultMaxTopicBacklogNum
|
||||||
|
log.SRelease("MaxProcessTopicBacklogNum config is set to the default value of ", maxProcessTopicBacklogNum)
|
||||||
|
} else {
|
||||||
|
ms.maxProcessTopicBacklogNum = int32(maxProcessTopicBacklogNum.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryQueueLen, ok := mapDBServiceCfg["MemoryQueueLen"]
|
||||||
|
if ok == false {
|
||||||
|
ms.memoryQueueLen = DefaultMemoryQueueLen
|
||||||
|
log.SRelease("MemoryQueueLen config is set to the default value of ", DefaultMemoryQueueLen)
|
||||||
|
} else {
|
||||||
|
ms.memoryQueueLen = int32(memoryQueueLen.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MessageQueueService) Setup(dataPersist QueueDataPersist) {
|
||||||
|
ms.dataPersist = dataPersist
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MessageQueueService) OnRelease() {
|
||||||
|
|
||||||
|
//停止所有的TopicRoom房间
|
||||||
|
ms.Lock()
|
||||||
|
for _, room := range ms.mapTopicRoom {
|
||||||
|
room.Stop()
|
||||||
|
}
|
||||||
|
ms.Unlock()
|
||||||
|
|
||||||
|
//释放时确保所有的协程退出
|
||||||
|
ms.queueWait.Wait()
|
||||||
|
|
||||||
|
//通知持久化对象
|
||||||
|
ms.dataPersist.OnExit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MessageQueueService) GetTopicRoom(topic string) *TopicRoom {
|
||||||
|
ms.Lock()
|
||||||
|
defer ms.Unlock()
|
||||||
|
topicRoom := ms.mapTopicRoom[topic]
|
||||||
|
if topicRoom != nil {
|
||||||
|
return topicRoom
|
||||||
|
}
|
||||||
|
|
||||||
|
topicRoom = &TopicRoom{}
|
||||||
|
topicRoom.Init(ms.maxProcessTopicBacklogNum, ms.memoryQueueLen, topic, &ms.queueWait, ms.dataPersist)
|
||||||
|
ms.mapTopicRoom[topic] = topicRoom
|
||||||
|
|
||||||
|
return topicRoom
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MessageQueueService) RPC_Publish(inParam *rpc.DBQueuePublishReq, outParam *rpc.DBQueuePublishRes) error {
|
||||||
|
|
||||||
|
topicRoom := ms.GetTopicRoom(inParam.TopicName)
|
||||||
|
return topicRoom.Publish(inParam.PushData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MessageQueueService) RPC_Subscribe(req *rpc.DBQueueSubscribeReq, res *rpc.DBQueueSubscribeRes) error {
|
||||||
|
topicRoom := ms.GetTopicRoom(req.TopicName)
|
||||||
|
return topicRoom.TopicSubscribe(ms.GetRpcHandler(), req.SubType, int32(req.Method), int(req.FromNodeId), req.RpcMethod, req.TopicName, req.CustomerId, req.StartIndex, req.OneBatchQuantity)
|
||||||
|
}
|
||||||
402
sysservice/messagequeueservice/MongoPersist.go
Normal file
402
sysservice/messagequeueservice/MongoPersist.go
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
package messagequeueservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duanhf2012/origin/log"
|
||||||
|
"github.com/duanhf2012/origin/service"
|
||||||
|
"github.com/duanhf2012/origin/sysmodule/mongodbmodule"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
"sunserver/common/util"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MaxDays = 180
|
||||||
|
|
||||||
|
type MongoPersist struct {
|
||||||
|
service.Module
|
||||||
|
mongo mongodbmodule.MongoModule
|
||||||
|
|
||||||
|
url string //连接url
|
||||||
|
dbName string //数据库名称
|
||||||
|
retryCount int //落地数据库重试次数
|
||||||
|
|
||||||
|
topic []TopicData //用于临时缓存
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomerCollectName = "SysCustomer"
|
||||||
|
|
||||||
|
func (mp *MongoPersist) OnInit() error {
|
||||||
|
if errC := mp.ReadCfg(); errC != nil {
|
||||||
|
return errC
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mp.mongo.Init(mp.url, time.Second*15)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mp.mongo.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.SError("start dbService[", mp.dbName, "], url[", mp.url, "] init error:", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//添加索引
|
||||||
|
var IndexKey [][]string
|
||||||
|
var keys []string
|
||||||
|
keys = append(keys, "Customer", "Topic")
|
||||||
|
IndexKey = append(IndexKey, keys)
|
||||||
|
s := mp.mongo.TakeSession()
|
||||||
|
if err := s.EnsureUniqueIndex(mp.dbName, CustomerCollectName, IndexKey, true, true); err != nil {
|
||||||
|
log.SError("EnsureUniqueIndex is fail ", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) ReadCfg() error {
|
||||||
|
mapDBServiceCfg, ok := mp.GetService().GetServiceCfg().(map[string]interface{})
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("MessageQueueService config is error")
|
||||||
|
}
|
||||||
|
|
||||||
|
//parse MsgRouter
|
||||||
|
url, ok := mapDBServiceCfg["Url"]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("MessageQueueService config is error")
|
||||||
|
}
|
||||||
|
mp.url = url.(string)
|
||||||
|
|
||||||
|
dbName, ok := mapDBServiceCfg["DBName"]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("MessageQueueService config is error")
|
||||||
|
}
|
||||||
|
mp.dbName = dbName.(string)
|
||||||
|
|
||||||
|
//
|
||||||
|
goroutineNum, ok := mapDBServiceCfg["RetryCount"]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("MongoPersist config is error")
|
||||||
|
}
|
||||||
|
mp.retryCount = int(goroutineNum.(float64))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) getTopicBuff(limit int) []TopicData {
|
||||||
|
if cap(mp.topic) < limit {
|
||||||
|
mp.topic = make([]TopicData, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mp.topic[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) OnExit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnReceiveTopicData 当收到推送过来的数据时
|
||||||
|
func (mp *MongoPersist) OnReceiveTopicData(topic string, topicData []TopicData) {
|
||||||
|
//1.收到推送过来的数据,在里面插入_id字段
|
||||||
|
for i := 0; i < len(topicData); i++ {
|
||||||
|
var document bson.D
|
||||||
|
err := bson.Unmarshal(topicData[i].RawData, &document)
|
||||||
|
if err != nil {
|
||||||
|
topicData[i].RawData = nil
|
||||||
|
log.SError(topic, " data Unmarshal is fail ", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
document = append(document, bson.E{Key: "_id", Value: topicData[i].Seq})
|
||||||
|
|
||||||
|
byteRet, err := bson.Marshal(document)
|
||||||
|
if err != nil {
|
||||||
|
topicData[i].RawData = nil
|
||||||
|
log.SError(topic, " data Marshal is fail ", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
topicData[i].ExtendParam = document
|
||||||
|
topicData[i].RawData = byteRet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPushTopicDataToCustomer 当推送数据到Customer时回调
|
||||||
|
func (mp *MongoPersist) OnPushTopicDataToCustomer(topic string, topicData []TopicData) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersistTopicData 持久化数据
|
||||||
|
func (mp *MongoPersist) persistTopicData(collectionName string, topicData []TopicData, retryCount int) bool {
|
||||||
|
s := mp.mongo.TakeSession()
|
||||||
|
ctx, cancel := s.GetDefaultContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var documents []interface{}
|
||||||
|
for _, tData := range topicData {
|
||||||
|
if tData.ExtendParam == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
documents = append(documents, tData.ExtendParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := s.Collection(mp.dbName, collectionName).InsertMany(ctx, documents)
|
||||||
|
if err != nil {
|
||||||
|
log.SError("PersistTopicData InsertMany fail,collect name is ", collectionName)
|
||||||
|
|
||||||
|
//失败最大重试数量
|
||||||
|
return retryCount >= mp.retryCount
|
||||||
|
}
|
||||||
|
|
||||||
|
//log.SRelease("+++++++++====", time.Now().UnixNano())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersistTopicData 持久化数据
|
||||||
|
func (mp *MongoPersist) PersistTopicData(topic string, topicData []TopicData, retryCount int) ([]TopicData, bool) {
|
||||||
|
if len(topicData) == 0 {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
preDate := topicData[0].Seq >> 32
|
||||||
|
var findPos int
|
||||||
|
for findPos = 1; findPos < len(topicData); findPos++ {
|
||||||
|
newDate := topicData[findPos].Seq >> 32
|
||||||
|
//说明换天了
|
||||||
|
if preDate != newDate {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collectName := fmt.Sprintf("%s_%s", topic, mp.GetDateByIndex(topicData[0].Seq))
|
||||||
|
ret := mp.persistTopicData(collectName, topicData[:findPos], retryCount)
|
||||||
|
//如果失败,下次重试
|
||||||
|
if ret == false {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果成功
|
||||||
|
return topicData[findPos:len(topicData)], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindTopicData 查找数据
|
||||||
|
func (mp *MongoPersist) findTopicData(topic string, startIndex uint64, limit int64) ([]TopicData, bool) {
|
||||||
|
s := mp.mongo.TakeSession()
|
||||||
|
|
||||||
|
|
||||||
|
condition := bson.D{{Key: "_id", Value: bson.D{{Key: "$gt", Value: startIndex}}}}
|
||||||
|
|
||||||
|
var findOption options.FindOptions
|
||||||
|
findOption.SetLimit(limit)
|
||||||
|
var findOptions []*options.FindOptions
|
||||||
|
findOptions = append(findOptions, &findOption)
|
||||||
|
|
||||||
|
ctx, cancel := s.GetDefaultContext()
|
||||||
|
defer cancel()
|
||||||
|
collectName := fmt.Sprintf("%s_%s", topic, mp.GetDateByIndex(startIndex))
|
||||||
|
cursor, err := s.Collection(mp.dbName, collectName).Find(ctx, condition, findOptions...)
|
||||||
|
if err != nil || cursor.Err() != nil {
|
||||||
|
if err == nil {
|
||||||
|
err = cursor.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.SError("find collect name ", topic, " is error:", err.Error())
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []interface{}
|
||||||
|
ctxAll, cancelAll := s.GetDefaultContext()
|
||||||
|
defer cancelAll()
|
||||||
|
err = cursor.All(ctxAll, &res)
|
||||||
|
if err != nil {
|
||||||
|
if err != nil {
|
||||||
|
log.SError("find collect name ", topic, " is error:", err.Error())
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
//序列化返回
|
||||||
|
topicBuff := mp.getTopicBuff(int(limit))
|
||||||
|
for i := 0; i < len(res); i++ {
|
||||||
|
rawData, errM := bson.Marshal(res[i])
|
||||||
|
if errM != nil {
|
||||||
|
if errM != nil {
|
||||||
|
log.SError("collect name ", topic, " Marshal is error:", err.Error())
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
topicBuff = append(topicBuff, TopicData{RawData: rawData})
|
||||||
|
}
|
||||||
|
|
||||||
|
return topicBuff, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) IsYesterday(startIndex uint64) (bool,string){
|
||||||
|
timeStamp := int64(startIndex>>32)
|
||||||
|
|
||||||
|
startTime := time.Unix(timeStamp, 0).AddDate(0,0,1)
|
||||||
|
nowTm := time.Now()
|
||||||
|
|
||||||
|
return startTime.Year() == nowTm.Year() && startTime.Month() == nowTm.Month()&&startTime.Day() == nowTm.Day(),nowTm.Format("20060102")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) getCollectCount(topic string,today string) (int64 ,error){
|
||||||
|
s := mp.mongo.TakeSession()
|
||||||
|
ctx, cancel := s.GetDefaultContext()
|
||||||
|
defer cancel()
|
||||||
|
collectName := fmt.Sprintf("%s_%s", topic, today)
|
||||||
|
count, err := s.Collection(mp.dbName, collectName).EstimatedDocumentCount(ctx)
|
||||||
|
return count,err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindTopicData 查找数据
|
||||||
|
func (mp *MongoPersist) FindTopicData(topic string, startIndex uint64, limit int64) []TopicData {
|
||||||
|
//某表找不到,一直往前找,找到当前置为止
|
||||||
|
for days := 1; days <= MaxDays; days++ {
|
||||||
|
//是否可以跳天
|
||||||
|
//在换天时,如果记录在其他协程还没insert完成时,因为没查到直接跳到第二天,导致漏掉数据
|
||||||
|
//解决的方法是在换天时,先判断新换的当天有没有记录,有记录时,说明昨天的数据已经插入完成,才可以跳天查询
|
||||||
|
IsJumpDays := true
|
||||||
|
|
||||||
|
//如果是昨天,先判断当天有没有表数据
|
||||||
|
bYesterday,strToday := mp.IsYesterday(startIndex)
|
||||||
|
if bYesterday {
|
||||||
|
count,err := mp.getCollectCount(topic,strToday)
|
||||||
|
if err != nil {
|
||||||
|
//失败时,重新开始
|
||||||
|
log.SError("getCollectCount ",topic,"_",strToday," is fail:",err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
//当天没有记录,则不能跳表,有可能当天还有数据
|
||||||
|
if count == 0 {
|
||||||
|
IsJumpDays = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//从startIndex开始一直往后查
|
||||||
|
topicData, isSucc := mp.findTopicData(topic, startIndex, limit)
|
||||||
|
//有数据或者数据库出错时返回,返回后,会进行下一轮的查询遍历
|
||||||
|
if len(topicData) > 0 || isSucc == false {
|
||||||
|
return topicData
|
||||||
|
}
|
||||||
|
|
||||||
|
//找不到数据时,判断当前日期是否一致
|
||||||
|
if mp.GetDateByIndex(startIndex) >= mp.GetNowTime() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
//不允许跳天,则直接跳出
|
||||||
|
if IsJumpDays == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex = mp.GetNextIndex(startIndex, days)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) GetNowTime() string {
|
||||||
|
now := time.Now()
|
||||||
|
zeroTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||||
|
return zeroTime.Format("20060102")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) GetDateByIndex(startIndex uint64) string {
|
||||||
|
startTm := int64(startIndex >> 32)
|
||||||
|
return time.Unix(startTm, 0).Format("20060102")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) GetNextIndex(startIndex uint64, addDay int) uint64 {
|
||||||
|
startTime := time.Unix(int64(startIndex>>32), 0)
|
||||||
|
dateTime := time.Date(startTime.Year(), startTime.Month(), startTime.Day(), 0, 0, 0, 0, startTime.Location())
|
||||||
|
newDateTime := dateTime.AddDate(0, 0, addDay)
|
||||||
|
nextIndex := uint64(newDateTime.Unix()) << 32
|
||||||
|
return nextIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadCustomerIndex false时代表获取失败,一般是读取错误,会进行重试。如果不存在时,返回(0,true)
|
||||||
|
func (mp *MongoPersist) LoadCustomerIndex(topic string, customerId string) (uint64, bool) {
|
||||||
|
s := mp.mongo.TakeSession()
|
||||||
|
ctx, cancel := s.GetDefaultContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
condition := bson.D{{Key: "Customer", Value: customerId}, {Key: "Topic", Value: topic}}
|
||||||
|
cursor, err := s.Collection(mp.dbName, CustomerCollectName).Find(ctx, condition)
|
||||||
|
if err != nil {
|
||||||
|
log.SError("Load topic ", topic, " customer ", customerId, " is fail:", err.Error())
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
type findRes struct {
|
||||||
|
Index uint64 `bson:"Index,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []findRes
|
||||||
|
ctxAll, cancelAll := s.GetDefaultContext()
|
||||||
|
defer cancelAll()
|
||||||
|
err = cursor.All(ctxAll, &res)
|
||||||
|
if err != nil {
|
||||||
|
log.SError("Load topic ", topic, " customer ", customerId, " is fail:", err.Error())
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) == 0 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return res[0].Index, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIndex 通过topic数据获取进度索引号
|
||||||
|
func (mp *MongoPersist) GetIndex(topicData *TopicData) uint64 {
|
||||||
|
if topicData.Seq > 0 {
|
||||||
|
return topicData.Seq
|
||||||
|
}
|
||||||
|
|
||||||
|
var document bson.D
|
||||||
|
err := bson.Unmarshal(topicData.RawData, &document)
|
||||||
|
if err != nil {
|
||||||
|
log.SError("GetIndex is fail ", err.Error())
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range document {
|
||||||
|
if e.Key == "_id" {
|
||||||
|
errC, seq := util.ConvertToNumber[uint64](e.Value)
|
||||||
|
if errC != nil {
|
||||||
|
log.Error("value is error:%s,%+v, ", errC.Error(), e.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return seq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return topicData.Seq
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersistIndex 持久化进度索引号
|
||||||
|
func (mp *MongoPersist) PersistIndex(topic string, customerId string, index uint64) {
|
||||||
|
s := mp.mongo.TakeSession()
|
||||||
|
|
||||||
|
condition := bson.D{{Key: "Customer", Value: customerId}, {Key: "Topic", Value: topic}}
|
||||||
|
upsert := bson.M{"Customer": customerId, "Topic": topic, "Index": index}
|
||||||
|
updata := bson.M{"$set": upsert}
|
||||||
|
|
||||||
|
var UpdateOptionsOpts []*options.UpdateOptions
|
||||||
|
UpdateOptionsOpts = append(UpdateOptionsOpts, options.Update().SetUpsert(true))
|
||||||
|
|
||||||
|
ctx, cancel := s.GetDefaultContext()
|
||||||
|
defer cancel()
|
||||||
|
ret, err := s.Collection(mp.dbName, CustomerCollectName).UpdateOne(ctx, condition, updata, UpdateOptionsOpts...)
|
||||||
|
fmt.Println(ret)
|
||||||
|
if err != nil {
|
||||||
|
log.SError("PersistIndex fail :", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
122
sysservice/messagequeueservice/MongoPersist_test.go
Normal file
122
sysservice/messagequeueservice/MongoPersist_test.go
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
package messagequeueservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var seq uint64
|
||||||
|
var lastTime int64
|
||||||
|
|
||||||
|
func NextSeq(addDays int) uint64 {
|
||||||
|
now := time.Now().AddDate(0, 0, addDays)
|
||||||
|
|
||||||
|
nowSec := now.Unix()
|
||||||
|
if nowSec != lastTime {
|
||||||
|
seq = 0
|
||||||
|
lastTime = nowSec
|
||||||
|
}
|
||||||
|
//必需从1开始,查询时seq>0
|
||||||
|
seq += 1
|
||||||
|
|
||||||
|
return uint64(nowSec)<<32 | uint64(seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_MongoPersist(t *testing.T) {
|
||||||
|
//1.初始化
|
||||||
|
var mongoPersist MongoPersist
|
||||||
|
mongoPersist.url = "mongodb://admin:123456@192.168.2.15:27017/?minPoolSize=5&maxPoolSize=35&maxIdleTimeMS=30000"
|
||||||
|
mongoPersist.dbName = "MongoPersistTest"
|
||||||
|
mongoPersist.retryCount = 10
|
||||||
|
mongoPersist.OnInit()
|
||||||
|
|
||||||
|
//2.
|
||||||
|
//加载索引
|
||||||
|
index, ret := mongoPersist.LoadCustomerIndex("TestTopic", "TestCustomer")
|
||||||
|
fmt.Println(index, ret)
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
zeroTime := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())
|
||||||
|
//fmt.Println(zeroTime.Unix())
|
||||||
|
startIndex := uint64(zeroTime.Unix()<<32) | 1
|
||||||
|
|
||||||
|
//存储索引
|
||||||
|
mongoPersist.PersistIndex("TestTopic", "TestCustomer", startIndex)
|
||||||
|
|
||||||
|
//加载索引
|
||||||
|
index, ret = mongoPersist.LoadCustomerIndex("TestTopic", "TestCustomer")
|
||||||
|
|
||||||
|
type RowTest struct {
|
||||||
|
Name string `bson:"Name,omitempty"`
|
||||||
|
MapTest map[int]int `bson:"MapTest,omitempty"`
|
||||||
|
Message string `bson:"Message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RowTest2 struct {
|
||||||
|
Id uint64 `bson:"_id,omitempty"`
|
||||||
|
Name string `bson:"Name,omitempty"`
|
||||||
|
MapTest map[int]int `bson:"MapTest,omitempty"`
|
||||||
|
Message string `bson:"Message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//存档
|
||||||
|
var findStartIndex uint64
|
||||||
|
var topicData []TopicData
|
||||||
|
for i := 1; i <= 1000; i++ {
|
||||||
|
|
||||||
|
var rowTest RowTest
|
||||||
|
rowTest.Name = fmt.Sprintf("Name_%d", i)
|
||||||
|
rowTest.MapTest = make(map[int]int, 1)
|
||||||
|
rowTest.MapTest[i] = i*1000 + i
|
||||||
|
rowTest.Message = fmt.Sprintf("xxxxxxxxxxxxxxxxxx%d", i)
|
||||||
|
byteRet, _ := bson.Marshal(rowTest)
|
||||||
|
|
||||||
|
var dataSeq uint64
|
||||||
|
if i <= 500 {
|
||||||
|
dataSeq = NextSeq(-1)
|
||||||
|
} else {
|
||||||
|
dataSeq = NextSeq(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
topicData = append(topicData, TopicData{RawData: byteRet, Seq: dataSeq})
|
||||||
|
|
||||||
|
if i == 1 {
|
||||||
|
findStartIndex = topicData[0].Seq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mongoPersist.OnReceiveTopicData("TestTopic", topicData)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if len(topicData) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
topicData, ret = mongoPersist.PersistTopicData("TestTopic", topicData, 1)
|
||||||
|
fmt.Println(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
for {
|
||||||
|
retTopicData := mongoPersist.FindTopicData("TestTopic", findStartIndex, 300)
|
||||||
|
for i, data := range retTopicData {
|
||||||
|
var rowTest RowTest2
|
||||||
|
bson.Unmarshal(data.RawData, &rowTest)
|
||||||
|
t.Log(rowTest.Name)
|
||||||
|
|
||||||
|
if i == len(retTopicData)-1 {
|
||||||
|
findStartIndex = mongoPersist.GetIndex(&data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("..................")
|
||||||
|
if len(retTopicData) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//t.Log(mongoPersist.GetIndex(&retTopicData[0]))
|
||||||
|
//t.Log(mongoPersist.GetIndex(&retTopicData[len(retTopicData)-1]))
|
||||||
|
}
|
||||||
91
sysservice/messagequeueservice/Subscriber.go
Normal file
91
sysservice/messagequeueservice/Subscriber.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package messagequeueservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/duanhf2012/origin/log"
|
||||||
|
"github.com/duanhf2012/origin/rpc"
|
||||||
|
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 订阅器
|
||||||
|
type Subscriber struct {
|
||||||
|
customerLocker sync.RWMutex
|
||||||
|
mapCustomer map[string]*CustomerSubscriber
|
||||||
|
queue MemoryQueue
|
||||||
|
dataPersist QueueDataPersist //对列数据处理器
|
||||||
|
queueWait *sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Subscriber) Init(memoryQueueCap int32) {
|
||||||
|
ss.queue.Init(memoryQueueCap)
|
||||||
|
ss.mapCustomer = make(map[string]*CustomerSubscriber, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Subscriber) PushTopicDataToQueue(topic string, topics []TopicData) {
|
||||||
|
for i := 0; i < len(topics); i++ {
|
||||||
|
ss.queue.Push(&topics[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Subscriber) PersistTopicData(topic string, topics []TopicData, retryCount int) ([]TopicData, bool) {
|
||||||
|
return ss.dataPersist.PersistTopicData(topic, topics, retryCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Subscriber) TopicSubscribe(rpcHandler rpc.IRpcHandler, subScribeType rpc.SubscribeType, subscribeMethod SubscribeMethod, fromNodeId int, callBackRpcMethod string, topic string, customerId string, StartIndex uint64, oneBatchQuantity int32) error {
|
||||||
|
//取消订阅时
|
||||||
|
if subScribeType == rpc.SubscribeType_Unsubscribe {
|
||||||
|
ss.UnSubscribe(customerId)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
ss.customerLocker.Lock()
|
||||||
|
customerSubscriber, ok := ss.mapCustomer[customerId]
|
||||||
|
if ok == true {
|
||||||
|
//已经订阅过,则取消订阅
|
||||||
|
customerSubscriber.UnSubscribe()
|
||||||
|
delete(ss.mapCustomer, customerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
//不存在,则订阅
|
||||||
|
customerSubscriber = &CustomerSubscriber{}
|
||||||
|
ss.mapCustomer[customerId] = customerSubscriber
|
||||||
|
ss.customerLocker.Unlock()
|
||||||
|
|
||||||
|
err := customerSubscriber.Subscribe(rpcHandler, ss, topic, subscribeMethod, customerId, fromNodeId, callBackRpcMethod, StartIndex, oneBatchQuantity)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok == true {
|
||||||
|
log.SRelease("repeat subscription for customer ", customerId)
|
||||||
|
} else {
|
||||||
|
log.SRelease("subscription for customer ", customerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Subscriber) UnSubscribe(customerId string) {
|
||||||
|
ss.customerLocker.RLocker()
|
||||||
|
defer ss.customerLocker.RUnlock()
|
||||||
|
|
||||||
|
customerSubscriber, ok := ss.mapCustomer[customerId]
|
||||||
|
if ok == false {
|
||||||
|
log.SWarning("failed to unsubscribe customer " + customerId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
customerSubscriber.UnSubscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Subscriber) removeCustomer(customerId string, cs *CustomerSubscriber) {
|
||||||
|
|
||||||
|
ss.customerLocker.Lock()
|
||||||
|
//确保删掉是当前的关系。有可能在替换订阅时,将该customer替换的情况
|
||||||
|
customer, _ := ss.mapCustomer[customerId]
|
||||||
|
if customer == cs {
|
||||||
|
delete(ss.mapCustomer, customerId)
|
||||||
|
}
|
||||||
|
ss.customerLocker.Unlock()
|
||||||
|
}
|
||||||
146
sysservice/messagequeueservice/TopicRoom.go
Normal file
146
sysservice/messagequeueservice/TopicRoom.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package messagequeueservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/duanhf2012/origin/log"
|
||||||
|
"github.com/duanhf2012/origin/util/coroutine"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TopicData struct {
|
||||||
|
Seq uint64 //序号
|
||||||
|
RawData []byte //原始数据
|
||||||
|
|
||||||
|
ExtendParam interface{} //扩展参数
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TopicData) GetValue() uint64 {
|
||||||
|
return t.Seq
|
||||||
|
}
|
||||||
|
|
||||||
|
var topicFullError = errors.New("topic room is full")
|
||||||
|
|
||||||
|
const DefaultOnceProcessTopicDataNum = 1024 //一次处理的topic数量,考虑批量落地的数量
|
||||||
|
const DefaultMaxTopicBacklogNum = 100000 //处理的channel最大数量
|
||||||
|
const DefaultMemoryQueueLen = 50000 //内存的最大长度
|
||||||
|
const maxTryPersistNum = 3000 //最大重试次数,约>5分钟
|
||||||
|
|
||||||
|
type TopicRoom struct {
|
||||||
|
topic string //主题名称
|
||||||
|
channelTopic chan TopicData //主题push过来待处理的数据
|
||||||
|
|
||||||
|
Subscriber //订阅器
|
||||||
|
|
||||||
|
//序号生成
|
||||||
|
seq uint32
|
||||||
|
lastTime int64
|
||||||
|
|
||||||
|
//onceProcessTopicDataNum int //一次处理的订阅数据最大量,方便订阅器Subscriber和QueueDataProcessor批量处理
|
||||||
|
StagingBuff []TopicData
|
||||||
|
|
||||||
|
isStop int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// maxProcessTopicBacklogNum:主题最大积压数量
|
||||||
|
func (tr *TopicRoom) Init(maxTopicBacklogNum int32, memoryQueueLen int32, topic string, queueWait *sync.WaitGroup, dataPersist QueueDataPersist) {
|
||||||
|
if maxTopicBacklogNum == 0 {
|
||||||
|
maxTopicBacklogNum = DefaultMaxTopicBacklogNum
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.channelTopic = make(chan TopicData, maxTopicBacklogNum)
|
||||||
|
tr.topic = topic
|
||||||
|
tr.dataPersist = dataPersist
|
||||||
|
tr.queueWait = queueWait
|
||||||
|
tr.StagingBuff = make([]TopicData, DefaultOnceProcessTopicDataNum)
|
||||||
|
tr.queueWait.Add(1)
|
||||||
|
tr.Subscriber.Init(memoryQueueLen)
|
||||||
|
coroutine.GoRecover(tr.topicRoomRun, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *TopicRoom) Publish(data [][]byte) error {
|
||||||
|
if len(tr.channelTopic)+len(data) > cap(tr.channelTopic) {
|
||||||
|
return topicFullError
|
||||||
|
}
|
||||||
|
|
||||||
|
//生成有序序号
|
||||||
|
for _, rawData := range data {
|
||||||
|
tr.channelTopic <- TopicData{RawData: rawData, Seq: tr.NextSeq()}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *TopicRoom) NextSeq() uint64 {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
nowSec := now.Unix()
|
||||||
|
if nowSec != tr.lastTime {
|
||||||
|
tr.seq = 0
|
||||||
|
tr.lastTime = nowSec
|
||||||
|
}
|
||||||
|
//必需从1开始,查询时seq>0
|
||||||
|
tr.seq += 1
|
||||||
|
|
||||||
|
return uint64(nowSec)<<32 | uint64(tr.seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *TopicRoom) Stop() {
|
||||||
|
atomic.StoreInt32(&tr.isStop, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *TopicRoom) topicRoomRun() {
|
||||||
|
defer tr.queueWait.Done()
|
||||||
|
|
||||||
|
log.SRelease("topic room ", tr.topic, " is running..")
|
||||||
|
for {
|
||||||
|
if atomic.LoadInt32(&tr.isStop) != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
stagingBuff := tr.StagingBuff[:0]
|
||||||
|
|
||||||
|
for i := 0; i < len(tr.channelTopic) && i < DefaultOnceProcessTopicDataNum; i++ {
|
||||||
|
topicData := <-tr.channelTopic
|
||||||
|
|
||||||
|
stagingBuff = append(stagingBuff, topicData)
|
||||||
|
}
|
||||||
|
tr.Subscriber.dataPersist.OnReceiveTopicData(tr.topic, stagingBuff)
|
||||||
|
//持久化与放内存
|
||||||
|
if len(stagingBuff) == 0 {
|
||||||
|
time.Sleep(time.Millisecond * 50)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果落地失败,最大重试maxTryPersistNum次数
|
||||||
|
var ret bool
|
||||||
|
for j := 0; j < maxTryPersistNum; {
|
||||||
|
//持久化处理
|
||||||
|
stagingBuff, ret = tr.PersistTopicData(tr.topic, stagingBuff, j+1)
|
||||||
|
//如果存档成功,并且有后续批次,则继续存档
|
||||||
|
if ret == true && len(stagingBuff) > 0 {
|
||||||
|
//二次存档不计次数
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//计数增加一次,并且等待100ms,继续重试
|
||||||
|
j += 1
|
||||||
|
if ret == false {
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.PushTopicDataToQueue(tr.topic, stagingBuff)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//将所有的订阅取消
|
||||||
|
tr.customerLocker.Lock()
|
||||||
|
for _, customer := range tr.mapCustomer {
|
||||||
|
customer.UnSubscribe()
|
||||||
|
}
|
||||||
|
tr.customerLocker.Unlock()
|
||||||
|
|
||||||
|
log.SRelease("topic room ", tr.topic, " is stop")
|
||||||
|
}
|
||||||
415
sysservice/rankservice/MongodbPersist.go
Normal file
415
sysservice/rankservice/MongodbPersist.go
Normal file
@@ -0,0 +1,415 @@
|
|||||||
|
package rankservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duanhf2012/origin/log"
|
||||||
|
"github.com/duanhf2012/origin/rpc"
|
||||||
|
"github.com/duanhf2012/origin/service"
|
||||||
|
"github.com/duanhf2012/origin/sysmodule/mongodbmodule"
|
||||||
|
"github.com/duanhf2012/origin/util/coroutine"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const batchRemoveNum = 128 //一切删除的最大数量
|
||||||
|
|
||||||
|
// RankDataDB 排行表数据
|
||||||
|
type RankDataDB struct {
|
||||||
|
Id uint64 `bson:"_id,omitempty"`
|
||||||
|
RefreshTime int64 `bson:"RefreshTime,omitempty"`
|
||||||
|
SortData []int64 `bson:"SortData,omitempty"`
|
||||||
|
Data []byte `bson:"Data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MongoPersist持久化Module
|
||||||
|
type MongoPersist struct {
|
||||||
|
service.Module
|
||||||
|
mongo mongodbmodule.MongoModule
|
||||||
|
|
||||||
|
url string //Mongodb连接url
|
||||||
|
dbName string //数据库名称
|
||||||
|
SaveInterval time.Duration //落地数据库时间间隔
|
||||||
|
|
||||||
|
sync.Mutex
|
||||||
|
mapRemoveRankData map[uint64]map[uint64]struct{} //将要删除的排行数据 map[RankId]map[Key]struct{}
|
||||||
|
mapUpsertRankData map[uint64]map[uint64]RankData //需要upsert的排行数据 map[RankId][key]RankData
|
||||||
|
|
||||||
|
mapRankSkip map[uint64]IRankSkip //所有的排行榜对象map[RankId]IRankSkip
|
||||||
|
maxRetrySaveCount int //存档重试次数
|
||||||
|
retryTimeIntervalMs time.Duration //重试时间间隔
|
||||||
|
|
||||||
|
lastSaveTime time.Time //最后一次存档时间
|
||||||
|
|
||||||
|
stop int32 //是否停服
|
||||||
|
waitGroup sync.WaitGroup //等待停服
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) OnInit() error {
|
||||||
|
mp.mapRemoveRankData = map[uint64]map[uint64]struct{}{}
|
||||||
|
mp.mapUpsertRankData = map[uint64]map[uint64]RankData{}
|
||||||
|
mp.mapRankSkip = map[uint64]IRankSkip{}
|
||||||
|
|
||||||
|
if errC := mp.ReadCfg(); errC != nil {
|
||||||
|
return errC
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化MongoDB
|
||||||
|
err := mp.mongo.Init(mp.url, time.Second*15)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//开始运行
|
||||||
|
err = mp.mongo.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.SError("start dbService[", mp.dbName, "], url[", mp.url, "] init error:", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//开启协程
|
||||||
|
coroutine.GoRecover(mp.persistCoroutine,-1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) ReadCfg() error {
|
||||||
|
mapDBServiceCfg, ok := mp.GetService().GetServiceCfg().(map[string]interface{})
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("RankService config is error")
|
||||||
|
}
|
||||||
|
|
||||||
|
//读取数据库配置
|
||||||
|
saveMongoCfg,ok := mapDBServiceCfg["SaveMongo"]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("RankService.SaveMongo config is error")
|
||||||
|
}
|
||||||
|
|
||||||
|
mongodbCfg,ok := saveMongoCfg.(map[string]interface{})
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("RankService.SaveMongo config is error")
|
||||||
|
}
|
||||||
|
|
||||||
|
url, ok := mongodbCfg["Url"]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("RankService.SaveMongo.Url config is error")
|
||||||
|
}
|
||||||
|
mp.url = url.(string)
|
||||||
|
|
||||||
|
dbName, ok := mongodbCfg["DBName"]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("RankService.SaveMongo.DBName config is error")
|
||||||
|
}
|
||||||
|
mp.dbName = dbName.(string)
|
||||||
|
|
||||||
|
saveInterval, ok := mongodbCfg["SaveIntervalMs"]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("RankService.SaveMongo.SaveIntervalMs config is error")
|
||||||
|
}
|
||||||
|
|
||||||
|
mp.SaveInterval = time.Duration(saveInterval.(float64))*time.Millisecond
|
||||||
|
|
||||||
|
maxRetrySaveCount, ok := mongodbCfg["MaxRetrySaveCount"]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("RankService.SaveMongo.MaxRetrySaveCount config is error")
|
||||||
|
}
|
||||||
|
mp.maxRetrySaveCount = int(maxRetrySaveCount.(float64))
|
||||||
|
|
||||||
|
retryTimeIntervalMs, ok := mongodbCfg["RetryTimeIntervalMs"]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("RankService.SaveMongo.RetryTimeIntervalMs config is error")
|
||||||
|
}
|
||||||
|
mp.retryTimeIntervalMs = time.Duration(retryTimeIntervalMs.(float64))*time.Millisecond
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//启服从数据库加载
|
||||||
|
func (mp *MongoPersist) OnStart() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) OnSetupRank(manual bool,rankSkip *RankSkip) error{
|
||||||
|
if mp.mapRankSkip == nil {
|
||||||
|
mp.mapRankSkip = map[uint64]IRankSkip{}
|
||||||
|
}
|
||||||
|
|
||||||
|
mp.mapRankSkip[rankSkip.GetRankID()] = rankSkip
|
||||||
|
if manual == true {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SRelease("start load rank ",rankSkip.GetRankName()," from mongodb.")
|
||||||
|
err := mp.loadFromDB(rankSkip.GetRankID(),rankSkip.GetRankName())
|
||||||
|
if err != nil {
|
||||||
|
log.SError("load from db is fail :%s",err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.SRelease("finish load rank ",rankSkip.GetRankName()," from mongodb.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) loadFromDB(rankId uint64,rankCollectName string) error{
|
||||||
|
s := mp.mongo.TakeSession()
|
||||||
|
ctx, cancel := s.GetDefaultContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
condition := bson.D{}
|
||||||
|
cursor, err := s.Collection(mp.dbName, rankCollectName).Find(ctx, condition)
|
||||||
|
if err != nil {
|
||||||
|
log.SError("find collect name ", rankCollectName, " is error:", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cursor.Err()!=nil {
|
||||||
|
log.SError("find collect name ", rankCollectName, " is error:", cursor.Err().Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rankSkip := mp.mapRankSkip[rankId]
|
||||||
|
if rankSkip == nil {
|
||||||
|
err = fmt.Errorf("rank ", rankCollectName, " is not setup:")
|
||||||
|
log.SError(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer cursor.Close(ctx)
|
||||||
|
for cursor.Next(ctx) {
|
||||||
|
var rankDataDB RankDataDB
|
||||||
|
err = cursor.Decode(&rankDataDB)
|
||||||
|
if err != nil {
|
||||||
|
log.SError(" collect name ", rankCollectName, " Decode is error:", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rankData rpc.RankData
|
||||||
|
rankData.Data = rankDataDB.Data
|
||||||
|
rankData.Key = rankDataDB.Id
|
||||||
|
rankData.SortData = rankDataDB.SortData
|
||||||
|
|
||||||
|
//更新到排行榜
|
||||||
|
rankSkip.UpsetRank(&rankData,rankDataDB.RefreshTime,true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) lazyInitRemoveMap(rankId uint64){
|
||||||
|
if mp.mapRemoveRankData[rankId] == nil {
|
||||||
|
mp.mapRemoveRankData[rankId] = make(map[uint64]struct{},256)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) lazyInitUpsertMap(rankId uint64){
|
||||||
|
if mp.mapUpsertRankData[rankId] == nil {
|
||||||
|
mp.mapUpsertRankData[rankId] = make(map[uint64]RankData,256)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) OnEnterRank(rankSkip IRankSkip, enterData *RankData){
|
||||||
|
mp.Lock()
|
||||||
|
defer mp.Unlock()
|
||||||
|
|
||||||
|
delete(mp.mapRemoveRankData,enterData.Key)
|
||||||
|
|
||||||
|
mp.lazyInitUpsertMap(rankSkip.GetRankID())
|
||||||
|
mp.mapUpsertRankData[rankSkip.GetRankID()][enterData.Key] = *enterData
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (mp *MongoPersist) OnLeaveRank(rankSkip IRankSkip, leaveData *RankData){
|
||||||
|
mp.Lock()
|
||||||
|
defer mp.Unlock()
|
||||||
|
|
||||||
|
//先删掉更新中的数据
|
||||||
|
delete(mp.mapUpsertRankData,leaveData.Key)
|
||||||
|
mp.lazyInitRemoveMap(rankSkip.GetRankID())
|
||||||
|
mp.mapRemoveRankData[rankSkip.GetRankID()][leaveData.Key] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) OnChangeRankData(rankSkip IRankSkip, changeData *RankData){
|
||||||
|
mp.Lock()
|
||||||
|
defer mp.Unlock()
|
||||||
|
|
||||||
|
//先删掉要删除的数据
|
||||||
|
delete(mp.mapRemoveRankData,changeData.Key)
|
||||||
|
|
||||||
|
//更新数据
|
||||||
|
mp.lazyInitUpsertMap(rankSkip.GetRankID())
|
||||||
|
mp.mapUpsertRankData[rankSkip.GetRankID()][changeData.Key] = *changeData
|
||||||
|
}
|
||||||
|
|
||||||
|
//停存持久化到DB
|
||||||
|
func (mp *MongoPersist) OnStop(mapRankSkip map[uint64]*RankSkip){
|
||||||
|
atomic.StoreInt32(&mp.stop,1)
|
||||||
|
mp.waitGroup.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) JugeTimeoutSave() bool{
|
||||||
|
timeout := time.Now()
|
||||||
|
isTimeOut := timeout.Sub(mp.lastSaveTime) >= mp.SaveInterval
|
||||||
|
if isTimeOut == true {
|
||||||
|
mp.lastSaveTime = timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
return isTimeOut
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) persistCoroutine(){
|
||||||
|
mp.waitGroup.Add(1)
|
||||||
|
defer mp.waitGroup.Done()
|
||||||
|
for atomic.LoadInt32(&mp.stop)==0 || mp.hasPersistData(){
|
||||||
|
//间隔时间sleep
|
||||||
|
time.Sleep(time.Second*1)
|
||||||
|
|
||||||
|
//没有持久化数据continue
|
||||||
|
if mp.hasPersistData() == false {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.JugeTimeoutSave() == false{
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//存档数据到数据库
|
||||||
|
mp.saveToDB()
|
||||||
|
}
|
||||||
|
|
||||||
|
//退出时存一次档
|
||||||
|
mp.saveToDB()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) hasPersistData() bool{
|
||||||
|
mp.Lock()
|
||||||
|
defer mp.Unlock()
|
||||||
|
|
||||||
|
return len(mp.mapUpsertRankData)>0 || len(mp.mapRemoveRankData) >0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) saveToDB(){
|
||||||
|
//1.copy数据
|
||||||
|
mp.Lock()
|
||||||
|
mapRemoveRankData := mp.mapRemoveRankData
|
||||||
|
mapUpsertRankData := mp.mapUpsertRankData
|
||||||
|
mp.mapRemoveRankData = map[uint64]map[uint64]struct{}{}
|
||||||
|
mp.mapUpsertRankData = map[uint64]map[uint64]RankData{}
|
||||||
|
mp.Unlock()
|
||||||
|
|
||||||
|
//2.存档
|
||||||
|
for len(mapUpsertRankData) > 0 {
|
||||||
|
mp.upsertRankDataToDB(mapUpsertRankData)
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(mapRemoveRankData) >0 {
|
||||||
|
mp.removeRankDataToDB(mapRemoveRankData)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) removeToDB(collectName string,keys []uint64) error{
|
||||||
|
s := mp.mongo.TakeSession()
|
||||||
|
ctx, cancel := s.GetDefaultContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
condition := bson.D{{Key: "_id", Value: bson.M{"$in": keys}}}
|
||||||
|
|
||||||
|
_, err := s.Collection(mp.dbName, collectName).DeleteMany(ctx, condition)
|
||||||
|
if err != nil {
|
||||||
|
log.SError("MongoPersist DeleteMany fail,collect name is ", collectName)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) removeRankData(rankId uint64,keys []uint64) bool {
|
||||||
|
rank := mp.mapRankSkip[rankId]
|
||||||
|
if rank== nil {
|
||||||
|
log.SError("cannot find rankId ",rankId,"config")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//不成功则重试maxRetrySaveCount次
|
||||||
|
for i:=0;i<mp.maxRetrySaveCount;i++{
|
||||||
|
if mp.removeToDB(rank.GetRankName(),keys)!= nil {
|
||||||
|
time.Sleep(mp.retryTimeIntervalMs)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) upsertToDB(collectName string,rankData *RankData) error{
|
||||||
|
condition := bson.D{{"_id", rankData.Key}}
|
||||||
|
upsert := bson.M{"_id":rankData.Key,"RefreshTime": rankData.refreshTimestamp, "SortData": rankData.SortData, "Data": rankData.Data}
|
||||||
|
update := bson.M{"$set": upsert}
|
||||||
|
|
||||||
|
s := mp.mongo.TakeSession()
|
||||||
|
ctx, cancel := s.GetDefaultContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
updateOpts := options.Update().SetUpsert(true)
|
||||||
|
_, err := s.Collection(mp.dbName, collectName).UpdateOne(ctx, condition,update,updateOpts)
|
||||||
|
if err != nil {
|
||||||
|
log.SError("MongoPersist upsertDB fail,collect name is ", collectName)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) upsertRankDataToDB(mapUpsertRankData map[uint64]map[uint64]RankData) error{
|
||||||
|
for rankId,mapRankData := range mapUpsertRankData{
|
||||||
|
rank,ok := mp.mapRankSkip[rankId]
|
||||||
|
if ok == false {
|
||||||
|
log.SError("cannot find rankId ",rankId,",config is error")
|
||||||
|
delete(mapUpsertRankData,rankId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for key,rankData := range mapRankData{
|
||||||
|
//最大重试mp.maxRetrySaveCount次
|
||||||
|
for i:=0;i<mp.maxRetrySaveCount;i++{
|
||||||
|
err := mp.upsertToDB(rank.GetRankName(),&rankData)
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(mp.retryTimeIntervalMs)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
//存完删掉指定key
|
||||||
|
delete(mapRankData,key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mapRankData) == 0 {
|
||||||
|
delete(mapUpsertRankData,rankId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MongoPersist) removeRankDataToDB(mapRemoveRankData map[uint64]map[uint64]struct{}) {
|
||||||
|
for rankId ,mapRemoveKey := range mapRemoveRankData{
|
||||||
|
//每100个一删
|
||||||
|
keyList := make([]uint64,0,batchRemoveNum)
|
||||||
|
for key := range mapRemoveKey {
|
||||||
|
delete(mapRemoveKey,key)
|
||||||
|
keyList = append(keyList,key)
|
||||||
|
if len(keyList) >= batchRemoveNum {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mp.removeRankData(rankId,keyList)
|
||||||
|
|
||||||
|
//如果删完,删掉rankid下所有
|
||||||
|
if len(mapRemoveKey) == 0 {
|
||||||
|
delete(mapRemoveRankData,rankId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
98
sysservice/rankservice/RankData.go
Normal file
98
sysservice/rankservice/RankData.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package rankservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/duanhf2012/origin/rpc"
|
||||||
|
"github.com/duanhf2012/origin/util/algorithms/skip"
|
||||||
|
"github.com/duanhf2012/origin/util/sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var emptyRankData RankData
|
||||||
|
|
||||||
|
var RankDataPool = sync.NewPoolEx(make(chan sync.IPoolData, 10240), func() sync.IPoolData {
|
||||||
|
var newRankData RankData
|
||||||
|
return &newRankData
|
||||||
|
})
|
||||||
|
|
||||||
|
type RankData struct {
|
||||||
|
*rpc.RankData
|
||||||
|
refreshTimestamp int64 //刷新时间
|
||||||
|
//bRelease bool
|
||||||
|
ref bool
|
||||||
|
compareFunc func(other skip.Comparator) int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRankData(isDec bool, data *rpc.RankData,refreshTimestamp int64) *RankData {
|
||||||
|
ret := RankDataPool.Get().(*RankData)
|
||||||
|
ret.compareFunc = ret.ascCompare
|
||||||
|
if isDec {
|
||||||
|
ret.compareFunc = ret.desCompare
|
||||||
|
}
|
||||||
|
ret.RankData = data
|
||||||
|
ret.refreshTimestamp = refreshTimestamp
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReleaseRankData(rankData *RankData) {
|
||||||
|
RankDataPool.Put(rankData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RankData) Reset() {
|
||||||
|
*p = emptyRankData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RankData) IsRef() bool {
|
||||||
|
return p.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RankData) Ref() {
|
||||||
|
p.ref = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RankData) UnRef() {
|
||||||
|
p.ref = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RankData) Compare(other skip.Comparator) int {
|
||||||
|
return p.compareFunc(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RankData) GetKey() uint64 {
|
||||||
|
return p.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RankData) ascCompare(other skip.Comparator) int {
|
||||||
|
otherRankData := other.(*RankData)
|
||||||
|
|
||||||
|
if otherRankData.Key == p.Key {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
retFlg := compareMoreThan(p.SortData, otherRankData.SortData)
|
||||||
|
if retFlg == 0 {
|
||||||
|
if p.Key > otherRankData.Key {
|
||||||
|
retFlg = 1
|
||||||
|
} else {
|
||||||
|
retFlg = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retFlg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RankData) desCompare(other skip.Comparator) int {
|
||||||
|
otherRankData := other.(*RankData)
|
||||||
|
|
||||||
|
if otherRankData.Key == p.Key {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
retFlg := compareMoreThan(otherRankData.SortData, p.SortData)
|
||||||
|
if retFlg == 0 {
|
||||||
|
if p.Key > otherRankData.Key {
|
||||||
|
retFlg = -1
|
||||||
|
} else {
|
||||||
|
retFlg = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retFlg
|
||||||
|
}
|
||||||
125
sysservice/rankservice/RankDataExpire.go
Normal file
125
sysservice/rankservice/RankDataExpire.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package rankservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/heap"
|
||||||
|
"github.com/duanhf2012/origin/util/sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var expireDataPool = sync.NewPoolEx(make(chan sync.IPoolData, 10240), func() sync.IPoolData {
|
||||||
|
return &ExpireData{}
|
||||||
|
})
|
||||||
|
|
||||||
|
type ExpireData struct {
|
||||||
|
Index int
|
||||||
|
Key uint64
|
||||||
|
RefreshTimestamp int64
|
||||||
|
ref bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type rankDataHeap struct {
|
||||||
|
rankDatas []*ExpireData
|
||||||
|
expireMs int64
|
||||||
|
mapExpireData map[uint64]*ExpireData
|
||||||
|
}
|
||||||
|
|
||||||
|
var expireData ExpireData
|
||||||
|
func (ed *ExpireData) Reset(){
|
||||||
|
*ed = expireData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ed *ExpireData) IsRef() bool{
|
||||||
|
return ed.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ed *ExpireData) Ref(){
|
||||||
|
ed.ref = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ed *ExpireData) UnRef(){
|
||||||
|
ed.ref = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *rankDataHeap) Init(maxRankDataCount int32,expireMs time.Duration){
|
||||||
|
rd.rankDatas = make([]*ExpireData,0,maxRankDataCount)
|
||||||
|
rd.expireMs = int64(expireMs)
|
||||||
|
rd.mapExpireData = make(map[uint64]*ExpireData,512)
|
||||||
|
heap.Init(rd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *rankDataHeap) Len() int {
|
||||||
|
return len(rd.rankDatas)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *rankDataHeap) Less(i, j int) bool {
|
||||||
|
return rd.rankDatas[i].RefreshTimestamp < rd.rankDatas[j].RefreshTimestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *rankDataHeap) Swap(i, j int) {
|
||||||
|
rd.rankDatas[i], rd.rankDatas[j] = rd.rankDatas[j], rd.rankDatas[i]
|
||||||
|
rd.rankDatas[i].Index,rd.rankDatas[j].Index = i,j
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *rankDataHeap) Push(x interface{}) {
|
||||||
|
ed := x.(*ExpireData)
|
||||||
|
ed.Index = len(rd.rankDatas)
|
||||||
|
rd.rankDatas = append(rd.rankDatas,ed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *rankDataHeap) Pop() (ret interface{}) {
|
||||||
|
l := len(rd.rankDatas)
|
||||||
|
var retData *ExpireData
|
||||||
|
rd.rankDatas, retData = rd.rankDatas[:l-1], rd.rankDatas[l-1]
|
||||||
|
retData.Index = -1
|
||||||
|
ret = retData
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *rankDataHeap) PopExpireKey() uint64{
|
||||||
|
if rd.Len() <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if rd.rankDatas[0].RefreshTimestamp+rd.expireMs > time.Now().UnixNano() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
rankData := heap.Pop(rd).(*ExpireData)
|
||||||
|
delete(rd.mapExpireData,rankData.Key)
|
||||||
|
|
||||||
|
return rankData.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *rankDataHeap) PushOrRefreshExpireKey(key uint64,refreshTimestamp int64){
|
||||||
|
//1.先删掉之前的
|
||||||
|
expData ,ok := rd.mapExpireData[key]
|
||||||
|
if ok == true {
|
||||||
|
expData.RefreshTimestamp = refreshTimestamp
|
||||||
|
heap.Fix(rd,expData.Index)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//2.直接插入
|
||||||
|
expData = expireDataPool.Get().(*ExpireData)
|
||||||
|
expData.Key = key
|
||||||
|
expData.RefreshTimestamp = refreshTimestamp
|
||||||
|
rd.mapExpireData[key] = expData
|
||||||
|
|
||||||
|
heap.Push(rd,expData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *rankDataHeap) RemoveExpireKey(key uint64){
|
||||||
|
expData ,ok := rd.mapExpireData[key]
|
||||||
|
if ok == false {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(rd.mapExpireData,key)
|
||||||
|
heap.Remove(rd,expData.Index)
|
||||||
|
expireDataPool.Put(expData)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
52
sysservice/rankservice/RankFunc.go
Normal file
52
sysservice/rankservice/RankFunc.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package rankservice
|
||||||
|
|
||||||
|
func transformLevel(level int32) interface{} {
|
||||||
|
switch level {
|
||||||
|
case 8:
|
||||||
|
return uint8(0)
|
||||||
|
case 16:
|
||||||
|
return uint16(0)
|
||||||
|
case 32:
|
||||||
|
return uint32(0)
|
||||||
|
case 64:
|
||||||
|
return uint64(0)
|
||||||
|
default:
|
||||||
|
return uint32(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareIsEqual(firstSortData, secondSortData []int64) bool {
|
||||||
|
firstLen := len(firstSortData)
|
||||||
|
if firstLen != len(secondSortData) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := firstLen - 1; i >= 0; i-- {
|
||||||
|
if firstSortData[i] != secondSortData[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareMoreThan(firstSortData, secondSortData []int64) int {
|
||||||
|
firstLen := len(firstSortData)
|
||||||
|
secondLen := len(secondSortData)
|
||||||
|
minLen := firstLen
|
||||||
|
if firstLen > secondLen {
|
||||||
|
minLen = secondLen
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < minLen; i++ {
|
||||||
|
if firstSortData[i] > secondSortData[i] {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if firstSortData[i] < secondSortData[i] {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
47
sysservice/rankservice/RankInterface.go
Normal file
47
sysservice/rankservice/RankInterface.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package rankservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/duanhf2012/origin/service"
|
||||||
|
"github.com/duanhf2012/origin/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RankDataChangeType int8
|
||||||
|
|
||||||
|
type IRankSkip interface {
|
||||||
|
GetRankID() uint64
|
||||||
|
GetRankName() string
|
||||||
|
GetRankLen() uint64
|
||||||
|
UpsetRank(upsetData *rpc.RankData,refreshTimestamp int64,fromLoad bool) RankDataChangeType
|
||||||
|
}
|
||||||
|
|
||||||
|
type IRankModule interface {
|
||||||
|
service.IModule
|
||||||
|
|
||||||
|
|
||||||
|
OnSetupRank(manual bool,rankSkip *RankSkip) error //当完成安装排行榜对象时
|
||||||
|
OnStart() //服务开启时回调
|
||||||
|
OnEnterRank(rankSkip IRankSkip, enterData *RankData) //进入排行
|
||||||
|
OnLeaveRank(rankSkip IRankSkip, leaveData *RankData) //离开排行
|
||||||
|
OnChangeRankData(rankSkip IRankSkip, changeData *RankData) //当排行数据变化时
|
||||||
|
OnStop(mapRankSkip map[uint64]*RankSkip) //服务结束时回调
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultRankModule struct {
|
||||||
|
service.Module
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dr *DefaultRankModule) OnStart(mapRankSkip map[uint64]*RankSkip) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dr *DefaultRankModule) OnEnterRank(rankSkip IRankSkip, enterData []*RankData) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dr *DefaultRankModule) OnLeaveRank(rankSkip IRankSkip, leaveData []*RankData) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dr *DefaultRankModule) OnChangeRankData(rankSkip IRankSkip, changeData []*RankData) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dr *DefaultRankModule) OnStop(mapRankSkip map[uint64]*RankSkip) {
|
||||||
|
}
|
||||||
223
sysservice/rankservice/RankService.go
Normal file
223
sysservice/rankservice/RankService.go
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
package rankservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duanhf2012/origin/log"
|
||||||
|
"github.com/duanhf2012/origin/rpc"
|
||||||
|
"github.com/duanhf2012/origin/service"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const PreMapRankSkipLen = 10
|
||||||
|
type RankService struct {
|
||||||
|
service.Service
|
||||||
|
|
||||||
|
mapRankSkip map[uint64]*RankSkip
|
||||||
|
rankModule IRankModule
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RankService) OnInit() error {
|
||||||
|
if rs.rankModule != nil {
|
||||||
|
_, err := rs.AddModule(rs.rankModule)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rs.AddModule(&DefaultRankModule{})
|
||||||
|
}
|
||||||
|
|
||||||
|
rs.mapRankSkip = make(map[uint64]*RankSkip, PreMapRankSkipLen)
|
||||||
|
err := rs.dealCfg()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RankService) OnStart() {
|
||||||
|
rs.rankModule.OnStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RankService) OnRelease() {
|
||||||
|
rs.rankModule.OnStop(rs.mapRankSkip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安装排行模块
|
||||||
|
func (rs *RankService) SetupRankModule(rankModule IRankModule) {
|
||||||
|
rs.rankModule = rankModule
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC_ManualAddRankSkip 提供手动添加排行榜
|
||||||
|
func (rs *RankService) RPC_ManualAddRankSkip(addInfo *rpc.AddRankList, addResult *rpc.RankResult) error {
|
||||||
|
for _, addRankListData := range addInfo.AddList {
|
||||||
|
if addRankListData.RankId == 0 {
|
||||||
|
return fmt.Errorf("RPC_AddRankSkip must has rank id")
|
||||||
|
}
|
||||||
|
|
||||||
|
//重复的排行榜信息不允许添加
|
||||||
|
rank := rs.mapRankSkip[addRankListData.RankId]
|
||||||
|
if rank != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newSkip := NewRankSkip(addRankListData.RankId,addRankListData.RankName,addRankListData.IsDec, transformLevel(addRankListData.SkipListLevel), addRankListData.MaxRank,time.Duration(addRankListData.ExpireMs)*time.Millisecond)
|
||||||
|
newSkip.SetupRankModule(rs.rankModule)
|
||||||
|
|
||||||
|
rs.mapRankSkip[addRankListData.RankId] = newSkip
|
||||||
|
rs.rankModule.OnSetupRank(true,newSkip)
|
||||||
|
}
|
||||||
|
|
||||||
|
addResult.AddCount = 1
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC_UpsetRank 更新排行榜
|
||||||
|
func (rs *RankService) RPC_UpsetRank(upsetInfo *rpc.UpsetRankData, upsetResult *rpc.RankResult) error {
|
||||||
|
rankSkip, ok := rs.mapRankSkip[upsetInfo.RankId]
|
||||||
|
if ok == false || rankSkip == nil {
|
||||||
|
return fmt.Errorf("RPC_UpsetRank[", upsetInfo.RankId, "] no this rank id")
|
||||||
|
}
|
||||||
|
|
||||||
|
addCount, updateCount := rankSkip.UpsetRankList(upsetInfo.RankDataList)
|
||||||
|
upsetResult.AddCount = addCount
|
||||||
|
upsetResult.ModifyCount = updateCount
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC_DeleteRankDataByKey 按key从排行榜中进行删除
|
||||||
|
func (rs *RankService) RPC_DeleteRankDataByKey(delInfo *rpc.DeleteByKey, delResult *rpc.RankResult) error {
|
||||||
|
rankSkip, ok := rs.mapRankSkip[delInfo.RankId]
|
||||||
|
if ok == false || rankSkip == nil {
|
||||||
|
return fmt.Errorf("RPC_DeleteRankDataByKey[", delInfo.RankId, "] no this rank type")
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCount := rankSkip.DeleteRankData(delInfo.KeyList)
|
||||||
|
if removeCount == 0 {
|
||||||
|
log.SError("remove count is zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
delResult.RemoveCount = removeCount
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC_FindRankDataByKey 按key查找,返回对应的排行名次信息
|
||||||
|
func (rs *RankService) RPC_FindRankDataByKey(findInfo *rpc.FindRankDataByKey, findResult *rpc.RankPosData) error {
|
||||||
|
rankObj, ok := rs.mapRankSkip[findInfo.RankId]
|
||||||
|
if ok == false || rankObj == nil {
|
||||||
|
return fmt.Errorf("RPC_FindRankDataByKey[", findInfo.RankId, "] no this rank type")
|
||||||
|
}
|
||||||
|
|
||||||
|
findRankData, rank := rankObj.GetRankNodeData(findInfo.Key)
|
||||||
|
if findRankData != nil {
|
||||||
|
findResult.Data = findRankData.Data
|
||||||
|
findResult.Key = findRankData.Key
|
||||||
|
findResult.SortData = findRankData.SortData
|
||||||
|
findResult.Rank = rank
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC_FindRankDataByRank 按pos查找
|
||||||
|
func (rs *RankService) RPC_FindRankDataByRank(findInfo *rpc.FindRankDataByRank, findResult *rpc.RankPosData) error {
|
||||||
|
rankObj, ok := rs.mapRankSkip[findInfo.RankId]
|
||||||
|
if ok == false || rankObj == nil {
|
||||||
|
return fmt.Errorf("RPC_FindRankDataByKey[", findInfo.RankId, "] no this rank type")
|
||||||
|
}
|
||||||
|
|
||||||
|
findRankData, rankPos := rankObj.GetRankNodeDataByRank(findInfo.Rank)
|
||||||
|
if findRankData != nil {
|
||||||
|
findResult.Data = findRankData.Data
|
||||||
|
findResult.Key = findRankData.Key
|
||||||
|
findResult.SortData = findRankData.SortData
|
||||||
|
findResult.Rank = rankPos
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC_FindRankDataList 按StartRank查找,从StartRank开始count个排行数据
|
||||||
|
func (rs *RankService) RPC_FindRankDataList(findInfo *rpc.FindRankDataList, findResult *rpc.RankDataList) error {
|
||||||
|
rankObj, ok := rs.mapRankSkip[findInfo.RankId]
|
||||||
|
if ok == false || rankObj == nil {
|
||||||
|
err := fmt.Errorf("not config rank %d",findInfo.RankId)
|
||||||
|
log.SError(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
findResult.RankDataCount = rankObj.GetRankLen()
|
||||||
|
err := rankObj.GetRankDataFromToLimit(findInfo.StartRank-1, findInfo.Count, findResult)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询附带的key
|
||||||
|
if findInfo.Key!= 0 {
|
||||||
|
findRankData, rank := rankObj.GetRankNodeData(findInfo.Key)
|
||||||
|
if findRankData != nil {
|
||||||
|
findResult.KeyRank = &rpc.RankPosData{}
|
||||||
|
findResult.KeyRank.Data = findRankData.Data
|
||||||
|
findResult.KeyRank.Key = findRankData.Key
|
||||||
|
findResult.KeyRank.SortData = findRankData.SortData
|
||||||
|
findResult.KeyRank.Rank = rank
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RankService) deleteRankList(delIdList []uint64) {
|
||||||
|
if rs.mapRankSkip == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range delIdList {
|
||||||
|
delete(rs.mapRankSkip, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RankService) dealCfg() error {
|
||||||
|
mapDBServiceCfg, ok := rs.GetServiceCfg().(map[string]interface{})
|
||||||
|
if ok == false {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgList, okList := mapDBServiceCfg["SortCfg"].([]interface{})
|
||||||
|
if okList == false {
|
||||||
|
return fmt.Errorf("RankService SortCfg must be list")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cfg := range cfgList {
|
||||||
|
mapCfg, okCfg := cfg.(map[string]interface{})
|
||||||
|
if okCfg == false {
|
||||||
|
return fmt.Errorf("RankService SortCfg data must be map or struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
rankId, okId := mapCfg["RankID"].(float64)
|
||||||
|
if okId == false || uint64(rankId)==0 {
|
||||||
|
return fmt.Errorf("RankService SortCfg data must has RankID[number]")
|
||||||
|
}
|
||||||
|
|
||||||
|
rankName, okId := mapCfg["RankName"].(string)
|
||||||
|
if okId == false || len(rankName)==0 {
|
||||||
|
return fmt.Errorf("RankService SortCfg data must has RankName[string]")
|
||||||
|
}
|
||||||
|
|
||||||
|
level, _ := mapCfg["SkipListLevel"].(float64)
|
||||||
|
isDec, _ := mapCfg["IsDec"].(bool)
|
||||||
|
maxRank, _ := mapCfg["MaxRank"].(float64)
|
||||||
|
expireMs, _ := mapCfg["ExpireMs"].(float64)
|
||||||
|
|
||||||
|
|
||||||
|
newSkip := NewRankSkip(uint64(rankId),rankName,isDec, transformLevel(int32(level)), uint64(maxRank),time.Duration(expireMs)*time.Millisecond)
|
||||||
|
newSkip.SetupRankModule(rs.rankModule)
|
||||||
|
rs.mapRankSkip[uint64(rankId)] = newSkip
|
||||||
|
err := rs.rankModule.OnSetupRank(false,newSkip)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
304
sysservice/rankservice/RankSkip.go
Normal file
304
sysservice/rankservice/RankSkip.go
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
package rankservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duanhf2012/origin/rpc"
|
||||||
|
"github.com/duanhf2012/origin/util/algorithms/skip"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RankSkip struct {
|
||||||
|
rankId uint64 //排行榜ID
|
||||||
|
rankName string //排行榜名称
|
||||||
|
isDes bool //是否为降序 true:降序 false:升序
|
||||||
|
skipList *skip.SkipList //跳表
|
||||||
|
mapRankData map[uint64]*RankData //排行数据map
|
||||||
|
maxLen uint64 //排行数据长度
|
||||||
|
expireMs time.Duration //有效时间
|
||||||
|
rankModule IRankModule
|
||||||
|
rankDataExpire rankDataHeap
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaxPickExpireNum = 128
|
||||||
|
const (
|
||||||
|
RankDataNone RankDataChangeType = 0
|
||||||
|
RankDataAdd RankDataChangeType = 1 //数据插入
|
||||||
|
RankDataUpdate RankDataChangeType = 2 //数据更新
|
||||||
|
RankDataDelete RankDataChangeType = 3 //数据删除
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewRankSkip 创建排行榜
|
||||||
|
func NewRankSkip(rankId uint64,rankName string,isDes bool, level interface{}, maxLen uint64,expireMs time.Duration) *RankSkip {
|
||||||
|
rs := &RankSkip{}
|
||||||
|
|
||||||
|
rs.rankId = rankId
|
||||||
|
rs.rankName = rankName
|
||||||
|
rs.isDes = isDes
|
||||||
|
rs.skipList = skip.New(level)
|
||||||
|
rs.mapRankData = make(map[uint64]*RankData, 10240)
|
||||||
|
rs.maxLen = maxLen
|
||||||
|
rs.expireMs = expireMs
|
||||||
|
rs.rankDataExpire.Init(int32(maxLen),expireMs)
|
||||||
|
|
||||||
|
return rs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RankSkip) pickExpireKey(){
|
||||||
|
if rs.expireMs == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i:=1;i<=MaxPickExpireNum;i++{
|
||||||
|
key := rs.rankDataExpire.PopExpireKey()
|
||||||
|
if key == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rs.DeleteRankData([]uint64{key})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RankSkip) SetupRankModule(rankModule IRankModule) {
|
||||||
|
rs.rankModule = rankModule
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRankID 获取排行榜Id
|
||||||
|
func (rs *RankSkip) GetRankID() uint64 {
|
||||||
|
return rs.rankId
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRankName 获取排行榜名称
|
||||||
|
func (rs *RankSkip) GetRankName() string {
|
||||||
|
return rs.rankName
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRankLen 获取排行榜长度
|
||||||
|
func (rs *RankSkip) GetRankLen() uint64 {
|
||||||
|
return rs.skipList.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RankSkip) UpsetRankList(upsetRankData []*rpc.RankData) (addCount int32, modifyCount int32) {
|
||||||
|
for _, upsetData := range upsetRankData {
|
||||||
|
changeType := rs.UpsetRank(upsetData,time.Now().UnixNano(),false)
|
||||||
|
if changeType == RankDataAdd{
|
||||||
|
addCount+=1
|
||||||
|
} else if changeType == RankDataUpdate{
|
||||||
|
modifyCount+=1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rs.pickExpireKey()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpsetRank 更新玩家排行数据,返回变化后的数据及变化类型
|
||||||
|
func (rs *RankSkip) UpsetRank(upsetData *rpc.RankData,refreshTimestamp int64,fromLoad bool) RankDataChangeType {
|
||||||
|
rankNode, ok := rs.mapRankData[upsetData.Key]
|
||||||
|
if ok == true {
|
||||||
|
//找到的情况对比排名数据是否有变化,无变化进行data更新,有变化则进行删除更新
|
||||||
|
if compareIsEqual(rankNode.SortData, upsetData.SortData) {
|
||||||
|
rankNode.Data = upsetData.GetData()
|
||||||
|
rankNode.refreshTimestamp = refreshTimestamp
|
||||||
|
|
||||||
|
if fromLoad == false {
|
||||||
|
rs.rankModule.OnChangeRankData(rs,rankNode)
|
||||||
|
}
|
||||||
|
rs.rankDataExpire.PushOrRefreshExpireKey(upsetData.Key,refreshTimestamp)
|
||||||
|
return RankDataUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
if upsetData.Data == nil {
|
||||||
|
upsetData.Data = rankNode.Data
|
||||||
|
}
|
||||||
|
rs.skipList.Delete(rankNode)
|
||||||
|
ReleaseRankData(rankNode)
|
||||||
|
|
||||||
|
newRankData := NewRankData(rs.isDes, upsetData,refreshTimestamp)
|
||||||
|
rs.skipList.Insert(newRankData)
|
||||||
|
rs.mapRankData[upsetData.Key] = newRankData
|
||||||
|
|
||||||
|
//刷新有效期
|
||||||
|
rs.rankDataExpire.PushOrRefreshExpireKey(upsetData.Key,refreshTimestamp)
|
||||||
|
|
||||||
|
if fromLoad == false {
|
||||||
|
rs.rankModule.OnChangeRankData(rs, newRankData)
|
||||||
|
}
|
||||||
|
return RankDataUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.checkInsertAndReplace(upsetData) {
|
||||||
|
newRankData := NewRankData(rs.isDes, upsetData,refreshTimestamp)
|
||||||
|
rs.skipList.Insert(newRankData)
|
||||||
|
rs.mapRankData[upsetData.Key] = newRankData
|
||||||
|
rs.rankDataExpire.PushOrRefreshExpireKey(upsetData.Key,refreshTimestamp)
|
||||||
|
|
||||||
|
if fromLoad == false {
|
||||||
|
rs.rankModule.OnEnterRank(rs, newRankData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return RankDataAdd
|
||||||
|
}
|
||||||
|
|
||||||
|
return RankDataNone
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRankData 删除排行数据
|
||||||
|
func (rs *RankSkip) DeleteRankData(delKeys []uint64) int32 {
|
||||||
|
var removeRankData int32
|
||||||
|
//预统计处理,进行回调
|
||||||
|
for _, key := range delKeys {
|
||||||
|
rankData, ok := rs.mapRankData[key]
|
||||||
|
if ok == false {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
removeRankData+=1
|
||||||
|
rs.skipList.Delete(rankData)
|
||||||
|
delete(rs.mapRankData, rankData.Key)
|
||||||
|
rs.rankDataExpire.RemoveExpireKey(rankData.Key)
|
||||||
|
rs.rankModule.OnLeaveRank(rs, rankData)
|
||||||
|
ReleaseRankData(rankData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return removeRankData
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRankNodeData 获取,返回排名节点与名次
|
||||||
|
func (rs *RankSkip) GetRankNodeData(findKey uint64) (*RankData, uint64) {
|
||||||
|
rankNode, ok := rs.mapRankData[findKey]
|
||||||
|
if ok == false {
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
rs.pickExpireKey()
|
||||||
|
_, index := rs.skipList.GetWithPosition(rankNode)
|
||||||
|
return rankNode, index+1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRankNodeDataByPos 获取,返回排名节点与名次
|
||||||
|
func (rs *RankSkip) GetRankNodeDataByRank(rank uint64) (*RankData, uint64) {
|
||||||
|
rs.pickExpireKey()
|
||||||
|
rankNode := rs.skipList.ByPosition(rank-1)
|
||||||
|
if rankNode == nil {
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return rankNode.(*RankData), rank
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRankKeyPrevToLimit 获取key前count名的数据
|
||||||
|
func (rs *RankSkip) GetRankKeyPrevToLimit(findKey, count uint64, result *rpc.RankDataList) error {
|
||||||
|
if rs.GetRankLen() <= 0 {
|
||||||
|
return fmt.Errorf("rank[", rs.rankId, "] no data")
|
||||||
|
}
|
||||||
|
|
||||||
|
findData, ok := rs.mapRankData[findKey]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("rank[", rs.rankId, "] no data")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, rankPos := rs.skipList.GetWithPosition(findData)
|
||||||
|
iter := rs.skipList.Iter(findData)
|
||||||
|
iterCount := uint64(0)
|
||||||
|
for iter.Prev() && iterCount < count {
|
||||||
|
rankData := iter.Value().(*RankData)
|
||||||
|
result.RankPosDataList = append(result.RankPosDataList, &rpc.RankPosData{
|
||||||
|
Key: rankData.Key,
|
||||||
|
Rank: rankPos - iterCount+1,
|
||||||
|
SortData: rankData.SortData,
|
||||||
|
Data: rankData.Data,
|
||||||
|
})
|
||||||
|
iterCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRankKeyPrevToLimit 获取key前count名的数据
|
||||||
|
func (rs *RankSkip) GetRankKeyNextToLimit(findKey, count uint64, result *rpc.RankDataList) error {
|
||||||
|
if rs.GetRankLen() <= 0 {
|
||||||
|
return fmt.Errorf("rank[", rs.rankId, "] no data")
|
||||||
|
}
|
||||||
|
|
||||||
|
findData, ok := rs.mapRankData[findKey]
|
||||||
|
if ok == false {
|
||||||
|
return fmt.Errorf("rank[", rs.rankId, "] no data")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, rankPos := rs.skipList.GetWithPosition(findData)
|
||||||
|
iter := rs.skipList.Iter(findData)
|
||||||
|
iterCount := uint64(0)
|
||||||
|
for iter.Next() && iterCount < count {
|
||||||
|
rankData := iter.Value().(*RankData)
|
||||||
|
result.RankPosDataList = append(result.RankPosDataList, &rpc.RankPosData{
|
||||||
|
Key: rankData.Key,
|
||||||
|
Rank: rankPos + iterCount+1,
|
||||||
|
SortData: rankData.SortData,
|
||||||
|
Data: rankData.Data,
|
||||||
|
})
|
||||||
|
iterCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRankList 获取排行榜数据,startPos开始的count个数据
|
||||||
|
func (rs *RankSkip) GetRankDataFromToLimit(startPos, count uint64, result *rpc.RankDataList) error {
|
||||||
|
if rs.GetRankLen() <= 0 {
|
||||||
|
//初始排行榜可能没有数据
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rs.pickExpireKey()
|
||||||
|
if result.RankDataCount < startPos {
|
||||||
|
startPos = result.RankDataCount - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
iter := rs.skipList.IterAtPosition(startPos)
|
||||||
|
iterCount := uint64(0)
|
||||||
|
for iter.Next() && iterCount < count {
|
||||||
|
rankData := iter.Value().(*RankData)
|
||||||
|
result.RankPosDataList = append(result.RankPosDataList, &rpc.RankPosData{
|
||||||
|
Key: rankData.Key,
|
||||||
|
Rank: iterCount + startPos+1,
|
||||||
|
SortData: rankData.SortData,
|
||||||
|
Data: rankData.Data,
|
||||||
|
})
|
||||||
|
iterCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkCanInsert 检查是否能插入
|
||||||
|
func (rs *RankSkip) checkInsertAndReplace(upsetData *rpc.RankData) bool {
|
||||||
|
//maxLen为0,不限制长度
|
||||||
|
if rs.maxLen == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//没有放满,则进行插入
|
||||||
|
rankLen := rs.skipList.Len()
|
||||||
|
if rs.maxLen > rankLen {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//已经放满了,进行数据比较
|
||||||
|
lastPosData := rs.skipList.ByPosition(rankLen - 1)
|
||||||
|
lastRankData := lastPosData.(*RankData)
|
||||||
|
moreThanFlag := compareMoreThan(upsetData.SortData, lastRankData.SortData)
|
||||||
|
//降序排列,比最后一位小,不能插入 升序排列,比最后一位大,不能插入
|
||||||
|
if (rs.isDes == true && moreThanFlag < 0) || (rs.isDes == false && moreThanFlag > 0) || moreThanFlag == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//移除最后一位
|
||||||
|
//回调模块,该RandData从排行中删除
|
||||||
|
rs.rankDataExpire.RemoveExpireKey(lastRankData.Key)
|
||||||
|
rs.rankModule.OnLeaveRank(rs, lastRankData)
|
||||||
|
rs.skipList.Delete(lastPosData)
|
||||||
|
delete(rs.mapRankData, lastRankData.Key)
|
||||||
|
ReleaseRankData(lastRankData)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/duanhf2012/origin/network/processor"
|
"github.com/duanhf2012/origin/network/processor"
|
||||||
"github.com/duanhf2012/origin/node"
|
"github.com/duanhf2012/origin/node"
|
||||||
"github.com/duanhf2012/origin/service"
|
"github.com/duanhf2012/origin/service"
|
||||||
|
"sync/atomic"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -20,9 +21,6 @@ type TcpService struct {
|
|||||||
mapClientLocker sync.RWMutex
|
mapClientLocker sync.RWMutex
|
||||||
mapClient map[uint64] *Client
|
mapClient map[uint64] *Client
|
||||||
process processor.IProcessor
|
process processor.IProcessor
|
||||||
|
|
||||||
ReadDeadline time.Duration
|
|
||||||
WriteDeadline time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TcpPackType int8
|
type TcpPackType int8
|
||||||
@@ -33,21 +31,13 @@ const(
|
|||||||
TPT_UnknownPack TcpPackType = 3
|
TPT_UnknownPack TcpPackType = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
const Default_MaxConnNum = 3000
|
|
||||||
const Default_PendingWriteNum = 10000
|
|
||||||
const Default_LittleEndian = false
|
|
||||||
const Default_MinMsgLen = 2
|
|
||||||
const Default_MaxMsgLen = 65535
|
|
||||||
const Default_ReadDeadline = 180 //30s
|
|
||||||
const Default_WriteDeadline = 180 //30s
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MaxNodeId = 1<<10 - 1 //Uint10
|
MaxNodeId = 1<<14 - 1 //最大值 16383
|
||||||
MaxSeed = 1<<22 - 1 //MaxUint24
|
MaxSeed = 1<<19 - 1 //最大值 524287
|
||||||
|
MaxTime = 1<<31 - 1 //最大值 2147483647
|
||||||
)
|
)
|
||||||
|
|
||||||
var seed uint32
|
var seed uint32
|
||||||
var seedLocker sync.Mutex
|
|
||||||
|
|
||||||
type TcpPack struct {
|
type TcpPack struct {
|
||||||
Type TcpPackType //0表示连接 1表示断开 2表示数据
|
Type TcpPackType //0表示连接 1表示断开 2表示数据
|
||||||
@@ -66,16 +56,14 @@ func (tcpService *TcpService) genId() uint64 {
|
|||||||
panic("nodeId exceeds the maximum!")
|
panic("nodeId exceeds the maximum!")
|
||||||
}
|
}
|
||||||
|
|
||||||
seedLocker.Lock()
|
newSeed := atomic.AddUint32(&seed,1) % MaxSeed
|
||||||
seed = (seed+1)%MaxSeed
|
nowTime := uint64(time.Now().Unix())%MaxTime
|
||||||
seedLocker.Unlock()
|
return (uint64(node.GetNodeId())<<50)|(nowTime<<19)|uint64(newSeed)
|
||||||
|
|
||||||
nowTime := uint64(time.Now().Second())
|
|
||||||
return (uint64(node.GetNodeId())<<54)|(nowTime<<22)|uint64(seed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func GetNodeId(agentId uint64) int {
|
func GetNodeId(agentId uint64) int {
|
||||||
return int(agentId>>54)
|
return int(agentId>>50)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tcpService *TcpService) OnInit() error{
|
func (tcpService *TcpService) OnInit() error{
|
||||||
@@ -90,14 +78,6 @@ func (tcpService *TcpService) OnInit() error{
|
|||||||
}
|
}
|
||||||
|
|
||||||
tcpService.tcpServer.Addr = addr.(string)
|
tcpService.tcpServer.Addr = addr.(string)
|
||||||
tcpService.tcpServer.MaxConnNum = Default_MaxConnNum
|
|
||||||
tcpService.tcpServer.PendingWriteNum = Default_PendingWriteNum
|
|
||||||
tcpService.tcpServer.LittleEndian = Default_LittleEndian
|
|
||||||
tcpService.tcpServer.MinMsgLen = Default_MinMsgLen
|
|
||||||
tcpService.tcpServer.MaxMsgLen = Default_MaxMsgLen
|
|
||||||
tcpService.ReadDeadline = Default_ReadDeadline
|
|
||||||
tcpService.WriteDeadline = Default_WriteDeadline
|
|
||||||
|
|
||||||
MaxConnNum,ok := tcpCfg["MaxConnNum"]
|
MaxConnNum,ok := tcpCfg["MaxConnNum"]
|
||||||
if ok == true {
|
if ok == true {
|
||||||
tcpService.tcpServer.MaxConnNum = int(MaxConnNum.(float64))
|
tcpService.tcpServer.MaxConnNum = int(MaxConnNum.(float64))
|
||||||
@@ -121,12 +101,12 @@ func (tcpService *TcpService) OnInit() error{
|
|||||||
|
|
||||||
readDeadline,ok := tcpCfg["ReadDeadline"]
|
readDeadline,ok := tcpCfg["ReadDeadline"]
|
||||||
if ok == true {
|
if ok == true {
|
||||||
tcpService.ReadDeadline = time.Second*time.Duration(readDeadline.(float64))
|
tcpService.tcpServer.ReadDeadline = time.Second*time.Duration(readDeadline.(float64))
|
||||||
}
|
}
|
||||||
|
|
||||||
writeDeadline,ok := tcpCfg["WriteDeadline"]
|
writeDeadline,ok := tcpCfg["WriteDeadline"]
|
||||||
if ok == true {
|
if ok == true {
|
||||||
tcpService.WriteDeadline = time.Second*time.Duration(writeDeadline.(float64))
|
tcpService.tcpServer.WriteDeadline = time.Second*time.Duration(writeDeadline.(float64))
|
||||||
}
|
}
|
||||||
|
|
||||||
tcpService.mapClient = make( map[uint64] *Client, tcpService.tcpServer.MaxConnNum)
|
tcpService.mapClient = make( map[uint64] *Client, tcpService.tcpServer.MaxConnNum)
|
||||||
@@ -196,7 +176,7 @@ func (slf *Client) Run() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
slf.tcpConn.SetReadDeadline(slf.tcpService.ReadDeadline)
|
slf.tcpConn.SetReadDeadline(slf.tcpService.tcpServer.ReadDeadline)
|
||||||
bytes,err := slf.tcpConn.ReadMsg()
|
bytes,err := slf.tcpConn.ReadMsg()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.SDebug("read client id ",slf.id," is error:",err.Error())
|
log.SDebug("read client id ",slf.id," is error:",err.Error())
|
||||||
@@ -232,7 +212,6 @@ func (tcpService *TcpService) SendMsg(clientId uint64,msg interface{}) error{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
client.tcpConn.SetWriteDeadline(tcpService.WriteDeadline)
|
|
||||||
return client.tcpConn.WriteMsg(bytes)
|
return client.tcpConn.WriteMsg(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +251,6 @@ func (tcpService *TcpService) SendRawMsg(clientId uint64,msg []byte) error{
|
|||||||
return fmt.Errorf("client %d is disconnect!",clientId)
|
return fmt.Errorf("client %d is disconnect!",clientId)
|
||||||
}
|
}
|
||||||
tcpService.mapClientLocker.Unlock()
|
tcpService.mapClientLocker.Unlock()
|
||||||
client.tcpConn.SetWriteDeadline(tcpService.WriteDeadline)
|
|
||||||
return client.tcpConn.WriteMsg(msg)
|
return client.tcpConn.WriteMsg(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +262,6 @@ func (tcpService *TcpService) SendRawData(clientId uint64,data []byte) error{
|
|||||||
return fmt.Errorf("client %d is disconnect!",clientId)
|
return fmt.Errorf("client %d is disconnect!",clientId)
|
||||||
}
|
}
|
||||||
tcpService.mapClientLocker.Unlock()
|
tcpService.mapClientLocker.Unlock()
|
||||||
client.tcpConn.SetWriteDeadline(tcpService.WriteDeadline)
|
|
||||||
return client.tcpConn.WriteRawMsg(data)
|
return client.tcpConn.WriteRawMsg(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,19 +7,27 @@ import (
|
|||||||
"github.com/duanhf2012/origin/network"
|
"github.com/duanhf2012/origin/network"
|
||||||
"github.com/duanhf2012/origin/network/processor"
|
"github.com/duanhf2012/origin/network/processor"
|
||||||
"github.com/duanhf2012/origin/service"
|
"github.com/duanhf2012/origin/service"
|
||||||
|
"github.com/duanhf2012/origin/node"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
type WSService struct {
|
type WSService struct {
|
||||||
service.Service
|
service.Service
|
||||||
wsServer network.WSServer
|
wsServer network.WSServer
|
||||||
|
|
||||||
mapClientLocker sync.RWMutex
|
mapClientLocker sync.RWMutex
|
||||||
mapClient map[uint64] *WSClient
|
mapClient map[uint64] *WSClient
|
||||||
initClientId uint64
|
|
||||||
process processor.IProcessor
|
process processor.IProcessor
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var seed uint32
|
||||||
|
|
||||||
type WSPackType int8
|
type WSPackType int8
|
||||||
const(
|
const(
|
||||||
WPT_Connected WSPackType = 0
|
WPT_Connected WSPackType = 0
|
||||||
@@ -32,6 +40,12 @@ const Default_WS_MaxConnNum = 3000
|
|||||||
const Default_WS_PendingWriteNum = 10000
|
const Default_WS_PendingWriteNum = 10000
|
||||||
const Default_WS_MaxMsgLen = 65535
|
const Default_WS_MaxMsgLen = 65535
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaxNodeId = 1<<14 - 1 //最大值 16383
|
||||||
|
MaxSeed = 1<<19 - 1 //最大值 524287
|
||||||
|
MaxTime = 1<<31 - 1 //最大值 2147483647
|
||||||
|
)
|
||||||
|
|
||||||
type WSClient struct {
|
type WSClient struct {
|
||||||
id uint64
|
id uint64
|
||||||
wsConn *network.WSConn
|
wsConn *network.WSConn
|
||||||
@@ -46,6 +60,7 @@ type WSPack struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WSService) OnInit() error{
|
func (ws *WSService) OnInit() error{
|
||||||
|
|
||||||
iConfig := ws.GetServiceCfg()
|
iConfig := ws.GetServiceCfg()
|
||||||
if iConfig == nil {
|
if iConfig == nil {
|
||||||
return fmt.Errorf("%s service config is error!", ws.GetName())
|
return fmt.Errorf("%s service config is error!", ws.GetName())
|
||||||
@@ -80,6 +95,10 @@ func (ws *WSService) OnInit() error{
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ws *WSService) SetMessageType(messageType int){
|
||||||
|
ws.wsServer.SetMessageType(messageType)
|
||||||
|
}
|
||||||
|
|
||||||
func (ws *WSService) WSEventHandler(ev event.IEvent) {
|
func (ws *WSService) WSEventHandler(ev event.IEvent) {
|
||||||
pack := ev.(*event.Event).Data.(*WSPack)
|
pack := ev.(*event.Event).Data.(*WSPack)
|
||||||
switch pack.Type {
|
switch pack.Type {
|
||||||
@@ -88,9 +107,9 @@ func (ws *WSService) WSEventHandler(ev event.IEvent) {
|
|||||||
case WPT_DisConnected:
|
case WPT_DisConnected:
|
||||||
pack.MsgProcessor.DisConnectedRoute(pack.ClientId)
|
pack.MsgProcessor.DisConnectedRoute(pack.ClientId)
|
||||||
case WPT_UnknownPack:
|
case WPT_UnknownPack:
|
||||||
pack.MsgProcessor.UnknownMsgRoute(pack.Data,pack.ClientId)
|
pack.MsgProcessor.UnknownMsgRoute(pack.ClientId,pack.Data)
|
||||||
case WPT_Pack:
|
case WPT_Pack:
|
||||||
pack.MsgProcessor.MsgRoute(pack.Data, pack.ClientId)
|
pack.MsgProcessor.MsgRoute(pack.ClientId,pack.Data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,20 +118,30 @@ func (ws *WSService) SetProcessor(process processor.IProcessor,handler event.IEv
|
|||||||
ws.RegEventReceiverFunc(event.Sys_Event_WebSocket,handler, ws.WSEventHandler)
|
ws.RegEventReceiverFunc(event.Sys_Event_WebSocket,handler, ws.WSEventHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ws *WSService) genId() uint64 {
|
||||||
|
if node.GetNodeId()>MaxNodeId{
|
||||||
|
panic("nodeId exceeds the maximum!")
|
||||||
|
}
|
||||||
|
|
||||||
|
newSeed := atomic.AddUint32(&seed,1) % MaxSeed
|
||||||
|
nowTime := uint64(time.Now().Unix())%MaxTime
|
||||||
|
return (uint64(node.GetNodeId())<<50)|(nowTime<<19)|uint64(newSeed)
|
||||||
|
}
|
||||||
|
|
||||||
func (ws *WSService) NewWSClient(conn *network.WSConn) network.Agent {
|
func (ws *WSService) NewWSClient(conn *network.WSConn) network.Agent {
|
||||||
ws.mapClientLocker.Lock()
|
ws.mapClientLocker.Lock()
|
||||||
defer ws.mapClientLocker.Unlock()
|
defer ws.mapClientLocker.Unlock()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
ws.initClientId+=1
|
clientId := ws.genId()
|
||||||
_,ok := ws.mapClient[ws.initClientId]
|
_,ok := ws.mapClient[clientId]
|
||||||
if ok == true {
|
if ok == true {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pClient := &WSClient{wsConn:conn, id: ws.initClientId}
|
pClient := &WSClient{wsConn:conn, id: clientId}
|
||||||
pClient.wsService = ws
|
pClient.wsService = ws
|
||||||
ws.mapClient[ws.initClientId] = pClient
|
ws.mapClient[clientId] = pClient
|
||||||
return pClient
|
return pClient
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +160,7 @@ func (slf *WSClient) Run() {
|
|||||||
log.Debug("read client id %d is error:%+v",slf.id,err)
|
log.Debug("read client id %d is error:%+v",slf.id,err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
data,err:=slf.wsService.process.Unmarshal(bytes)
|
data,err:=slf.wsService.process.Unmarshal(slf.id,bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slf.wsService.NotifyEvent(&event.Event{Type:event.Sys_Event_WebSocket,Data:&WSPack{ClientId:slf.id,Type:WPT_UnknownPack,Data:bytes,MsgProcessor:slf.wsService.process}})
|
slf.wsService.NotifyEvent(&event.Event{Type:event.Sys_Event_WebSocket,Data:&WSPack{ClientId:slf.id,Type:WPT_UnknownPack,Data:bytes,MsgProcessor:slf.wsService.process}})
|
||||||
continue
|
continue
|
||||||
@@ -156,7 +185,7 @@ func (ws *WSService) SendMsg(clientid uint64,msg interface{}) error{
|
|||||||
}
|
}
|
||||||
|
|
||||||
ws.mapClientLocker.Unlock()
|
ws.mapClientLocker.Unlock()
|
||||||
bytes,err := ws.process.Marshal(msg)
|
bytes,err := ws.process.Marshal(clientid,msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
66
util/algorithms/BiSearch.go
Normal file
66
util/algorithms/BiSearch.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package algorithms
|
||||||
|
|
||||||
|
type NumberType interface {
|
||||||
|
int | int8 | int16 | int32 | int64 | string | float32 | float64 | uint | uint8 | uint16 | uint32 | uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Element[ValueType NumberType] interface {
|
||||||
|
GetValue() ValueType
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
BiSearch 二分查找,切片必需有序
|
||||||
|
matchUp规则如下:
|
||||||
|
参数为0时,则一定要找到相等的值
|
||||||
|
参数-1时,找value左边的值,例如:[10,20,30,40],当value为9时返回-1; 当value为11时,返回0 当value为41时,返回 3
|
||||||
|
参数 1时,找value右边的值,例如:[10,20,30,40],当value为9时返回 0; 当value为11时,返回1 当value为41时,返回-1
|
||||||
|
|
||||||
|
返回-1时代表没有找到下标
|
||||||
|
*/
|
||||||
|
func BiSearch[ValueType NumberType, T Element[ValueType]](sElement []T, value ValueType, matchUp int) int {
|
||||||
|
low, high := 0, len(sElement)-1
|
||||||
|
if high == -1 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var mid int
|
||||||
|
for low <= high {
|
||||||
|
mid = low + (high-low)>>1
|
||||||
|
if sElement[mid].GetValue() > value {
|
||||||
|
high = mid - 1
|
||||||
|
} else if sElement[mid].GetValue() < value {
|
||||||
|
low = mid + 1
|
||||||
|
} else {
|
||||||
|
return mid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch matchUp {
|
||||||
|
case 1:
|
||||||
|
if (sElement[mid].GetValue()) < value {
|
||||||
|
if mid+1 >= len(sElement) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return mid + 1
|
||||||
|
}
|
||||||
|
return mid
|
||||||
|
case -1:
|
||||||
|
if (sElement[mid].GetValue()) > value {
|
||||||
|
if mid-1 < 0 {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return mid - 1
|
||||||
|
}
|
||||||
|
} else if (sElement[mid].GetValue()) < value {
|
||||||
|
//if mid+1 < len(sElement)-1 {
|
||||||
|
// return mid + 1
|
||||||
|
//} else {
|
||||||
|
return mid
|
||||||
|
//}
|
||||||
|
} else {
|
||||||
|
return mid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
28
util/algorithms/BiSearch_test.go
Normal file
28
util/algorithms/BiSearch_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package algorithms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyElement struct {
|
||||||
|
Score int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s MyElement) GetValue() int {
|
||||||
|
return s.Score
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_BiSearch(t *testing.T){
|
||||||
|
var schedulePoolCfgList []MyElement = []MyElement{MyElement{10}, MyElement{12}, MyElement{14}, MyElement{16}} //
|
||||||
|
index := BiSearch[int, MyElement](schedulePoolCfgList, 9, true)
|
||||||
|
index = BiSearch[int, MyElement](schedulePoolCfgList, 10, true)
|
||||||
|
index = BiSearch[int, MyElement](schedulePoolCfgList, 11, true)
|
||||||
|
index = BiSearch[int, MyElement](schedulePoolCfgList, 12, true)
|
||||||
|
index = BiSearch[int, MyElement](schedulePoolCfgList, 13, true)
|
||||||
|
index = BiSearch[int, MyElement](schedulePoolCfgList, 14, true)
|
||||||
|
index = BiSearch[int, MyElement](schedulePoolCfgList, 15, true)
|
||||||
|
index = BiSearch[int, MyElement](schedulePoolCfgList, 16, true)
|
||||||
|
index = BiSearch[int, MyElement](schedulePoolCfgList, 17, true)
|
||||||
|
fmt.Println(index)
|
||||||
|
}
|
||||||
61
util/algorithms/BitwiseOperation.go
Normal file
61
util/algorithms/BitwiseOperation.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package algorithms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BitNumber interface {
|
||||||
|
int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnsignedNumber interface {
|
||||||
|
uint | uint8 | uint16 | uint32 | uint64 | uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBitTagIndex[Number BitNumber, UNumber UnsignedNumber](bitBuff []Number, bitPositionIndex UNumber) (uintptr, uintptr, bool) {
|
||||||
|
sliceIndex := uintptr(bitPositionIndex) / (8 * unsafe.Sizeof(bitBuff[0]))
|
||||||
|
sliceBitIndex := uintptr(bitPositionIndex) % (8 * unsafe.Sizeof(bitBuff[0]))
|
||||||
|
|
||||||
|
//位index不能越界
|
||||||
|
if uintptr(bitPositionIndex) >= uintptr(len(bitBuff))*unsafe.Sizeof(bitBuff[0])*8 {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
return sliceIndex, sliceBitIndex, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func setBitTagByIndex[Number BitNumber, UNumber UnsignedNumber](bitBuff []Number, bitPositionIndex UNumber, setTag bool) bool {
|
||||||
|
sliceIndex, sliceBitIndex, ret := getBitTagIndex(bitBuff, bitPositionIndex)
|
||||||
|
if ret == false {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
if setTag {
|
||||||
|
bitBuff[sliceIndex] = bitBuff[sliceIndex] | 1<<sliceBitIndex
|
||||||
|
} else {
|
||||||
|
bitBuff[sliceIndex] = bitBuff[sliceIndex] &^ (1 << sliceBitIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBitwiseTag[Number BitNumber, UNumber UnsignedNumber](bitBuff []Number, bitPositionIndex UNumber) (bool, error) {
|
||||||
|
sliceIndex, sliceBitIndex, ret := getBitTagIndex(bitBuff, bitPositionIndex)
|
||||||
|
if ret == false {
|
||||||
|
return false, errors.New("Invalid parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bitBuff[sliceIndex] & (1 << sliceBitIndex)) > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetBitwiseTag[Number BitNumber, UNumber UnsignedNumber](bitBuff []Number, bitPositionIndex UNumber) bool {
|
||||||
|
return setBitTagByIndex(bitBuff, bitPositionIndex, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearBitwiseTag[Number BitNumber, UNumber UnsignedNumber](bitBuff []Number, bitPositionIndex UNumber) bool {
|
||||||
|
return setBitTagByIndex(bitBuff, bitPositionIndex, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBitwiseNum[Number BitNumber](bitBuff []Number) int {
|
||||||
|
return len(bitBuff) * int(unsafe.Sizeof(bitBuff[0])*8)
|
||||||
|
}
|
||||||
37
util/algorithms/BitwiseOperation_test.go
Normal file
37
util/algorithms/BitwiseOperation_test.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package algorithms
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func Test_Bitwise(t *testing.T) {
|
||||||
|
//1.预分配10个byte切片,用于存储位标识
|
||||||
|
byteBuff := make([]byte, 10)
|
||||||
|
|
||||||
|
//2.获取buff总共位数
|
||||||
|
bitNum := GetBitwiseNum(byteBuff)
|
||||||
|
t.Log(bitNum)
|
||||||
|
|
||||||
|
//3..对索引79位打标记,注意是从0开始,79即为最后一个位
|
||||||
|
idx := uint(79)
|
||||||
|
|
||||||
|
//4.对byteBuff索引idx位置打上标记
|
||||||
|
SetBitwiseTag(byteBuff, idx)
|
||||||
|
|
||||||
|
//5.获取索引idx位置标记
|
||||||
|
isTag, ret := GetBitwiseTag(byteBuff, idx)
|
||||||
|
t.Log("set index ", idx, " :", isTag, ret)
|
||||||
|
if isTag != true {
|
||||||
|
t.Fatal("error")
|
||||||
|
}
|
||||||
|
|
||||||
|
//6.清除掉索引idx位标记
|
||||||
|
ClearBitwiseTag(byteBuff, idx)
|
||||||
|
|
||||||
|
//7.获取索引idx位置标记
|
||||||
|
isTag, ret = GetBitwiseTag(byteBuff, idx)
|
||||||
|
t.Log("get index ", idx, " :", isTag, ret)
|
||||||
|
|
||||||
|
if isTag != false {
|
||||||
|
t.Fatal("error")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
47
util/algorithms/skip/interface.go
Normal file
47
util/algorithms/skip/interface.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Workiva, LLC
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package skip
|
||||||
|
|
||||||
|
// Comparator is a generic interface that represents items that can
|
||||||
|
// be compared.
|
||||||
|
type Comparator interface {
|
||||||
|
// Compare compares this interface with another. Returns a positive
|
||||||
|
// number if this interface is greater, 0 if equal, negative number
|
||||||
|
// if less.
|
||||||
|
Compare(Comparator) int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparators is a typed list of type Comparator.
|
||||||
|
type Comparators []Comparator
|
||||||
|
|
||||||
|
// Iterator defines an interface that allows a consumer to iterate
|
||||||
|
// all results of a query. All values will be visited in-order.
|
||||||
|
type Iterator interface {
|
||||||
|
// Next returns a bool indicating if there is future value
|
||||||
|
// in the iterator and moves the iterator to that value.
|
||||||
|
Next() bool
|
||||||
|
// Prev returns a bool indicating if there is Previous value
|
||||||
|
// in the iterator and moves the iterator to that value.
|
||||||
|
Prev() bool
|
||||||
|
// Value returns a Comparator representing the iterator's current
|
||||||
|
// position. If there is no value, this returns nil.
|
||||||
|
Value() Comparator
|
||||||
|
// exhaust is a helper method that will iterate this iterator
|
||||||
|
// to completion and return a list of resulting Entries
|
||||||
|
// in order.
|
||||||
|
exhaust() Comparators
|
||||||
|
}
|
||||||
86
util/algorithms/skip/iterator.go
Normal file
86
util/algorithms/skip/iterator.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Workiva, LLC
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package skip
|
||||||
|
|
||||||
|
const iteratorExhausted = -2
|
||||||
|
|
||||||
|
// iterator represents an object that can be iterated. It will
|
||||||
|
// return false on Next and nil on Value if there are no further
|
||||||
|
// values to be iterated.
|
||||||
|
type iterator struct {
|
||||||
|
first bool
|
||||||
|
n *node
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns a bool indicating if there are any further values
|
||||||
|
// in this iterator.
|
||||||
|
func (iter *iterator) Next() bool {
|
||||||
|
if iter.first {
|
||||||
|
iter.first = false
|
||||||
|
return iter.n != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if iter.n == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
iter.n = iter.n.forward[0]
|
||||||
|
return iter.n != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prev returns a bool indicating if there are any Previous values
|
||||||
|
// in this iterator.
|
||||||
|
func (iter *iterator) Prev() bool {
|
||||||
|
if iter.first {
|
||||||
|
iter.first = false
|
||||||
|
return iter.n != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if iter.n == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
iter.n = iter.n.preNode
|
||||||
|
return iter.n != nil && iter.n.entry != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns a Comparator representing the iterator's present
|
||||||
|
// position in the query. Returns nil if no values remain to iterate.
|
||||||
|
func (iter *iterator) Value() Comparator {
|
||||||
|
if iter.n == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return iter.n.entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// exhaust is a helper method to exhaust this iterator and return
|
||||||
|
// all remaining entries.
|
||||||
|
func (iter *iterator) exhaust() Comparators {
|
||||||
|
entries := make(Comparators, 0, 10)
|
||||||
|
for i := iter; i.Next(); {
|
||||||
|
entries = append(entries, i.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
// nilIterator returns an iterator that will always return false
|
||||||
|
// for Next and nil for Value.
|
||||||
|
func nilIterator() *iterator {
|
||||||
|
return &iterator{}
|
||||||
|
}
|
||||||
50
util/algorithms/skip/node.go
Normal file
50
util/algorithms/skip/node.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Workiva, LLC
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package skip
|
||||||
|
|
||||||
|
type widths []uint64
|
||||||
|
|
||||||
|
type nodes []*node
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
// forward denotes the forward pointing pointers in this
|
||||||
|
// node.
|
||||||
|
forward nodes
|
||||||
|
//zero level pre node
|
||||||
|
preNode *node
|
||||||
|
// widths keeps track of the distance between this pointer
|
||||||
|
// and the forward pointers so we can access skip list
|
||||||
|
// values by position in logarithmic time.
|
||||||
|
widths widths
|
||||||
|
// entry is the associated value with this node.
|
||||||
|
entry Comparator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) Compare(e Comparator) int {
|
||||||
|
return n.entry.Compare(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newNode will allocate and return a new node with the entry
|
||||||
|
// provided. maxLevels will determine the length of the forward
|
||||||
|
// pointer list associated with this node.
|
||||||
|
func newNode(cmp Comparator, maxLevels uint8) *node {
|
||||||
|
return &node{
|
||||||
|
entry: cmp,
|
||||||
|
forward: make(nodes, maxLevels),
|
||||||
|
widths: make(widths, maxLevels),
|
||||||
|
}
|
||||||
|
}
|
||||||
494
util/algorithms/skip/skip.go
Normal file
494
util/algorithms/skip/skip.go
Normal file
@@ -0,0 +1,494 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Workiva, LLC
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package skip defines a skiplist datastructure. That is, a data structure
|
||||||
|
that probabilistically determines relationships between keys. By doing
|
||||||
|
so, it becomes easier to program than a binary search tree but maintains
|
||||||
|
similar speeds.
|
||||||
|
|
||||||
|
Performance characteristics:
|
||||||
|
Insert: O(log n)
|
||||||
|
Search: O(log n)
|
||||||
|
Delete: O(log n)
|
||||||
|
Space: O(n)
|
||||||
|
|
||||||
|
Recently added is the capability to address, insert, and replace an
|
||||||
|
entry by position. This capability is acheived by saving the width
|
||||||
|
of the "gap" between two nodes. Searching for an item by position is
|
||||||
|
very similar to searching by value in that the same basic algorithm is
|
||||||
|
used but we are searching for width instead of value. Because this avoids
|
||||||
|
the overhead associated with Golang interfaces, operations by position
|
||||||
|
are about twice as fast as operations by value. Time complexities listed
|
||||||
|
below.
|
||||||
|
|
||||||
|
SearchByPosition: O(log n)
|
||||||
|
InsertByPosition: O(log n)
|
||||||
|
|
||||||
|
More information here: http://cglab.ca/~morin/teaching/5408/refs/p90b.pdf
|
||||||
|
|
||||||
|
Benchmarks:
|
||||||
|
BenchmarkInsert-8 2000000 930 ns/op
|
||||||
|
BenchmarkGet-8 2000000 989 ns/op
|
||||||
|
BenchmarkDelete-8 3000000 600 ns/op
|
||||||
|
BenchmarkPrepend-8 1000000 1468 ns/op
|
||||||
|
BenchmarkByPosition-8 10000000 202 ns/op
|
||||||
|
BenchmarkInsertAtPosition-8 3000000 485 ns/op
|
||||||
|
|
||||||
|
CPU profiling has shown that the most expensive thing we do here
|
||||||
|
is call Compare. A potential optimization for gets only is to
|
||||||
|
do a binary search in the forward/width lists instead of visiting
|
||||||
|
every value. We could also use generics if Golang had them and
|
||||||
|
let the consumer specify primitive types, which would speed up
|
||||||
|
these operation dramatically.
|
||||||
|
*/
|
||||||
|
package skip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const p = .5 // the p level defines the probability that a node
|
||||||
|
// with a value at level i also has a value at i+1. This number
|
||||||
|
// is also important in determining max level. Max level will
|
||||||
|
// be defined as L(N) where L = log base (1/p) of n where n
|
||||||
|
// is the number of items in the list and N is the number of possible
|
||||||
|
// items in the universe. If p = .5 then maxlevel = 32 is appropriate
|
||||||
|
// for uint32.
|
||||||
|
|
||||||
|
// lockedSource is an implementation of rand.Source that is safe for
|
||||||
|
// concurrent use by multiple goroutines. The code is modeled after
|
||||||
|
// https://golang.org/src/math/rand/rand.go.
|
||||||
|
type lockedSource struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
src rand.Source
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int63 implements the rand.Source interface.
|
||||||
|
func (ls *lockedSource) Int63() (n int64) {
|
||||||
|
ls.mu.Lock()
|
||||||
|
n = ls.src.Int63()
|
||||||
|
ls.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seed implements the rand.Source interface.
|
||||||
|
func (ls *lockedSource) Seed(seed int64) {
|
||||||
|
ls.mu.Lock()
|
||||||
|
ls.src.Seed(seed)
|
||||||
|
ls.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// generator will be the common generator to create random numbers. It
|
||||||
|
// is seeded with unix nanosecond when this line is executed at runtime,
|
||||||
|
// and only executed once ensuring all random numbers come from the same
|
||||||
|
// randomly seeded generator.
|
||||||
|
var generator = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())})
|
||||||
|
|
||||||
|
func generateLevel(maxLevel uint8) uint8 {
|
||||||
|
var level uint8
|
||||||
|
for level = uint8(1); level < maxLevel-1; level++ {
|
||||||
|
if generator.Float64() >= p {
|
||||||
|
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertNode(sl *SkipList, n *node, cmp Comparator, pos uint64, cache nodes, posCache widths, allowDuplicate bool) Comparator {
|
||||||
|
if !allowDuplicate && n != nil && n.Compare(cmp) == 0 { // a simple update in this case
|
||||||
|
oldEntry := n.entry
|
||||||
|
n.entry = cmp
|
||||||
|
return oldEntry
|
||||||
|
}
|
||||||
|
atomic.AddUint64(&sl.num, 1)
|
||||||
|
|
||||||
|
nodeLevel := generateLevel(sl.maxLevel)
|
||||||
|
if nodeLevel > sl.level {
|
||||||
|
for i := sl.level; i < nodeLevel; i++ {
|
||||||
|
cache[i] = sl.head
|
||||||
|
}
|
||||||
|
sl.level = nodeLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
nn := newNode(cmp, nodeLevel)
|
||||||
|
for i := uint8(0); i < nodeLevel; i++ {
|
||||||
|
if i == 0 {
|
||||||
|
nn.preNode = cache[i]
|
||||||
|
if cache[i].forward[i] != nil {
|
||||||
|
cache[i].forward[i].preNode = nn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nn.forward[i] = cache[i].forward[i]
|
||||||
|
cache[i].forward[i] = nn
|
||||||
|
|
||||||
|
formerWidth := cache[i].widths[i]
|
||||||
|
if formerWidth == 0 {
|
||||||
|
nn.widths[i] = 0
|
||||||
|
} else {
|
||||||
|
nn.widths[i] = posCache[i] + formerWidth + 1 - pos
|
||||||
|
}
|
||||||
|
|
||||||
|
if cache[i].forward[i] != nil {
|
||||||
|
cache[i].widths[i] = pos - posCache[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := nodeLevel; i < sl.level; i++ {
|
||||||
|
if cache[i].forward[i] == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cache[i].widths[i]++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitAt(sl *SkipList, index uint64) (*SkipList, *SkipList) {
|
||||||
|
right := &SkipList{}
|
||||||
|
right.maxLevel = sl.maxLevel
|
||||||
|
right.level = sl.level
|
||||||
|
right.cache = make(nodes, sl.maxLevel)
|
||||||
|
right.posCache = make(widths, sl.maxLevel)
|
||||||
|
right.head = newNode(nil, sl.maxLevel)
|
||||||
|
sl.searchByPosition(index, sl.cache, sl.posCache) // populate the cache that needs updating
|
||||||
|
|
||||||
|
for i := uint8(0); i <= sl.level; i++ {
|
||||||
|
right.head.forward[i] = sl.cache[i].forward[i]
|
||||||
|
if sl.cache[i].forward[i] != nil {
|
||||||
|
right.head.widths[i] = sl.cache[i].widths[i] - (index - sl.posCache[i])
|
||||||
|
}
|
||||||
|
sl.cache[i].widths[i] = 0
|
||||||
|
sl.cache[i].forward[i] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
right.num = sl.Len() - index // right is not in user's hands yet
|
||||||
|
atomic.AddUint64(&sl.num, -right.num)
|
||||||
|
|
||||||
|
sl.resetMaxLevel()
|
||||||
|
right.resetMaxLevel()
|
||||||
|
|
||||||
|
return sl, right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip list is a datastructure that probabalistically determines
|
||||||
|
// relationships between nodes. This results in a structure
|
||||||
|
// that performs similarly to a BST but is much easier to build
|
||||||
|
// from a programmatic perspective (no rotations).
|
||||||
|
type SkipList struct {
|
||||||
|
maxLevel, level uint8
|
||||||
|
head *node
|
||||||
|
num uint64
|
||||||
|
// a list of nodes that can be reused, should reduce
|
||||||
|
// the number of allocations in the insert/delete case.
|
||||||
|
cache nodes
|
||||||
|
posCache widths
|
||||||
|
}
|
||||||
|
|
||||||
|
// init will initialize this skiplist. The parameter is expected
|
||||||
|
// to be of some uint type which will set this skiplist's maximum
|
||||||
|
// level.
|
||||||
|
func (sl *SkipList) init(ifc interface{}) {
|
||||||
|
switch ifc.(type) {
|
||||||
|
case uint8:
|
||||||
|
sl.maxLevel = 8
|
||||||
|
case uint16:
|
||||||
|
sl.maxLevel = 16
|
||||||
|
case uint32:
|
||||||
|
sl.maxLevel = 32
|
||||||
|
case uint64, uint:
|
||||||
|
sl.maxLevel = 64
|
||||||
|
}
|
||||||
|
sl.cache = make(nodes, sl.maxLevel)
|
||||||
|
sl.posCache = make(widths, sl.maxLevel)
|
||||||
|
sl.head = newNode(nil, sl.maxLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SkipList) search(cmp Comparator, update nodes, widths widths) (*node, uint64) {
|
||||||
|
if sl.Len() == 0 { // nothing in the list
|
||||||
|
return nil, 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos uint64 = 0
|
||||||
|
var offset uint8
|
||||||
|
var alreadyChecked *node
|
||||||
|
n := sl.head
|
||||||
|
for i := uint8(0); i <= sl.level; i++ {
|
||||||
|
offset = sl.level - i
|
||||||
|
for n.forward[offset] != nil && n.forward[offset] != alreadyChecked && n.forward[offset].Compare(cmp) < 0 {
|
||||||
|
pos += n.widths[offset]
|
||||||
|
n = n.forward[offset]
|
||||||
|
}
|
||||||
|
|
||||||
|
alreadyChecked = n
|
||||||
|
if update != nil {
|
||||||
|
update[offset] = n
|
||||||
|
widths[offset] = pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n.forward[0], pos + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SkipList) resetMaxLevel() {
|
||||||
|
if sl.level < 1 {
|
||||||
|
sl.level = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for sl.head.forward[sl.level-1] == nil && sl.level > 1 {
|
||||||
|
sl.level--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SkipList) searchByPosition(position uint64, update nodes, widths widths) (*node, uint64) {
|
||||||
|
if sl.Len() == 0 { // nothing in the list
|
||||||
|
return nil, 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if position > sl.Len() {
|
||||||
|
return nil, 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos uint64 = 0
|
||||||
|
var offset uint8
|
||||||
|
n := sl.head
|
||||||
|
for i := uint8(0); i <= sl.level; i++ {
|
||||||
|
offset = sl.level - i
|
||||||
|
for n.forward[offset] != nil && pos+n.widths[offset] <= position {
|
||||||
|
pos += n.widths[offset]
|
||||||
|
n = n.forward[offset]
|
||||||
|
}
|
||||||
|
|
||||||
|
if update != nil {
|
||||||
|
update[offset] = n
|
||||||
|
widths[offset] = pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, pos + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get will retrieve values associated with the keys provided. If an
|
||||||
|
// associated value could not be found, a nil is returned in its place.
|
||||||
|
// This is an O(log n) operation.
|
||||||
|
func (sl *SkipList) Get(comparators ...Comparator) Comparators {
|
||||||
|
result := make(Comparators, 0, len(comparators))
|
||||||
|
|
||||||
|
var n *node
|
||||||
|
for _, cmp := range comparators {
|
||||||
|
n, _ = sl.search(cmp, nil, nil)
|
||||||
|
if n != nil && n.Compare(cmp) == 0 {
|
||||||
|
result = append(result, n.entry)
|
||||||
|
} else {
|
||||||
|
result = append(result, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWithPosition will retrieve the value with the provided key and
|
||||||
|
// return the position of that value within the list. Returns nil, 0
|
||||||
|
// if an associated value could not be found.
|
||||||
|
func (sl *SkipList) GetWithPosition(cmp Comparator) (Comparator, uint64) {
|
||||||
|
n, pos := sl.search(cmp, nil, nil)
|
||||||
|
if n == nil {
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return n.entry, pos - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByPosition returns the Comparator at the given position.
|
||||||
|
func (sl *SkipList) ByPosition(position uint64) Comparator {
|
||||||
|
n, _ := sl.searchByPosition(position+1, nil, nil)
|
||||||
|
if n == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return n.entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SkipList) insert(cmp Comparator) Comparator {
|
||||||
|
n, pos := sl.search(cmp, sl.cache, sl.posCache)
|
||||||
|
return insertNode(sl, n, cmp, pos, sl.cache, sl.posCache, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert will insert the provided comparators into the list. Returned
|
||||||
|
// is a list of comparators that were overwritten. This is expected to
|
||||||
|
// be an O(log n) operation.
|
||||||
|
func (sl *SkipList) Insert(comparators ...Comparator) Comparators {
|
||||||
|
overwritten := make(Comparators, 0, len(comparators))
|
||||||
|
for _, cmp := range comparators {
|
||||||
|
overwritten = append(overwritten, sl.insert(cmp))
|
||||||
|
}
|
||||||
|
|
||||||
|
return overwritten
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SkipList) insertAtPosition(position uint64, cmp Comparator) {
|
||||||
|
if position > sl.Len() {
|
||||||
|
position = sl.Len()
|
||||||
|
}
|
||||||
|
n, pos := sl.searchByPosition(position, sl.cache, sl.posCache)
|
||||||
|
insertNode(sl, n, cmp, pos, sl.cache, sl.posCache, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertAtPosition will insert the provided Comparator at the provided position.
|
||||||
|
// If position is greater than the length of the skiplist, the Comparator
|
||||||
|
// is appended. This method bypasses order checks and checks for
|
||||||
|
// duplicates so use with caution.
|
||||||
|
func (sl *SkipList) InsertAtPosition(position uint64, cmp Comparator) {
|
||||||
|
sl.insertAtPosition(position, cmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SkipList) replaceAtPosition(position uint64, cmp Comparator) {
|
||||||
|
n, _ := sl.searchByPosition(position+1, nil, nil)
|
||||||
|
if n == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n.entry = cmp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace at position will replace the Comparator at the provided position
|
||||||
|
// with the provided Comparator. If the provided position does not exist,
|
||||||
|
// this operation is a no-op.
|
||||||
|
func (sl *SkipList) ReplaceAtPosition(position uint64, cmp Comparator) {
|
||||||
|
sl.replaceAtPosition(position, cmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SkipList) delete(cmp Comparator) Comparator {
|
||||||
|
n, _ := sl.search(cmp, sl.cache, sl.posCache)
|
||||||
|
|
||||||
|
if n == nil || n.Compare(cmp) != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(&sl.num, ^uint64(0)) // decrement
|
||||||
|
|
||||||
|
for i := uint8(0); i <= sl.level; i++ {
|
||||||
|
if sl.cache[i].forward[i] != n {
|
||||||
|
if sl.cache[i].forward[i] != nil {
|
||||||
|
sl.cache[i].widths[i]--
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
if n.forward[i] != nil {
|
||||||
|
n.forward[i].preNode = sl.cache[i]
|
||||||
|
}
|
||||||
|
n.preNode = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sl.cache[i].widths[i] += n.widths[i] - 1
|
||||||
|
sl.cache[i].forward[i] = n.forward[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
for sl.level > 1 && sl.head.forward[sl.level-1] == nil {
|
||||||
|
sl.head.widths[sl.level] = 0
|
||||||
|
sl.level--
|
||||||
|
}
|
||||||
|
|
||||||
|
return n.entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will remove the provided keys from the skiplist and return
|
||||||
|
// a list of in-order Comparators that were deleted. This is a no-op if
|
||||||
|
// an associated key could not be found. This is an O(log n) operation.
|
||||||
|
func (sl *SkipList) Delete(comparators ...Comparator) Comparators {
|
||||||
|
deleted := make(Comparators, 0, len(comparators))
|
||||||
|
|
||||||
|
for _, cmp := range comparators {
|
||||||
|
deleted = append(deleted, sl.delete(cmp))
|
||||||
|
}
|
||||||
|
|
||||||
|
return deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in this skiplist.
|
||||||
|
func (sl *SkipList) Len() uint64 {
|
||||||
|
return atomic.LoadUint64(&sl.num)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SkipList) iterAtPosition(pos uint64) *iterator {
|
||||||
|
n, _ := sl.searchByPosition(pos, nil, nil)
|
||||||
|
if n == nil || n.entry == nil {
|
||||||
|
return nilIterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &iterator{
|
||||||
|
first: true,
|
||||||
|
n: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IterAtPosition is the sister method to Iter only the user defines
|
||||||
|
// a position in the skiplist to begin iteration instead of a value.
|
||||||
|
func (sl *SkipList) IterAtPosition(pos uint64) Iterator {
|
||||||
|
return sl.iterAtPosition(pos + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SkipList) iter(cmp Comparator) *iterator {
|
||||||
|
n, _ := sl.search(cmp, nil, nil)
|
||||||
|
if n == nil {
|
||||||
|
return nilIterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &iterator{
|
||||||
|
first: true,
|
||||||
|
n: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iter will return an iterator that can be used to iterate
|
||||||
|
// over all the values with a key equal to or greater than
|
||||||
|
// the key provided.
|
||||||
|
func (sl *SkipList) Iter(cmp Comparator) Iterator {
|
||||||
|
return sl.iter(cmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitAt will split the current skiplist into two lists. The first
|
||||||
|
// skiplist returned is the "left" list and the second is the "right."
|
||||||
|
// The index defines the last item in the left list. If index is greater
|
||||||
|
// then the length of this list, only the left skiplist is returned
|
||||||
|
// and the right will be nil. This is a mutable operation and modifies
|
||||||
|
// the content of this list.
|
||||||
|
func (sl *SkipList) SplitAt(index uint64) (*SkipList, *SkipList) {
|
||||||
|
index++ // 0-index offset
|
||||||
|
if index >= sl.Len() {
|
||||||
|
return sl, nil
|
||||||
|
}
|
||||||
|
return splitAt(sl, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New will allocate, initialize, and return a new skiplist.
|
||||||
|
// The provided parameter should be of type uint and will determine
|
||||||
|
// the maximum possible level that will be created to ensure
|
||||||
|
// a random and quick distribution of levels. Parameter must
|
||||||
|
// be a uint type.
|
||||||
|
func New(ifc interface{}) *SkipList {
|
||||||
|
sl := &SkipList{}
|
||||||
|
sl.init(ifc)
|
||||||
|
return sl
|
||||||
|
}
|
||||||
20
util/buildtime/build.go
Normal file
20
util/buildtime/build.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package buildtime
|
||||||
|
|
||||||
|
/*
|
||||||
|
//查询buildtime包中的位置,在github.com/duanhf2012/origin/util/buildtime.BuildTime中
|
||||||
|
go tool nm ./originserver.exe |grep buildtime
|
||||||
|
|
||||||
|
//编译传入编译时间信息
|
||||||
|
go build -ldflags "-X 'github.com/duanhf2012/origin/util/buildtime.BuildTime=20200101'"
|
||||||
|
go build -ldflags "-X github.com/duanhf2012/origin/util/buildtime.BuildTime=20200101 -X github.com/duanhf2012/origin/util/buildtime.BuildTag=debug"
|
||||||
|
*/
|
||||||
|
var BuildTime string
|
||||||
|
var BuildTag string
|
||||||
|
|
||||||
|
func GetBuildDateTime() string {
|
||||||
|
return BuildTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBuildTag() string {
|
||||||
|
return BuildTag
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package coroutine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/duanhf2012/origin/log"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
)
|
)
|
||||||
@@ -12,10 +13,11 @@ func F(callback interface{},recoverNum int, args ...interface{}) {
|
|||||||
var coreInfo string
|
var coreInfo string
|
||||||
coreInfo = string(debug.Stack())
|
coreInfo = string(debug.Stack())
|
||||||
coreInfo += "\n" + fmt.Sprintf("Core information is %v\n", r)
|
coreInfo += "\n" + fmt.Sprintf("Core information is %v\n", r)
|
||||||
fmt.Print(coreInfo)
|
log.SError(coreInfo)
|
||||||
|
if recoverNum > 0{
|
||||||
if recoverNum==-1 ||recoverNum-1 >= 0 {
|
|
||||||
recoverNum -= 1
|
recoverNum -= 1
|
||||||
|
}
|
||||||
|
if recoverNum == -1 || recoverNum > 0 {
|
||||||
go F(callback,recoverNum, args...)
|
go F(callback,recoverNum, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
78
util/math/math.go
Normal file
78
util/math/math.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package math
|
||||||
|
|
||||||
|
import "github.com/duanhf2012/origin/log"
|
||||||
|
|
||||||
|
type NumberType interface {
|
||||||
|
int | int8 | int16 | int32 | int64 | float32 | float64 | uint | uint8 | uint16 | uint32 | uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignedNumberType interface {
|
||||||
|
int | int8 | int16 | int32 | int64 | float32 | float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type FloatType interface {
|
||||||
|
float32 | float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func Max[NumType NumberType](number1 NumType, number2 NumType) NumType {
|
||||||
|
if number1 > number2 {
|
||||||
|
return number1
|
||||||
|
}
|
||||||
|
|
||||||
|
return number2
|
||||||
|
}
|
||||||
|
|
||||||
|
func Min[NumType NumberType](number1 NumType, number2 NumType) NumType {
|
||||||
|
if number1 < number2 {
|
||||||
|
return number1
|
||||||
|
}
|
||||||
|
|
||||||
|
return number2
|
||||||
|
}
|
||||||
|
|
||||||
|
func Abs[NumType SignedNumberType](Num NumType) NumType {
|
||||||
|
if Num < 0 {
|
||||||
|
return -1 * Num
|
||||||
|
}
|
||||||
|
|
||||||
|
return Num
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func Add[NumType NumberType](number1 NumType, number2 NumType) NumType {
|
||||||
|
ret := number1 + number2
|
||||||
|
if number2> 0 && ret < number1 {
|
||||||
|
log.SStack("Calculation overflow , number1 is ",number1," number2 is ",number2)
|
||||||
|
}else if (number2<0 && ret > number1){
|
||||||
|
log.SStack("Calculation overflow , number1 is ",number1," number2 is ",number2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sub[NumType NumberType](number1 NumType, number2 NumType) NumType {
|
||||||
|
ret := number1 - number2
|
||||||
|
if number2> 0 && ret > number1 {
|
||||||
|
log.SStack("Calculation overflow , number1 is ",number1," number2 is ",number2)
|
||||||
|
}else if (number2<0 && ret < number1){
|
||||||
|
log.SStack("Calculation overflow , number1 is ",number1," number2 is ",number2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func Mul[NumType NumberType](number1 NumType, number2 NumType) NumType {
|
||||||
|
ret := number1 * number2
|
||||||
|
if number1 == 0 || number2 == 0 {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret / number2 == number1 {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SStack("Calculation overflow , number1 is ",number1," number2 is ",number2)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
167
util/queue/squeue.go
Normal file
167
util/queue/squeue.go
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
这是一个循环队列
|
||||||
|
*/
|
||||||
|
type SQueue[ElementType any] struct {
|
||||||
|
elements []ElementType
|
||||||
|
head int
|
||||||
|
tail int
|
||||||
|
locker sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
//游标,通过该游标获取数据
|
||||||
|
type SCursor[ElementType any] struct {
|
||||||
|
pos int
|
||||||
|
squeue *SQueue[ElementType]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSQueue[ElementType any](maxElementNum int) *SQueue[ElementType]{
|
||||||
|
queue := &SQueue[ElementType]{}
|
||||||
|
queue.elements = make([]ElementType,maxElementNum+1)
|
||||||
|
|
||||||
|
return queue
|
||||||
|
}
|
||||||
|
|
||||||
|
//游标移动到队首
|
||||||
|
func (s *SCursor[ElementType]) First(){
|
||||||
|
s.squeue.locker.RLock()
|
||||||
|
defer s.squeue.locker.RUnlock()
|
||||||
|
s.pos = s.squeue.head
|
||||||
|
}
|
||||||
|
|
||||||
|
//从当前位置移动游标,注意如果在多协程读或者pop时,可能会导致游标失效
|
||||||
|
func (s *SCursor[ElementType]) Next() (elem ElementType,ret bool){
|
||||||
|
s.squeue.locker.RLock()
|
||||||
|
defer s.squeue.locker.RUnlock()
|
||||||
|
|
||||||
|
if s.pos == s.squeue.tail {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.pos++
|
||||||
|
s.pos = (s.pos)%(len(s.squeue.elements))
|
||||||
|
return s.squeue.elements[s.pos],true
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取队列元数个数
|
||||||
|
func (s *SQueue[ElementType]) Len() int {
|
||||||
|
s.locker.RLock()
|
||||||
|
defer s.locker.RUnlock()
|
||||||
|
|
||||||
|
return s.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQueue[ElementType]) len() int {
|
||||||
|
if s.head <= s.tail {
|
||||||
|
return s.tail - s.head
|
||||||
|
}
|
||||||
|
|
||||||
|
//(len(s.elements)-1-s.head)+(s.tail+1)
|
||||||
|
return len(s.elements)-s.head+s.tail
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取游标,默认是队首
|
||||||
|
func (s *SQueue[ElementType]) GetCursor() (cur SCursor[ElementType]){
|
||||||
|
s.locker.RLock()
|
||||||
|
defer s.locker.RUnlock()
|
||||||
|
|
||||||
|
cur.squeue = s
|
||||||
|
cur.pos = s.head
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取指定位置的游标
|
||||||
|
func (s *SQueue[ElementType]) GetPosCursor(pos int) (cur SCursor[ElementType],ret bool){
|
||||||
|
s.locker.RLock()
|
||||||
|
defer s.locker.RUnlock()
|
||||||
|
|
||||||
|
if s.head < s.tail {
|
||||||
|
if pos<=s.head || pos>s.tail{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true
|
||||||
|
cur.squeue = s
|
||||||
|
cur.pos = pos
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if pos >s.tail && pos <=s.head {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cur.squeue = s
|
||||||
|
cur.pos = pos
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//从队首移除掉指定数量元素
|
||||||
|
func (s *SQueue[ElementType]) RemoveElement(elementNum int) (removeNum int) {
|
||||||
|
s.locker.Lock()
|
||||||
|
defer s.locker.Unlock()
|
||||||
|
|
||||||
|
lens := s.len()
|
||||||
|
if elementNum > lens{
|
||||||
|
removeNum = lens
|
||||||
|
}else{
|
||||||
|
removeNum = elementNum
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
s.head = (s.head + removeNum)%len(s.elements)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//从队首Pop元素
|
||||||
|
func (s *SQueue[ElementType]) Pop() (elem ElementType,ret bool){
|
||||||
|
s.locker.Lock()
|
||||||
|
defer s.locker.Unlock()
|
||||||
|
|
||||||
|
if s.head == s.tail {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.head++
|
||||||
|
s.head = s.head%len(s.elements)
|
||||||
|
return s.elements[s.head],true
|
||||||
|
}
|
||||||
|
|
||||||
|
//从队尾Push数据
|
||||||
|
func (s *SQueue[ElementType]) Push(elem ElementType) bool {
|
||||||
|
s.locker.Lock()
|
||||||
|
defer s.locker.Unlock()
|
||||||
|
|
||||||
|
nextPos := (s.tail+1) % len(s.elements)
|
||||||
|
if nextPos == s.head {
|
||||||
|
//is full
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.tail = nextPos
|
||||||
|
s.elements[s.tail] = elem
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断队列是否为空
|
||||||
|
func (s *SQueue[ElementType]) IsEmpty() bool{
|
||||||
|
s.locker.RLock()
|
||||||
|
defer s.locker.RUnlock()
|
||||||
|
|
||||||
|
return s.head == s.tail
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断队列是否已满
|
||||||
|
func (s *SQueue[ElementType]) IsFull() bool{
|
||||||
|
s.locker.RLock()
|
||||||
|
defer s.locker.RUnlock()
|
||||||
|
|
||||||
|
nextPos := (s.tail+1) % len(s.elements)
|
||||||
|
return nextPos == s.head
|
||||||
|
}
|
||||||
|
|
||||||
66
util/queue/syncqueue_test.go
Normal file
66
util/queue/syncqueue_test.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Example(t *testing.T) {
|
||||||
|
//1.创建阶列
|
||||||
|
queue := NewSQueue[int](5)
|
||||||
|
|
||||||
|
//2.判断是否为空
|
||||||
|
t.Log("is empty :", queue.IsEmpty())
|
||||||
|
t.Log("is full :", queue.IsFull())
|
||||||
|
|
||||||
|
//3.游标使用,打印所有数据
|
||||||
|
cursor := queue.GetCursor()
|
||||||
|
cursor.First()
|
||||||
|
for {
|
||||||
|
elem, ret := cursor.Next()
|
||||||
|
if ret == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t.Log("elem:", elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
//4.push数据,塞满队列
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
t.Log("push:", queue.Push(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("is empty :", queue.IsEmpty())
|
||||||
|
t.Log("is full :", queue.IsFull())
|
||||||
|
|
||||||
|
//5.使用游标遍历所有数据
|
||||||
|
cursor.First()
|
||||||
|
for {
|
||||||
|
elem, ret := cursor.Next()
|
||||||
|
if ret == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t.Log("elem:", elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
//6.删除2个元素
|
||||||
|
removeNum := queue.RemoveElement(2)
|
||||||
|
t.Log("Remove Num:", removeNum)
|
||||||
|
|
||||||
|
//7.游标遍历
|
||||||
|
cursor.First()
|
||||||
|
for {
|
||||||
|
elem, ret := cursor.Next()
|
||||||
|
if ret == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t.Log("elem:", elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
//8.pop数据所有
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
elem, ret := queue.Pop()
|
||||||
|
t.Log("pop:", elem, "-", ret, " len:", queue.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("is empty :", queue.IsEmpty())
|
||||||
|
t.Log("is full :", queue.IsFull())
|
||||||
|
}
|
||||||
@@ -51,7 +51,6 @@ func NewPool(C chan interface{},New func()interface{}) *Pool{
|
|||||||
func NewPoolEx(C chan IPoolData,New func()IPoolData) *PoolEx{
|
func NewPoolEx(C chan IPoolData,New func()IPoolData) *PoolEx{
|
||||||
var pool PoolEx
|
var pool PoolEx
|
||||||
pool.C = C
|
pool.C = C
|
||||||
//pool.New = New
|
|
||||||
pool.syncPool.New = func() interface{} {
|
pool.syncPool.New = func() interface{} {
|
||||||
return New()
|
return New()
|
||||||
}
|
}
|
||||||
@@ -61,10 +60,18 @@ func NewPoolEx(C chan IPoolData,New func()IPoolData) *PoolEx{
|
|||||||
func (pool *PoolEx) Get() IPoolData{
|
func (pool *PoolEx) Get() IPoolData{
|
||||||
select {
|
select {
|
||||||
case d := <-pool.C:
|
case d := <-pool.C:
|
||||||
|
if d.IsRef() {
|
||||||
|
panic("Pool data is in use.")
|
||||||
|
}
|
||||||
|
|
||||||
d.Ref()
|
d.Ref()
|
||||||
return d
|
return d
|
||||||
default:
|
default:
|
||||||
data := pool.syncPool.Get().(IPoolData)
|
data := pool.syncPool.Get().(IPoolData)
|
||||||
|
if data.IsRef() {
|
||||||
|
panic("Pool data is in use.")
|
||||||
|
}
|
||||||
|
|
||||||
data.Ref()
|
data.Ref()
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
@@ -76,7 +83,10 @@ func (pool *PoolEx) Put(data IPoolData){
|
|||||||
if data.IsRef() == false {
|
if data.IsRef() == false {
|
||||||
panic("Repeatedly freeing memory")
|
panic("Repeatedly freeing memory")
|
||||||
}
|
}
|
||||||
|
//提前解引用,防止递归释放
|
||||||
|
data.UnRef()
|
||||||
data.Reset()
|
data.Reset()
|
||||||
|
//再次解引用,防止Rest时错误标记
|
||||||
data.UnRef()
|
data.UnRef()
|
||||||
select {
|
select {
|
||||||
case pool.C <- data:
|
case pool.C <- data:
|
||||||
|
|||||||
Reference in New Issue
Block a user