├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── _config.yml
├── client
├── group
│ └── group.go
└── p2p
│ └── p2p.go
├── codec
├── binary.go
├── protobuf.go
├── reader.go
├── spliter.go
└── writer.go
├── common
├── conf
│ └── conf.go
├── constant
│ └── const.go
├── dao
│ ├── kafka
│ │ └── kafka.go
│ ├── mongodb
│ │ └── mongodb.go
│ ├── xhbase
│ │ └── xhbase.go
│ ├── xmysql
│ │ └── xmysql.go
│ └── xredis
│ │ └── xredis.go
├── ecode
│ ├── common.go
│ └── ecode.go
├── itime
│ ├── debug.go
│ ├── tick.go
│ ├── tick_test.go
│ ├── timer.go
│ └── timer_test.go
├── model
│ ├── kafka.go
│ └── mongo.go
├── net
│ ├── netutil
│ │ ├── listen.go
│ │ └── listen_test.go
│ ├── trace
│ │ └── trace.go
│ ├── xhttp
│ │ ├── router
│ │ │ └── router.go
│ │ ├── server.go
│ │ └── xhttp.go
│ └── xweb
│ │ ├── context
│ │ └── context.go
│ │ ├── handler.go
│ │ └── router.go
└── xtime
│ └── xtime.go
├── conf_discovery
├── conf_discovery.go
├── etcd
│ ├── master.go
│ └── work.go
└── zookeeper
│ └── zk.go
├── deploy
├── docker
│ ├── access
│ │ └── Dockerfile
│ ├── gateway
│ │ └── Dockerfile
│ ├── logic
│ │ └── Dockerfile
│ └── register
│ │ └── Dockerfile
└── k8s
│ ├── kafka
│ ├── kafka-rc.yaml
│ └── kafka-svc.yaml
│ ├── logic
│ ├── logic-rc.yaml
│ └── logic-svc.yaml
│ ├── register
│ ├── register-rc.yaml
│ └── register-svc.yaml
│ └── zookeeper
│ ├── zookeeper-rc.yaml
│ └── zookeeper-svc.yaml
├── doc
├── architecture.png
├── architecture.xml
├── db
│ ├── hbase
│ │ └── hbase.data
│ └── mysql
│ │ └── schema.sql
├── msg.png
└── msg.xml
├── http_server
├── group-api
│ ├── conf
│ │ └── conf.go
│ ├── group-api.go
│ ├── group-api.toml
│ ├── http
│ │ └── http.go
│ ├── model
│ │ └── model.go
│ ├── rpc
│ │ ├── client
│ │ │ └── register.go
│ │ └── rpc_client.go
│ └── service
│ │ └── service.go
├── msg-api
│ ├── conf
│ │ └── conf.go
│ ├── http
│ │ └── http.go
│ ├── model
│ │ └── model.go
│ ├── msg-api.go
│ ├── msg-api.toml
│ ├── rpc
│ │ ├── client
│ │ │ └── manager.go
│ │ └── rpc_client.go
│ └── service
│ │ └── service.go
└── user-api
│ ├── conf
│ └── conf.go
│ ├── http
│ └── http.go
│ ├── model
│ └── model.go
│ ├── rpc
│ ├── client
│ │ └── register.go
│ └── rpc_client.go
│ ├── service
│ └── service.go
│ ├── user-api.go
│ └── user-api.toml
├── jobs
└── msg-job
│ ├── msg-job-client
│ ├── msg-job-client.iml
│ └── pom.xml
│ ├── msg-job-core
│ ├── pom.xml
│ └── src
│ │ ├── main
│ │ ├── java
│ │ │ └── org
│ │ │ │ └── miaohong
│ │ │ │ └── jobs
│ │ │ │ └── msgjob
│ │ │ │ ├── ApplicationMain.java
│ │ │ │ ├── dal
│ │ │ │ ├── hbase
│ │ │ │ │ └── HBaseManager.java
│ │ │ │ ├── kafka
│ │ │ │ │ ├── KafkaConsumer.java
│ │ │ │ │ └── KafkaProducer.java
│ │ │ │ └── model
│ │ │ │ │ ├── KafkaGroupMsg.java
│ │ │ │ │ └── KafkaP2PMsg.java
│ │ │ │ ├── manager
│ │ │ │ └── InitManager.java
│ │ │ │ └── service
│ │ │ │ └── kafka
│ │ │ │ └── ConsumerService.java
│ │ └── resources
│ │ │ ├── META-INF
│ │ │ └── spring
│ │ │ │ └── application-service.xml
│ │ │ ├── application.properties
│ │ │ └── logback.xml
│ │ └── test
│ │ └── java
│ │ └── org
│ │ └── miaohong
│ │ └── jobs
│ │ └── msgjob
│ │ ├── AbstractTest.java
│ │ └── dal
│ │ ├── HBaseManagerTest.java
│ │ └── KafkaConsumerTest.java
│ └── pom.xml
├── libnet
├── api.go
├── channel.go
├── channel_gen.go
├── manager.go
├── server.go
└── session.go
├── protocol
├── common
│ └── common.proto
├── external
│ ├── access.pb.go
│ ├── access.proto
│ ├── base.pb.go
│ ├── base.proto
│ ├── cmd.go
│ ├── error.pb.go
│ ├── error.proto
│ ├── gateway.pb.go
│ └── gateway.proto
├── genpb.sh
└── rpc
│ ├── access.pb.go
│ ├── access.proto
│ ├── idgen.pb.go
│ ├── idgen.proto
│ ├── logic.pb.go
│ ├── logic.proto
│ ├── manager.pb.go
│ ├── manager.proto
│ ├── notify.pb.go
│ ├── notify.proto
│ ├── register.pb.go
│ └── register.proto
├── server
├── access
│ ├── access.go
│ ├── access.toml
│ ├── access_11001_20001.toml
│ ├── access_11002_20002.toml
│ ├── client
│ │ ├── client.go
│ │ ├── proto_proc.go
│ │ └── util.go
│ ├── conf
│ │ └── conf.go
│ ├── global
│ │ └── session.go
│ ├── rpc
│ │ ├── client
│ │ │ └── logic.go
│ │ ├── rpc_client.go
│ │ └── rpc_server.go
│ └── server
│ │ └── server.go
├── gateway
│ ├── client
│ │ ├── client.go
│ │ └── proto_proc.go
│ ├── conf
│ │ └── conf.go
│ ├── gateway.go
│ ├── gateway.toml
│ ├── job
│ │ └── conf_discovery.go
│ └── server
│ │ └── server.go
├── logic
│ ├── conf
│ │ └── conf.go
│ ├── dao
│ │ ├── dao.go
│ │ ├── es.go
│ │ └── kafka.go
│ ├── logic.go
│ ├── logic.toml
│ ├── logic_21001.toml
│ ├── logic_21002.toml
│ └── rpc
│ │ ├── client
│ │ ├── idgen.go
│ │ ├── manager.go
│ │ ├── notify.go
│ │ └── register.go
│ │ ├── rpc_client.go
│ │ └── rpc_server.go
├── manager
│ ├── conf
│ │ └── conf.go
│ ├── dao
│ │ ├── dao.go
│ │ ├── hbase.go
│ │ ├── mongodb.go
│ │ ├── mysql.go
│ │ └── redis.go
│ ├── manager.go
│ ├── manager.toml
│ ├── manager_24001.toml
│ ├── manager_24002.toml
│ ├── model
│ │ └── model.go
│ └── rpc
│ │ ├── rpc_server.go
│ │ └── rpc_server_test.go
├── notify
│ ├── conf
│ │ └── conf.go
│ ├── conf_discovery
│ │ └── conf_discovery.go
│ ├── dao
│ │ ├── dao.go
│ │ ├── mysql.go
│ │ └── redis.go
│ ├── model
│ │ └── model.go
│ ├── notify.go
│ ├── notify.toml
│ └── rpc
│ │ ├── client
│ │ └── access.go
│ │ ├── rpc_client.go
│ │ └── rpc_server.go
├── port
└── register
│ ├── conf
│ └── conf.go
│ ├── dao
│ ├── dao.go
│ ├── mysql.go
│ └── redis.go
│ ├── model
│ └── user.go
│ ├── register.go
│ ├── register.toml
│ ├── register_23001.toml
│ ├── register_23002.toml
│ └── rpc
│ ├── client
│ ├── idegen_test.go
│ └── idgen.go
│ ├── rpc_client.go
│ └── rpc_server.go
├── service
└── idgen
│ ├── README.md
│ ├── conf
│ └── conf.go
│ ├── dao
│ ├── dao.go
│ └── etcd.go
│ ├── idgen.go
│ ├── idgen.toml
│ ├── idgen_31001.toml
│ ├── idgen_31002.toml
│ └── rpc
│ ├── rpc_server.go
│ └── rpc_server_test.go
├── service_discovery
├── etcd
│ ├── register.go
│ ├── resolver.go
│ └── watcher.go
└── lib
│ ├── lib.go
│ └── lib_test.go
└── trash
├── es_job
├── conf
│ └── conf.go
├── es_job.toml
├── main.go
└── service
│ └── service.go
└── msg_job
├── conf
└── conf.go
├── conf_discovery
└── conf_discovery.go
├── dao
├── dao.go
├── kafka.go
└── mongodb.go
├── msg_job.go
├── msg_job.toml
├── rpc
├── client
│ └── access_server.go
└── rpc_client.go
└── service
└── service.go
/.gitignore:
--------------------------------------------------------------------------------
1 | #folder
2 | target/
3 | .vagrant/
4 |
5 | #fixed file
6 | .class
7 | .exe
8 | .log
9 | .prefs
10 | .classpath
11 | .metadata
12 | .settings
13 | .DS_Store
14 | Thumbs.db
15 | .project
16 | *.iml
17 | *.ipr
18 | *.iws
19 | *.tar.gz
20 | *.bak
21 | .idea
22 | *.log
23 | *.swp
24 | *.swo
25 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | sudo: required
3 | install: true
4 | go:
5 | - 1.7.5
6 |
7 | env:
8 | global:
9 | - GOARCH=amd64
10 | - GO_FOR_RELEASE=1.7.5
11 |
12 | install:
13 | - go get -u -d github.com/golang/glog
14 | - go get -u -d github.com/coreos/etcd
15 | - go get -u -d github.com/Shopify/sarama
16 | - go get -u -d github.com/wvanbergen/kafka/consumergroup
17 | - go get -u -d github.com/tsuna/gohbase
18 | - go get -u -d github.com/garyburd/redigo/redis
19 | - go get -u -d github.com/BurntSushi/toml
20 | - go get -u -d gopkg.in/olivere/elastic.v5
21 | - go get -u -d gopkg.in/mgo.v2
22 | - go get -u -d github.com/go-sql-driver/mysql
23 | - go get -u -d github.com/satori/go.uuid
24 |
25 | exclude: [trash]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Harold Miao (miaohong@miaohong.org)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/codec/protobuf.go:
--------------------------------------------------------------------------------
1 | package codec
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/golang/protobuf/proto"
6 | "github.com/oikomi/FishChatServer2/libnet"
7 | "io"
8 | "reflect"
9 | )
10 |
11 | type ProtobufProtocol struct {
12 | types map[string]reflect.Type
13 | names map[reflect.Type]string
14 | }
15 |
16 | func Protobuf() *ProtobufProtocol {
17 | return &ProtobufProtocol{
18 | types: make(map[string]reflect.Type),
19 | names: make(map[reflect.Type]string),
20 | }
21 | }
22 |
23 | func (j *ProtobufProtocol) Register(t interface{}) {
24 | rt := reflect.TypeOf(t)
25 | if rt.Kind() == reflect.Ptr {
26 | rt = rt.Elem()
27 | }
28 | name := rt.PkgPath() + "/" + rt.Name()
29 | j.types[name] = rt
30 | j.names[rt] = name
31 | }
32 |
33 | func (j *ProtobufProtocol) RegisterName(name string, t interface{}) {
34 | rt := reflect.TypeOf(t)
35 | if rt.Kind() == reflect.Ptr {
36 | rt = rt.Elem()
37 | }
38 | j.types[name] = rt
39 | j.names[rt] = name
40 | }
41 |
42 | func (p *ProtobufProtocol) NewCodec(rw io.ReadWriter) libnet.Codec {
43 | codec := &protobufCodec{
44 | p: p,
45 | w: NewWriter(rw),
46 | r: NewReader(rw),
47 | }
48 | codec.closer, _ = rw.(io.Closer)
49 | return codec
50 | }
51 |
52 | type protobufCodec struct {
53 | p *ProtobufProtocol
54 | w *Writer
55 | r *Reader
56 | closer io.Closer
57 | }
58 |
59 | func (c *protobufCodec) Receive() ([]byte, error) {
60 | data := c.r.ReadPacket(SplitByUint16BE)
61 | return data, nil
62 | }
63 |
64 | func (c *protobufCodec) Send(msg interface{}) error {
65 | data, err := proto.Marshal(msg.(proto.Message))
66 | if err != nil {
67 | glog.Error(err)
68 | return err
69 | }
70 | c.w.WritePacket(data, SplitByUint16BE)
71 | return nil
72 | }
73 | func (c *protobufCodec) Close() error {
74 | if c.closer != nil {
75 | return c.closer.Close()
76 | }
77 | return nil
78 | }
79 |
--------------------------------------------------------------------------------
/codec/spliter.go:
--------------------------------------------------------------------------------
1 | package codec
2 |
3 | import (
4 | "io"
5 | )
6 |
7 | type HeadSpliter struct {
8 | ReadHead func(r *Reader) int
9 | WriteHead func(w *Writer, l int)
10 | }
11 |
12 | func (s HeadSpliter) Read(r *Reader) []byte {
13 | n := s.ReadHead(r)
14 | if r.Error() != nil {
15 | return nil
16 | }
17 | b := make([]byte, n)
18 | if _, err := io.ReadFull(r, b); err != nil {
19 | return nil
20 | }
21 | return b
22 | }
23 |
24 | func (s HeadSpliter) Write(w *Writer, b []byte) {
25 | s.WriteHead(w, len(b))
26 | if w.Error() != nil {
27 | return
28 | }
29 | w.Write(b)
30 | }
31 |
32 | func (s HeadSpliter) Limit(r *Reader) *io.LimitedReader {
33 | n := s.ReadHead(r)
34 | return &io.LimitedReader{r, int64(n)}
35 | }
36 |
37 | var (
38 | SplitByUint16BE = HeadSpliter{
39 | ReadHead: func(r *Reader) int { return int(r.ReadUint16BE()) },
40 | WriteHead: func(w *Writer, l int) { w.WriteUint16BE(uint16(l)) },
41 | }
42 | SplitByUint16LE = HeadSpliter{
43 | ReadHead: func(r *Reader) int { return int(r.ReadUint16LE()) },
44 | WriteHead: func(w *Writer, l int) { w.WriteUint16LE(uint16(l)) },
45 | }
46 | )
47 |
--------------------------------------------------------------------------------
/common/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "github.com/oikomi/FishChatServer2/common/xtime"
5 | )
6 |
7 | type CommConf struct {
8 | Ver string
9 | LogPath string
10 | }
11 |
12 | // =================================== HTTP ==================================
13 | // HTTPServer http server settings.
14 | type HTTPServer struct {
15 | Addrs []string
16 | MaxListen int32
17 | ReadTimeout xtime.Duration
18 | WriteTimeout xtime.Duration
19 | }
20 |
21 | // HTTPClient http client settings.
22 | type HTTPClient struct {
23 | Dial xtime.Duration
24 | Timeout xtime.Duration
25 | KeepAlive xtime.Duration
26 | Timer int
27 | }
28 |
29 | // MultiHttp outer/inner/local http server settings.
30 | type MultiHTTP struct {
31 | Outer *HTTPServer
32 | Inner *HTTPServer
33 | Local *HTTPServer
34 | }
35 |
36 | type Server struct {
37 | Proto string
38 | Addr string
39 | }
40 |
41 | type RPCServer struct {
42 | Proto string
43 | Addr string
44 | }
45 |
46 | type ConfDiscovery struct {
47 | Role string
48 | Interval xtime.Duration
49 | }
50 |
51 | type ServiceDiscoveryServer struct {
52 | ServiceName string
53 | RPCAddr string
54 | EtcdAddr string
55 | Interval xtime.Duration
56 | TTL xtime.Duration
57 | }
58 |
59 | type ServiceDiscoveryClient struct {
60 | ServiceName string
61 | EtcdAddr string
62 | Balancer string
63 | }
64 |
65 | type Etcd struct {
66 | Name string
67 | Root string
68 | Addrs []string
69 | Timeout xtime.Duration
70 | }
71 |
72 | type Zookeeper struct {
73 | Root string
74 | Addrs []string
75 | Timeout xtime.Duration
76 | }
77 |
78 | // Redis client settings.
79 | type Redis struct {
80 | Name string // redis name, for trace
81 | Proto string
82 | Addr string
83 | Active int // pool
84 | Idle int // pool
85 | DialTimeout xtime.Duration
86 | ReadTimeout xtime.Duration
87 | WriteTimeout xtime.Duration
88 | IdleTimeout xtime.Duration
89 | }
90 |
91 | // KafkaProducer kafka producer settings.
92 | type KafkaProducer struct {
93 | Zookeeper *Zookeeper
94 | Brokers []string
95 | Sync bool // true: sync, false: async
96 | }
97 |
98 | // KafkaConsumer kafka client settings.
99 | type KafkaConsumer struct {
100 | Group string
101 | Topics []string
102 | Offset bool // true: new, false: old
103 | Zookeeper *Zookeeper
104 | }
105 |
106 | type MySQL struct {
107 | Name string // for trace
108 | DSN string // data source name
109 | Active int // pool
110 | Idle int // pool
111 | }
112 |
113 | type MongoDB struct {
114 | Addrs string
115 | DB string
116 | DialTimeout xtime.Duration
117 | }
118 |
119 | type ES struct {
120 | Addrs string
121 | }
122 |
--------------------------------------------------------------------------------
/common/constant/const.go:
--------------------------------------------------------------------------------
1 | package constant
2 |
3 | const (
4 | ZOOKEEPER int = 0
5 | ETCD int = 1
6 | )
7 |
--------------------------------------------------------------------------------
/common/dao/mongodb/mongodb.go:
--------------------------------------------------------------------------------
1 | package mongodb
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/common/conf"
6 | "gopkg.in/mgo.v2"
7 | "time"
8 | )
9 |
10 | type MongoDB struct {
11 | Session *mgo.Session
12 | }
13 |
14 | func NewMongoDB(c *conf.MongoDB) (m *MongoDB, err error) {
15 | session, err := mgo.DialWithTimeout(c.Addrs, time.Duration(c.DialTimeout))
16 | if err != nil {
17 | glog.Error(err)
18 | return
19 | }
20 | m = &MongoDB{
21 | Session: session,
22 | }
23 | return
24 | }
25 |
--------------------------------------------------------------------------------
/common/dao/xhbase/xhbase.go:
--------------------------------------------------------------------------------
1 | package xhbase
2 |
3 | import (
4 | "github.com/tsuna/gohbase"
5 | "github.com/tsuna/gohbase/hrpc"
6 | "golang.org/x/net/context"
7 | )
8 |
9 | const (
10 | _module = "hbase"
11 | )
12 |
13 | type Client struct {
14 | c gohbase.Client
15 | // testCell *gohbase.HBaseCell
16 | }
17 |
18 | func (c *Client) Scan(ctx context.Context, s *hrpc.Scan) ([]*hrpc.Result, error) {
19 | return c.c.Scan(s)
20 | }
21 |
22 | func (c *Client) Get(ctx context.Context, g *hrpc.Get) (*hrpc.Result, error) {
23 | return c.c.Get(g)
24 | }
25 |
26 | func (c *Client) Put(ctx context.Context, p *hrpc.Mutate) (*hrpc.Result, error) {
27 | return c.c.Put(p)
28 | }
29 |
30 | func (c *Client) Delete(ctx context.Context, d *hrpc.Mutate) (*hrpc.Result, error) {
31 | return c.c.Delete(d)
32 | }
33 |
34 | func (c *Client) Append(ctx context.Context, a *hrpc.Mutate) (*hrpc.Result, error) {
35 | return c.c.Append(a)
36 | }
37 |
38 | func (c *Client) Increment(ctx context.Context, i *hrpc.Mutate) (int64, error) {
39 | return c.c.Increment(i)
40 | }
41 |
42 | func (c *Client) Close() {
43 | c.c.Close()
44 | }
45 |
46 | func NewClient(zkquorum string, options ...gohbase.Option) *Client {
47 | return &Client{
48 | c: gohbase.NewClient(zkquorum),
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/common/dao/xredis/xredis.go:
--------------------------------------------------------------------------------
1 | package xredis
2 |
3 | import (
4 | "fmt"
5 | "github.com/garyburd/redigo/redis"
6 | "github.com/golang/glog"
7 | "github.com/oikomi/FishChatServer2/common/conf"
8 | "golang.org/x/net/context"
9 | "time"
10 | )
11 |
12 | const (
13 | _module = "redis"
14 | )
15 |
16 | type conn struct {
17 | p *Pool
18 | c redis.Conn
19 | ctx context.Context
20 | }
21 |
22 | type Pool struct {
23 | *redis.Pool
24 | env string
25 | }
26 |
27 | func NewPool(c *conf.Redis) (p *Pool) {
28 | p = &Pool{env: fmt.Sprintf("[%s]%s@%s", c.Name, c.Proto, c.Addr)}
29 | // dt := redis.DialTimer(itime.NewTimer(c.Active))
30 | cnop := redis.DialConnectTimeout(time.Duration(c.DialTimeout))
31 | rdop := redis.DialReadTimeout(time.Duration(c.ReadTimeout))
32 | wrop := redis.DialWriteTimeout(time.Duration(c.WriteTimeout))
33 | p.Pool = redis.NewPool(func() (rconn redis.Conn, err error) {
34 | rconn, err = redis.Dial(c.Proto, c.Addr, cnop, rdop, wrop)
35 | if err != nil {
36 | glog.Error(err)
37 | // panic(err)
38 | }
39 | return
40 | }, c.Idle)
41 | p.IdleTimeout = time.Duration(c.IdleTimeout)
42 | p.MaxActive = c.Active
43 | return
44 | }
45 |
46 | func (p *Pool) Get(ctx context.Context) redis.Conn {
47 | return &conn{p: p, c: p.Pool.Get(), ctx: ctx}
48 | }
49 |
50 | func (p *Pool) Close() error {
51 | return p.Pool.Close()
52 | }
53 |
54 | func (c *conn) Err() error {
55 | return c.c.Err()
56 | }
57 |
58 | func (c *conn) Close() error {
59 | return c.c.Close()
60 | }
61 |
62 | func (c *conn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
63 | if err = c.Err(); err != nil {
64 | return
65 | }
66 | return c.c.Do(commandName, args...)
67 | }
68 |
69 | // NOTE not goroutine safe
70 | func (c *conn) Send(commandName string, args ...interface{}) (err error) {
71 | if err = c.Err(); err != nil {
72 | return
73 | }
74 | return c.c.Send(commandName, args...)
75 | }
76 |
77 | func (c *conn) Flush() error {
78 | return c.c.Flush()
79 | }
80 |
81 | // NOTE not goroutine safe
82 | func (c *conn) Receive() (reply interface{}, err error) {
83 | if err = c.Err(); err != nil {
84 | return
85 | }
86 | return c.c.Receive()
87 | }
88 |
--------------------------------------------------------------------------------
/common/ecode/common.go:
--------------------------------------------------------------------------------
1 | package ecode
2 |
3 | const (
4 | //api
5 | RequestErr ecode = 10001
6 |
7 | // common error code
8 | OK ecode = 0
9 | // server
10 | ServerErr ecode = 90001
11 |
12 | //gateway
13 | NoAccessServer ecode = 92001
14 |
15 | // access
16 | NoToken ecode = 93001
17 | CalcTokenFailed ecode = 93002
18 |
19 | // register
20 | UserIsAlreadyExist ecode = 94001
21 |
22 | // network
23 | NoData ecode = 91001
24 | //
25 | )
26 |
--------------------------------------------------------------------------------
/common/ecode/ecode.go:
--------------------------------------------------------------------------------
1 | package ecode
2 |
3 | import (
4 | "strconv"
5 | )
6 |
7 | type ecode uint32
8 |
9 | func (e ecode) Error() string {
10 | return strconv.FormatInt(int64(e), 10)
11 | }
12 |
13 | func (e ecode) String() string {
14 | return ecodeMessage[e]
15 | }
16 |
17 | func (e ecode) Uint32() uint32 {
18 | return uint32(e)
19 | }
20 |
21 | func To(i uint32) error {
22 | return ecode(i)
23 | }
24 |
25 | func From(e error) ecode {
26 | i, err := strconv.ParseInt(e.Error(), 10, 64)
27 | if err != nil {
28 | return ServerErr
29 | }
30 | if _, ok := ecodeMessage[ecode(i)]; !ok {
31 | return ServerErr
32 | }
33 | return ecode(i)
34 | }
35 |
36 | var (
37 | ecodeMessage = map[ecode]string{
38 | // api
39 | RequestErr: "request err",
40 |
41 | // common
42 | OK: "ok",
43 | // common
44 | ServerErr: "server error", // 服务器错误
45 |
46 | // gateway
47 | NoAccessServer: "no accessServer",
48 |
49 | // access
50 | NoToken: "no token",
51 | CalcTokenFailed: "calc token failed",
52 |
53 | // register
54 | UserIsAlreadyExist: "user is already exist",
55 |
56 | // network
57 | NoData: "no data found",
58 | }
59 | )
60 |
--------------------------------------------------------------------------------
/common/itime/debug.go:
--------------------------------------------------------------------------------
1 | package itime
2 |
3 | const (
4 | debug = false
5 | )
6 |
--------------------------------------------------------------------------------
/common/itime/tick.go:
--------------------------------------------------------------------------------
1 | package itime
2 |
3 | import (
4 | "errors"
5 | itime "time"
6 | )
7 |
8 | // A Ticker holds a channel that delivers `ticks' of a clock
9 | // at intervals.
10 | type Ticker struct {
11 | C <-chan itime.Time // The channel on which the ticks are delivered.
12 | c chan itime.Time
13 | td *TimerData
14 | t *Timer
15 | }
16 |
17 | // NewTicker returns a new Ticker containing a channel that will send the
18 | // time with a period specified by the duration argument.
19 | // It adjusts the intervals or drops ticks to make up for slow receivers.
20 | // The duration d must be greater than zero; if not, NewTicker will panic.
21 | // Stop the ticker to release associated resources.
22 | func NewTicker(t *Timer, d itime.Duration) *Ticker {
23 | if d <= 0 {
24 | panic(errors.New("non-positive interval for NewTicker"))
25 | }
26 | var (
27 | c = timePool.Get().(chan itime.Time)
28 | td = t.StartPeriod(d, func() {
29 | // Non-blocking send of time on c.
30 | // Used in NewTimer, it cannot block anyway (buffer).
31 | // Used in NewTicker, dropping sends on the floor is
32 | // the desired behavior when the reader gets behind,
33 | // because the sends are periodic.
34 | select {
35 | case c <- itime.Now():
36 | default:
37 | }
38 | })
39 | )
40 | return &Ticker{
41 | C: c,
42 | c: c,
43 | t: t,
44 | td: td,
45 | }
46 | }
47 |
48 | // Stop turns off a ticker. After Stop, no more ticks will be sent.
49 | // Stop does not close the channel, to prevent a read from the channel succeeding
50 | // incorrectly.
51 | func (t *Ticker) Stop() {
52 | if t.td.Stop() {
53 | timePool.Put(t.c)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/common/itime/tick_test.go:
--------------------------------------------------------------------------------
1 | package itime
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func TestTicker(t *testing.T) {
9 | tr := NewTicker(globalTimer, 1*time.Second)
10 | now := time.Now().Unix()
11 | for i := 0; i < 3; i++ {
12 | if after := <-tr.C; after.Unix()-now != int64(i+1) {
13 | t.FailNow()
14 | }
15 | }
16 | tr.Stop()
17 | }
18 |
--------------------------------------------------------------------------------
/common/itime/timer_test.go:
--------------------------------------------------------------------------------
1 | package itime
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func TestTimerFreeList(t *testing.T) {
9 | // get, put, grow
10 | timer := new(Timer)
11 | timer.size = 1
12 | td := timer.get()
13 | if td == nil {
14 | t.FailNow()
15 | }
16 | if timer.free != nil {
17 | t.FailNow()
18 | }
19 | td1 := timer.get()
20 | if td1 == nil {
21 | t.FailNow()
22 | }
23 | timer.put(td)
24 | if timer.free != td {
25 | t.FailNow()
26 | }
27 | timer.put(td1)
28 | if timer.free != td1 {
29 | t.FailNow()
30 | }
31 | if td1.next != td {
32 | t.FailNow()
33 | }
34 | }
35 |
36 | func TestTimer(t *testing.T) {
37 | timer := NewTimer(100)
38 | tds := make([]*TimerData, 100)
39 | for i := 0; i < 100; i++ {
40 | tds[i] = timer.Start(time.Duration(i)*time.Second+5*time.Minute, nil)
41 | }
42 | for i := 0; i < 100; i++ {
43 | tds[i].Stop()
44 | }
45 | for i := 0; i < 100; i++ {
46 | tds[i] = timer.Start(time.Duration(i)*time.Second+5*time.Minute, nil)
47 | }
48 | for i := 0; i < 100; i++ {
49 | tds[i].Stop()
50 | }
51 | // Start
52 | timer.Start(time.Second, nil)
53 | time.Sleep(time.Second * 2)
54 | if len(timer.timers) != 0 {
55 | t.FailNow()
56 | }
57 | i := 0
58 | timer.Start(time.Second, func() {
59 | i++
60 | })
61 | timer.Start(time.Millisecond*500, func() {
62 | i++
63 | })
64 | time.Sleep(time.Millisecond * 510)
65 | if i != 1 {
66 | t.Errorf("i: %d", i)
67 | t.FailNow()
68 | }
69 | time.Sleep(time.Millisecond * 510)
70 | if i != 2 {
71 | t.Errorf("i: %d", i)
72 | t.FailNow()
73 | }
74 | // StartPeriod
75 | }
76 |
77 | func TestAfter(t *testing.T) {
78 | now := time.Now().Unix()
79 | after := After(time.Second * 1)
80 | if after.Unix()-now != 1 {
81 | t.FailNow()
82 | }
83 | }
84 |
85 | func TestAfterFunc(t *testing.T) {
86 | i := 0
87 | td := AfterFunc(time.Second*1, func() {
88 | i++
89 | })
90 | time.Sleep(time.Second * 2)
91 | td.Stop()
92 | if i != 1 {
93 | t.Errorf("i: %d", i)
94 | t.FailNow()
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/common/model/kafka.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | const (
4 | SendP2PMsgKey = "send_p2p_msg"
5 | SendGroupMsgKey = "send_group_msg"
6 | )
7 |
8 | type SendP2PMsgKafka struct {
9 | IncrementID int64 `json:"incrementID"`
10 | SourceUID int64 `json:"sourceUID"`
11 | TargetUID int64 `json:"targetUID"`
12 | MsgID string `json:"msgID"`
13 | MsgType string `json:"msgType"`
14 | Msg string `json:"msg"`
15 | AccessServerAddr string `json:"accessServerAddr"`
16 | Online bool `json:"online"`
17 | }
18 |
19 | type SendGroupMsgKafka struct {
20 | IncrementID int64 `json:"incrementID"`
21 | SourceUID int64 `json:"sourceUID"`
22 | TargetUID int64 `json:"targetUID"`
23 | GroupID int64 `json:"groupID"`
24 | MsgType string `json:"msgType"`
25 | MsgID string `json:"msgID"`
26 | Msg string `json:"msg"`
27 | Online bool `json:"online"`
28 | }
29 |
--------------------------------------------------------------------------------
/common/model/mongo.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "github.com/oikomi/FishChatServer2/common/xtime"
5 | )
6 |
7 | type ExceptionMsg struct {
8 | SourceUID int64
9 | TargetUID int64
10 | MsgID string
11 | Msg string
12 | }
13 |
14 | type OfflineMsg struct {
15 | MsgID string
16 | SourceUID int64
17 | TargetUID int64
18 | Msg string
19 | CTime xtime.Time
20 | MTime xtime.Time
21 | Flag bool
22 | }
23 |
24 | type OfflineMsgs struct {
25 | Msgs []*OfflineMsg
26 | }
27 |
28 | type Group struct {
29 | }
30 |
--------------------------------------------------------------------------------
/common/net/netutil/listen.go:
--------------------------------------------------------------------------------
1 | package netutil
2 |
3 | import (
4 | "errors"
5 | "net"
6 | "sync"
7 | "sync/atomic"
8 | )
9 |
10 | var (
11 | ErrLimitListener = errors.New("LimitListener: limit")
12 | )
13 |
14 | // LimitListener returns a Listener that accepts at most n simultaneous
15 | // connections from the provided Listener.
16 | func LimitListener(l net.Listener, n int32) net.Listener {
17 | return &limitListener{l, 0, n, make(chan struct{}, n)}
18 | }
19 |
20 | type limitListener struct {
21 | net.Listener
22 | cur int32
23 | max int32
24 | sem chan struct{}
25 | }
26 |
27 | func (l *limitListener) acquire() (ok bool) {
28 | ok = true
29 | if cur := atomic.AddInt32(&l.cur, 1); cur > l.max {
30 | select {
31 | case l.sem <- struct{}{}:
32 | default:
33 | ok = false
34 | }
35 | }
36 | return
37 | }
38 |
39 | func (l *limitListener) release() {
40 | if cur := atomic.AddInt32(&l.cur, -1); cur >= l.max {
41 | <-l.sem
42 | }
43 | return
44 | }
45 |
46 | func (l *limitListener) Accept() (net.Conn, error) {
47 | ok := l.acquire()
48 | c, err := l.Listener.Accept()
49 | if err != nil {
50 | l.release()
51 | return nil, err
52 | }
53 | if !ok {
54 | l.release()
55 | c.Close()
56 | return nil, ErrLimitListener
57 | }
58 | return &limitListenerConn{Conn: c, release: l.release}, nil
59 | }
60 |
61 | type limitListenerConn struct {
62 | net.Conn
63 | once sync.Once
64 | release func()
65 | }
66 |
67 | func (l *limitListenerConn) Close() error {
68 | err := l.Conn.Close()
69 | l.once.Do(l.release)
70 | return err
71 | }
72 |
--------------------------------------------------------------------------------
/common/net/netutil/listen_test.go:
--------------------------------------------------------------------------------
1 | package netutil
2 |
--------------------------------------------------------------------------------
/common/net/xhttp/server.go:
--------------------------------------------------------------------------------
1 | package xhttp
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/common/conf"
6 | "github.com/oikomi/FishChatServer2/common/net/netutil"
7 | "net"
8 | "net/http"
9 | "runtime"
10 | "time"
11 | )
12 |
13 | // Serve listen and serve http handlers with limit count.
14 | func Serve(mux *http.ServeMux, c *conf.HTTPServer) (err error) {
15 | for _, addr := range c.Addrs {
16 | l, err := net.Listen("tcp", addr)
17 | if err != nil {
18 | glog.Errorf("net.Listen(\"tcp\", \"%s\") error(%v)", addr, err)
19 | return err
20 | }
21 | if c.MaxListen > 0 {
22 | l = netutil.LimitListener(l, c.MaxListen)
23 | }
24 | glog.Infof("start http listen addr: %s", addr)
25 | for i := 0; i < runtime.NumCPU(); i++ {
26 | go func() {
27 | server := &http.Server{Handler: mux, ReadTimeout: time.Duration(c.ReadTimeout), WriteTimeout: time.Duration(c.WriteTimeout)}
28 | if err := server.Serve(l); err != nil {
29 | glog.Errorf("server.Serve error(%v)", err)
30 | }
31 | }()
32 | }
33 | }
34 | return nil
35 | }
36 |
--------------------------------------------------------------------------------
/common/net/xweb/context/context.go:
--------------------------------------------------------------------------------
1 | package context
2 |
3 | import (
4 | ctx "golang.org/x/net/context"
5 | "net/http"
6 | "strings"
7 | "sync"
8 | "time"
9 | )
10 |
11 | // Context web context interface
12 | type Context interface {
13 | ctx.Context
14 | Request() *http.Request
15 | Response() http.ResponseWriter
16 | Result() map[string]interface{}
17 | Cancel()
18 | Now() time.Time
19 | Get(string) (interface{}, bool)
20 | Set(string, interface{})
21 | RemoteIP() string
22 | }
23 |
24 | // webCtx only used in xhttp/router
25 | type webCtx struct {
26 | ctx.Context
27 | cancel ctx.CancelFunc
28 | req *http.Request
29 | resp http.ResponseWriter
30 | res map[string]interface{}
31 | now time.Time
32 | lock sync.RWMutex
33 | data map[string]interface{}
34 | remoteIP string
35 | }
36 |
37 | // NewContext new a web context.
38 | func NewContext(c ctx.Context, req *http.Request, resp http.ResponseWriter) Context {
39 | wc := &webCtx{req: req, resp: resp, now: time.Now()}
40 | wc.Context, wc.cancel = ctx.WithCancel(c)
41 | wc.remoteIP = remoteIP(req)
42 | return wc
43 | }
44 |
45 | // Request get a http request.
46 | func (c *webCtx) Request() *http.Request {
47 | return c.req
48 | }
49 |
50 | // Response get a http response.
51 | func (c *webCtx) Response() http.ResponseWriter {
52 | return c.resp
53 | }
54 |
55 | // Cancel cancel handlers.
56 | func (c *webCtx) Cancel() {
57 | c.cancel()
58 | }
59 |
60 | // Now get current time.
61 | func (c *webCtx) Now() time.Time {
62 | return c.now
63 | }
64 |
65 | // Result set a web request when nil then return it.
66 | func (c *webCtx) Result() (res map[string]interface{}) {
67 | if res = c.res; res == nil {
68 | res = make(map[string]interface{})
69 | c.res = res
70 | }
71 | return
72 | }
73 |
74 | // Get a value by key in context.
75 | func (c *webCtx) Get(key string) (interface{}, bool) {
76 | c.lock.RLock()
77 | c.lock.RUnlock()
78 | if c.data == nil {
79 | return nil, false
80 | }
81 | v, ok := c.data[key]
82 | return v, ok
83 | }
84 |
85 | // Set a key-value to the context.
86 | func (c *webCtx) Set(key string, value interface{}) {
87 | c.lock.Lock()
88 | c.lock.Unlock()
89 | if c.data == nil {
90 | c.data = make(map[string]interface{})
91 | }
92 | c.data[key] = value
93 | return
94 | }
95 |
96 | // RemoteAddr allows HTTP servers and other software to record
97 | // the network address that sent the request.
98 | func (c *webCtx) RemoteIP() string {
99 | return c.remoteIP
100 | }
101 |
102 | // remoteIP get remote real ip.
103 | func remoteIP(r *http.Request) (remote string) {
104 | remote = r.Header.Get("X-Real-IP")
105 | if remote != "" {
106 | return
107 | }
108 | remote = r.Header.Get("X-Forwarded-For")
109 | if idx := strings.LastIndex(remote, ","); idx > -1 {
110 | if remote = strings.TrimSpace(remote[idx+1:]); remote != "" {
111 | return
112 | }
113 | }
114 | remote = r.RemoteAddr[0:strings.Index(r.RemoteAddr, ":")]
115 | return
116 | }
117 |
--------------------------------------------------------------------------------
/common/net/xweb/handler.go:
--------------------------------------------------------------------------------
1 | package xweb
2 |
3 | import (
4 | "github.com/oikomi/FishChatServer2/common/net/xweb/context"
5 | )
6 |
7 | type Handler interface {
8 | ServeHTTP(context.Context)
9 | }
10 |
11 | type HandlerFunc func(context.Context)
12 |
13 | // ServeHTTP calls f(w, r).
14 | func (f HandlerFunc) ServeHTTP(c context.Context) {
15 | f(c)
16 | }
17 |
--------------------------------------------------------------------------------
/common/xtime/xtime.go:
--------------------------------------------------------------------------------
1 | package xtime
2 |
3 | import (
4 | "database/sql/driver"
5 | "strconv"
6 | "time"
7 | )
8 |
9 | // Time be used to MySql timestamp converting.
10 | type Time int64
11 |
12 | func (jt *Time) Scan(src interface{}) (err error) {
13 | switch sc := src.(type) {
14 | case time.Time:
15 | *jt = Time(sc.Unix())
16 | case string:
17 | var i int64
18 | i, err = strconv.ParseInt(sc, 10, 64)
19 | *jt = Time(i)
20 | }
21 | return
22 | }
23 |
24 | func (jt Time) Value() (driver.Value, error) {
25 | return time.Unix(int64(jt), 0), nil
26 | }
27 |
28 | func (jt Time) Time() time.Time {
29 | return time.Unix(int64(jt), 0)
30 | }
31 |
32 | // Duration be used toml unmarshal string time, like 1s, 500ms.
33 | type Duration time.Duration
34 |
35 | func (d *Duration) UnmarshalText(text []byte) error {
36 | tmp, err := time.ParseDuration(string(text))
37 | if err == nil {
38 | *d = Duration(tmp)
39 | }
40 | return err
41 | }
42 |
--------------------------------------------------------------------------------
/conf_discovery/conf_discovery.go:
--------------------------------------------------------------------------------
1 | package conf_discovery
2 |
3 | import (
4 | "github.com/oikomi/FishChatServer2/common/constant"
5 | )
6 |
7 | type Heart struct {
8 | }
9 |
10 | func (h *Heart) Heart(protocol int) {
11 | if protocol == constant.ZOOKEEPER {
12 |
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/conf_discovery/etcd/master.go:
--------------------------------------------------------------------------------
1 | package etcd
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "github.com/coreos/etcd/clientv3"
7 | "github.com/golang/glog"
8 | commconf "github.com/oikomi/FishChatServer2/common/conf"
9 | "time"
10 | )
11 |
12 | // Master is a server
13 | type Master struct {
14 | members map[string]*Member
15 | etcCli *clientv3.Client
16 | rootPath string
17 | }
18 |
19 | // Member is a client
20 | type Member struct {
21 | InGroup bool
22 | IP string
23 | Name string
24 | CPU int
25 | }
26 |
27 | func NewMaster(c *commconf.Etcd) (master *Master, err error) {
28 | var etcdClient *clientv3.Client
29 | cfg := clientv3.Config{
30 | Endpoints: c.Addrs,
31 | DialTimeout: time.Duration(c.Timeout),
32 | }
33 | if etcdClient, err = clientv3.New(cfg); err != nil {
34 | glog.Error("Error: cannot connec to etcd:", err)
35 | return
36 | }
37 | master = &Master{
38 | members: make(map[string]*Member),
39 | etcCli: etcdClient,
40 | rootPath: c.Root,
41 | }
42 | return
43 | }
44 |
45 | func (m *Master) Members() (ms map[string]*Member) {
46 | ms = m.members
47 | return
48 | }
49 |
50 | func (m *Master) addWorker(key string, info *WorkerInfo) {
51 | member := &Member{
52 | InGroup: true,
53 | IP: info.IP,
54 | Name: info.Name,
55 | CPU: info.CPU,
56 | }
57 | m.members[key] = member
58 | }
59 |
60 | func (m *Master) updateWorker(key string, info *WorkerInfo) {
61 | member := m.members[key]
62 | member.InGroup = true
63 | }
64 |
65 | func (m *Master) WatchWorkers() {
66 | rch := m.etcCli.Watch(context.Background(), m.rootPath, clientv3.WithPrefix())
67 | for wresp := range rch {
68 | for _, ev := range wresp.Events {
69 | // glog.Info(ev.Type, string(ev.Kv.Key), string(ev.Kv.Value))
70 | if ev.Type.String() == "EXPIRE" {
71 | member, ok := m.members[string(ev.Kv.Key)]
72 | if ok {
73 | member.InGroup = false
74 | delete(m.members, string(ev.Kv.Key))
75 | }
76 | } else if ev.Type.String() == "PUT" {
77 | info := &WorkerInfo{}
78 | err := json.Unmarshal(ev.Kv.Value, info)
79 | if err != nil {
80 | glog.Error(err)
81 | }
82 | if _, ok := m.members[string(ev.Kv.Key)]; ok {
83 | m.updateWorker(string(ev.Kv.Key), info)
84 | } else {
85 | m.addWorker(string(ev.Kv.Key), info)
86 | }
87 | } else if ev.Type.String() == "DELETE" {
88 | delete(m.members, string(ev.Kv.Key))
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/conf_discovery/etcd/work.go:
--------------------------------------------------------------------------------
1 | package etcd
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/coreos/etcd/clientv3"
6 | "github.com/golang/glog"
7 | "golang.org/x/net/context"
8 | "runtime"
9 | "time"
10 | )
11 |
12 | type Worker struct {
13 | Name string
14 | IP string
15 | rootPath string
16 | etcCli *clientv3.Client
17 | }
18 |
19 | // workerInfo is the service register information to etcd
20 | type WorkerInfo struct {
21 | Name string
22 | IP string
23 | CPU int
24 | }
25 |
26 | func NewWorker(name, ip, rootPath string, endpoints []string) *Worker {
27 | cfg := clientv3.Config{
28 | Endpoints: endpoints,
29 | DialTimeout: time.Second,
30 | }
31 | etcdClient, err := clientv3.New(cfg)
32 | if err != nil {
33 | glog.Error("Error: cannot connec to etcd:", err)
34 | }
35 | w := &Worker{
36 | Name: name,
37 | IP: ip,
38 | rootPath: rootPath,
39 | etcCli: etcdClient,
40 | }
41 | return w
42 | }
43 |
44 | func (w *Worker) HeartBeat() {
45 | for {
46 | info := &WorkerInfo{
47 | Name: w.Name,
48 | IP: w.IP,
49 | CPU: runtime.NumCPU(),
50 | }
51 | key := w.rootPath + w.Name
52 | value, err := json.Marshal(info)
53 | if err != nil {
54 | glog.Error(err)
55 | }
56 | resp, err := w.etcCli.Grant(context.TODO(), 10)
57 | if err != nil {
58 | glog.Error(err)
59 | }
60 | _, err = w.etcCli.Put(context.TODO(), key, string(value), clientv3.WithLease(resp.ID))
61 | if err != nil {
62 | glog.Error("Error: cannot put to etcd:", err)
63 | }
64 | time.Sleep(time.Second * 5)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/deploy/docker/access/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang
2 |
3 | MAINTAINER miaohong@miaohong.org
4 |
5 | ENV GOPATH /go
6 | ENV USER root
7 |
8 | RUN mkdir -p "$GOPATH/src/" "$GOPATH/bin"
9 | RUN go get -u -v github.com/BurntSushi/toml
10 | RUN go get -u -v github.com/golang/glog
11 | RUN go get -u -v github.com/golang/protobuf/proto
12 | RUN go get -u -v github.com/coreos/etcd/clientv3
13 | RUN go get -u -v gopkg.in/mgo.v2
14 |
15 | RUN go get -d github.com/oikomi/FishChatServer2
16 |
17 | RUN cd "$GOPATH/src/github.com/oikomi/FishChatServer2/server/access" && go build
18 | RUN cp "$GOPATH/src/github.com/oikomi/FishChatServer2/server/access/access" "/bin/"
19 | RUN cp "$GOPATH/src/github.com/oikomi/FishChatServer2/server/access/access.toml" "/etc/"
20 |
21 | EXPOSE 11000
22 | EXPOSE 20000
23 | ENTRYPOINT ["/bin/access", "-conf", "/etc/access.toml"]
--------------------------------------------------------------------------------
/deploy/docker/gateway/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang
2 |
3 | MAINTAINER miaohong@miaohong.org
4 |
5 | ENV GOPATH /go
6 | ENV USER root
7 |
8 | RUN mkdir -p "$GOPATH/src/" "$GOPATH/bin"
9 | RUN go get -u -v github.com/BurntSushi/toml
10 | RUN go get -u -v github.com/golang/glog
11 | RUN go get -u -v github.com/golang/protobuf/proto
12 | RUN go get -u -v github.com/coreos/etcd/clientv3
13 | RUN go get -u -v gopkg.in/mgo.v2
14 |
15 | RUN go get -u -v github.com/oikomi/FishChatServer2
16 |
17 | RUN cd "$GOPATH/src/github.com/oikomi/FishChatServer2/server/gateway" && go build
18 | RUN cp "$GOPATH/src/github.com/oikomi/FishChatServer2/server/gateway/gateway" "/bin/"
19 | RUN cp "$GOPATH/src/github.com/oikomi/FishChatServer2/server/gateway/gateway.toml" "/etc/"
20 |
21 | EXPOSE 10000
22 | ENTRYPOINT ["/bin/gateway", "-conf", "/etc/gateway.toml"]
--------------------------------------------------------------------------------
/deploy/docker/logic/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang
2 |
3 | MAINTAINER miaohong@miaohong.org
4 |
5 | ENV GOPATH /go
6 | ENV USER root
7 |
8 | RUN mkdir -p "$GOPATH/src/" "$GOPATH/bin"
9 | RUN go get -u -v github.com/BurntSushi/toml
10 | RUN go get -u -v github.com/golang/glog
11 | RUN go get -u -v github.com/golang/protobuf/proto
12 | RUN go get -u -v github.com/coreos/etcd/clientv3
13 | RUN go get -u -v gopkg.in/mgo.v2
14 | RUN go get -u -v github.com/Shopify/sarama
15 | RUN go get -u -v github.com/wvanbergen/kafka/consumergroup
16 | RUN go get -u -v gopkg.in/olivere/elastic.v5
17 |
18 | RUN go get -u -v github.com/oikomi/FishChatServer2
19 |
20 | RUN cd "$GOPATH/src/github.com/oikomi/FishChatServer2/server/logic" && go build
21 | RUN cp "$GOPATH/src/github.com/oikomi/FishChatServer2/server/logic/logic" "/bin/"
22 | RUN cp "$GOPATH/src/github.com/oikomi/FishChatServer2/server/logic/logic.toml" "/etc/"
23 |
24 | EXPOSE 21000
25 | ENTRYPOINT ["/bin/logic", "-conf", "/etc/logic.toml"]
--------------------------------------------------------------------------------
/deploy/docker/register/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang
2 |
3 | MAINTAINER miaohong@miaohong.org
4 |
5 | ENV GOPATH /go
6 | ENV USER root
7 |
8 | RUN mkdir -p "$GOPATH/src/" "$GOPATH/bin"
9 | RUN go get -u -v github.com/BurntSushi/toml
10 | RUN go get -u -v github.com/golang/glog
11 | RUN go get -u -v github.com/golang/protobuf/proto
12 | RUN go get -u -v github.com/coreos/etcd/clientv3
13 | RUN go get -u -v gopkg.in/mgo.v2
14 | RUN go get -u -v github.com/garyburd/redigo/redis
15 |
16 | RUN go get -u -v github.com/oikomi/FishChatServer2
17 |
18 | RUN cd "$GOPATH/src/github.com/oikomi/FishChatServer2/server/register" && go build
19 | RUN cp "$GOPATH/src/github.com/oikomi/FishChatServer2/server/register/register" "/bin/"
20 | RUN cp "$GOPATH/src/github.com/oikomi/FishChatServer2/server/register/register.toml" "/etc/"
21 |
22 | EXPOSE 23000
23 | ENTRYPOINT ["/bin/register", "-conf", "/etc/register.toml"]
--------------------------------------------------------------------------------
/deploy/k8s/kafka/kafka-rc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ReplicationController
3 | metadata:
4 | name: kafka
5 | spec:
6 | replicas: 1
7 | selector:
8 | app: kafka
9 | template:
10 | metadata:
11 | labels:
12 | app: kafka
13 | spec:
14 | containers:
15 | - name: kafka
16 | image: wurstmeister/kafka
17 | imagePullPolicy: IfNotPresent
18 | ports:
19 | - containerPort: 9092
--------------------------------------------------------------------------------
/deploy/k8s/kafka/kafka-svc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: kafka
5 | labels:
6 | name: kafka
7 | spec:
8 | type: NodePort
9 | ports:
10 | - port: 9092
11 | nodePort: 30011
12 | selector:
13 | name: kafka
14 |
--------------------------------------------------------------------------------
/deploy/k8s/logic/logic-rc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ReplicationController
3 | metadata:
4 | name: logic
5 | spec:
6 | replicas: 1
7 | selector:
8 | app: logic
9 | template:
10 | metadata:
11 | labels:
12 | app: logic
13 | spec:
14 | containers:
15 | - name: logic
16 | image: logic
17 | imagePullPolicy: IfNotPresent
18 | ports:
19 | - containerPort: 21000
--------------------------------------------------------------------------------
/deploy/k8s/logic/logic-svc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: logic
5 | labels:
6 | name: logic
7 | spec:
8 | type: NodePort
9 | ports:
10 | - port: 21000
11 | nodePort: 30002
12 | selector:
13 | name: logic
14 |
--------------------------------------------------------------------------------
/deploy/k8s/register/register-rc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ReplicationController
3 | metadata:
4 | name: register
5 | spec:
6 | replicas: 1
7 | selector:
8 | app: register
9 | template:
10 | metadata:
11 | labels:
12 | app: register
13 | spec:
14 | containers:
15 | - name: register
16 | image: register
17 | imagePullPolicy: IfNotPresent
18 | ports:
19 | - containerPort: 23000
--------------------------------------------------------------------------------
/deploy/k8s/register/register-svc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: register
5 | labels:
6 | name: register
7 | spec:
8 | type: NodePort
9 | ports:
10 | - port: 23000
11 | nodePort: 30001
12 | selector:
13 | name: register
14 |
--------------------------------------------------------------------------------
/deploy/k8s/zookeeper/zookeeper-rc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ReplicationController
3 | metadata:
4 | name: zookeeper
5 | spec:
6 | replicas: 1
7 | selector:
8 | app: zookeeper
9 | template:
10 | metadata:
11 | labels:
12 | app: zookeeper
13 | spec:
14 | containers:
15 | - name: zookeeper
16 | image: wurstmeister/zookeeper
17 | imagePullPolicy: IfNotPresent
18 | ports:
19 | - containerPort: 2181
--------------------------------------------------------------------------------
/deploy/k8s/zookeeper/zookeeper-svc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: zookeeper
5 | labels:
6 | name: zookeeper
7 | spec:
8 | type: NodePort
9 | ports:
10 | - port: 2181
11 | nodePort: 30012
12 | selector:
13 | name: zookeeper
14 |
--------------------------------------------------------------------------------
/doc/architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oikomi/FishChatServer2/b9a86dcd71c37e7a402a16014745044f55ccb896/doc/architecture.png
--------------------------------------------------------------------------------
/doc/architecture.xml:
--------------------------------------------------------------------------------
1 | 3Vxbc5s4FP41eWwGIXF7bNJs+9DOZJqH3T51CMiYBltekJN4f/0KgzAcyTa1BaZuZjpwELfvfDo3HXyD7xfvn/NwNf/GYprd2Fb8foM/3di2F1ji/1KwqQTEDipBkqdxJUI7wVP6H62F9XnJOo1p0RnIGct4uuoKI7Zc0oh3ZGGes7fusBnLunddhQlVBE9RmKnSv9OYzyupb7s7+ReaJnN5Z+TW7/ccRi9JztbL+n43Np5t/1WHF6G8Vv2ixTyM2VtLhB9u8H3OGK+2Fu/3NCuhlbBV5/2152jz3Dld8l4n2KR+EL6RL09jgUW9y3I+ZwlbhtnDTnq3fUFaXsISe3O+yMQmEpv0PeX/lOJbp977UQ8Sz5Nv6kMekoIf8rxy55Hm6YJymten/KKcb2puhGvOhGj3OF8ZW9Unq+9cw1CwdR5Jjdq4EpZv1xpVw/KZMnHrfCMG5DQLefraZUJYEyppxjWnPrJU3Ne2au4TyeGa+o7vdC/BwzyhvD5rpxqx0XqMnWirsH3Kq9/pNczWVBIOaHOnqxKrt3nK6dMq3MLyJuZuV39xWMybsXtxfaU5p+8HQayPYrcLhotqMN52swpZ9Zh5a0bJ83S4dxA7BA9yh+c2AtxuM9sIiSu6yDdCY5HY7urNAySuppYZEjujW6AA2+NbID8YR3cOCcYzQMPMsLbmdlOq0p1nforptDTwtOtrw+TjTNbCkwBYeIxUC4881cI33DnLxFsDmXjvcjbeGilQsR0H2AmgEHM23pG0miqLEWCx7RKFxVja0TaLMTLAYs+eODzE6sKDLQUdWxfFIWQgjHP9iaPj2oA8jkoeRDQmEBNyPjxYhSeMIloUCkhHYDGAhA2QwEgliqfjCTHAE3mNQzwpXyUVaf7X8Jlmj6xIecqW4tAz45wtBAhywMcsTcoDnAGUmoTdajOp3Cl4zl6ackE9dlXeePGelFWS23AZ5yyNb1dztqRlBDpLs+yeZSzfPp2sFMhLtY5EVvlnRkke0FGgpmRYoyPPhIp6+IEW1ke1lZUH7pqSiw5KvULP0WLKCu92kRbRbbpV5ET0aI+pR9VhJSGnb+HmAkYHOQCInkbHiM1xjxOaLuOPZUlS7EVZWBRptGVGmHNVrIlF0a3lep2EyBaBWrUP89VWkGp3otTe6Q/RRKRS20cD0hbgjgZwKTszbkXQyWCgyD1xq3ohOIN8cCFziTLpEcOcypNG5520pCZAQyLUoZCDyB4KncwSGYdMhSUYsATavd4s8cGF7OFY0iPZN8wSQBKMjdLEmjQlyKmUgGEugRbIHCWcHkHt2ZS4VXzFPrthm6DDhH0LKAMQmL/2NhoEGA24nmGOIYGnEuL8ChhQu2O6zOpqPIjr9uSC8SprEAyPoeccMcOD4xuQS+GL8CAAH15lhjZtcHwRNk7g7anCipeJVTNgVVqNYr8Rg9U6aRz3GSviHhwvNqonOHmVSBrn0dZh0bmzC2jwNDpUJb8Rppe8eytIyFgi/P34yThc8Ld9tSrha/x3U4A/CwcyQrCkUqsXQ2R61HF4fZcVxwl+MOhcITDT6Rv9QBIouZe56MdVCzA5TdKiDE3HZz8BuWITTx5hP1yhP2kRSc0Uchqn6jKArGdGmywVFjY/vmBiABkXUMLRFekCDbV9A8g4CjIPYo7zNHqiYR7Np4kQdlTLqUPIRKeUN0x41q0lDL2I3ra2nqta294FKuP+2VON1KJIPvxizwrsv7WQacJiwVJGP4Nlonbuq976y11Y0GlOx+ZxjxksE8gM0hGHpjUdfePZqD4YcUA2BO1l36DG9RHw7cO11Tl4bAKMnTD5GkZUfnrsbNkFpTpkBYd5AEt79Ww1lS37qrOgxc9J+AoQ3crexBF8RaAGt6lg3FLBZIQMF6waurbGMRANDNgADKrLXITLMJlEsoN9rxcffAMRVdMkOdmmMLi4rO2L1fZCmemLVZmyLmj+IVylF6AK8rutpq6mQ87VdFfK1aHzoFDTv7JdaHUhLGz4ScyoWOhWmN1M3OJuJTYSvn3HSlCswqWURWw5+xmnRcTEi27kCHG79qCWuHWx8e1S4HcBlpa3BXCgidxNGOim4bUPwF2cemhBTODXNKJ/qiI82aw/iiKm3nKOHbuLDvJVB2FrHISJBj6EdP6zBwUpj+Jpsw7GZ56mWT3QhalGWDdgn9cJXTr78jtr9wVu704OJcPTfLSyVylBl+vEVbl+aGHjNxo44I2C7hVO7Q10YBZjMNtHqlNOvj/eK8QRE4B3OdHtL16qzci1KKybnyOh1G39DHZFL9I43hYRdPPTxKSEH5BI49ciANEQAK5AnTQpbdUnXxu+BHddLbbUXGw4fFVXe334dr+pJdaY/PWuH18Ev0VUa0vD4as6bR6JEObuMWecPa9n14Q0/FoCE3WZcTCksWoprhhp2G5sjchpbCtIf6cFn63LUXPOV9eEtA9jtRGdH1YTqWszzrBi5Wi+NBsOX7WQeb0mA36FrikNDge0WiYtmxQuVCQFbQm60tFgRVI8Qkf9oeV3483KTWNyZ/39Yt32SCYrV9MNrgXYuyDA6hrySzh7CS8wk4mM+eTyuqtmTo7mtxFcAy2RSPOTAN82xb+ZgsMkWowIHq/jD8lvDf+cOfi7LSbNT26oTYBjzEGiZu1LxtPZiZ/IK7TSvP9+dwrSa+Krk3CovmTkqBHcBCPks/DFNsB3zAjZ0axvXxm+jTeQ+Gp6ow3hK3Z3v75aVfN3v3CLH/4H
--------------------------------------------------------------------------------
/doc/db/hbase/hbase.data:
--------------------------------------------------------------------------------
1 |
2 | create 'im', 'user', 'msg'
--------------------------------------------------------------------------------
/doc/db/mysql/schema.sql:
--------------------------------------------------------------------------------
1 | CREATE DATABASE im DEFAULT CHARACTER SET utf8;
2 |
3 | DROP TABLE IF EXISTS `user`;
4 | CREATE TABLE `user` (
5 | `id` bigint(19) NOT NULL AUTO_INCREMENT,
6 | `uid` bigint(19) NOT NULL,
7 | `user_name` varchar(255) NOT NULL,
8 | `password` varchar(255) NOT NULL,
9 | `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
10 | `gmt_modify` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改日期',
11 | PRIMARY KEY (`id`)
12 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
13 |
14 | DROP TABLE IF EXISTS `group`;
15 | CREATE TABLE `group` (
16 | `id` bigint(19) NOT NULL AUTO_INCREMENT,
17 | `gid` bigint(19) NOT NULL,
18 | `group_name` varchar(255) NOT NULL,
19 | `owner_id` bigint(19) NOT NULL,
20 | `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
21 | `gmt_modify` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改日期',
22 | PRIMARY KEY (`id`)
23 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
24 |
25 | DROP TABLE IF EXISTS `user_group`;
26 | CREATE TABLE `user_group` (
27 | `id` bigint(19) NOT NULL AUTO_INCREMENT,
28 | `gid` bigint(19) NOT NULL,
29 | `uid` varchar(255) NOT NULL,
30 | `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
31 | `gmt_modify` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改日期',
32 | PRIMARY KEY (`id`)
33 | ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
34 |
35 | DROP TABLE IF EXISTS `user_msg_id`;
36 | CREATE TABLE `user_msg_id` (
37 | `id` bigint(19) NOT NULL AUTO_INCREMENT,
38 | `uid` bigint(19) NOT NULL,
39 | `current_msg_id` bigint(19) NOT NULL,
40 | `total_msg_id` bigint(19) NOT NULL,
41 | `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
42 | `gmt_modify` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改日期',
43 | PRIMARY KEY (`id`)
44 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--------------------------------------------------------------------------------
/doc/msg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oikomi/FishChatServer2/b9a86dcd71c37e7a402a16014745044f55ccb896/doc/msg.png
--------------------------------------------------------------------------------
/doc/msg.xml:
--------------------------------------------------------------------------------
1 | 5VlNc5swFPw1vhuECRwTN20P7UymPvSYUUCAJgIxkojt/vpKSIAl2W3c8cfUySGDFp6Edvc9PZIZWNabLwy21XeaIzIL5/lmBj7NwvAuncvfCthqIApTDZQM5xoKJmCFfyEDmriywzni1oOCUiJwa4MZbRqUCQuDjNG1/VhBib1qC0vkAasMEh/9iXNRaTQJ4wn/inBZDSsHsdnfC8xeS0a7xqw3C0HR/+jbNRzmMhvlFczpegcCjzOwZJQKfVVvlogoagfadNznA3fH92aoEe8JADrgDZLObH1JsIrVbye2AyPyRVt12dXkGy4QwY0cPbSI4RoJxOQdYuCnCXtYV1igVQszFbqWVpFYJWoiR4G8lPIJKEPYOCYEthy/9KvOJcJQ1jGO39APxLVLFEo7oVZajuor0OwEMYE2B9kIRo6ldRGV78m28pEhIDGyDLY1w/XkgWBusGpH/8UAQuO7cpx64l5eGPr3SxF5UqwQk9v5mFKAGFxPioUnhScCavJ7VWjkKCOQc5zZfOoAlHt15q8E7OxwsXeDGmOIQCHVsGvfnk2bFZ4oVmk98BvNbasHLm+cdixDJmq3gjgTjcoMEy2ciQRkJRLeRL0G47bfJUvsycL7DHluqMDF1tNIuk/YqnDB6Ks0K6HK5w3tM6fAhDgQJLhslLRSrD57lJexPB/uzY0a57laZm9ancD+YZJYrKaJZ/9ojznCE5g/uX3zu571eHuv+aPAMX98NvOnnixZf1I/822T3bL1x97qAt4f5LyO+Ud32gTI7tkj4E/JccwhcGCl4w8BZ6LUmeh0eRAEh04BLpW5qURIY0cdvwc6WyKEHsu0KDiSS9yPrcKt0JzaNCcXpNn//rpGvbF9Bgbxz1BvDqx0fL1xJorPV2/877KbrTeRo84FE8H/5NqpN8Ft0Xxn07y4IM3+J9Q16o3dLIChWTh7fwPctuRf+5vIbZROWG/uPk69cYp4esFE8L9yd+qNnxT/Nc12fxOBs9Esh9OfrrX3p38PgMff
--------------------------------------------------------------------------------
/http_server/group-api/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | "github.com/oikomi/FishChatServer2/common/xtime"
8 | )
9 |
10 | var (
11 | confPath string
12 | Conf *Config
13 | )
14 |
15 | type Config struct {
16 | commconf.CommConf
17 | MultiHTTP *commconf.MultiHTTP
18 | RPCClient *RPCClient
19 | Redis *Redis
20 | }
21 |
22 | type RPCClient struct {
23 | RegisterClient *commconf.ServiceDiscoveryClient
24 | }
25 |
26 | type Redis struct {
27 | *commconf.Redis
28 | Expire xtime.Duration
29 | }
30 |
31 | func init() {
32 | flag.StringVar(&confPath, "conf", "./group-api.toml", "config path")
33 | }
34 |
35 | func Init() error {
36 | if _, err := toml.DecodeFile(confPath, &Conf); err != nil {
37 | return err
38 | }
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/http_server/group-api/group-api.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/http_server/group-api/conf"
7 | "github.com/oikomi/FishChatServer2/http_server/group-api/http"
8 | "os"
9 | "os/signal"
10 | "syscall"
11 | )
12 |
13 | func init() {
14 | flag.Set("alsologtostderr", "true")
15 | flag.Set("log_dir", "false")
16 | }
17 |
18 | func main() {
19 | flag.Parse()
20 | if err := conf.Init(); err != nil {
21 | glog.Errorf("conf.Init() error(%v)", err)
22 | panic(err)
23 | }
24 | glog.Infof("group-api [version: %s] start", conf.Conf.Ver)
25 | http.Init(conf.Conf)
26 | // init signal
27 | c := make(chan os.Signal, 1)
28 | signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGSTOP)
29 | for {
30 | s := <-c
31 | glog.Info("group-api get a signal %s", s.String())
32 | switch s {
33 | case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGINT:
34 | glog.Infof("group-api [version: %s] exit", conf.Conf.Ver)
35 | return
36 | case syscall.SIGHUP:
37 | // TODO reload
38 | default:
39 | return
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/http_server/group-api/group-api.toml:
--------------------------------------------------------------------------------
1 | # This is a TOML document.
2 |
3 | version = "1.0.0"
4 | user = "nobody"
5 | pid = "/tmp/group-api.pid"
6 | dir = "./"
7 | perf = "0.0.0.0:6000"
8 | checkFile = "/data/www/group-api.html"
9 | log = "/tmp/log/group-api/"
10 | trace = true
11 | debug = false
12 |
13 | [multiHTTP]
14 | [multiHTTP.outer]
15 | addrs = ["0.0.0.0:7001"]
16 | maxListen = 10
17 | [multiHTTP.inner]
18 | addrs = ["0.0.0.0:7002"]
19 | maxListen = 10
20 | [multiHTTP.local]
21 | addrs = ["0.0.0.0:7003"]
22 | maxListen = 10
23 |
24 | [rpcClient]
25 | [rpcClient.registerClient]
26 | serviceName = "register"
27 | etcdAddr = "localhost:2379"
28 | balancer = "rr"
29 |
30 | [redis]
31 | name = "group-api"
32 | proto = "tcp"
33 | addr = "172.16.0.148:6379"
34 | idle = 100
35 | active = 100
36 | dialTimeout = "1s"
37 | readTimeout = "1s"
38 | writeTimeout = "1s"
39 | idleTimeout = "10s"
40 | expire = "10s"
41 |
--------------------------------------------------------------------------------
/http_server/group-api/http/http.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/common/ecode"
6 | "github.com/oikomi/FishChatServer2/common/net/xhttp"
7 | "github.com/oikomi/FishChatServer2/common/net/xhttp/router"
8 | wctx "github.com/oikomi/FishChatServer2/common/net/xweb/context"
9 | "github.com/oikomi/FishChatServer2/http_server/group-api/conf"
10 | "github.com/oikomi/FishChatServer2/http_server/group-api/service"
11 | "net/http"
12 | "strconv"
13 | )
14 |
15 | var (
16 | groupSvc *service.Service
17 | )
18 |
19 | func Init(c *conf.Config) {
20 | var err error
21 | groupSvc, err = service.New()
22 | if err != nil {
23 | glog.Error(err)
24 | return
25 | }
26 | // init external router
27 | extM := http.NewServeMux()
28 | extR := router.New(extM)
29 | outerRouter(extR)
30 | // init Outer serve
31 | if err = xhttp.Serve(extM, c.MultiHTTP.Outer); err != nil {
32 | glog.Errorf("xhttp.Serve error(%v)", err)
33 | panic(err)
34 | }
35 | // init local router
36 | localM := http.NewServeMux()
37 | localR := router.New(localM)
38 | localRouter(localR)
39 | // init local server
40 | if err = xhttp.Serve(localM, c.MultiHTTP.Local); err != nil {
41 | glog.Errorf("xhttp.Serve error(%v)", err)
42 | panic(err)
43 | }
44 | }
45 |
46 | // outerRouter init outer router api path.
47 | func outerRouter(r *router.Router) {
48 | r.Group("/x/group", func(cr *router.Router) {
49 | cr.GuestPost("/create", createGroup)
50 | cr.GuestPost("/join", joinGroup)
51 | cr.GuestPost("/quit", quitGroup)
52 | })
53 | return
54 | }
55 |
56 | // innerRouter init local router api path.
57 | func localRouter(r *router.Router) {
58 | }
59 |
60 | func createGroup(c wctx.Context) {
61 | res := c.Result()
62 | uidStr := c.Request().Form.Get("uid")
63 | groupNameStr := c.Request().Form.Get("name")
64 | uid, err := strconv.ParseInt(uidStr, 10, 64)
65 | if err != nil {
66 | glog.Error(err)
67 | res["code"] = ecode.RequestErr
68 | return
69 | }
70 | res["code"] = groupSvc.CreateGroup(uid, groupNameStr)
71 | }
72 |
73 | func joinGroup(c wctx.Context) {
74 | res := c.Result()
75 | uidStr := c.Request().Form.Get("uid")
76 | gidStr := c.Request().Form.Get("gid")
77 | uid, err := strconv.ParseInt(uidStr, 10, 64)
78 | if err != nil {
79 | glog.Error(err)
80 | res["code"] = ecode.RequestErr
81 | return
82 | }
83 | gid, err := strconv.ParseInt(gidStr, 10, 64)
84 | if err != nil {
85 | glog.Error(err)
86 | res["code"] = ecode.RequestErr
87 | return
88 | }
89 | res["code"] = groupSvc.JoinGroup(uid, gid)
90 | }
91 |
92 | func quitGroup(c wctx.Context) {
93 | res := c.Result()
94 | uidStr := c.Request().Form.Get("uid")
95 | gidStr := c.Request().Form.Get("gid")
96 | uid, err := strconv.ParseInt(uidStr, 10, 64)
97 | if err != nil {
98 | glog.Error(err)
99 | res["code"] = ecode.RequestErr
100 | return
101 | }
102 | gid, err := strconv.ParseInt(gidStr, 10, 64)
103 | if err != nil {
104 | glog.Error(err)
105 | res["code"] = ecode.RequestErr
106 | return
107 | }
108 | res["code"] = groupSvc.QuitGroup(uid, gid)
109 | }
110 |
--------------------------------------------------------------------------------
/http_server/group-api/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
--------------------------------------------------------------------------------
/http_server/group-api/rpc/client/register.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/http_server/group-api/conf"
6 | "github.com/oikomi/FishChatServer2/protocol/rpc"
7 | sd "github.com/oikomi/FishChatServer2/service_discovery/etcd"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | )
11 |
12 | type RegisterRPCCli struct {
13 | conn *grpc.ClientConn
14 | }
15 |
16 | func NewRegisterRPCCli() (registerRPCCli *RegisterRPCCli, err error) {
17 | r := sd.NewResolver(conf.Conf.RPCClient.RegisterClient.ServiceName)
18 | b := grpc.RoundRobin(r)
19 | conn, err := grpc.Dial(conf.Conf.RPCClient.RegisterClient.EtcdAddr, grpc.WithInsecure(), grpc.WithBalancer(b))
20 | if err != nil {
21 | glog.Error(err)
22 | panic(err)
23 | }
24 | registerRPCCli = &RegisterRPCCli{
25 | conn: conn,
26 | }
27 | return
28 | }
29 |
30 | func (registerRPCCli *RegisterRPCCli) CreateGroup(createGroupReq *rpc.RGCreateGroupReq) (res *rpc.RGCreateGroupRes, err error) {
31 | r := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
32 | if res, err = r.CreateGroup(context.Background(), createGroupReq); err != nil {
33 | glog.Error(err)
34 | }
35 | return
36 | }
37 |
38 | func (registerRPCCli *RegisterRPCCli) JoinGroup(joinGroupReq *rpc.RGJoinGroupReq) (res *rpc.RGJoinGroupRes, err error) {
39 | r := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
40 | if res, err = r.JoinGroup(context.Background(), joinGroupReq); err != nil {
41 | glog.Error(err)
42 | }
43 | return
44 | }
45 |
46 | func (registerRPCCli *RegisterRPCCli) QuitGroup(quitGroupReq *rpc.RGQuitGroupReq) (res *rpc.RGQuitGroupRes, err error) {
47 | r := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
48 | if res, err = r.QuitGroup(context.Background(), quitGroupReq); err != nil {
49 | glog.Error(err)
50 | }
51 | return
52 | }
53 |
--------------------------------------------------------------------------------
/http_server/group-api/rpc/rpc_client.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/http_server/group-api/rpc/client"
6 | )
7 |
8 | type RPCClient struct {
9 | Register *client.RegisterRPCCli
10 | }
11 |
12 | func NewRPCClient() (c *RPCClient, err error) {
13 | register, err := client.NewRegisterRPCCli()
14 | if err != nil {
15 | glog.Error(err)
16 | return
17 | }
18 | c = &RPCClient{
19 | Register: register,
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/http_server/group-api/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "github.com/golang/glog"
5 | // "github.com/oikomi/FishChatServer2/http_server/group-api/model"
6 | groupRpc "github.com/oikomi/FishChatServer2/http_server/group-api/rpc"
7 | "github.com/oikomi/FishChatServer2/protocol/rpc"
8 | )
9 |
10 | type Service struct {
11 | rpcClient *groupRpc.RPCClient
12 | }
13 |
14 | func New() (service *Service, err error) {
15 | rpcClient, err := groupRpc.NewRPCClient()
16 | if err != nil {
17 | glog.Error(err)
18 | return
19 | }
20 | service = &Service{
21 | rpcClient: rpcClient,
22 | }
23 | return
24 | }
25 |
26 | func (s *Service) CreateGroup(uid int64, groupName string) (err error) {
27 | rgCreateGroupReq := &rpc.RGCreateGroupReq{
28 | Uid: uid,
29 | GroupName: groupName,
30 | }
31 | _, err = s.rpcClient.Register.CreateGroup(rgCreateGroupReq)
32 | if err != nil {
33 | glog.Error(err)
34 | }
35 | return
36 | }
37 |
38 | func (s *Service) JoinGroup(uid, gid int64) (err error) {
39 | rgJoinGroupReq := &rpc.RGJoinGroupReq{
40 | Uid: uid,
41 | Gid: gid,
42 | }
43 | _, err = s.rpcClient.Register.JoinGroup(rgJoinGroupReq)
44 | if err != nil {
45 | glog.Error(err)
46 | }
47 | return
48 | }
49 |
50 | func (s *Service) QuitGroup(uid, gid int64) (err error) {
51 | rgQuitGroupReq := &rpc.RGQuitGroupReq{
52 | Uid: uid,
53 | Gid: gid,
54 | }
55 | _, err = s.rpcClient.Register.QuitGroup(rgQuitGroupReq)
56 | if err != nil {
57 | glog.Error(err)
58 | }
59 | return
60 | }
61 |
--------------------------------------------------------------------------------
/http_server/msg-api/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | "github.com/oikomi/FishChatServer2/common/xtime"
8 | )
9 |
10 | var (
11 | confPath string
12 | Conf *Config
13 | )
14 |
15 | type Config struct {
16 | commconf.CommConf
17 | MultiHTTP *commconf.MultiHTTP
18 | RPCClient *RPCClient
19 | Redis *Redis
20 | }
21 |
22 | type RPCClient struct {
23 | ManagerClient *commconf.ServiceDiscoveryClient
24 | }
25 |
26 | type Redis struct {
27 | *commconf.Redis
28 | Expire xtime.Duration
29 | }
30 |
31 | func init() {
32 | flag.StringVar(&confPath, "conf", "./msg-api.toml", "config path")
33 | }
34 |
35 | func Init() error {
36 | if _, err := toml.DecodeFile(confPath, &Conf); err != nil {
37 | return err
38 | }
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/http_server/msg-api/http/http.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "github.com/golang/glog"
5 | // "github.com/oikomi/FishChatServer2/common/ecode"
6 | "github.com/oikomi/FishChatServer2/common/net/xhttp"
7 | "github.com/oikomi/FishChatServer2/common/net/xhttp/router"
8 | // wctx "github.com/oikomi/FishChatServer2/common/net/xweb/context"
9 | "github.com/oikomi/FishChatServer2/http_server/msg-api/conf"
10 | "github.com/oikomi/FishChatServer2/http_server/msg-api/service"
11 | "net/http"
12 | // "strconv"
13 | )
14 |
15 | var (
16 | managerSvc *service.Service
17 | )
18 |
19 | func Init(c *conf.Config) {
20 | var err error
21 | managerSvc, err = service.New()
22 | if err != nil {
23 | glog.Error(err)
24 | return
25 | }
26 | // init external router
27 | extM := http.NewServeMux()
28 | extR := router.New(extM)
29 | outerRouter(extR)
30 | // init Outer serve
31 | if err = xhttp.Serve(extM, c.MultiHTTP.Outer); err != nil {
32 | glog.Errorf("xhttp.Serve error(%v)", err)
33 | panic(err)
34 | }
35 | // init local router
36 | localM := http.NewServeMux()
37 | localR := router.New(localM)
38 | localRouter(localR)
39 | // init local server
40 | if err = xhttp.Serve(localM, c.MultiHTTP.Local); err != nil {
41 | glog.Errorf("xhttp.Serve error(%v)", err)
42 | panic(err)
43 | }
44 | }
45 |
46 | // outerRouter init outer router api path.
47 | func outerRouter(r *router.Router) {
48 | glog.Info("outerRouter")
49 | r.Group("/x/msg", func(cr *router.Router) {
50 | // cr.GuestGet("/offline", offlineMsgs)
51 | })
52 | return
53 | }
54 |
55 | // innerRouter init local router api path.
56 | func localRouter(r *router.Router) {
57 | }
58 |
59 | // func offlineMsgs(c wctx.Context) {
60 | // glog.Info("offlineMsgs")
61 | // res := c.Result()
62 | // uidStr := c.Request().Form.Get("uid")
63 | // uid, err := strconv.ParseInt(uidStr, 10, 64)
64 | // if err != nil {
65 | // glog.Error(err)
66 | // res["code"] = ecode.RequestErr
67 | // return
68 | // }
69 | // res["data"], res["code"] = managerSvc.GetOfflineMsgs(uid)
70 | // }
71 |
--------------------------------------------------------------------------------
/http_server/msg-api/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type Login struct {
4 | Token string `json:"token"`
5 | }
6 |
7 | type OfflineMsg struct {
8 | SourceUID int64
9 | TargetUID int64
10 | MsgID string
11 | Msg string
12 | }
13 |
14 | type OfflineMsgs struct {
15 | Msgs []*OfflineMsg
16 | }
17 |
--------------------------------------------------------------------------------
/http_server/msg-api/msg-api.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/http_server/msg-api/conf"
7 | "github.com/oikomi/FishChatServer2/http_server/msg-api/http"
8 | "os"
9 | "os/signal"
10 | "syscall"
11 | )
12 |
13 | func init() {
14 | flag.Set("alsologtostderr", "true")
15 | flag.Set("log_dir", "false")
16 | }
17 |
18 | func main() {
19 | flag.Parse()
20 | if err := conf.Init(); err != nil {
21 | glog.Errorf("conf.Init() error(%v)", err)
22 | panic(err)
23 | }
24 | glog.Infof("msg-api [version: %s] start", conf.Conf.Ver)
25 | http.Init(conf.Conf)
26 | // init signal
27 | c := make(chan os.Signal, 1)
28 | signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGSTOP)
29 | for {
30 | s := <-c
31 | glog.Info("msg-api get a signal %s", s.String())
32 | switch s {
33 | case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGINT:
34 | glog.Infof("msg-api [version: %s] exit", conf.Conf.Ver)
35 | return
36 | case syscall.SIGHUP:
37 | // TODO reload
38 | default:
39 | return
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/http_server/msg-api/msg-api.toml:
--------------------------------------------------------------------------------
1 | # This is a TOML document. Boom.
2 |
3 | ver = "1.0.0"
4 | user = "nobody"
5 | pid = "/tmp/msg-api.pid"
6 | dir = "./"
7 | perf = "0.0.0.0:6000"
8 | checkFile = "/data/www/msg-api.html"
9 | log = "/tmp/log/msg-api/"
10 | trace = true
11 | debug = false
12 |
13 | [multiHTTP]
14 | [multiHTTP.outer]
15 | addrs = ["0.0.0.0:8001"]
16 | maxListen = 10
17 | [multiHTTP.inner]
18 | addrs = ["0.0.0.0:8002"]
19 | maxListen = 10
20 | [multiHTTP.local]
21 | addrs = ["0.0.0.0:8003"]
22 | maxListen = 10
23 |
24 | [rpcClient]
25 | [rpcClient.managerClient]
26 | serviceName = "manager"
27 | etcdAddr = "localhost:2379"
28 | balancer = "rr"
29 |
30 | [redis]
31 | name = "msg-api"
32 | proto = "tcp"
33 | addr = "172.16.0.148:6379"
34 | idle = 100
35 | active = 100
36 | dialTimeout = "1s"
37 | readTimeout = "1s"
38 | writeTimeout = "1s"
39 | idleTimeout = "10s"
40 | expire = "10s"
41 |
--------------------------------------------------------------------------------
/http_server/msg-api/rpc/client/manager.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/http_server/msg-api/conf"
6 | sd "github.com/oikomi/FishChatServer2/service_discovery/etcd"
7 | "google.golang.org/grpc"
8 | )
9 |
10 | type ManagerRPCCli struct {
11 | conn *grpc.ClientConn
12 | }
13 |
14 | func NewManagerRPCCli() (managerRPCCli *ManagerRPCCli, err error) {
15 | r := sd.NewResolver(conf.Conf.RPCClient.ManagerClient.ServiceName)
16 | b := grpc.RoundRobin(r)
17 | conn, err := grpc.Dial(conf.Conf.RPCClient.ManagerClient.EtcdAddr, grpc.WithInsecure(), grpc.WithBalancer(b))
18 | if err != nil {
19 | glog.Error(err)
20 | panic(err)
21 | }
22 | managerRPCCli = &ManagerRPCCli{
23 | conn: conn,
24 | }
25 | return
26 | }
27 |
28 | // func (managerRPCCli *ManagerRPCCli) GetOfflineMsgs(offlineMsgsReq *rpc.MGOfflineMsgReq) (res *rpc.MGOfflineMsgRes, err error) {
29 | // m := rpc.NewManagerServerRPCClient(managerRPCCli.conn)
30 | // if res, err = m.GetOfflineMsgs(context.Background(), offlineMsgsReq); err != nil {
31 | // glog.Error(err)
32 | // }
33 | // return
34 | // }
35 |
--------------------------------------------------------------------------------
/http_server/msg-api/rpc/rpc_client.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/http_server/msg-api/rpc/client"
6 | )
7 |
8 | type RPCClient struct {
9 | Manager *client.ManagerRPCCli
10 | }
11 |
12 | func NewRPCClient() (c *RPCClient, err error) {
13 | manager, err := client.NewManagerRPCCli()
14 | if err != nil {
15 | glog.Error(err)
16 | return
17 | }
18 | c = &RPCClient{
19 | Manager: manager,
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/http_server/msg-api/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "github.com/golang/glog"
5 | // "github.com/oikomi/FishChatServer2/http_server/msg-api/model"
6 | managerRpc "github.com/oikomi/FishChatServer2/http_server/msg-api/rpc"
7 | // "github.com/oikomi/FishChatServer2/protocol/rpc"
8 | )
9 |
10 | type Service struct {
11 | rpcClient *managerRpc.RPCClient
12 | }
13 |
14 | func New() (service *Service, err error) {
15 | rpcClient, err := managerRpc.NewRPCClient()
16 | if err != nil {
17 | glog.Error(err)
18 | return
19 | }
20 | service = &Service{
21 | rpcClient: rpcClient,
22 | }
23 | return
24 | }
25 |
26 | // func (s *Service) GetOfflineMsgs(uid int64) (offlineMsgsModel *model.OfflineMsgs, err error) {
27 | // rgGetOfflineMsg := &rpc.MGOfflineMsgReq{
28 | // Uid: uid,
29 | // }
30 | // res, err := s.rpcClient.Manager.GetOfflineMsgs(rgGetOfflineMsg)
31 | // if err != nil {
32 | // glog.Error(err)
33 | // return
34 | // }
35 | // offlineMsgsModel = &model.OfflineMsgs{}
36 | // for _, msg := range res.Msgs {
37 | // tmpMsg := &model.OfflineMsg{
38 | // SourceUID: msg.SourceUID,
39 | // TargetUID: msg.TargetUID,
40 | // MsgID: msg.MsgID,
41 | // Msg: msg.Msg,
42 | // }
43 | // offlineMsgsModel.Msgs = append(offlineMsgsModel.Msgs, tmpMsg)
44 | // }
45 | // return
46 | // }
47 |
--------------------------------------------------------------------------------
/http_server/user-api/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | "github.com/oikomi/FishChatServer2/common/xtime"
8 | )
9 |
10 | var (
11 | confPath string
12 | Conf *Config
13 | )
14 |
15 | type Config struct {
16 | commconf.CommConf
17 | MultiHTTP *commconf.MultiHTTP
18 | RPCClient *RPCClient
19 | Redis *Redis
20 | }
21 |
22 | type RPCClient struct {
23 | RegisterClient *commconf.ServiceDiscoveryClient
24 | }
25 |
26 | type Redis struct {
27 | *commconf.Redis
28 | Expire xtime.Duration
29 | }
30 |
31 | func init() {
32 | flag.StringVar(&confPath, "conf", "./user-api.toml", "config path")
33 | }
34 |
35 | func Init() error {
36 | if _, err := toml.DecodeFile(confPath, &Conf); err != nil {
37 | return err
38 | }
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/http_server/user-api/http/http.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/common/ecode"
6 | "github.com/oikomi/FishChatServer2/common/net/xhttp"
7 | "github.com/oikomi/FishChatServer2/common/net/xhttp/router"
8 | wctx "github.com/oikomi/FishChatServer2/common/net/xweb/context"
9 | "github.com/oikomi/FishChatServer2/http_server/user-api/conf"
10 | "github.com/oikomi/FishChatServer2/http_server/user-api/service"
11 | "net/http"
12 | "strconv"
13 | )
14 |
15 | var (
16 | userSvc *service.Service
17 | )
18 |
19 | func Init(c *conf.Config) {
20 | var err error
21 | userSvc, err = service.New()
22 | if err != nil {
23 | glog.Error(err)
24 | return
25 | }
26 | // init external router
27 | extM := http.NewServeMux()
28 | extR := router.New(extM)
29 | outerRouter(extR)
30 | // init Outer serve
31 | if err = xhttp.Serve(extM, c.MultiHTTP.Outer); err != nil {
32 | glog.Errorf("xhttp.Serve error(%v)", err)
33 | panic(err)
34 | }
35 | // init local router
36 | localM := http.NewServeMux()
37 | localR := router.New(localM)
38 | localRouter(localR)
39 | // init local server
40 | if err = xhttp.Serve(localM, c.MultiHTTP.Local); err != nil {
41 | glog.Errorf("xhttp.Serve error(%v)", err)
42 | panic(err)
43 | }
44 | }
45 |
46 | // outerRouter init outer router api path.
47 | func outerRouter(r *router.Router) {
48 | r.Group("/x/user", func(cr *router.Router) {
49 | cr.GuestPost("/auth", auth)
50 | cr.GuestPost("/register", register)
51 | })
52 | return
53 | }
54 |
55 | // innerRouter init local router api path.
56 | func localRouter(r *router.Router) {
57 | }
58 |
59 | func auth(c wctx.Context) {
60 | res := c.Result()
61 | uidStr := c.Request().Form.Get("uid")
62 | pwStr := c.Request().Form.Get("pw")
63 | uid, err := strconv.ParseInt(uidStr, 10, 64)
64 | if err != nil {
65 | glog.Error(err)
66 | res["code"] = ecode.RequestErr
67 | return
68 | }
69 | res["data"], res["code"] = userSvc.Auth(uid, pwStr)
70 | }
71 |
72 | func register(c wctx.Context) {
73 | res := c.Result()
74 | uidStr := c.Request().Form.Get("uid")
75 | name := c.Request().Form.Get("name")
76 | pwStr := c.Request().Form.Get("pw")
77 | uid, err := strconv.ParseInt(uidStr, 10, 64)
78 | if err != nil {
79 | glog.Error(err)
80 | res["code"] = ecode.RequestErr
81 | return
82 | }
83 | res["code"] = userSvc.Register(uid, name, pwStr)
84 | }
85 |
--------------------------------------------------------------------------------
/http_server/user-api/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type Login struct {
4 | Token string `json:"token"`
5 | }
6 |
--------------------------------------------------------------------------------
/http_server/user-api/rpc/client/register.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/http_server/user-api/conf"
6 | "github.com/oikomi/FishChatServer2/protocol/rpc"
7 | sd "github.com/oikomi/FishChatServer2/service_discovery/etcd"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | )
11 |
12 | type RegisterRPCCli struct {
13 | conn *grpc.ClientConn
14 | }
15 |
16 | func NewRegisterRPCCli() (registerRPCCli *RegisterRPCCli, err error) {
17 | r := sd.NewResolver(conf.Conf.RPCClient.RegisterClient.ServiceName)
18 | b := grpc.RoundRobin(r)
19 | conn, err := grpc.Dial(conf.Conf.RPCClient.RegisterClient.EtcdAddr, grpc.WithInsecure(), grpc.WithBalancer(b))
20 | if err != nil {
21 | glog.Error(err)
22 | panic(err)
23 | }
24 | registerRPCCli = &RegisterRPCCli{
25 | conn: conn,
26 | }
27 | return
28 | }
29 |
30 | func (registerRPCCli *RegisterRPCCli) Register(authReq *rpc.RGRegisterReq) (res *rpc.RGRegisterRes, err error) {
31 | r := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
32 | if res, err = r.Register(context.Background(), authReq); err != nil {
33 | glog.Error(err)
34 | }
35 | return
36 | }
37 |
38 | func (registerRPCCli *RegisterRPCCli) Auth(authReq *rpc.RGAuthReq) (res *rpc.RGAuthRes, err error) {
39 | r := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
40 | if res, err = r.Auth(context.Background(), authReq); err != nil {
41 | glog.Error(err)
42 | }
43 | return
44 | }
45 |
--------------------------------------------------------------------------------
/http_server/user-api/rpc/rpc_client.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/http_server/user-api/rpc/client"
6 | )
7 |
8 | type RPCClient struct {
9 | Register *client.RegisterRPCCli
10 | }
11 |
12 | func NewRPCClient() (c *RPCClient, err error) {
13 | register, err := client.NewRegisterRPCCli()
14 | if err != nil {
15 | glog.Error(err)
16 | return
17 | }
18 | c = &RPCClient{
19 | Register: register,
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/http_server/user-api/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/http_server/user-api/model"
6 | authRpc "github.com/oikomi/FishChatServer2/http_server/user-api/rpc"
7 | "github.com/oikomi/FishChatServer2/protocol/rpc"
8 | )
9 |
10 | type Service struct {
11 | rpcClient *authRpc.RPCClient
12 | }
13 |
14 | func New() (service *Service, err error) {
15 | rpcClient, err := authRpc.NewRPCClient()
16 | if err != nil {
17 | glog.Error(err)
18 | return
19 | }
20 | service = &Service{
21 | rpcClient: rpcClient,
22 | }
23 | return
24 | }
25 |
26 | func (s *Service) Auth(uid int64, pw string) (loginModel *model.Login, err error) {
27 | // check uid pw
28 | rgAuthReq := &rpc.RGAuthReq{
29 | UID: uid,
30 | }
31 | res, err := s.rpcClient.Register.Auth(rgAuthReq)
32 | if err != nil {
33 | glog.Error(err)
34 | return
35 | }
36 | loginModel = &model.Login{
37 | Token: res.Token,
38 | }
39 | return
40 | }
41 |
42 | func (s *Service) Register(uid int64, userName, pw string) (err error) {
43 | rgRegisterReq := &rpc.RGRegisterReq{
44 | UID: uid,
45 | Name: userName,
46 | Password: pw,
47 | }
48 | _, err = s.rpcClient.Register.Register(rgRegisterReq)
49 | if err != nil {
50 | glog.Error(err)
51 | return
52 | }
53 | return
54 | }
55 |
--------------------------------------------------------------------------------
/http_server/user-api/user-api.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/http_server/user-api/conf"
7 | "github.com/oikomi/FishChatServer2/http_server/user-api/http"
8 | "os"
9 | "os/signal"
10 | "syscall"
11 | )
12 |
13 | func init() {
14 | flag.Set("alsologtostderr", "true")
15 | flag.Set("log_dir", "false")
16 | }
17 |
18 | func main() {
19 | flag.Parse()
20 | if err := conf.Init(); err != nil {
21 | glog.Errorf("conf.Init() error(%v)", err)
22 | panic(err)
23 | }
24 | glog.Infof("user-api [version: %s] start", conf.Conf.Ver)
25 | http.Init(conf.Conf)
26 | // init signal
27 | c := make(chan os.Signal, 1)
28 | signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGSTOP)
29 | for {
30 | s := <-c
31 | glog.Info("user-api get a signal %s", s.String())
32 | switch s {
33 | case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGINT:
34 | glog.Infof("user-api [version: %s] exit", conf.Conf.Ver)
35 | return
36 | case syscall.SIGHUP:
37 | // TODO reload
38 | default:
39 | return
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/http_server/user-api/user-api.toml:
--------------------------------------------------------------------------------
1 | # This is a TOML document. Boom.
2 |
3 | ver = "1.0.0"
4 | user = "nobody"
5 | pid = "/tmp/user-api.pid"
6 | dir = "./"
7 | perf = "0.0.0.0:6000"
8 | checkFile = "/data/www/user-api.html"
9 | log = "/tmp/log/user-api/"
10 | trace = true
11 | debug = false
12 |
13 | [multiHTTP]
14 | [multiHTTP.outer]
15 | addrs = ["0.0.0.0:6001"]
16 | maxListen = 10
17 | [multiHTTP.inner]
18 | addrs = ["0.0.0.0:6002"]
19 | maxListen = 10
20 | [multiHTTP.local]
21 | addrs = ["0.0.0.0:6003"]
22 | maxListen = 10
23 |
24 | [rpcClient]
25 | [rpcClient.registerClient]
26 | serviceName = "register"
27 | etcdAddr = "localhost:2379"
28 | balancer = "rr"
29 |
30 | [redis]
31 | name = "user-api"
32 | proto = "tcp"
33 | addr = "172.16.0.148:6379"
34 | idle = 100
35 | active = 100
36 | dialTimeout = "1s"
37 | readTimeout = "1s"
38 | writeTimeout = "1s"
39 | idleTimeout = "10s"
40 | expire = "10s"
41 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-client/msg-job-client.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-client/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | msg-job
7 | org.miaohong.jobs
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | msg-job-client
13 |
14 |
15 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/main/java/org/miaohong/jobs/msgjob/ApplicationMain.java:
--------------------------------------------------------------------------------
1 | package org.miaohong.jobs.msgjob;
2 |
3 |
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 | import org.springframework.context.annotation.ComponentScan;
8 | import org.springframework.context.annotation.ImportResource;
9 |
10 | @SpringBootApplication
11 | @ComponentScan
12 | @EnableAutoConfiguration
13 | @ImportResource(value={"META-INF/spring/application-service.xml"})
14 | public class
15 | ApplicationMain {
16 | public static void main(String[] args) {
17 | SpringApplication.run(ApplicationMain.class, args);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/main/java/org/miaohong/jobs/msgjob/dal/kafka/KafkaProducer.java:
--------------------------------------------------------------------------------
1 | package org.miaohong.jobs.msgjob.dal.kafka;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | /**
6 | * Created by miaohong on 17/1/10.
7 | */
8 | @Component
9 | public class KafkaProducer {
10 | }
11 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/main/java/org/miaohong/jobs/msgjob/dal/model/KafkaGroupMsg.java:
--------------------------------------------------------------------------------
1 | package org.miaohong.jobs.msgjob.dal.model;
2 |
3 | /**
4 | * Created by miaohong on 17/2/10.
5 | */
6 | public class KafkaGroupMsg {
7 | private Long incrementID;
8 | private Long sourceUID;
9 | private Long targetUID;
10 | private Long groupID;
11 | private String msgID;
12 | private String Msg;
13 |
14 | public Long getIncrementID() {
15 | return incrementID;
16 | }
17 |
18 | public void setIncrementID(Long incrementID) {
19 | this.incrementID = incrementID;
20 | }
21 |
22 | public Long getSourceUID() {
23 | return sourceUID;
24 | }
25 |
26 | public void setSourceUID(Long sourceUID) {
27 | this.sourceUID = sourceUID;
28 | }
29 |
30 | public Long getTargetUID() {
31 | return targetUID;
32 | }
33 |
34 | public void setTargetUID(Long targetUID) {
35 | this.targetUID = targetUID;
36 | }
37 |
38 | public Long getGroupID() {
39 | return groupID;
40 | }
41 |
42 | public void setGroupID(Long groupID) {
43 | this.groupID = groupID;
44 | }
45 |
46 | public String getMsgID() {
47 | return msgID;
48 | }
49 |
50 | public void setMsgID(String msgID) {
51 | this.msgID = msgID;
52 | }
53 |
54 | public String getMsg() {
55 | return Msg;
56 | }
57 |
58 | public void setMsg(String msg) {
59 | Msg = msg;
60 | }
61 |
62 |
63 | // private String AccessServerAddr;
64 | // private Boolean Online;
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/main/java/org/miaohong/jobs/msgjob/dal/model/KafkaP2PMsg.java:
--------------------------------------------------------------------------------
1 | package org.miaohong.jobs.msgjob.dal.model;
2 |
3 | import com.alibaba.fastjson.annotation.JSONField;
4 |
5 | /**
6 | * Created by miaohong on 17/1/14.
7 | */
8 | public class KafkaP2PMsg {
9 | private Long incrementID;
10 | private Long sourceUID;
11 | private Long targetUID;
12 | private String msgID;
13 | private String Msg;
14 | private String AccessServerAddr;
15 | private Boolean Online;
16 |
17 | public Long getIncrementID() {
18 | return incrementID;
19 | }
20 |
21 | public void setIncrementID(Long incrementID) {
22 | this.incrementID = incrementID;
23 | }
24 |
25 | public Long getSourceUID() {
26 | return sourceUID;
27 | }
28 |
29 | public void setSourceUID(Long sourceUID) {
30 | this.sourceUID = sourceUID;
31 | }
32 |
33 | public Long getTargetUID() {
34 | return targetUID;
35 | }
36 |
37 | public void setTargetUID(Long targetUID) {
38 | this.targetUID = targetUID;
39 | }
40 |
41 | public String getMsgID() {
42 | return msgID;
43 | }
44 |
45 | public void setMsgID(String msgID) {
46 | this.msgID = msgID;
47 | }
48 |
49 | public String getMsg() {
50 | return Msg;
51 | }
52 |
53 | public void setMsg(String msg) {
54 | Msg = msg;
55 | }
56 |
57 | public String getAccessServerAddr() {
58 | return AccessServerAddr;
59 | }
60 |
61 | public void setAccessServerAddr(String accessServerAddr) {
62 | AccessServerAddr = accessServerAddr;
63 | }
64 |
65 | public Boolean getOnline() {
66 | return Online;
67 | }
68 |
69 | public void setOnline(Boolean online) {
70 | Online = online;
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/main/java/org/miaohong/jobs/msgjob/manager/InitManager.java:
--------------------------------------------------------------------------------
1 | package org.miaohong.jobs.msgjob.manager;
2 |
3 | import org.miaohong.jobs.msgjob.service.kafka.ConsumerService;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.context.ApplicationEvent;
8 | import org.springframework.context.ApplicationListener;
9 | import org.springframework.stereotype.Component;
10 |
11 | /**
12 | * Created by miaohong on 17/1/14.
13 | */
14 | @Component("startUpEvent")
15 | public class InitManager implements ApplicationListener {
16 | private static final Logger logger = LoggerFactory.getLogger(InitManager.class);
17 | private static boolean isInit = false;
18 | @Autowired
19 | ConsumerService consumerService;
20 | public void onApplicationEvent(ApplicationEvent applicationEvent) {
21 | logger.info("onApplicationEvent");
22 | if (!isInit) {
23 | consumerService.start();
24 | isInit = true;
25 | }
26 | }
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/main/java/org/miaohong/jobs/msgjob/service/kafka/ConsumerService.java:
--------------------------------------------------------------------------------
1 | package org.miaohong.jobs.msgjob.service.kafka;
2 |
3 | import org.miaohong.jobs.msgjob.dal.kafka.KafkaConsumer;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.stereotype.Component;
6 | import java.util.concurrent.ExecutorService;
7 | import java.util.concurrent.Executors;
8 | import java.util.concurrent.TimeUnit;
9 |
10 | /**
11 | * Created by miaohong on 17/1/13.
12 | */
13 | @Component
14 | public class ConsumerService {
15 | @Autowired
16 | KafkaConsumer kafkaConsumer;
17 | private ExecutorService executor = null;
18 |
19 | public void start() {
20 | ExecutorService executor = Executors.newFixedThreadPool(1);
21 | executor.execute(kafkaConsumer);
22 | }
23 |
24 | public void stop() {
25 | try {
26 | System.out.println("attempt to shutdown executor");
27 | executor.shutdown();
28 | executor.awaitTermination(5, TimeUnit.SECONDS);
29 | }
30 | catch (InterruptedException e) {
31 | System.err.println("tasks interrupted");
32 | }
33 | finally {
34 | if (!executor.isTerminated()) {
35 | System.err.println("cancel non-finished tasks");
36 | executor.shutdownNow();
37 | }
38 | //executor.shutdownNow();
39 | System.out.println("shutdown finished");
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/main/resources/META-INF/spring/application-service.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | # kafka
2 | kafka.consumer.bootstrap.servers=localhost:9092
3 | kafka.consumer.group.id=msg_group
4 | kafka.consumer.topics=logic_producer_p2p,logic_producer_group
5 |
6 |
7 | # hbase
8 | hbase.master=127.0.0.1:16010
9 | hbase.zookeeper.property.clientPort=2181
10 |
11 | hbase.msg.table=im
12 |
13 | hbase.msg.user.family=user
14 | hbase.msg.user.qual.sourceUID=sourceUID
15 | hbase.msg.user.qual.targetUID=targetUID
16 | hbase.msg.user.qual.groupID=groupID
17 | hbase.msg.user.qual.online=online
18 |
19 | hbase.msg.msg.family=msg
20 | hbase.msg.msg.qual.incrementID=incrementID
21 | hbase.msg.msg.qual.msgType=msgType
22 | hbase.msg.msg.qual.msgID=msgID
23 | hbase.msg.msg.qual.msg=msg
24 | hbase.msg.msg.qual.accessServerAddr=accessServerAddr
25 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/test/java/org/miaohong/jobs/msgjob/AbstractTest.java:
--------------------------------------------------------------------------------
1 | package org.miaohong.jobs.msgjob;
2 |
3 | import org.junit.Before;
4 | import org.junit.runner.RunWith;
5 | import org.kubek2k.springockito.annotations.SpringockitoContextLoader;
6 | import org.mockito.MockitoAnnotations;
7 | import org.springframework.test.context.ContextConfiguration;
8 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
9 | import org.springframework.test.context.transaction.TransactionConfiguration;
10 |
11 | /**
12 | * Created by miaohong on 17/1/10.
13 | */
14 | @RunWith(SpringJUnit4ClassRunner.class)
15 | @ContextConfiguration(loader = SpringockitoContextLoader.class,locations = {
16 | "classpath:META-INF/spring/application-service.xml" })
17 | @TransactionConfiguration(transactionManager = "transactionManager" ,defaultRollback = true)
18 | public abstract class AbstractTest {
19 | @Before
20 | public void init() {
21 | MockitoAnnotations.initMocks(this);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/test/java/org/miaohong/jobs/msgjob/dal/HBaseManagerTest.java:
--------------------------------------------------------------------------------
1 | package org.miaohong.jobs.msgjob.dal;
2 |
3 | import org.junit.Test;
4 | import org.miaohong.jobs.msgjob.AbstractTest;
5 | import org.miaohong.jobs.msgjob.dal.hbase.HBaseManager;
6 |
7 | import javax.annotation.Resource;
8 |
9 | /**
10 | * Created by miaohong on 17/1/10.
11 | */
12 | public class HBaseManagerTest extends AbstractTest {
13 | @Resource
14 | HBaseManager hBaseManager;
15 |
16 | // @Test
17 | // public void testHBaseManager() {
18 | // hBaseManager.createTable("test1");
19 | // }
20 |
21 | // @Test
22 | // public void testInsert() {
23 | // hBaseManager.insert("test1");
24 | // }
25 | @Test
26 | public void test() {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/jobs/msg-job/msg-job-core/src/test/java/org/miaohong/jobs/msgjob/dal/KafkaConsumerTest.java:
--------------------------------------------------------------------------------
1 | package org.miaohong.jobs.msgjob.dal;
2 |
3 | import org.junit.Test;
4 | import org.miaohong.jobs.msgjob.AbstractTest;
5 | import org.miaohong.jobs.msgjob.service.kafka.ConsumerService;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 |
8 |
9 | /**
10 | * Created by miaohong on 17/1/10.
11 | */
12 | public class KafkaConsumerTest extends AbstractTest {
13 | @Autowired
14 | ConsumerService consumerService;
15 |
16 | @Test
17 | public void testConsume() {
18 | consumerService.start();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/libnet/api.go:
--------------------------------------------------------------------------------
1 | package libnet
2 |
3 | import (
4 | "io"
5 | "net"
6 | "time"
7 | )
8 |
9 | type Protocol interface {
10 | NewCodec(rw io.ReadWriter) Codec
11 | }
12 |
13 | type Codec interface {
14 | Receive() ([]byte, error)
15 | Send(interface{}) error
16 | Close() error
17 | }
18 |
19 | func Serve(network, address string, protocol Protocol, sendChanSize int) (*Server, error) {
20 | listener, err := net.Listen(network, address)
21 | if err != nil {
22 | return nil, err
23 | }
24 | return NewServer(listener, protocol, sendChanSize), nil
25 | }
26 |
27 | func Connect(network, address string, protocol Protocol, sendChanSize int) (*Session, error) {
28 | conn, err := net.Dial(network, address)
29 | if err != nil {
30 | return nil, err
31 | }
32 | return NewSession(protocol.NewCodec(conn), sendChanSize), nil
33 | }
34 |
35 | func ConnectTimeout(network, address string, timeout time.Duration, protocol Protocol, sendChanSize int) (*Session, error) {
36 | conn, err := net.DialTimeout(network, address, timeout)
37 | if err != nil {
38 | return nil, err
39 | }
40 | return NewSession(protocol.NewCodec(conn), sendChanSize), nil
41 | }
42 |
--------------------------------------------------------------------------------
/libnet/channel.go:
--------------------------------------------------------------------------------
1 | // +build generate
2 |
3 | //go:generate go run channel_gen.go Int32Channel int32 channel_int32.go
4 | //go:generate go run channel_gen.go Uint32Channel uint32 channel_uint32.go
5 | //go:generate go run channel_gen.go Int64Channel int64 channel_int64.go
6 | //go:generate go run channel_gen.go Uint64Channel uint64 channel_uint64.go
7 | //go:generate go run channel_gen.go StringChannel string channel_string.go
8 | package libnet
9 |
10 | import (
11 | "sync"
12 | )
13 |
14 | type Channel struct {
15 | mutex sync.RWMutex
16 | sessions map[KEY]*Session
17 |
18 | // channel state
19 | State interface{}
20 | }
21 |
22 | func NewChannel() *Channel {
23 | return &Channel{
24 | sessions: make(map[KEY]*Session),
25 | }
26 | }
27 |
28 | func (channel *Channel) Len() int {
29 | channel.mutex.RLock()
30 | defer channel.mutex.RUnlock()
31 | return len(channel.sessions)
32 | }
33 |
34 | // Fetch the sessions. NOTE: Dead lock happends if invoke Exit() in fetch callback.
35 | func (channel *Channel) Fetch(callback func(*Session)) {
36 | channel.mutex.RLock()
37 | defer channel.mutex.RUnlock()
38 | for _, session := range channel.sessions {
39 | callback(session)
40 | }
41 | }
42 |
43 | func (channel *Channel) Get(key KEY) *Session {
44 | channel.mutex.RLock()
45 | defer channel.mutex.RUnlock()
46 | session, _ := channel.sessions[key]
47 | return session
48 | }
49 |
50 | func (channel *Channel) Put(key KEY, session *Session) {
51 | channel.mutex.Lock()
52 | defer channel.mutex.Unlock()
53 | if session, exists := channel.sessions[key]; exists {
54 | channel.remove(key, session)
55 | }
56 | session.addCloseCallback(channel, func() {
57 | channel.Remove(key)
58 | })
59 | channel.sessions[key] = session
60 | }
61 |
62 | func (channel *Channel) remove(key KEY, session *Session) {
63 | session.removeCloseCallback(channel)
64 | delete(channel.sessions, key)
65 | }
66 |
67 | func (channel *Channel) Remove(key KEY) bool {
68 | channel.mutex.Lock()
69 | defer channel.mutex.Unlock()
70 | session, exists := channel.sessions[key]
71 | if exists {
72 | channel.remove(key, session)
73 | }
74 | return exists
75 | }
76 |
77 | func (channel *Channel) Close() {
78 | channel.mutex.Lock()
79 | defer channel.mutex.Unlock()
80 | for key, session := range channel.sessions {
81 | channel.remove(key, session)
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/libnet/channel_gen.go:
--------------------------------------------------------------------------------
1 | // +build ignore
2 |
3 | //go:generate go run channel_gen.go Int32Channel int32 channel_int32.go
4 | //go:generate go run channel_gen.go Uint32Channel uint32 channel_uint32.go
5 | //go:generate go run channel_gen.go Int64Channel int64 channel_int64.go
6 | //go:generate go run channel_gen.go Uint64Channel uint64 channel_uint64.go
7 | //go:generate go run channel_gen.go StringChannel string channel_string.go
8 | package main
9 |
10 | import (
11 | "bytes"
12 | "flag"
13 | "log"
14 | "os"
15 | "os/exec"
16 | )
17 |
18 | func main() {
19 | flag.Parse()
20 |
21 | gofmt := os.Getenv("GOROOT") + "/bin/gofmt"
22 | log.Print("use 'gofmt' at ", gofmt)
23 |
24 | var cmdErr bytes.Buffer
25 | var cmd1Out bytes.Buffer
26 | var cmd2Out bytes.Buffer
27 | var cmd3Out bytes.Buffer
28 |
29 | log.Print("gofmt -r 'Channel -> " + flag.Arg(0) + "'")
30 | cmd1 := exec.Command(gofmt, "-r", "Channel -> "+flag.Arg(0), "channel.go")
31 | cmd1.Stdout = &cmd1Out
32 | cmd1.Stderr = &cmdErr
33 | if err := cmd1.Run(); err != nil {
34 | log.Fatal(cmdErr.String())
35 | }
36 |
37 | log.Print("gofmt -r 'NewChannel -> New" + flag.Arg(0) + "'")
38 | cmd2 := exec.Command(gofmt, "-r", "NewChannel -> New"+flag.Arg(0))
39 | cmd2.Stdin = &cmd1Out
40 | cmd2.Stdout = &cmd2Out
41 | cmd2.Stderr = &cmdErr
42 | if err := cmd2.Run(); err != nil {
43 | log.Fatal(cmdErr.String())
44 | }
45 |
46 | log.Print("gofmt -r 'KEY -> " + flag.Arg(1) + "'")
47 | cmd3 := exec.Command(gofmt, "-r", "KEY -> "+flag.Arg(1))
48 | cmd3.Stdin = &cmd2Out
49 | cmd3.Stdout = &cmd3Out
50 | cmd3.Stderr = &cmdErr
51 | if err := cmd3.Run(); err != nil {
52 | log.Fatal(cmdErr.String())
53 | }
54 |
55 | cmd3Out.ReadBytes('\n') // ignore build
56 | cmd3Out.ReadBytes('\n') // empty line
57 | cmd3Out.ReadBytes('\n') // Int32Channel
58 | cmd3Out.ReadBytes('\n') // Uint32Channel
59 | cmd3Out.ReadBytes('\n') // Int64Channel
60 | cmd3Out.ReadBytes('\n') // Uint64Channel
61 | cmd3Out.ReadBytes('\n') // StringChannel
62 |
63 | log.Print("save to target file '", flag.Arg(2), "'")
64 | file, err := os.Create(flag.Arg(2))
65 | if err != nil {
66 | log.Fatal(err)
67 | }
68 | defer file.Close()
69 | if _, err := file.WriteString(
70 | "// DO NOT EDIT\n// GENERATE BY 'go run channel_gen.go " +
71 | flag.Arg(0) + " " + flag.Arg(1) + " " + flag.Arg(2) + "'\n"); err != nil {
72 | log.Fatal(err)
73 | }
74 |
75 | var code = cmd3Out.Bytes()
76 |
77 | if len(flag.Args()) > 3 {
78 | log.Print("rename package as '" + flag.Arg(3) + "'")
79 | code = bytes.Replace(code, []byte("package link"), []byte("package "+flag.Arg(3)), 1)
80 | }
81 |
82 | if _, err := file.Write(code); err != nil {
83 | log.Fatal(err)
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/libnet/manager.go:
--------------------------------------------------------------------------------
1 | package libnet
2 |
3 | import "sync"
4 |
5 | const sessionMapNum = 32
6 |
7 | type Manager struct {
8 | sessionMaps [sessionMapNum]sessionMap
9 | disposeFlag bool
10 | disposeOnce sync.Once
11 | disposeWait sync.WaitGroup
12 | }
13 |
14 | type sessionMap struct {
15 | sync.RWMutex
16 | sessions map[uint64]*Session
17 | }
18 |
19 | func NewManager() *Manager {
20 | manager := &Manager{}
21 | for i := 0; i < len(manager.sessionMaps); i++ {
22 | manager.sessionMaps[i].sessions = make(map[uint64]*Session)
23 | }
24 | return manager
25 | }
26 |
27 | func (manager *Manager) Dispose() {
28 | manager.disposeOnce.Do(func() {
29 | manager.disposeFlag = true
30 | for i := 0; i < sessionMapNum; i++ {
31 | smap := &manager.sessionMaps[i]
32 | smap.Lock()
33 | for _, session := range smap.sessions {
34 | session.Close()
35 | }
36 | smap.Unlock()
37 | }
38 | manager.disposeWait.Wait()
39 | })
40 | }
41 |
42 | func (manager *Manager) NewSession(codec Codec, sendChanSize int) *Session {
43 | session := newSession(manager, codec, sendChanSize)
44 | manager.putSession(session)
45 | return session
46 | }
47 |
48 | func (manager *Manager) GetSession(sessionID uint64) *Session {
49 | smap := &manager.sessionMaps[sessionID%sessionMapNum]
50 | smap.RLock()
51 | defer smap.RUnlock()
52 | session, _ := smap.sessions[sessionID]
53 | return session
54 | }
55 |
56 | func (manager *Manager) putSession(session *Session) {
57 | smap := &manager.sessionMaps[session.id%sessionMapNum]
58 | smap.Lock()
59 | defer smap.Unlock()
60 | smap.sessions[session.id] = session
61 | manager.disposeWait.Add(1)
62 | }
63 |
64 | func (manager *Manager) delSession(session *Session) {
65 | if manager.disposeFlag {
66 | manager.disposeWait.Done()
67 | return
68 | }
69 | smap := &manager.sessionMaps[session.id%sessionMapNum]
70 | smap.Lock()
71 | defer smap.Unlock()
72 | delete(smap.sessions, session.id)
73 | manager.disposeWait.Done()
74 | }
75 |
--------------------------------------------------------------------------------
/libnet/server.go:
--------------------------------------------------------------------------------
1 | package libnet
2 |
3 | import (
4 | "io"
5 | "net"
6 | "strings"
7 | "time"
8 | )
9 |
10 | type Server struct {
11 | manager *Manager
12 | listener net.Listener
13 | protocol Protocol
14 | sendChanSize int
15 | }
16 |
17 | func NewServer(l net.Listener, p Protocol, sendChanSize int) *Server {
18 | return &Server{
19 | manager: NewManager(),
20 | listener: l,
21 | protocol: p,
22 | sendChanSize: sendChanSize,
23 | }
24 | }
25 |
26 | func (server *Server) Listener() net.Listener {
27 | return server.listener
28 | }
29 |
30 | func (server *Server) Accept() (*Session, error) {
31 | var tempDelay time.Duration
32 | for {
33 | conn, err := server.listener.Accept()
34 | if err != nil {
35 | if ne, ok := err.(net.Error); ok && ne.Temporary() {
36 | if tempDelay == 0 {
37 | tempDelay = 5 * time.Millisecond
38 | } else {
39 | tempDelay *= 2
40 | }
41 | if max := 1 * time.Second; tempDelay > max {
42 | tempDelay = max
43 | }
44 | time.Sleep(tempDelay)
45 | continue
46 | }
47 | if strings.Contains(err.Error(), "use of closed network connection") {
48 | return nil, io.EOF
49 | }
50 | return nil, err
51 | }
52 | return server.manager.NewSession(
53 | server.protocol.NewCodec(conn),
54 | server.sendChanSize,
55 | ), nil
56 | }
57 | }
58 |
59 | func (server *Server) Stop() {
60 | server.listener.Close()
61 | server.manager.Dispose()
62 | }
63 |
--------------------------------------------------------------------------------
/protocol/common/common.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package common;
4 |
5 | message offsetP2PMsg {
6 | int64 sourceUID = 1;
7 | int64 targetUID = 2;
8 | string msgID = 3;
9 | string msg = 4;
10 | }
--------------------------------------------------------------------------------
/protocol/external/base.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go.
2 | // source: base.proto
3 | // DO NOT EDIT!
4 |
5 | package external
6 |
7 | import proto "github.com/golang/protobuf/proto"
8 | import fmt "fmt"
9 | import math "math"
10 |
11 | // Reference imports to suppress errors if they are not otherwise used.
12 | var _ = proto.Marshal
13 | var _ = fmt.Errorf
14 | var _ = math.Inf
15 |
16 | type Base struct {
17 | Cmd uint32 `protobuf:"varint,1,opt,name=cmd" json:"cmd,omitempty"`
18 | }
19 |
20 | func (m *Base) Reset() { *m = Base{} }
21 | func (m *Base) String() string { return proto.CompactTextString(m) }
22 | func (*Base) ProtoMessage() {}
23 | func (*Base) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
24 |
25 | func (m *Base) GetCmd() uint32 {
26 | if m != nil {
27 | return m.Cmd
28 | }
29 | return 0
30 | }
31 |
32 | func init() {
33 | proto.RegisterType((*Base)(nil), "external.Base")
34 | }
35 |
36 | func init() { proto.RegisterFile("base.proto", fileDescriptor1) }
37 |
38 | var fileDescriptor1 = []byte{
39 | // 75 bytes of a gzipped FileDescriptorProto
40 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x4a, 0x2c, 0x4e,
41 | 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x48, 0xad, 0x28, 0x49, 0x2d, 0xca, 0x4b, 0xcc,
42 | 0x51, 0x92, 0xe0, 0x62, 0x71, 0x4a, 0x2c, 0x4e, 0x15, 0x12, 0xe0, 0x62, 0x4e, 0xce, 0x4d, 0x91,
43 | 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0d, 0x02, 0x31, 0x93, 0xd8, 0xc0, 0x4a, 0x8d, 0x01, 0x01, 0x00,
44 | 0x00, 0xff, 0xff, 0x42, 0xe9, 0x84, 0x20, 0x38, 0x00, 0x00, 0x00,
45 | }
46 |
--------------------------------------------------------------------------------
/protocol/external/base.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package external;
4 |
5 | message Base {
6 | uint32 cmd = 1;
7 | }
--------------------------------------------------------------------------------
/protocol/external/cmd.go:
--------------------------------------------------------------------------------
1 | package external
2 |
3 | const (
4 | // error
5 | ErrServerCMD = 90001
6 |
7 | // gateway
8 | ReqAccessServerCMD = 10001
9 | // ResSelectAccessServerForClientCMD = 10002
10 |
11 | // acess
12 | LoginCMD = 20001
13 | PingCMD = 20002
14 | SendP2PMsgCMD = 20003
15 | AcceptP2PMsgAckCMD = 20004
16 | SendGroupMsgCMD = 20005
17 | SyncMsgCMD = 20006
18 | NotifyCMD = 20007
19 | )
20 |
--------------------------------------------------------------------------------
/protocol/external/error.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go.
2 | // source: error.proto
3 | // DO NOT EDIT!
4 |
5 | package external
6 |
7 | import proto "github.com/golang/protobuf/proto"
8 | import fmt "fmt"
9 | import math "math"
10 |
11 | // Reference imports to suppress errors if they are not otherwise used.
12 | var _ = proto.Marshal
13 | var _ = fmt.Errorf
14 | var _ = math.Inf
15 |
16 | type Error struct {
17 | Cmd uint32 `protobuf:"varint,1,opt,name=cmd" json:"cmd,omitempty"`
18 | ErrCode uint32 `protobuf:"varint,2,opt,name=errCode" json:"errCode,omitempty"`
19 | ErrStr string `protobuf:"bytes,3,opt,name=errStr" json:"errStr,omitempty"`
20 | }
21 |
22 | func (m *Error) Reset() { *m = Error{} }
23 | func (m *Error) String() string { return proto.CompactTextString(m) }
24 | func (*Error) ProtoMessage() {}
25 | func (*Error) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} }
26 |
27 | func (m *Error) GetCmd() uint32 {
28 | if m != nil {
29 | return m.Cmd
30 | }
31 | return 0
32 | }
33 |
34 | func (m *Error) GetErrCode() uint32 {
35 | if m != nil {
36 | return m.ErrCode
37 | }
38 | return 0
39 | }
40 |
41 | func (m *Error) GetErrStr() string {
42 | if m != nil {
43 | return m.ErrStr
44 | }
45 | return ""
46 | }
47 |
48 | func init() {
49 | proto.RegisterType((*Error)(nil), "external.Error")
50 | }
51 |
52 | func init() { proto.RegisterFile("error.proto", fileDescriptor2) }
53 |
54 | var fileDescriptor2 = []byte{
55 | // 110 bytes of a gzipped FileDescriptorProto
56 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0x2d, 0x2a, 0xca,
57 | 0x2f, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x48, 0xad, 0x28, 0x49, 0x2d, 0xca, 0x4b,
58 | 0xcc, 0x51, 0xf2, 0xe6, 0x62, 0x75, 0x05, 0x49, 0x08, 0x09, 0x70, 0x31, 0x27, 0xe7, 0xa6, 0x48,
59 | 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x06, 0x81, 0x98, 0x42, 0x12, 0x5c, 0xec, 0xa9, 0x45, 0x45, 0xce,
60 | 0xf9, 0x29, 0xa9, 0x12, 0x4c, 0x60, 0x51, 0x18, 0x57, 0x48, 0x8c, 0x8b, 0x2d, 0xb5, 0xa8, 0x28,
61 | 0xb8, 0xa4, 0x48, 0x82, 0x59, 0x81, 0x51, 0x83, 0x33, 0x08, 0xca, 0x4b, 0x62, 0x03, 0x9b, 0x6e,
62 | 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x49, 0xa9, 0xa4, 0x02, 0x6c, 0x00, 0x00, 0x00,
63 | }
64 |
--------------------------------------------------------------------------------
/protocol/external/error.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package external;
4 |
5 | message Error {
6 | uint32 cmd = 1;
7 | uint32 errCode = 2;
8 | string errStr = 3;
9 | }
--------------------------------------------------------------------------------
/protocol/external/gateway.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package external;
4 |
5 | message ReqAccessServer {
6 | uint32 cmd = 1;
7 | string cmdStr = 2;
8 | }
9 |
10 | message ResSelectAccessServerForClient {
11 | uint32 cmd = 1;
12 | string cmdStr = 2;
13 | uint32 errCode = 3;
14 | string errStr = 4;
15 | string addr = 5;
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/protocol/genpb.sh:
--------------------------------------------------------------------------------
1 | protoc -I external/ external/*.proto -I common/ --go_out=external/
2 |
3 | protoc -I rpc/ rpc/*.proto -I common/ --go_out=plugins=grpc:rpc
4 |
--------------------------------------------------------------------------------
/protocol/rpc/access.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package rpc;
4 |
5 | // import "common.proto";
6 |
7 | service AccessServerRPC {
8 | rpc SendP2PMsgFromJob (ASSendP2PMsgFromJobReq) returns (ASSendP2PMsgFromJobRes) {}
9 | rpc SendNotify (ASSendNotifyReq) returns (ASSendNotifyRes) {}
10 | // rpc Sync (ASSyncReq) returns (ASSyncRes) {}
11 | // rpc SendGroupMsg (ASSendGroupMsgReq) returns (ASSendGroupMsgRes) {}
12 | }
13 |
14 | message ASSendP2PMsgReq {
15 | int64 sourceUID = 1;
16 | int64 targetUID = 2;
17 | string msgID = 3;
18 | string msg = 4;
19 | }
20 |
21 | message ASSendP2PMsgRes {
22 | uint32 errCode = 1;
23 | string errStr = 2;
24 | }
25 |
26 | message ASSendP2PMsgFromJobReq {
27 | int64 sourceUID = 1;
28 | int64 targetUID = 2;
29 | string msgID = 3;
30 | string msg = 4;
31 | string accessServerAddr = 5;
32 | }
33 |
34 | message ASSendP2PMsgFromJobRes {
35 | uint32 errCode = 1;
36 | string errStr = 2;
37 | }
38 |
39 | message ASSendNotifyReq {
40 | int64 UID = 1;
41 | int64 currentID = 2;
42 | int64 totalID = 3;
43 | string accessServerAddr = 4;
44 | }
45 |
46 | message ASSendNotifyRes {
47 | uint32 errCode = 1;
48 | string errStr = 2;
49 | }
50 |
51 | // message ASSendGroupMsgReq {
52 | // int64 groupID = 1;
53 | // string msgID = 2;
54 | // string msg = 3;
55 | // }
56 |
57 | // message ASSendGroupMsgRes {
58 | // uint32 errCode = 1;
59 | // string errStr = 2;
60 | // }
--------------------------------------------------------------------------------
/protocol/rpc/idgen.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package rpc;
4 |
5 | service IDGenServerRPC {
6 | rpc Next(Snowflake.Key) returns (Snowflake.Value); // 产生下一个序号
7 | rpc GetUUID(Snowflake.NullRequest) returns (Snowflake.UUID); // UUID 发生器
8 | }
9 |
10 | message Snowflake{
11 | message Key {
12 | string name=1;
13 | }
14 | message Value {
15 | int64 value=1;
16 | }
17 | message NullRequest{
18 | }
19 | message UUID {
20 | uint64 uuid =1;
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/protocol/rpc/logic.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package rpc;
4 |
5 | // import "common.proto";
6 |
7 | service LogicRPC {
8 | rpc Login (LoginReq) returns (LoginRes) {}
9 | rpc Ping (PingReq) returns (PingRes) {}
10 | rpc SendP2PMsg (SendP2PMsgReq) returns (SendP2PMsgRes) {}
11 | // client sync msg use current id
12 | rpc SyncMsg (SyncMsgReq) returns (SyncMsgRes) {}
13 | rpc AcceptP2PMsgAck (AcceptP2PMsgAckReq) returns (AcceptP2PMsgAckRes) {}
14 | rpc SendGroupMsg (SendGroupMsgReq) returns (SendGroupMsgRes) {}
15 | }
16 |
17 | message LoginReq {
18 | int64 UID = 1;
19 | string token = 2;
20 | string accessAddr = 3;
21 | }
22 |
23 | message LoginRes {
24 | uint32 errCode = 1;
25 | string errStr = 2;
26 | }
27 |
28 | message PingReq {
29 | int64 UID = 1;
30 | }
31 |
32 | message PingRes {
33 | uint32 errCode = 1;
34 | string errStr = 2;
35 | }
36 |
37 | message SendP2PMsgReq {
38 | int64 sourceUID = 1;
39 | int64 targetUID = 2;
40 | string msgID = 3;
41 | string msgType = 4;
42 | string msg = 5;
43 | }
44 |
45 | message SendP2PMsgRes {
46 | uint32 errCode = 1;
47 | string errStr = 2;
48 | }
49 |
50 | // sync msg
51 | message SyncMsgReq {
52 | int64 UID = 1;
53 | int64 currentID = 2;
54 | int64 totalID = 3;
55 | }
56 |
57 | message SyncMsgRes {
58 | uint32 errCode = 1;
59 | string errStr = 2;
60 | int64 currentID = 3;
61 | message offsetMsg {
62 | int64 sourceUID = 1;
63 | int64 targetUID = 2;
64 | int64 groupID = 3;
65 | string msgType = 4;
66 | string msgID = 5;
67 | string msg = 6;
68 | }
69 | repeated offsetMsg Msgs = 4;
70 | }
71 |
72 | // p2p msg accept ack
73 | message AcceptP2PMsgAckReq {
74 | int64 sourceUID = 1;
75 | int64 targetUID = 2;
76 | string msgID = 3;
77 | }
78 |
79 | message AcceptP2PMsgAckRes {
80 | uint32 errCode = 1;
81 | string errStr = 2;
82 | }
83 |
84 | // group msg
85 | message SendGroupMsgReq {
86 | int64 sourceUID = 1;
87 | int64 groupID = 2;
88 | string msgType = 3;
89 | string msgID = 4;
90 | string msg = 5;
91 | }
92 |
93 | message SendGroupMsgRes {
94 | uint32 errCode = 1;
95 | string errStr = 2;
96 | }
97 |
98 |
99 |
--------------------------------------------------------------------------------
/protocol/rpc/manager.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package rpc;
4 |
5 | // import "common.proto";
6 |
7 | service ManagerServerRPC {
8 | rpc SetExceptionMsg (MGExceptionMsgReq) returns (MGExceptionMsgRes) {}
9 | // rpc GetOfflineMsgs (MGOfflineMsgReq) returns (MGOfflineMsgRes) {}
10 | rpc Sync (MGSyncMsgReq) returns (MGSyncMsgRes) {}
11 | }
12 |
13 | message MGExceptionMsgReq {
14 | int64 sourceUID = 1;
15 | int64 targetUID = 2;
16 | string msgID = 3;
17 | string msg = 4;
18 | }
19 |
20 | message MGExceptionMsgRes {
21 | uint32 errCode = 1;
22 | string errStr = 2;
23 | }
24 |
25 | // message MGOfflineMsgReq {
26 | // int64 uid = 1;
27 | // }
28 |
29 | // message offlineMsg {
30 | // int64 sourceUID = 1;
31 | // int64 targetUID = 2;
32 | // string msgID = 3;
33 | // string msg = 4;
34 | // }
35 |
36 | // message MGOfflineMsgRes {
37 | // uint32 errCode = 1;
38 | // string errStr = 2;
39 | // repeated offlineMsg msgs = 3;
40 | // }
41 |
42 |
43 | // Sync
44 | message MGSyncMsgReq {
45 | int64 UID = 1;
46 | int64 currentID = 2;
47 | int64 totalID = 3;
48 | }
49 |
50 | message MGSyncMsgRes {
51 | uint32 errCode = 1;
52 | string errStr = 2;
53 | int64 currentID = 3;
54 | message offsetMsg {
55 | int64 sourceUID = 1;
56 | int64 targetUID = 2;
57 | int64 groupID = 3;
58 | string msgType = 4;
59 | string msgID = 5;
60 | string msg = 6;
61 | }
62 | repeated offsetMsg Msgs = 4;
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/protocol/rpc/notify.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package rpc;
4 |
5 | service NotifyServerRPC {
6 | rpc Notify (NFNotifyMsgReq) returns (NFNotifyMsgRes) {}
7 | }
8 |
9 | message NFNotifyMsgReq {
10 | int64 targetUID = 1;
11 | int64 totalID = 2;
12 | string accessServerAddr = 3;
13 | }
14 |
15 | message NFNotifyMsgRes {
16 | uint32 errCode = 1;
17 | string errStr = 2;
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/protocol/rpc/register.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package rpc;
4 |
5 | service RegisterServerRPC {
6 | rpc Register (RGRegisterReq) returns (RGRegisterRes) {}
7 | rpc Login (RGLoginReq) returns (RGLoginRes) {}
8 | rpc RouterAccess (RGAccessReq) returns (RGAccessRes) {}
9 | rpc Auth (RGAuthReq) returns (RGAuthRes) {}
10 | rpc Ping (RGPingReq) returns (RGPingRes) {}
11 | rpc Online (RGOnlineReq) returns (RGOnlineRes) {}
12 | rpc GetUsersByGroupID(RGGetUsersByGroupIDReq) returns (RGGetUsersByGroupIDRes) {}
13 | // group
14 | rpc CreateGroup (RGCreateGroupReq) returns (RGCreateGroupRes) {}
15 | rpc JoinGroup (RGJoinGroupReq) returns (RGJoinGroupRes) {}
16 | rpc QuitGroup (RGQuitGroupReq) returns (RGQuitGroupRes) {}
17 | }
18 |
19 | message RGRegisterReq {
20 | int64 UID = 1;
21 | string name = 2;
22 | string password = 3;
23 | }
24 |
25 | message RGRegisterRes {
26 | uint32 errCode = 1;
27 | string errStr = 2;
28 | }
29 |
30 | message RGLoginReq {
31 | int64 UID = 1;
32 | string token = 2;
33 | string accessAddr = 3;
34 | }
35 |
36 | message RGLoginRes {
37 | uint32 errCode = 1;
38 | string errStr = 2;
39 | string token = 3;
40 | }
41 |
42 | message RGAccessReq {
43 | int64 UID = 1;
44 | }
45 |
46 | message RGAccessRes {
47 | uint32 errCode = 1;
48 | string errStr = 2;
49 | string accessAddr = 3;
50 | }
51 |
52 | message RGAuthReq {
53 | int64 UID = 1;
54 | // string token = 2;
55 | }
56 |
57 | message RGAuthRes {
58 | uint32 errCode = 1;
59 | string errStr = 2;
60 | string token = 3;
61 | }
62 |
63 | message RGPingReq {
64 | int64 UID = 1;
65 | }
66 |
67 | message RGPingRes {
68 | uint32 errCode = 1;
69 | string errStr = 2;
70 | }
71 |
72 | message RGOnlineReq {
73 | int64 UID = 1;
74 | }
75 |
76 | message RGOnlineRes {
77 | uint32 errCode = 1;
78 | string errStr = 2;
79 | bool online = 3;
80 | }
81 |
82 | message RGGetUsersByGroupIDReq {
83 | int64 gid = 1;
84 | }
85 |
86 | message RGGetUsersByGroupIDRes {
87 | uint32 errCode = 1;
88 | string errStr = 2;
89 | repeated int64 uids = 3;
90 | }
91 |
92 | // group
93 | message RGCreateGroupReq {
94 | int64 gid = 1;
95 | int64 uid = 2;
96 | string groupName = 3;
97 | }
98 |
99 | message RGCreateGroupRes {
100 | uint32 errCode = 1;
101 | string errStr = 2;
102 | }
103 |
104 | message RGJoinGroupReq {
105 | int64 uid = 1;
106 | int64 gid = 2;
107 | }
108 |
109 | message RGJoinGroupRes {
110 | uint32 errCode = 1;
111 | string errStr = 2;
112 | }
113 |
114 | message RGQuitGroupReq {
115 | int64 uid = 1;
116 | int64 gid = 2;
117 | }
118 |
119 | message RGQuitGroupRes {
120 | uint32 errCode = 1;
121 | string errStr = 2;
122 | }
--------------------------------------------------------------------------------
/server/access/access.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/codec"
7 | "github.com/oikomi/FishChatServer2/libnet"
8 | "github.com/oikomi/FishChatServer2/server/access/conf"
9 | "github.com/oikomi/FishChatServer2/server/access/rpc"
10 | "github.com/oikomi/FishChatServer2/server/access/server"
11 | )
12 |
13 | func init() {
14 | flag.Set("alsologtostderr", "true")
15 | flag.Set("log_dir", "false")
16 | }
17 |
18 | func main() {
19 | var err error
20 | flag.Parse()
21 | if err = conf.Init(); err != nil {
22 | glog.Error("conf.Init() error: ", err)
23 | panic(err)
24 | }
25 | accessServer := server.New()
26 | protobuf := codec.Protobuf()
27 | if accessServer.Server, err = libnet.Serve(conf.Conf.Server.Proto, conf.Conf.Server.Addr, protobuf, 0); err != nil {
28 | glog.Error(err)
29 | panic(err)
30 | }
31 | go accessServer.SDHeart()
32 | rpcClient, err := rpc.NewRPCClient()
33 | if err != nil {
34 | glog.Error(err)
35 | panic(err)
36 | }
37 | accessServer.Loop(rpcClient)
38 | }
39 |
--------------------------------------------------------------------------------
/server/access/access.toml:
--------------------------------------------------------------------------------
1 | # access conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/access.log"
5 |
6 | [server]
7 | proto = "tcp"
8 | addr = "127.0.0.1:11000"
9 |
10 | [rpcServer]
11 | proto = "tcp"
12 | addr = "127.0.0.1:20000"
13 |
14 | [serviceDiscoveryServer]
15 | serviceName = "access"
16 | rpcAddr = "127.0.0.1:20000"
17 | etcdAddr = "localhost:2379"
18 | interval = "5s"
19 | ttl = "10s"
20 |
21 | [rpcClient]
22 | [rpcClient.logicClient]
23 | serviceName = "logic"
24 | etcdAddr = "localhost:2379"
25 | balancer = "rr"
26 |
27 | [confDiscovery]
28 | [confDiscovery.gateway]
29 | name = "access_server_11000"
30 | root = "/server/access_server_gateway/"
31 | addrs = ["localhost:2379"]
32 | timeout = "1s"
33 | [confDiscovery.msgJob]
34 | name = "access_server_20000"
35 | root = "/server/access_server_msgjob/"
36 | addrs = ["localhost:2379"]
37 | timeout = "1s"
38 | [confDiscovery.notify]
39 | name = "access_server_20000"
40 | root = "/server/access_server_notify/"
41 | addrs = ["localhost:2379"]
42 | timeout = "1s"
43 | [confDiscovery.logic]
44 | name = "access_server_20000"
45 | root = "/server/access_server_logic/"
46 | addrs = ["localhost:2379"]
47 | timeout = "1s"
48 |
49 |
--------------------------------------------------------------------------------
/server/access/access_11001_20001.toml:
--------------------------------------------------------------------------------
1 | # access conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/access.log"
5 |
6 | [server]
7 | proto = "tcp"
8 | addr = "127.0.0.1:11001"
9 |
10 | [rpcServer]
11 | proto = "tcp"
12 | addr = "127.0.0.1:20001"
13 |
14 | [serviceDiscoveryServer]
15 | serviceName = "access"
16 | rpcAddr = "127.0.0.1:20001"
17 | etcdAddr = "localhost:2379"
18 | interval = "5s"
19 | ttl = "10s"
20 |
21 | [rpcClient]
22 | [rpcClient.logicClient]
23 | serviceName = "logic"
24 | etcdAddr = "localhost:2379"
25 | balancer = "rr"
26 |
27 | [confDiscovery]
28 | [confDiscovery.gateway]
29 | name = "access_server_11001"
30 | root = "/server/access_server_gateway/"
31 | addrs = ["localhost:2379"]
32 | timeout = "1s"
33 | [confDiscovery.msgJob]
34 | name = "access_server_20001"
35 | root = "/server/access_server_msgjob/"
36 | addrs = ["localhost:2379"]
37 | timeout = "1s"
--------------------------------------------------------------------------------
/server/access/access_11002_20002.toml:
--------------------------------------------------------------------------------
1 | # access conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/access.log"
5 |
6 | [server]
7 | proto = "tcp"
8 | addr = "127.0.0.1:11002"
9 |
10 | [rpcServer]
11 | proto = "tcp"
12 | addr = "127.0.0.1:20002"
13 |
14 | [serviceDiscoveryServer]
15 | serviceName = "access"
16 | rpcAddr = "127.0.0.1:20002"
17 | etcdAddr = "localhost:2379"
18 | interval = "5s"
19 | ttl = "10s"
20 |
21 | [rpcClient]
22 | [rpcClient.logicClient]
23 | serviceName = "logic"
24 | etcdAddr = "localhost:2379"
25 | balancer = "rr"
26 |
27 | [confDiscovery]
28 | [confDiscovery.gateway]
29 | name = "access_server_11002"
30 | root = "/server/access_server_gateway/"
31 | addrs = ["localhost:2379"]
32 | timeout = "1s"
33 | [confDiscovery.msgJob]
34 | name = "access_server_20002"
35 | root = "/server/access_server_msgjob/"
36 | addrs = ["localhost:2379"]
37 | timeout = "1s"
--------------------------------------------------------------------------------
/server/access/client/client.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/libnet"
6 | "github.com/oikomi/FishChatServer2/protocol/external"
7 | "github.com/oikomi/FishChatServer2/server/access/rpc"
8 | )
9 |
10 | type Client struct {
11 | Session *libnet.Session
12 | RPCClient *rpc.RPCClient
13 | }
14 |
15 | func New(session *libnet.Session, rpcClient *rpc.RPCClient) (c *Client) {
16 | c = &Client{
17 | Session: session,
18 | RPCClient: rpcClient,
19 | }
20 | return
21 | }
22 |
23 | func (c *Client) Parse(cmd uint32, reqData []byte) (err error) {
24 | switch cmd {
25 | case external.LoginCMD:
26 | if err = c.procReqLogin(reqData); err != nil {
27 | glog.Error(err)
28 | return
29 | }
30 | case external.PingCMD:
31 | if err = c.procReqPing(reqData); err != nil {
32 | glog.Error(err)
33 | return
34 | }
35 | case external.SendP2PMsgCMD:
36 | if err = c.procSendP2PMsg(reqData); err != nil {
37 | glog.Error(err)
38 | return
39 | }
40 | // case external.AcceptP2PMsgAckCMD:
41 | // if err = c.procAcceptP2PMsgAck(reqData); err != nil {
42 | // glog.Error(err)
43 | // return
44 | // }
45 | case external.SendGroupMsgCMD:
46 | if err = c.procSendGroupMsg(reqData); err != nil {
47 | glog.Error(err)
48 | return
49 | }
50 | case external.SyncMsgCMD:
51 | if err = c.procSyncMsg(reqData); err != nil {
52 | glog.Error(err)
53 | return
54 | }
55 | }
56 | return
57 | }
58 |
--------------------------------------------------------------------------------
/server/access/client/util.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | func offsetP2PMsgsConvert() {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/server/access/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | )
8 |
9 | var (
10 | confPath string
11 | Conf *Config
12 | )
13 |
14 | type Config struct {
15 | *commconf.CommConf
16 | configFile string
17 | Server *commconf.Server
18 | ServiceDiscoveryServer *commconf.ServiceDiscoveryServer
19 | RPCServer *commconf.RPCServer
20 | RPCClient *RPCClient
21 | ConfDiscovery *ConfDiscovery
22 | // Etcd *commconf.Etcd
23 | }
24 |
25 | type RPCClient struct {
26 | LogicClient *commconf.ServiceDiscoveryClient
27 | }
28 |
29 | type ConfDiscovery struct {
30 | Gateway *commconf.Etcd
31 | MsgJob *commconf.Etcd
32 | Notify *commconf.Etcd
33 | Logic *commconf.Etcd
34 | }
35 |
36 | func init() {
37 | flag.StringVar(&confPath, "conf", "./access.toml", "config path")
38 | }
39 |
40 | func Init() (err error) {
41 | _, err = toml.DecodeFile(confPath, &Conf)
42 | return
43 | }
44 |
--------------------------------------------------------------------------------
/server/access/global/session.go:
--------------------------------------------------------------------------------
1 | package global
2 |
3 | import (
4 | "github.com/oikomi/FishChatServer2/libnet"
5 | )
6 |
7 | type SessionMap map[int64]*libnet.Session
8 |
9 | var GSessions SessionMap
10 |
11 | func init() {
12 | GSessions = make(SessionMap)
13 | }
14 |
--------------------------------------------------------------------------------
/server/access/rpc/client/logic.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/protocol/rpc"
6 | "github.com/oikomi/FishChatServer2/server/access/conf"
7 | sd "github.com/oikomi/FishChatServer2/service_discovery/etcd"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | )
11 |
12 | type LogicRPCCli struct {
13 | conn *grpc.ClientConn
14 | }
15 |
16 | func NewLogicRPCCli() (logicRPCCli *LogicRPCCli, err error) {
17 | r := sd.NewResolver(conf.Conf.RPCClient.LogicClient.ServiceName)
18 | b := grpc.RoundRobin(r)
19 | conn, err := grpc.Dial(conf.Conf.RPCClient.LogicClient.EtcdAddr, grpc.WithInsecure(), grpc.WithBalancer(b))
20 | if err != nil {
21 | glog.Error(err)
22 | panic(err)
23 | }
24 | logicRPCCli = &LogicRPCCli{
25 | conn: conn,
26 | }
27 | return
28 | }
29 |
30 | func (logicRPCCli *LogicRPCCli) Login(loginReq *rpc.LoginReq) (res *rpc.LoginRes, err error) {
31 | l := rpc.NewLogicRPCClient(logicRPCCli.conn)
32 | if res, err = l.Login(context.Background(), loginReq); err != nil {
33 | glog.Error(err)
34 | }
35 | return
36 | }
37 |
38 | func (logicRPCCli *LogicRPCCli) Ping(pingReq *rpc.PingReq) (res *rpc.PingRes, err error) {
39 | l := rpc.NewLogicRPCClient(logicRPCCli.conn)
40 | if res, err = l.Ping(context.Background(), pingReq); err != nil {
41 | glog.Error(err)
42 | }
43 | return
44 | }
45 |
46 | func (logicRPCCli *LogicRPCCli) SendP2PMsg(sendP2PMsgReq *rpc.SendP2PMsgReq) (res *rpc.SendP2PMsgRes, err error) {
47 | l := rpc.NewLogicRPCClient(logicRPCCli.conn)
48 | if res, err = l.SendP2PMsg(context.Background(), sendP2PMsgReq); err != nil {
49 | glog.Error(err)
50 | }
51 | return
52 | }
53 |
54 | func (logicRPCCli *LogicRPCCli) AcceptP2PMsgAck(acceptP2PMsgAckReq *rpc.AcceptP2PMsgAckReq) (res *rpc.AcceptP2PMsgAckRes, err error) {
55 | l := rpc.NewLogicRPCClient(logicRPCCli.conn)
56 | if res, err = l.AcceptP2PMsgAck(context.Background(), acceptP2PMsgAckReq); err != nil {
57 | glog.Error(err)
58 | }
59 | return
60 | }
61 |
62 | func (logicRPCCli *LogicRPCCli) SendGroupMsg(sendGroupMsgReq *rpc.SendGroupMsgReq) (res *rpc.SendGroupMsgRes, err error) {
63 | l := rpc.NewLogicRPCClient(logicRPCCli.conn)
64 | if res, err = l.SendGroupMsg(context.Background(), sendGroupMsgReq); err != nil {
65 | glog.Error(err)
66 | }
67 | return
68 | }
69 |
70 | func (logicRPCCli *LogicRPCCli) SyncMsg(sendGroupMsgReq *rpc.SyncMsgReq) (res *rpc.SyncMsgRes, err error) {
71 | l := rpc.NewLogicRPCClient(logicRPCCli.conn)
72 | if res, err = l.SyncMsg(context.Background(), sendGroupMsgReq); err != nil {
73 | glog.Error(err)
74 | }
75 | return
76 | }
77 |
--------------------------------------------------------------------------------
/server/access/rpc/rpc_client.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/server/access/rpc/client"
6 | )
7 |
8 | type RPCClient struct {
9 | Logic *client.LogicRPCCli
10 | }
11 |
12 | func NewRPCClient() (c *RPCClient, err error) {
13 | logic, err := client.NewLogicRPCCli()
14 | if err != nil {
15 | glog.Error(err)
16 | return
17 | }
18 | c = &RPCClient{
19 | Logic: logic,
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/server/access/server/server.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/golang/protobuf/proto"
6 | "github.com/oikomi/FishChatServer2/common/ecode"
7 | "github.com/oikomi/FishChatServer2/conf_discovery/etcd"
8 | "github.com/oikomi/FishChatServer2/libnet"
9 | "github.com/oikomi/FishChatServer2/protocol/external"
10 | "github.com/oikomi/FishChatServer2/server/access/client"
11 | "github.com/oikomi/FishChatServer2/server/access/conf"
12 | "github.com/oikomi/FishChatServer2/server/access/rpc"
13 | )
14 |
15 | type Server struct {
16 | Server *libnet.Server
17 | RPCServer *rpc.RPCServer
18 | }
19 |
20 | func New() (s *Server) {
21 | s = &Server{}
22 | go rpc.RPCServerInit()
23 | return
24 | }
25 |
26 | func (s *Server) sessionLoop(client *client.Client) {
27 | for {
28 | reqData, err := client.Session.Receive()
29 | if err != nil {
30 | glog.Error(err)
31 | }
32 | if reqData != nil {
33 | baseCMD := &external.Base{}
34 | if err = proto.Unmarshal(reqData, baseCMD); err != nil {
35 | if err = client.Session.Send(&external.Error{
36 | Cmd: external.ErrServerCMD,
37 | ErrCode: ecode.ServerErr.Uint32(),
38 | ErrStr: ecode.ServerErr.String(),
39 | }); err != nil {
40 | glog.Error(err)
41 | }
42 | continue
43 | }
44 | if err = client.Parse(baseCMD.Cmd, reqData); err != nil {
45 | glog.Error(err)
46 | continue
47 | }
48 | }
49 | }
50 | }
51 |
52 | func (s *Server) Loop(rpcClient *rpc.RPCClient) {
53 | for {
54 | session, err := s.Server.Accept()
55 | if err != nil {
56 | glog.Error(err)
57 | }
58 | glog.Info("a new client ", session.ID())
59 | c := client.New(session, rpcClient)
60 | go s.sessionLoop(c)
61 | }
62 | }
63 |
64 | func (s *Server) SDHeart() {
65 | glog.Info("SDHeart")
66 | work1 := etcd.NewWorker(conf.Conf.ConfDiscovery.Gateway.Name, conf.Conf.Server.Addr, conf.Conf.ConfDiscovery.Gateway.Root,
67 | conf.Conf.ConfDiscovery.Gateway.Addrs)
68 | go work1.HeartBeat()
69 | work2 := etcd.NewWorker(conf.Conf.ConfDiscovery.MsgJob.Name, conf.Conf.RPCServer.Addr, conf.Conf.ConfDiscovery.MsgJob.Root,
70 | conf.Conf.ConfDiscovery.MsgJob.Addrs)
71 | go work2.HeartBeat()
72 | glog.Info(conf.Conf.ConfDiscovery.Notify)
73 | work3 := etcd.NewWorker(conf.Conf.ConfDiscovery.Notify.Name, conf.Conf.RPCServer.Addr, conf.Conf.ConfDiscovery.Notify.Root,
74 | conf.Conf.ConfDiscovery.Notify.Addrs)
75 | go work3.HeartBeat()
76 | work4 := etcd.NewWorker(conf.Conf.ConfDiscovery.Logic.Name, conf.Conf.RPCServer.Addr, conf.Conf.ConfDiscovery.Logic.Root,
77 | conf.Conf.ConfDiscovery.Logic.Addrs)
78 | go work4.HeartBeat()
79 | }
80 |
--------------------------------------------------------------------------------
/server/gateway/client/client.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/libnet"
6 | "github.com/oikomi/FishChatServer2/protocol/external"
7 | )
8 |
9 | type Client struct {
10 | Session *libnet.Session
11 | }
12 |
13 | func New(session *libnet.Session) (c *Client) {
14 | c = &Client{
15 | Session: session,
16 | }
17 | return
18 | }
19 |
20 | func (c *Client) Parse(cmd uint32, reqData []byte) (err error) {
21 | switch cmd {
22 | case external.ReqAccessServerCMD:
23 | if err = c.procReqAccessServer(reqData); err != nil {
24 | glog.Error(err)
25 | return
26 | }
27 | }
28 | return
29 | }
30 |
--------------------------------------------------------------------------------
/server/gateway/client/proto_proc.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/common/ecode"
6 | "github.com/oikomi/FishChatServer2/protocol/external"
7 | "github.com/oikomi/FishChatServer2/server/gateway/job"
8 | "math/rand"
9 | )
10 |
11 | func (c *Client) procReqAccessServer(reqData []byte) (err error) {
12 | var addr string
13 | var accessServerList []string
14 | for _, v := range job.AccessServerList {
15 | accessServerList = append(accessServerList, v.IP)
16 | }
17 | if len(accessServerList) == 0 {
18 | if err = c.Session.Send(&external.ResSelectAccessServerForClient{
19 | Cmd: external.ReqAccessServerCMD,
20 | ErrCode: ecode.NoAccessServer.Uint32(),
21 | ErrStr: ecode.NoAccessServer.String(),
22 | }); err != nil {
23 | glog.Error(err)
24 | }
25 | return
26 | }
27 | addr = accessServerList[rand.Intn(len(accessServerList))]
28 | if err = c.Session.Send(&external.ResSelectAccessServerForClient{
29 | Cmd: external.ReqAccessServerCMD,
30 | ErrCode: ecode.OK.Uint32(),
31 | ErrStr: ecode.OK.String(),
32 | Addr: addr,
33 | }); err != nil {
34 | glog.Error(err)
35 | }
36 | return
37 | }
38 |
--------------------------------------------------------------------------------
/server/gateway/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | )
8 |
9 | var (
10 | confPath string
11 | Conf *Config
12 | )
13 |
14 | type Config struct {
15 | *commconf.CommConf
16 | configFile string
17 | Server *commconf.Server
18 | ConfDiscovery *commconf.ConfDiscovery
19 | Etcd *commconf.Etcd
20 | }
21 |
22 | func init() {
23 | flag.StringVar(&confPath, "conf", "./gateway.toml", "config path")
24 | }
25 |
26 | func Init() (err error) {
27 | _, err = toml.DecodeFile(confPath, &Conf)
28 | return
29 | }
30 |
--------------------------------------------------------------------------------
/server/gateway/gateway.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/codec"
7 | "github.com/oikomi/FishChatServer2/libnet"
8 | "github.com/oikomi/FishChatServer2/server/gateway/conf"
9 | "github.com/oikomi/FishChatServer2/server/gateway/job"
10 | "github.com/oikomi/FishChatServer2/server/gateway/server"
11 | )
12 |
13 | func init() {
14 | flag.Set("alsologtostderr", "true")
15 | flag.Set("log_dir", "false")
16 | }
17 |
18 | func main() {
19 | var err error
20 | flag.Parse()
21 | if err = conf.Init(); err != nil {
22 | glog.Error("conf.Init() error: ", err)
23 | panic(err)
24 | }
25 | gwServer := server.New()
26 | protobuf := codec.Protobuf()
27 | if gwServer.Server, err = libnet.Serve(conf.Conf.Server.Proto, conf.Conf.Server.Addr, protobuf, 0); err != nil {
28 | glog.Error(err)
29 | panic(err)
30 | }
31 | go job.ConfDiscoveryProc()
32 | gwServer.Loop()
33 | }
34 |
--------------------------------------------------------------------------------
/server/gateway/gateway.toml:
--------------------------------------------------------------------------------
1 | # gateway conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/gateway.log"
5 |
6 | [server]
7 | proto = "tcp"
8 | addr = "0.0.0.0:10000"
9 |
10 | [confDiscovery]
11 | role = "master"
12 | interval = "10s"
13 |
14 | [etcd]
15 | root = "/server/access_server_gateway/"
16 | addrs = ["127.0.0.1:2379"]
17 | timeout = "1s"
18 |
19 |
--------------------------------------------------------------------------------
/server/gateway/job/conf_discovery.go:
--------------------------------------------------------------------------------
1 | package job
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/conf_discovery/etcd"
6 | "github.com/oikomi/FishChatServer2/server/gateway/conf"
7 | "time"
8 | )
9 |
10 | var (
11 | AccessServerList map[string]*etcd.Member
12 | )
13 |
14 | func loadAccessServerProc(master *etcd.Master) {
15 | for {
16 | // glog.Info("loadAccessServerProc")
17 | AccessServerList = master.Members()
18 | time.Sleep(time.Second * 5)
19 | }
20 | }
21 |
22 | func ConfDiscoveryProc() {
23 | master, err := etcd.NewMaster(conf.Conf.Etcd)
24 | if err != nil {
25 | glog.Error("Error: cannot connect to etcd:", err)
26 | panic(err)
27 | }
28 | go loadAccessServerProc(master)
29 | master.WatchWorkers()
30 | }
31 |
--------------------------------------------------------------------------------
/server/gateway/server/server.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/golang/protobuf/proto"
6 | "github.com/oikomi/FishChatServer2/common/ecode"
7 | "github.com/oikomi/FishChatServer2/conf_discovery/etcd"
8 | "github.com/oikomi/FishChatServer2/libnet"
9 | "github.com/oikomi/FishChatServer2/protocol/external"
10 | "github.com/oikomi/FishChatServer2/server/gateway/client"
11 | )
12 |
13 | type Server struct {
14 | Server *libnet.Server
15 | Master *etcd.Master
16 | MsgServerList []*etcd.Member
17 | }
18 |
19 | func New() (s *Server) {
20 | s = &Server{}
21 | return
22 | }
23 |
24 | func (s *Server) sessionLoop(client *client.Client) {
25 | for {
26 | reqData, err := client.Session.Receive()
27 | if err != nil {
28 | glog.Error(err)
29 | }
30 | if reqData != nil {
31 | baseCMD := &external.Base{}
32 | if err = proto.Unmarshal(reqData, baseCMD); err != nil {
33 | if err = client.Session.Send(&external.Error{
34 | Cmd: external.ErrServerCMD,
35 | ErrCode: ecode.ServerErr.Uint32(),
36 | ErrStr: ecode.ServerErr.String(),
37 | }); err != nil {
38 | glog.Error(err)
39 | }
40 | continue
41 | }
42 | if err = client.Parse(baseCMD.Cmd, reqData); err != nil {
43 | glog.Error(err)
44 | continue
45 | }
46 | }
47 | }
48 | }
49 |
50 | func (s *Server) Loop() {
51 | glog.Info("loop")
52 | for {
53 | session, err := s.Server.Accept()
54 | if err != nil {
55 | glog.Error(err)
56 | }
57 | go s.sessionLoop(client.New(session))
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/server/logic/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | )
8 |
9 | var (
10 | confPath string
11 | Conf *Config
12 | )
13 |
14 | type Config struct {
15 | *commconf.CommConf
16 | configFile string
17 | Server *commconf.Server
18 | ServiceDiscoveryServer *commconf.ServiceDiscoveryServer
19 | RPCServer *commconf.RPCServer
20 | RPCClient *RPCClient
21 | Etcd *commconf.Etcd
22 | KafkaProducer *KafkaProducer
23 | }
24 |
25 | type KafkaProducer struct {
26 | P2PTopic string
27 | GroupTopic string
28 | Producer *commconf.KafkaProducer
29 | }
30 |
31 | type RPCClient struct {
32 | RegisterClient *commconf.ServiceDiscoveryClient
33 | ManagerClient *commconf.ServiceDiscoveryClient
34 | IdgenClient *commconf.ServiceDiscoveryClient
35 | NotifyClient *commconf.ServiceDiscoveryClient
36 | }
37 |
38 | // type MongoDB struct {
39 | // *commconf.MongoDB
40 | // OfflineMsgCollection string
41 | // }
42 |
43 | // type ES struct {
44 | // *commconf.ES
45 | // Index string
46 | // }
47 |
48 | func init() {
49 | flag.StringVar(&confPath, "conf", "./logic.toml", "config path")
50 | }
51 |
52 | func Init() (err error) {
53 | _, err = toml.DecodeFile(confPath, &Conf)
54 | return
55 | }
56 |
--------------------------------------------------------------------------------
/server/logic/dao/dao.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import ()
4 |
5 | type Dao struct {
6 | KafkaProducer *KafkaProducer
7 | }
8 |
9 | func NewDao() (dao *Dao, err error) {
10 | KafkaProducer := NewKafkaProducer()
11 | dao = &Dao{
12 | KafkaProducer: KafkaProducer,
13 | }
14 | go dao.KafkaProducer.HandleError()
15 | go dao.KafkaProducer.HandleSuccess()
16 | go dao.KafkaProducer.Process()
17 | return
18 | }
19 |
--------------------------------------------------------------------------------
/server/logic/dao/es.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/golang/glog"
5 | // "github.com/oikomi/FishChatServer2/server/logic/conf"
6 | elastic "gopkg.in/olivere/elastic.v5"
7 | )
8 |
9 | type ES struct {
10 | esCli *elastic.Client
11 | }
12 |
13 | func NewES() (es *ES, err error) {
14 | // client, err := elastic.NewClient(elastic.SetURL(conf.Conf.ES.ES.Addrs))
15 | client, err := elastic.NewClient(elastic.SetURL())
16 | if err != nil {
17 | glog.Error(err)
18 | return
19 | }
20 | es = &ES{
21 | esCli: client,
22 | }
23 | return
24 | }
25 |
--------------------------------------------------------------------------------
/server/logic/dao/kafka.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/Shopify/sarama"
6 | "github.com/golang/glog"
7 | "github.com/oikomi/FishChatServer2/common/dao/kafka"
8 | "github.com/oikomi/FishChatServer2/common/model"
9 | "github.com/oikomi/FishChatServer2/server/logic/conf"
10 | "golang.org/x/net/context"
11 | )
12 |
13 | type KafkaProducer struct {
14 | producer *kafka.Producer
15 | sendP2PMsgChan chan *model.SendP2PMsgKafka
16 | sendGroupMsgChan chan *model.SendGroupMsgKafka
17 | }
18 |
19 | func NewKafkaProducer() (kafkaProducer *KafkaProducer) {
20 | producer := kafka.NewProducer(conf.Conf.KafkaProducer.Producer)
21 | kafkaProducer = &KafkaProducer{
22 | producer: producer,
23 | sendP2PMsgChan: make(chan *model.SendP2PMsgKafka, 1),
24 | sendGroupMsgChan: make(chan *model.SendGroupMsgKafka, 1),
25 | }
26 | return
27 | }
28 |
29 | func (kp *KafkaProducer) SendP2PMsg(data *model.SendP2PMsgKafka) {
30 | kp.sendP2PMsgChan <- data
31 | }
32 |
33 | func (kp *KafkaProducer) SendGroupMsg(data *model.SendGroupMsgKafka) {
34 | kp.sendGroupMsgChan <- data
35 | }
36 |
37 | func (kp *KafkaProducer) HandleSuccess() {
38 | var (
39 | pm *sarama.ProducerMessage
40 | )
41 | for {
42 | pm = <-kp.producer.Successes()
43 | if pm != nil {
44 | glog.Info("producer message success, partition:%d offset:%d key:%v valus:%s", pm.Partition, pm.Offset, pm.Key, pm.Value)
45 | }
46 | }
47 | }
48 |
49 | func (kp *KafkaProducer) HandleError() {
50 | var (
51 | err *sarama.ProducerError
52 | )
53 | for {
54 | err = <-kp.producer.Errors()
55 | if err != nil {
56 | glog.Error("producer message error, partition:%d offset:%d key:%v valus:%s error(%v)", err.Msg.Partition, err.Msg.Offset, err.Msg.Key, err.Msg.Value, err.Err)
57 | }
58 | }
59 | }
60 |
61 | func (kp *KafkaProducer) Process() {
62 | var sendP2PMsg *model.SendP2PMsgKafka
63 | var sendGroupMsg *model.SendGroupMsgKafka
64 | for {
65 | select {
66 | case sendP2PMsg = <-kp.sendP2PMsgChan:
67 | var (
68 | err error
69 | vBytes []byte
70 | )
71 | if vBytes, err = json.Marshal(sendP2PMsg); err != nil {
72 | glog.Error(err)
73 | return
74 | }
75 | glog.Info("send to kafka : ", string(vBytes))
76 | if err := kp.producer.Input(context.Background(), &sarama.ProducerMessage{
77 | Topic: conf.Conf.KafkaProducer.P2PTopic,
78 | Key: sarama.StringEncoder(model.SendP2PMsgKey),
79 | Value: sarama.ByteEncoder(vBytes),
80 | }); err != nil {
81 | glog.Error(err)
82 | }
83 | case sendGroupMsg = <-kp.sendGroupMsgChan:
84 | var (
85 | err error
86 | vBytes []byte
87 | )
88 | if vBytes, err = json.Marshal(sendGroupMsg); err != nil {
89 | glog.Error(err)
90 | return
91 | }
92 | glog.Info("send to kafka : ", string(vBytes))
93 | if err := kp.producer.Input(context.Background(), &sarama.ProducerMessage{
94 | Topic: conf.Conf.KafkaProducer.GroupTopic,
95 | Key: sarama.StringEncoder(model.SendGroupMsgKey),
96 | Value: sarama.ByteEncoder(vBytes),
97 | }); err != nil {
98 | glog.Error(err)
99 | }
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/server/logic/logic.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/server/logic/conf"
7 | "github.com/oikomi/FishChatServer2/server/logic/rpc"
8 | )
9 |
10 | func init() {
11 | flag.Set("alsologtostderr", "true")
12 | flag.Set("log_dir", "false")
13 | }
14 |
15 | func main() {
16 | flag.Parse()
17 | if err := conf.Init(); err != nil {
18 | glog.Error("conf.Init() error: ", err)
19 | panic(err)
20 | }
21 | rpcClient, err := rpc.NewRPCClient()
22 | if err != nil {
23 | glog.Error(err)
24 | panic(err)
25 | }
26 | rpc.RPCServerInit(rpcClient)
27 | }
28 |
--------------------------------------------------------------------------------
/server/logic/logic.toml:
--------------------------------------------------------------------------------
1 | # logic conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/logic.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:21000"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "logic"
12 | rpcAddr = "127.0.0.1:21000"
13 | etcdAddr = "localhost:2379"
14 | interval = "5s"
15 | ttl = "10s"
16 |
17 | [rpcClient]
18 | [rpcClient.registerClient]
19 | serviceName = "register"
20 | etcdAddr = "localhost:2379"
21 | balancer = "rr"
22 | [rpcClient.managerClient]
23 | serviceName = "manager"
24 | etcdAddr = "localhost:2379"
25 | balancer = "rr"
26 | [rpcClient.idgenClient]
27 | serviceName = "idgen"
28 | etcdAddr = "localhost:2379"
29 | balancer = "rr"
30 | [rpcClient.notifyClient]
31 | serviceName = "notify"
32 | etcdAddr = "localhost:2379"
33 | balancer = "rr"
34 |
35 | [kafkaProducer]
36 | p2pTopic = "logic_producer_p2p"
37 | groupTopic = "logic_producer_group"
38 | [kafkaProducer.producer]
39 | sync = false
40 | brokers = ["127.0.0.1:9092"]
41 | [kafkaProducer.producer.zookeeper]
42 | root = "/kafka"
43 | addrs = ["127.0.0.1:2182"]
44 | timeout = "2s"
45 |
46 | [mongoDB]
47 | addrs = "127.0.0.1:27017"
48 | db = "im"
49 | dialTimeout = "1s"
50 | offlineMsgCollection = "offline_msg"
51 |
52 | [etcd]
53 | root = "/server/access_server_logic/"
54 | addrs = ["127.0.0.1:2379"]
55 | timeout = "1s"
56 |
57 |
--------------------------------------------------------------------------------
/server/logic/logic_21001.toml:
--------------------------------------------------------------------------------
1 | # logic conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/logic.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:21001"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "logic"
12 | rpcAddr = "127.0.0.1:21001"
13 | etcdAddr = "localhost:2379"
14 | interval = "5s"
15 | ttl = "10s"
16 |
17 | [rpcClient]
18 | [rpcClient.registerClient]
19 | serviceName = "register"
20 | etcdAddr = "localhost:2379"
21 | balancer = "rr"
22 | [rpcClient.managerClient]
23 | serviceName = "manager"
24 | etcdAddr = "localhost:2379"
25 | balancer = "rr"
26 |
27 | [kafkaProducer]
28 | p2pTopic = "logic_producer_p2p"
29 | GroupTopic = "logic_producer_group"
30 | [kafkaProducer.producer]
31 | sync = false
32 | brokers = ["127.0.0.1:9092"]
33 | [kafkaProducer.producer.zookeeper]
34 | root = "/kafka"
35 | addrs = ["127.0.0.1:2181"]
36 | timeout = "2s"
37 |
38 | [mongoDB]
39 | addrs = "127.0.0.1:27017"
40 | db = "im"
41 | dialTimeout = "1s"
42 | offlineMsgCollection = "offline_msg"
43 |
--------------------------------------------------------------------------------
/server/logic/logic_21002.toml:
--------------------------------------------------------------------------------
1 | # logic conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/logic.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:21002"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "logic"
12 | rpcAddr = "127.0.0.1:21002"
13 | etcdAddr = "localhost:2379"
14 | interval = "5s"
15 | ttl = "10s"
16 |
17 | [rpcClient]
18 | [rpcClient.registerClient]
19 | serviceName = "register"
20 | etcdAddr = "localhost:2379"
21 | balancer = "rr"
22 | [rpcClient.managerClient]
23 | serviceName = "manager"
24 | etcdAddr = "localhost:2379"
25 | balancer = "rr"
26 |
27 | [kafkaProducer]
28 | p2pTopic = "logic_producer_p2p"
29 | GroupTopic = "logic_producer_group"
30 | [kafkaProducer.producer]
31 | sync = false
32 | brokers = ["127.0.0.1:9092"]
33 | [kafkaProducer.producer.zookeeper]
34 | root = "/kafka"
35 | addrs = ["127.0.0.1:2181"]
36 | timeout = "2s"
37 |
38 | [mongoDB]
39 | addrs = "127.0.0.1:27017"
40 | db = "im"
41 | dialTimeout = "1s"
42 | offlineMsgCollection = "offline_msg"
43 |
44 |
--------------------------------------------------------------------------------
/server/logic/rpc/client/idgen.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/protocol/rpc"
6 | "github.com/oikomi/FishChatServer2/server/logic/conf"
7 | sd "github.com/oikomi/FishChatServer2/service_discovery/etcd"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | "strconv"
11 | )
12 |
13 | type IdgenRPCCli struct {
14 | conn *grpc.ClientConn
15 | }
16 |
17 | func NewIdgenRPCCli() (idgenRPCCli *IdgenRPCCli, err error) {
18 | r := sd.NewResolver(conf.Conf.RPCClient.IdgenClient.ServiceName)
19 | b := grpc.RoundRobin(r)
20 | conn, err := grpc.Dial(conf.Conf.RPCClient.IdgenClient.EtcdAddr, grpc.WithInsecure(), grpc.WithBalancer(b))
21 | if err != nil {
22 | glog.Error(err)
23 | panic(err)
24 | }
25 | idgenRPCCli = &IdgenRPCCli{
26 | conn: conn,
27 | }
28 | return
29 | }
30 |
31 | func (idgenRPCCli *IdgenRPCCli) Next(ctx context.Context, targetUID int64) (res *rpc.Snowflake_Value, err error) {
32 | i := rpc.NewIDGenServerRPCClient(idgenRPCCli.conn)
33 | if res, err = i.Next(ctx, &rpc.Snowflake_Key{Name: strconv.FormatInt(targetUID, 10)}); err != nil {
34 | glog.Error(err)
35 | }
36 | return
37 | }
38 |
--------------------------------------------------------------------------------
/server/logic/rpc/client/manager.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/protocol/rpc"
6 | "github.com/oikomi/FishChatServer2/server/logic/conf"
7 | sd "github.com/oikomi/FishChatServer2/service_discovery/etcd"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | )
11 |
12 | type ManagerRPCCli struct {
13 | conn *grpc.ClientConn
14 | }
15 |
16 | func NewManagerRPCCli() (managerRPCCli *ManagerRPCCli, err error) {
17 | r := sd.NewResolver(conf.Conf.RPCClient.ManagerClient.ServiceName)
18 | b := grpc.RoundRobin(r)
19 | conn, err := grpc.Dial(conf.Conf.RPCClient.ManagerClient.EtcdAddr, grpc.WithInsecure(), grpc.WithBalancer(b))
20 | if err != nil {
21 | glog.Error(err)
22 | panic(err)
23 | }
24 | managerRPCCli = &ManagerRPCCli{
25 | conn: conn,
26 | }
27 | return
28 | }
29 |
30 | func (managerRPCCli *ManagerRPCCli) SetExceptionMsg(ctx context.Context, sourceUID, targetUID int64, msgID, msg string) (res *rpc.MGExceptionMsgRes, err error) {
31 | m := rpc.NewManagerServerRPCClient(managerRPCCli.conn)
32 | if res, err = m.SetExceptionMsg(ctx, &rpc.MGExceptionMsgReq{SourceUID: sourceUID, TargetUID: targetUID, MsgID: msgID, Msg: msg}); err != nil {
33 | glog.Error(err)
34 | }
35 | return
36 | }
37 |
38 | func (managerRPCCli *ManagerRPCCli) SyncMsg(ctx context.Context, uid, currentID, totalID int64) (res *rpc.MGSyncMsgRes, err error) {
39 | m := rpc.NewManagerServerRPCClient(managerRPCCli.conn)
40 | if res, err = m.Sync(ctx, &rpc.MGSyncMsgReq{UID: uid, CurrentID: currentID, TotalID: totalID}); err != nil {
41 | glog.Error(err)
42 | }
43 | return
44 | }
45 |
--------------------------------------------------------------------------------
/server/logic/rpc/client/notify.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/protocol/rpc"
6 | "github.com/oikomi/FishChatServer2/server/logic/conf"
7 | sd "github.com/oikomi/FishChatServer2/service_discovery/etcd"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | )
11 |
12 | type NotifyRPCCli struct {
13 | conn *grpc.ClientConn
14 | }
15 |
16 | func NewNotifyRPCCli() (notifyRPCCli *NotifyRPCCli, err error) {
17 | r := sd.NewResolver(conf.Conf.RPCClient.NotifyClient.ServiceName)
18 | b := grpc.RoundRobin(r)
19 | conn, err := grpc.Dial(conf.Conf.RPCClient.NotifyClient.EtcdAddr, grpc.WithInsecure(), grpc.WithBalancer(b))
20 | if err != nil {
21 | glog.Error(err)
22 | panic(err)
23 | }
24 | notifyRPCCli = &NotifyRPCCli{
25 | conn: conn,
26 | }
27 | return
28 | }
29 |
30 | func (notifyRPCCli *NotifyRPCCli) Notify(ctx context.Context, targetUID, totalID int64, accessAddr string) (res *rpc.NFNotifyMsgRes, err error) {
31 | n := rpc.NewNotifyServerRPCClient(notifyRPCCli.conn)
32 | if res, err = n.Notify(ctx, &rpc.NFNotifyMsgReq{
33 | TargetUID: targetUID,
34 | TotalID: totalID,
35 | AccessServerAddr: accessAddr,
36 | }); err != nil {
37 | glog.Error(err)
38 | }
39 | return
40 | }
41 |
--------------------------------------------------------------------------------
/server/logic/rpc/client/register.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/protocol/rpc"
6 | "github.com/oikomi/FishChatServer2/server/logic/conf"
7 | sd "github.com/oikomi/FishChatServer2/service_discovery/etcd"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | )
11 |
12 | type RegisterRPCCli struct {
13 | conn *grpc.ClientConn
14 | }
15 |
16 | func NewRegisterRPCCli() (registerRPCCli *RegisterRPCCli, err error) {
17 | r := sd.NewResolver(conf.Conf.RPCClient.RegisterClient.ServiceName)
18 | b := grpc.RoundRobin(r)
19 | conn, err := grpc.Dial(conf.Conf.RPCClient.RegisterClient.EtcdAddr, grpc.WithInsecure(), grpc.WithBalancer(b))
20 | if err != nil {
21 | glog.Error(err)
22 | panic(err)
23 | }
24 | registerRPCCli = &RegisterRPCCli{
25 | conn: conn,
26 | }
27 | return
28 | }
29 |
30 | func (registerRPCCli *RegisterRPCCli) Login(ctx context.Context, uid int64, token, accessAddr string) (res *rpc.RGLoginRes, err error) {
31 | r := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
32 | if res, err = r.Login(ctx, &rpc.RGLoginReq{UID: uid, Token: token, AccessAddr: accessAddr}); err != nil {
33 | glog.Error(err)
34 | }
35 | return
36 | }
37 |
38 | func (registerRPCCli *RegisterRPCCli) Auth(ctx context.Context, uid int64) (res *rpc.RGAuthRes, err error) {
39 | a := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
40 | if res, err = a.Auth(ctx, &rpc.RGAuthReq{UID: uid}); err != nil {
41 | glog.Error(err)
42 | }
43 | return
44 | }
45 |
46 | func (registerRPCCli *RegisterRPCCli) Online(ctx context.Context, uid int64) (res *rpc.RGOnlineRes, err error) {
47 | r := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
48 | if res, err = r.Online(ctx, &rpc.RGOnlineReq{UID: uid}); err != nil {
49 | glog.Error(err)
50 | }
51 | return
52 | }
53 |
54 | func (registerRPCCli *RegisterRPCCli) RouterAccess(ctx context.Context, uid int64) (res *rpc.RGAccessRes, err error) {
55 | r := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
56 | if res, err = r.RouterAccess(ctx, &rpc.RGAccessReq{UID: uid}); err != nil {
57 | glog.Error(err)
58 | }
59 | return
60 | }
61 |
62 | func (registerRPCCli *RegisterRPCCli) Ping(ctx context.Context, uid int64) (res *rpc.RGPingRes, err error) {
63 | r := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
64 | if res, err = r.Ping(ctx, &rpc.RGPingReq{UID: uid}); err != nil {
65 | glog.Error(err)
66 | }
67 | return
68 | }
69 |
70 | func (registerRPCCli *RegisterRPCCli) GetUsersByGroupID(ctx context.Context, gid int64) (res *rpc.RGGetUsersByGroupIDRes, err error) {
71 | r := rpc.NewRegisterServerRPCClient(registerRPCCli.conn)
72 | if res, err = r.GetUsersByGroupID(ctx, &rpc.RGGetUsersByGroupIDReq{Gid: gid}); err != nil {
73 | glog.Error(err)
74 | }
75 | return
76 | }
77 |
--------------------------------------------------------------------------------
/server/logic/rpc/rpc_client.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/server/logic/rpc/client"
6 | )
7 |
8 | type RPCClient struct {
9 | Register *client.RegisterRPCCli
10 | Manager *client.ManagerRPCCli
11 | Idgen *client.IdgenRPCCli
12 | Notify *client.NotifyRPCCli
13 | }
14 |
15 | func NewRPCClient() (c *RPCClient, err error) {
16 | register, err := client.NewRegisterRPCCli()
17 | if err != nil {
18 | glog.Error(err)
19 | return
20 | }
21 | manager, err := client.NewManagerRPCCli()
22 | if err != nil {
23 | glog.Error(err)
24 | return
25 | }
26 | idgen, err := client.NewIdgenRPCCli()
27 | if err != nil {
28 | glog.Error(err)
29 | return
30 | }
31 | notify, err := client.NewNotifyRPCCli()
32 | if err != nil {
33 | glog.Error(err)
34 | return
35 | }
36 | c = &RPCClient{
37 | Register: register,
38 | Manager: manager,
39 | Idgen: idgen,
40 | Notify: notify,
41 | }
42 | return
43 | }
44 |
--------------------------------------------------------------------------------
/server/manager/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | "github.com/oikomi/FishChatServer2/common/xtime"
8 | )
9 |
10 | var (
11 | confPath string
12 | Conf *Config
13 | )
14 |
15 | type Config struct {
16 | *commconf.CommConf
17 | configFile string
18 | RPCServer *commconf.RPCServer
19 | ServiceDiscoveryServer *commconf.ServiceDiscoveryServer
20 | Redis *Redis
21 | HBase *HBase
22 | Mysql *Mysql
23 | }
24 |
25 | type Redis struct {
26 | *commconf.Redis
27 | Expire xtime.Duration
28 | }
29 |
30 | type Mysql struct {
31 | IM *commconf.MySQL
32 | }
33 |
34 | type HBase struct {
35 | ZKAddr string
36 | Table string
37 | UserFamily string
38 | MsgFamily string
39 | }
40 |
41 | func init() {
42 | flag.StringVar(&confPath, "conf", "./manager.toml", "config path")
43 | }
44 |
45 | func Init() (err error) {
46 | _, err = toml.DecodeFile(confPath, &Conf)
47 | return
48 | }
49 |
--------------------------------------------------------------------------------
/server/manager/dao/dao.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | type Dao struct {
4 | Redis *Redis
5 | HBase *HBase
6 | Mysql *Mysql
7 | }
8 |
9 | func NewDao() (dao *Dao) {
10 | redis := NewRedis()
11 | h := NewHBase()
12 | mysql := NewMysql()
13 | dao = &Dao{
14 | Redis: redis,
15 | HBase: h,
16 | Mysql: mysql,
17 | }
18 | return
19 | }
20 |
--------------------------------------------------------------------------------
/server/manager/dao/hbase.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/common/dao/xhbase"
6 | "github.com/oikomi/FishChatServer2/server/manager/conf"
7 | "github.com/tsuna/gohbase/hrpc"
8 | "golang.org/x/net/context"
9 | )
10 |
11 | type HBase struct {
12 | client *xhbase.Client
13 | }
14 |
15 | func NewHBase() *HBase {
16 | return &HBase{
17 | client: xhbase.NewClient(conf.Conf.HBase.ZKAddr),
18 | }
19 | }
20 |
21 | func (h *HBase) GetMsgs(ctx context.Context, rowKey string) (res *hrpc.Result, err error) {
22 | getRequest, err := hrpc.NewGetStr(ctx, conf.Conf.HBase.Table, rowKey)
23 | if err != nil {
24 | glog.Error(err)
25 | return
26 | }
27 | res, err = h.client.Get(ctx, getRequest)
28 | if err != nil {
29 | glog.Error(err)
30 | }
31 | return
32 | }
33 |
--------------------------------------------------------------------------------
/server/manager/dao/mongodb.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | // import (
4 | // "github.com/golang/glog"
5 | // "github.com/oikomi/FishChatServer2/common/dao/mongodb"
6 | // commmodel "github.com/oikomi/FishChatServer2/common/model"
7 | // "github.com/oikomi/FishChatServer2/server/manager/conf"
8 | // "gopkg.in/mgo.v2/bson"
9 | // )
10 |
11 | // type MongoDB struct {
12 | // m *mongodb.MongoDB
13 | // }
14 |
15 | // func NewMongoDB() (mdb *MongoDB, err error) {
16 | // m, err := mongodb.NewMongoDB(conf.Conf.MongoDB.MongoDB)
17 | // if err != nil {
18 | // glog.Error(err)
19 | // }
20 | // mdb = &MongoDB{
21 | // m: m,
22 | // }
23 | // return
24 | // }
25 |
26 | // func (m *MongoDB) GetOfflineMsg(uid int64) (res []*commmodel.OfflineMsg, err error) {
27 | // c := m.m.Session.DB(conf.Conf.MongoDB.DB).C(conf.Conf.MongoDB.OfflineMsgCollection)
28 | // res = make([]*commmodel.OfflineMsg, 0)
29 | // if err = c.Find(bson.M{"targetuid": uid}).All(&res); err != nil {
30 | // glog.Error(err)
31 | // }
32 | // glog.Info(res)
33 | // return
34 | // }
35 |
--------------------------------------------------------------------------------
/server/manager/dao/mysql.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "database/sql"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/common/dao/xmysql"
7 | "github.com/oikomi/FishChatServer2/server/manager/conf"
8 | "github.com/oikomi/FishChatServer2/server/manager/model"
9 | "golang.org/x/net/context"
10 | )
11 |
12 | const (
13 | _getUserMsgIDSQL = "SELECT uid,current_msg_id,total_msg_id FROM user_msg_id WHERE uid=?"
14 | _upUserMsgIDSQL = "UPDATE user_msg_id SET current_msg_id=? WHERE uid=?"
15 | )
16 |
17 | type Mysql struct {
18 | im *xmysql.DB
19 | getUserMsgIDStmt *xmysql.Stmt
20 | upUserMsgIDStmt *xmysql.Stmt
21 | }
22 |
23 | func NewMysql() (mysql *Mysql) {
24 | mysql = &Mysql{
25 | im: xmysql.NewMySQL(conf.Conf.Mysql.IM),
26 | }
27 | mysql.initStmt()
28 | return
29 | }
30 |
31 | func (mysql *Mysql) initStmt() {
32 | mysql.getUserMsgIDStmt = mysql.im.Prepared(_getUserMsgIDSQL)
33 | mysql.upUserMsgIDStmt = mysql.im.Prepared(_upUserMsgIDSQL)
34 | }
35 |
36 | func (mysql *Mysql) GetUserMsgID(c context.Context, uid int64) (userMsgID *model.UserMsgID, err error) {
37 | row := mysql.im.QueryRow(c, _getUserMsgIDSQL, uid)
38 | userMsgID = &model.UserMsgID{}
39 | if err = row.Scan(&userMsgID.UID, &userMsgID.CurrentMsgID, &userMsgID.TotalMsgID); err != nil {
40 | if err == sql.ErrNoRows {
41 | userMsgID = nil
42 | err = nil
43 | } else {
44 | glog.Error(err)
45 | }
46 | }
47 | return
48 | }
49 |
50 | func (mysql *Mysql) UpdateUserMsgID(c context.Context, uid, currentMsgID int64) (rows int64, err error) {
51 | res, err := mysql.upUserMsgIDStmt.Exec(c, currentMsgID, uid)
52 | if err != nil {
53 | glog.Error(err)
54 | return
55 | }
56 | return res.RowsAffected()
57 | }
58 |
--------------------------------------------------------------------------------
/server/manager/dao/redis.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/garyburd/redigo/redis"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/common/dao/xredis"
7 | "github.com/oikomi/FishChatServer2/server/manager/conf"
8 | "golang.org/x/net/context"
9 | )
10 |
11 | const (
12 | _keyExceptionMsg = "mge_"
13 | _keyNormalMsg = "mgn_"
14 | )
15 |
16 | func keyExceptionMsg(msgID string) string {
17 | return _keyExceptionMsg + msgID
18 | }
19 |
20 | func keyNormalMsg(msgID string) string {
21 | return _keyNormalMsg + msgID
22 | }
23 |
24 | type Redis struct {
25 | redis *xredis.Pool
26 | }
27 |
28 | func NewRedis() (redis *Redis) {
29 | redis = &Redis{
30 | redis: xredis.NewPool(conf.Conf.Redis.Redis),
31 | }
32 | return
33 | }
34 |
35 | func (r *Redis) SetExceptionMsg(ctx context.Context, msgID string, data string) (err error) {
36 | conn := r.redis.Get(ctx)
37 | defer conn.Close()
38 | _, err = conn.Do("SET", keyExceptionMsg(msgID), data)
39 | if err != nil {
40 | glog.Error(err)
41 | }
42 | return
43 | }
44 |
45 | func (r *Redis) ExceptionMsg(ctx context.Context, msgID string) (res string, err error) {
46 | conn := r.redis.Get(ctx)
47 | defer conn.Close()
48 | res, err = redis.String(conn.Do("GET", keyExceptionMsg(msgID)))
49 | if err != nil {
50 | glog.Error(err)
51 | }
52 | return
53 | }
54 |
55 | func (r *Redis) SetNormalMsg(ctx context.Context, msgID string, data string) (err error) {
56 | conn := r.redis.Get(ctx)
57 | defer conn.Close()
58 | _, err = conn.Do("SET", keyNormalMsg(msgID), data)
59 | if err != nil {
60 | glog.Error(err)
61 | }
62 | return
63 | }
64 |
--------------------------------------------------------------------------------
/server/manager/manager.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/server/manager/conf"
7 | "github.com/oikomi/FishChatServer2/server/manager/rpc"
8 | )
9 |
10 | func init() {
11 | flag.Set("alsologtostderr", "true")
12 | flag.Set("log_dir", "false")
13 | }
14 |
15 | func main() {
16 | flag.Parse()
17 | if err := conf.Init(); err != nil {
18 | glog.Error("conf.Init() error: ", err)
19 | panic(err)
20 | }
21 | rpc.RPCServerInit()
22 | }
23 |
--------------------------------------------------------------------------------
/server/manager/manager.toml:
--------------------------------------------------------------------------------
1 | # manager conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/manager.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:24000"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "manager"
12 | rpcAddr = "127.0.0.1:24000"
13 | etcdAddr = "localhost:2379"
14 | interval = "5s"
15 | ttl = "10s"
16 |
17 | [redis]
18 | name = "manager"
19 | proto = "tcp"
20 | addr = "127.0.0.1:6379"
21 | idle = 100
22 | active = 100
23 | dialTimeout = "1s"
24 | readTimeout = "1s"
25 | writeTimeout = "1s"
26 | idleTimeout = "10s"
27 | expire = "15s"
28 |
29 | [mongoDB]
30 | addrs = "127.0.0.1:27017"
31 | db = "im"
32 | dialTimeout = "1s"
33 | offlineMsgCollection = "offline_msg"
34 |
35 | [hbase]
36 | zkAddr = "127.0.0.1:2181"
37 | table = "im"
38 | userFamily = "user"
39 | msgFamily = "msg"
40 |
41 | [mysql]
42 | [mysql.im]
43 | name = "[im]tcp@127.0.0.1:3306"
44 | dsn = "root:1@tcp(127.0.0.1:3306)/im?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
45 | active = 5
46 | idle = 2
47 |
48 |
49 |
--------------------------------------------------------------------------------
/server/manager/manager_24001.toml:
--------------------------------------------------------------------------------
1 | # manager conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/manager.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:24001"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "manager"
12 | rpcAddr = "127.0.0.1:24001"
13 | etcdAddr = "localhost:2379"
14 | interval = "5s"
15 | ttl = "10s"
16 |
17 | [redis]
18 | name = "manager"
19 | proto = "tcp"
20 | addr = "127.0.0.1:6379"
21 | idle = 100
22 | active = 100
23 | dialTimeout = "1s"
24 | readTimeout = "1s"
25 | writeTimeout = "1s"
26 | idleTimeout = "10s"
27 | expire = "15s"
28 |
29 | [mongoDB]
30 | addrs = "127.0.0.1:27017"
31 | db = "im"
32 | dialTimeout = "1s"
33 | offlineMsgCollection = "offline_msg"
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/server/manager/manager_24002.toml:
--------------------------------------------------------------------------------
1 | # manager conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/manager.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:24001"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "manager"
12 | rpcAddr = "127.0.0.1:24001"
13 | etcdAddr = "localhost:2379"
14 | interval = "5s"
15 | ttl = "10s"
16 |
17 | [redis]
18 | name = "manager"
19 | proto = "tcp"
20 | addr = "127.0.0.1:6379"
21 | idle = 100
22 | active = 100
23 | dialTimeout = "1s"
24 | readTimeout = "1s"
25 | writeTimeout = "1s"
26 | idleTimeout = "10s"
27 | expire = "15s"
28 |
29 | [mongoDB]
30 | addrs = "127.0.0.1:27017"
31 | db = "im"
32 | dialTimeout = "1s"
33 | offlineMsgCollection = "offline_msg"
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/server/manager/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | var (
4 | HbaseTable = []byte("im")
5 |
6 | HbaseFamilyUser = []byte("user")
7 | HbaseFamilyMsg = []byte("msg")
8 |
9 | HbaseColumnSourceUID = []byte("sourceUID")
10 | HbaseColumnTargetUID = []byte("targetUID")
11 | HbaseColumnGroupID = []byte("groupID")
12 | HbaseColumnOnline = []byte("online")
13 | HbaseColumnIncrementID = []byte("incrementID")
14 | HbaseColumnMsgType = []byte("msgType")
15 | HbaseColumnMsgID = []byte("msgID")
16 | HbaseColumnMsg = []byte("msg")
17 | HbaseColumnAccessServerAddr = []byte("accessServerAddr")
18 | )
19 |
20 | type OffsetMsg struct {
21 | SourceUID int64 `json:"sourceUID"`
22 | TargetUID int64 `json:"targetUID"`
23 | MsgID string `json:"msgID"`
24 | Msg string `json:"msg"`
25 | }
26 |
27 | type UserMsgID struct {
28 | UID int64 `json:"uid"`
29 | CurrentMsgID int64 `json:"current_msg_id"`
30 | TotalMsgID int64 `json:"total_msg_id"`
31 | }
32 |
--------------------------------------------------------------------------------
/server/manager/rpc/rpc_server_test.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | // import (
4 | // "github.com/oikomi/FishChatServer2/protocol/rpc"
5 | // "golang.org/x/net/context"
6 | // "google.golang.org/grpc"
7 | // "testing"
8 | // )
9 |
10 | // func TestSync(t *testing.T) {
11 | // // Set up a connection to the server.
12 | // conn, err := grpc.Dial("127.0.0.1:24000", grpc.WithInsecure())
13 | // if err != nil {
14 | // t.Fatalf("did not connect: %v", err)
15 | // }
16 | // defer conn.Close()
17 | // m := rpc.NewManagerServerRPCClient(conn)
18 | // // Contact the server and print out its response.
19 | // r, err := m.Sync(context.Background(), &rpc.MGSyncMsgReq{UID: 123, CurrentID: 25, TotalID: 26})
20 | // if err != nil {
21 | // t.Fatalf("could not get next value: %v", err)
22 | // }
23 | // t.Log(r)
24 | // }
25 |
--------------------------------------------------------------------------------
/server/notify/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | "github.com/oikomi/FishChatServer2/common/xtime"
8 | )
9 |
10 | var (
11 | confPath string
12 | Conf *Config
13 | )
14 |
15 | type Config struct {
16 | *commconf.CommConf
17 | configFile string
18 | RPCServer *commconf.RPCServer
19 | ServiceDiscoveryServer *commconf.ServiceDiscoveryServer
20 | RPCClient *RPCClient
21 | Redis *Redis
22 | Mysql *Mysql
23 | Etcd *commconf.Etcd
24 | }
25 |
26 | type RPCClient struct {
27 | AccessClient *commconf.ServiceDiscoveryClient
28 | }
29 |
30 | type Redis struct {
31 | *commconf.Redis
32 | Expire xtime.Duration
33 | }
34 |
35 | type Mysql struct {
36 | IM *commconf.MySQL
37 | }
38 |
39 | // type MongoDB struct {
40 | // *commconf.MongoDB
41 | // GroupCollection string
42 | // }
43 |
44 | func init() {
45 | flag.StringVar(&confPath, "conf", "./notify.toml", "config path")
46 | }
47 |
48 | func Init() (err error) {
49 | _, err = toml.DecodeFile(confPath, &Conf)
50 | return
51 | }
52 |
--------------------------------------------------------------------------------
/server/notify/conf_discovery/conf_discovery.go:
--------------------------------------------------------------------------------
1 | package conf_discovery
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/conf_discovery/etcd"
6 | "github.com/oikomi/FishChatServer2/server/notify/conf"
7 | "time"
8 | )
9 |
10 | var (
11 | AccessServerList map[string]*etcd.Member
12 | )
13 |
14 | func loadAccessServerProc(master *etcd.Master) {
15 | for {
16 | // glog.Info("loadAccessServerProc")
17 | AccessServerList = master.Members()
18 | time.Sleep(time.Second * 5)
19 | }
20 | }
21 |
22 | func ConfDiscoveryProc() {
23 | glog.Info("ConfDiscoveryProc")
24 | master, err := etcd.NewMaster(conf.Conf.Etcd)
25 | if err != nil {
26 | glog.Error("Error: cannot connect to etcd:", err)
27 | panic(err)
28 | }
29 | go loadAccessServerProc(master)
30 | master.WatchWorkers()
31 | }
32 |
--------------------------------------------------------------------------------
/server/notify/dao/dao.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/oikomi/FishChatServer2/common/dao/xredis"
5 | "github.com/oikomi/FishChatServer2/server/notify/conf"
6 | )
7 |
8 | type Dao struct {
9 | redis *xredis.Pool
10 | Mysql *Mysql
11 | }
12 |
13 | func NewDao() (dao *Dao) {
14 | mysql := NewMysql()
15 | dao = &Dao{
16 | redis: xredis.NewPool(conf.Conf.Redis.Redis),
17 | Mysql: mysql,
18 | }
19 | return
20 | }
21 |
--------------------------------------------------------------------------------
/server/notify/dao/mysql.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "database/sql"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/common/dao/xmysql"
7 | "github.com/oikomi/FishChatServer2/server/notify/conf"
8 | "github.com/oikomi/FishChatServer2/server/notify/model"
9 | "golang.org/x/net/context"
10 | )
11 |
12 | const (
13 | _upUserMsgIDSQL = "UPDATE user_msg_id SET current_msg_id=?,total_msg_id=? WHERE uid=?"
14 | _getUserMsgIDSQL = "SELECT uid,current_msg_id,total_msg_id FROM user_msg_id WHERE uid=?"
15 | )
16 |
17 | type Mysql struct {
18 | im *xmysql.DB
19 | upUserMsgIDStmt *xmysql.Stmt
20 | getUserMsgIDStmt *xmysql.Stmt
21 | }
22 |
23 | func NewMysql() (mysql *Mysql) {
24 | mysql = &Mysql{
25 | im: xmysql.NewMySQL(conf.Conf.Mysql.IM),
26 | }
27 | mysql.initStmt()
28 | return
29 | }
30 |
31 | func (mysql *Mysql) initStmt() {
32 | mysql.upUserMsgIDStmt = mysql.im.Prepared(_upUserMsgIDSQL)
33 | mysql.getUserMsgIDStmt = mysql.im.Prepared(_getUserMsgIDSQL)
34 | }
35 |
36 | func (mysql *Mysql) UpdateUserMsgID(c context.Context, uid, currentMsgID, totalMsgID int64) (rows int64, err error) {
37 | res, err := mysql.upUserMsgIDStmt.Exec(c, currentMsgID, totalMsgID, uid)
38 | if err != nil {
39 | glog.Error(err)
40 | return
41 | }
42 | return res.RowsAffected()
43 | }
44 |
45 | func (mysql *Mysql) GetUserMsgID(c context.Context, uid int64) (userMsgID *model.UserMsgID, err error) {
46 | row := mysql.im.QueryRow(c, _getUserMsgIDSQL, uid)
47 | userMsgID = &model.UserMsgID{}
48 | if err = row.Scan(&userMsgID.UID, &userMsgID.CurrentMsgID, &userMsgID.TotalMsgID); err != nil {
49 | if err == sql.ErrNoRows {
50 | userMsgID = nil
51 | err = nil
52 | } else {
53 | glog.Error(err)
54 | }
55 | }
56 | return
57 | }
58 |
--------------------------------------------------------------------------------
/server/notify/dao/redis.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/garyburd/redigo/redis"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/server/notify/conf"
7 | "golang.org/x/net/context"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | const (
13 | _keyOnline = "rgo_"
14 | _keyAccess = "rga_"
15 | _keyToken = "rgt_"
16 | _online = 1
17 | _offline = 0
18 | )
19 |
20 | func keyOnline(uid int64) string {
21 | return _keyOnline + strconv.FormatInt(uid, 10)
22 | }
23 |
24 | func keyAccess(uid int64) string {
25 | return _keyAccess + strconv.FormatInt(uid, 10)
26 | }
27 |
28 | func keyToken(uid int64) string {
29 | return _keyToken + strconv.FormatInt(uid, 10)
30 | }
31 |
32 | func (dao *Dao) Token(ctx context.Context, uid int64) (res string, err error) {
33 | conn := dao.redis.Get(ctx)
34 | defer conn.Close()
35 | res, err = redis.String(conn.Do("GET", keyToken(uid)))
36 | if err != nil {
37 | glog.Error(err)
38 | }
39 | return
40 | }
41 |
42 | func (dao *Dao) SetToken(ctx context.Context, uid int64, token string) (err error) {
43 | conn := dao.redis.Get(ctx)
44 | defer conn.Close()
45 | _, err = conn.Do("SETEX", keyToken(uid), int(time.Duration(conf.Conf.Redis.Expire)/time.Second), token)
46 | if err != nil {
47 | glog.Error(err)
48 | }
49 | return
50 | }
51 |
52 | func (dao *Dao) RegisterAccess(ctx context.Context, uid int64, accessAddr string) (err error) {
53 | conn := dao.redis.Get(ctx)
54 | defer conn.Close()
55 | _, err = conn.Do("SET", keyAccess(uid), accessAddr)
56 | if err != nil {
57 | glog.Error(err)
58 | }
59 | return
60 | }
61 |
62 | func (dao *Dao) RouterAccess(ctx context.Context, uid int64) (res string, err error) {
63 | conn := dao.redis.Get(ctx)
64 | defer conn.Close()
65 | res, err = redis.String(conn.Do("GET", keyAccess(uid)))
66 | if err != nil {
67 | glog.Error(err)
68 | }
69 | return
70 | }
71 |
72 | func (dao *Dao) GetOnline(ctx context.Context, uid int64) (res int, err error) {
73 | conn := dao.redis.Get(ctx)
74 | defer conn.Close()
75 | res, err = redis.Int(conn.Do("GET", keyOnline(uid)))
76 | if err != nil {
77 | res = _offline
78 | glog.Error(err)
79 | }
80 | return
81 | }
82 |
83 | func (dao *Dao) SetOnline(ctx context.Context, uid int64) (err error) {
84 | conn := dao.redis.Get(ctx)
85 | defer conn.Close()
86 | _, err = conn.Do("SETEX", keyOnline(uid), int(time.Duration(conf.Conf.Redis.Expire)/time.Second), _online)
87 | if err != nil {
88 | glog.Error(err)
89 | }
90 | return
91 | }
92 |
--------------------------------------------------------------------------------
/server/notify/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type UserMsgID struct {
4 | UID int64 `json:"uid"`
5 | CurrentMsgID int64 `json:"current_msg_id"`
6 | TotalMsgID int64 `json:"total_msg_id"`
7 | }
8 |
--------------------------------------------------------------------------------
/server/notify/notify.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/server/notify/conf"
7 | "github.com/oikomi/FishChatServer2/server/notify/conf_discovery"
8 | "github.com/oikomi/FishChatServer2/server/notify/rpc"
9 | )
10 |
11 | func init() {
12 | flag.Set("alsologtostderr", "true")
13 | flag.Set("log_dir", "false")
14 | }
15 |
16 | func main() {
17 | flag.Parse()
18 | if err := conf.Init(); err != nil {
19 | glog.Error("conf.Init() error: ", err)
20 | panic(err)
21 | }
22 | go conf_discovery.ConfDiscoveryProc()
23 | rpcClient, err := rpc.NewRPCClient()
24 | if err != nil {
25 | glog.Error(err)
26 | panic(err)
27 | }
28 | rpc.RPCServerInit(rpcClient)
29 | }
30 |
--------------------------------------------------------------------------------
/server/notify/notify.toml:
--------------------------------------------------------------------------------
1 | # notify conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/notify.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:25000"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "notify"
12 | rpcAddr = "127.0.0.1:25000"
13 | etcdAddr = "localhost:2379"
14 | interval = "5s"
15 | ttl = "10s"
16 |
17 | [rpcClient]
18 | [rpcClient.accessClient]
19 | serviceName = "access"
20 | etcdAddr = "localhost:2379"
21 | balancer = "rr"
22 |
23 | [redis]
24 | name = "notify"
25 | proto = "tcp"
26 | addr = "127.0.0.1:6379"
27 | idle = 100
28 | active = 100
29 | dialTimeout = "1s"
30 | readTimeout = "1s"
31 | writeTimeout = "1s"
32 | idleTimeout = "10s"
33 | expire = "15s"
34 |
35 | [etcd]
36 | root = "/server/access_server_notify/"
37 | addrs = ["127.0.0.1:2379"]
38 | timeout = "1s"
39 |
40 | [mysql]
41 | [mysql.im]
42 | name = "[im]tcp@127.0.0.1:3306"
43 | dsn = "root:1@tcp(127.0.0.1:3306)/im?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
44 | active = 5
45 | idle = 2
46 |
47 |
48 |
--------------------------------------------------------------------------------
/server/notify/rpc/client/access.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "errors"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/protocol/rpc"
7 | "github.com/oikomi/FishChatServer2/server/notify/conf_discovery"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | "time"
11 | )
12 |
13 | type AccessServerRPCCli struct {
14 | conns map[string]*grpc.ClientConn
15 | }
16 |
17 | func NewAccessServerRPCCli() (accessServerRPCCli *AccessServerRPCCli, err error) {
18 | glog.Info("NewAccessServerRPCCli")
19 | var accessServerList []string
20 | conns := make(map[string]*grpc.ClientConn)
21 | for {
22 | if len(conf_discovery.AccessServerList) <= 0 {
23 | time.Sleep(time.Second * 5)
24 | } else {
25 | glog.Info(conf_discovery.AccessServerList)
26 | for _, v := range conf_discovery.AccessServerList {
27 | accessServerList = append(accessServerList, v.IP)
28 | }
29 | for _, accessServer := range accessServerList {
30 | conn, err := grpc.Dial(accessServer, grpc.WithInsecure())
31 | if err != nil {
32 | glog.Error(err)
33 | }
34 | conns[accessServer] = conn
35 | }
36 | accessServerRPCCli = &AccessServerRPCCli{
37 | conns: conns,
38 | }
39 | break
40 | }
41 | }
42 | go accessServerRPCCli.connProc()
43 | return
44 | }
45 |
46 | func (accessServerRPCCli *AccessServerRPCCli) connProc() {
47 | glog.Info(conf_discovery.AccessServerList)
48 | var accessServerList []string
49 | conns := make(map[string]*grpc.ClientConn)
50 | for {
51 | for _, v := range conf_discovery.AccessServerList {
52 | glog.Info(v.IP)
53 | accessServerList = append(accessServerList, v.IP)
54 | }
55 | for _, accessServer := range accessServerList {
56 | conn, err := grpc.Dial(accessServer, grpc.WithInsecure())
57 | if err != nil {
58 | glog.Error(err)
59 | }
60 | conns[accessServer] = conn
61 | }
62 | accessServerRPCCli.conns = conns
63 | time.Sleep(time.Second * 10)
64 | }
65 | }
66 |
67 | // FIXME can not use rr
68 | func (accessServerRPCCli *AccessServerRPCCli) SendNotify(ctx context.Context, sendNotifyReq *rpc.ASSendNotifyReq) (res *rpc.ASSendNotifyRes, err error) {
69 | glog.Info(accessServerRPCCli.conns)
70 | glog.Info(sendNotifyReq.AccessServerAddr)
71 | if conn, ok := accessServerRPCCli.conns[sendNotifyReq.AccessServerAddr]; ok {
72 | a := rpc.NewAccessServerRPCClient(conn)
73 | if res, err = a.SendNotify(ctx, sendNotifyReq); err != nil {
74 | glog.Error(err)
75 | }
76 | } else {
77 | err = errors.New("no access server")
78 | }
79 | return
80 | }
81 |
--------------------------------------------------------------------------------
/server/notify/rpc/rpc_client.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/server/notify/rpc/client"
6 | )
7 |
8 | type RPCClient struct {
9 | Access *client.AccessServerRPCCli
10 | }
11 |
12 | func NewRPCClient() (c *RPCClient, err error) {
13 | access, err := client.NewAccessServerRPCCli()
14 | if err != nil {
15 | glog.Error(err)
16 | return
17 | }
18 | c = &RPCClient{
19 | Access: access,
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/server/notify/rpc/rpc_server.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/common/ecode"
6 | "github.com/oikomi/FishChatServer2/protocol/rpc"
7 | "github.com/oikomi/FishChatServer2/server/notify/conf"
8 | "github.com/oikomi/FishChatServer2/server/notify/dao"
9 | sd "github.com/oikomi/FishChatServer2/service_discovery/etcd"
10 | "golang.org/x/net/context"
11 | "google.golang.org/grpc"
12 | "net"
13 | )
14 |
15 | type RPCServer struct {
16 | dao *dao.Dao
17 | rpcClient *RPCClient
18 | }
19 |
20 | func (s *RPCServer) Notify(ctx context.Context, in *rpc.NFNotifyMsgReq) (res *rpc.NFNotifyMsgRes, err error) {
21 | glog.Info("notify recive Notify")
22 | userMsgID, err := s.dao.Mysql.GetUserMsgID(ctx, in.TargetUID)
23 | if err != nil {
24 | glog.Error(err)
25 | return
26 | }
27 | _, err = s.dao.Mysql.UpdateUserMsgID(ctx, in.TargetUID, userMsgID.CurrentMsgID, in.TotalID)
28 | if err != nil {
29 | glog.Error(err)
30 | return
31 | }
32 | sendNotifyReqRPC := &rpc.ASSendNotifyReq{
33 | UID: in.TargetUID,
34 | CurrentID: userMsgID.CurrentMsgID,
35 | TotalID: in.TotalID,
36 | AccessServerAddr: in.AccessServerAddr,
37 | }
38 | _, err = s.rpcClient.Access.SendNotify(ctx, sendNotifyReqRPC)
39 | if err != nil {
40 | glog.Error(err)
41 | return
42 | }
43 | res = &rpc.NFNotifyMsgRes{
44 | ErrCode: ecode.OK.Uint32(),
45 | ErrStr: ecode.OK.String(),
46 | }
47 | return
48 | }
49 |
50 | func RPCServerInit(rpcClient *RPCClient) {
51 | glog.Info("[notify] rpc server init: ", conf.Conf.RPCServer.Addr)
52 | lis, err := net.Listen(conf.Conf.RPCServer.Proto, conf.Conf.RPCServer.Addr)
53 | if err != nil {
54 | glog.Error(err)
55 | panic(err)
56 | }
57 | err = sd.Register(conf.Conf.ServiceDiscoveryServer.ServiceName, conf.Conf.ServiceDiscoveryServer.RPCAddr, conf.Conf.ServiceDiscoveryServer.EtcdAddr, conf.Conf.ServiceDiscoveryServer.Interval, conf.Conf.ServiceDiscoveryServer.TTL)
58 | if err != nil {
59 | glog.Error(err)
60 | panic(err)
61 | }
62 | s := grpc.NewServer()
63 | rpcServer := &RPCServer{
64 | dao: dao.NewDao(),
65 | rpcClient: rpcClient,
66 | }
67 | rpc.RegisterNotifyServerRPCServer(s, rpcServer)
68 | s.Serve(lis)
69 | }
70 |
--------------------------------------------------------------------------------
/server/port:
--------------------------------------------------------------------------------
1 |
2 | [http]
3 | auth-api : 6001
4 |
5 | [external]
6 | access : 11000
7 |
8 | [rpc]
9 | access : 20000
10 | logic : 21000
11 | auth : 22000
12 | register : 23000
13 | manager : 24000
14 | notify : 25000
15 |
16 |
--------------------------------------------------------------------------------
/server/register/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | "github.com/oikomi/FishChatServer2/common/xtime"
8 | )
9 |
10 | var (
11 | confPath string
12 | Conf *Config
13 | )
14 |
15 | type Config struct {
16 | *commconf.CommConf
17 | configFile string
18 | RPCServer *commconf.RPCServer
19 | ServiceDiscoveryServer *commconf.ServiceDiscoveryServer
20 | RPCClient *RPCClient
21 | Auth *Auth
22 | Redis *Redis
23 | Mysql *Mysql
24 | MongoDB *MongoDB
25 | }
26 |
27 | type RPCClient struct {
28 | IdgenClient *commconf.ServiceDiscoveryClient
29 | }
30 |
31 | type Auth struct {
32 | Encryption string
33 | Salt string
34 | }
35 |
36 | type Redis struct {
37 | *commconf.Redis
38 | Expire xtime.Duration
39 | }
40 |
41 | type Mysql struct {
42 | IM *commconf.MySQL
43 | }
44 |
45 | type MongoDB struct {
46 | *commconf.MongoDB
47 | GroupCollection string
48 | }
49 |
50 | func init() {
51 | flag.StringVar(&confPath, "conf", "./register.toml", "config path")
52 | }
53 |
54 | func Init() (err error) {
55 | _, err = toml.DecodeFile(confPath, &Conf)
56 | return
57 | }
58 |
--------------------------------------------------------------------------------
/server/register/dao/dao.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | // "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/common/dao/xredis"
6 | "github.com/oikomi/FishChatServer2/server/register/conf"
7 | )
8 |
9 | type Dao struct {
10 | redis *xredis.Pool
11 | Mysql *Mysql
12 | }
13 |
14 | func NewDao() (dao *Dao) {
15 | mysql := NewMysql()
16 | dao = &Dao{
17 | redis: xredis.NewPool(conf.Conf.Redis.Redis),
18 | Mysql: mysql,
19 | }
20 | return
21 | }
22 |
--------------------------------------------------------------------------------
/server/register/dao/redis.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/garyburd/redigo/redis"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/server/register/conf"
7 | "golang.org/x/net/context"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | const (
13 | _keyOnline = "rgo_"
14 | _keyAccess = "rga_"
15 | _keyToken = "rgt_"
16 | _online = 1
17 | _offline = 0
18 | )
19 |
20 | func keyOnline(uid int64) string {
21 | return _keyOnline + strconv.FormatInt(uid, 10)
22 | }
23 |
24 | func keyAccess(uid int64) string {
25 | return _keyAccess + strconv.FormatInt(uid, 10)
26 | }
27 |
28 | func keyToken(uid int64) string {
29 | return _keyToken + strconv.FormatInt(uid, 10)
30 | }
31 |
32 | func (dao *Dao) Token(ctx context.Context, uid int64) (res string, err error) {
33 | conn := dao.redis.Get(ctx)
34 | defer conn.Close()
35 | res, err = redis.String(conn.Do("GET", keyToken(uid)))
36 | if err != nil {
37 | glog.Error(err)
38 | }
39 | return
40 | }
41 |
42 | func (dao *Dao) SetToken(ctx context.Context, uid int64, token string) (err error) {
43 | conn := dao.redis.Get(ctx)
44 | defer conn.Close()
45 | _, err = conn.Do("SETEX", keyToken(uid), int(time.Duration(conf.Conf.Redis.Expire)/time.Second), token)
46 | if err != nil {
47 | glog.Error(err)
48 | }
49 | return
50 | }
51 |
52 | func (dao *Dao) RegisterAccess(ctx context.Context, uid int64, accessAddr string) (err error) {
53 | conn := dao.redis.Get(ctx)
54 | defer conn.Close()
55 | _, err = conn.Do("SET", keyAccess(uid), accessAddr)
56 | if err != nil {
57 | glog.Error(err)
58 | }
59 | return
60 | }
61 |
62 | func (dao *Dao) RouterAccess(ctx context.Context, uid int64) (res string, err error) {
63 | conn := dao.redis.Get(ctx)
64 | defer conn.Close()
65 | res, err = redis.String(conn.Do("GET", keyAccess(uid)))
66 | if err != nil {
67 | glog.Error(err)
68 | }
69 | return
70 | }
71 |
72 | func (dao *Dao) GetOnline(ctx context.Context, uid int64) (res int, err error) {
73 | conn := dao.redis.Get(ctx)
74 | defer conn.Close()
75 | res, err = redis.Int(conn.Do("GET", keyOnline(uid)))
76 | if err != nil {
77 | res = _offline
78 | glog.Error(err)
79 | }
80 | return
81 | }
82 |
83 | func (dao *Dao) SetOnline(ctx context.Context, uid int64) (err error) {
84 | conn := dao.redis.Get(ctx)
85 | defer conn.Close()
86 | _, err = conn.Do("SETEX", keyOnline(uid), int(time.Duration(conf.Conf.Redis.Expire)/time.Second), _online)
87 | if err != nil {
88 | glog.Error(err)
89 | }
90 | return
91 | }
92 |
--------------------------------------------------------------------------------
/server/register/model/user.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type User struct {
4 | Uid int64 `json:"uid"`
5 | UserName string `json:"user_name"`
6 | Password string `json:"password"`
7 | }
8 |
--------------------------------------------------------------------------------
/server/register/register.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/server/register/conf"
7 | "github.com/oikomi/FishChatServer2/server/register/rpc"
8 | )
9 |
10 | func init() {
11 | flag.Set("alsologtostderr", "true")
12 | flag.Set("log_dir", "false")
13 | }
14 |
15 | func main() {
16 | flag.Parse()
17 | if err := conf.Init(); err != nil {
18 | glog.Error("conf.Init() error: ", err)
19 | panic(err)
20 | }
21 | rpcClient, err := rpc.NewRPCClient()
22 | if err != nil {
23 | glog.Error(err)
24 | panic(err)
25 | }
26 | rpc.RPCServerInit(rpcClient)
27 | }
28 |
--------------------------------------------------------------------------------
/server/register/register.toml:
--------------------------------------------------------------------------------
1 | # register conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/register.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:23000"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "register"
12 | rpcAddr = "127.0.0.1:23000"
13 | etcdAddr = "localhost:2379"
14 | interval = "5s"
15 | ttl = "10s"
16 |
17 | [rpcClient]
18 | [rpcClient.idgenClient]
19 | serviceName = "idgen"
20 | etcdAddr = "localhost:2379"
21 | balancer = "rr"
22 |
23 | [auth]
24 | encryption = "md5"
25 | salt = "!0h#?123(ABM"
26 |
27 | [redis]
28 | name = "register"
29 | proto = "tcp"
30 | addr = "127.0.0.1:6379"
31 | idle = 100
32 | active = 100
33 | dialTimeout = "1s"
34 | readTimeout = "1s"
35 | writeTimeout = "1s"
36 | idleTimeout = "10s"
37 | expire = "15s"
38 |
39 | # [mongoDB]
40 | # addrs = "127.0.0.1:27017"
41 | # db = "im"
42 | # dialTimeout = "1s"
43 | # groupCollection = "group"
44 |
45 | [mysql]
46 | [mysql.im]
47 | name= "[im]tcp@127.0.0.1:3306"
48 | dsn = "root:1@tcp(127.0.0.1:3306)/im?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
49 | active = 5
50 | idle = 2
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/server/register/register_23001.toml:
--------------------------------------------------------------------------------
1 | # register conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/register.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:23001"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "register"
12 | rpcAddr = "127.0.0.1:23001"
13 | etcdAddr = "localhost:2379"
14 | interval = "5s"
15 | ttl = "10s"
16 |
17 | [rpcClient]
18 | [rpcClient.idgenClient]
19 | serviceName = "idgen"
20 | etcdAddr = "localhost:2379"
21 | balancer = "rr"
22 |
23 | [auth]
24 | encryption = "md5"
25 | salt = "!0h#?123(ABM"
26 |
27 | [redis]
28 | name = "register"
29 | proto = "tcp"
30 | addr = "127.0.0.1:6379"
31 | idle = 100
32 | active = 100
33 | dialTimeout = "1s"
34 | readTimeout = "1s"
35 | writeTimeout = "1s"
36 | idleTimeout = "10s"
37 | expire = "15s"
38 |
39 | [mysql]
40 | [mysql.im]
41 | name= "[im]tcp@127.0.0.1:3306"
42 | dsn = "root:1@tcp(127.0.0.1:3306)/im?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
43 | active = 5
44 | idle = 2
45 |
46 | [mongoDB]
47 | addrs = "127.0.0.1:27017"
48 | db = "im"
49 | dialTimeout = "1s"
50 | groupCollection = "group"
51 |
52 |
53 |
--------------------------------------------------------------------------------
/server/register/register_23002.toml:
--------------------------------------------------------------------------------
1 | # register conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/register.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:23002"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "register"
12 | rpcAddr = "127.0.0.1:23002"
13 | etcdAddr = "localhost:2379"
14 | interval = "5s"
15 | ttl = "10s"
16 |
17 | [rpcClient]
18 | [rpcClient.idgenClient]
19 | serviceName = "idgen"
20 | etcdAddr = "localhost:2379"
21 | balancer = "rr"
22 |
23 | [auth]
24 | encryption = "md5"
25 | salt = "!0h#?123(ABM"
26 |
27 | [redis]
28 | name = "register"
29 | proto = "tcp"
30 | addr = "127.0.0.1:6379"
31 | idle = 100
32 | active = 100
33 | dialTimeout = "1s"
34 | readTimeout = "1s"
35 | writeTimeout = "1s"
36 | idleTimeout = "10s"
37 | expire = "15s"
38 |
39 | [mysql]
40 | [mysql.im]
41 | name= "[im]tcp@127.0.0.1:3306"
42 | dsn = "root:1@tcp(127.0.0.1:3306)/im?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
43 | active = 5
44 | idle = 2
45 |
46 | [mongoDB]
47 | addrs = "127.0.0.1:27017"
48 | db = "im"
49 | dialTimeout = "1s"
50 | groupCollection = "group"
51 |
52 |
53 |
--------------------------------------------------------------------------------
/server/register/rpc/client/idegen_test.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | // import (
4 | // "github.com/oikomi/FishChatServer2/protocol/rpc"
5 | // "testing"
6 | // )
7 |
8 | // func TestGetUUID(t *testing.T) {
9 | // idgenRPC, err := NewIdgenRPCCli()
10 | // if err != nil {
11 | // t.Error(err)
12 | // }
13 | // r, err := idgenRPC.GetUUID(&rpc.Snowflake_NullRequest{})
14 | // if err != nil {
15 | // t.Fatalf("could not get next value: %v", err)
16 | // }
17 | // t.Logf("%b", r.Uuid)
18 | // }
19 |
--------------------------------------------------------------------------------
/server/register/rpc/client/idgen.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/protocol/rpc"
6 | "github.com/oikomi/FishChatServer2/server/register/conf"
7 | sd "github.com/oikomi/FishChatServer2/service_discovery/etcd"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | )
11 |
12 | type IdgenRPCCli struct {
13 | conn *grpc.ClientConn
14 | }
15 |
16 | func NewIdgenRPCCli() (idgenRPCCli *IdgenRPCCli, err error) {
17 | r := sd.NewResolver(conf.Conf.RPCClient.IdgenClient.ServiceName)
18 | b := grpc.RoundRobin(r)
19 | conn, err := grpc.Dial(conf.Conf.RPCClient.IdgenClient.EtcdAddr, grpc.WithInsecure(), grpc.WithBalancer(b))
20 | if err != nil {
21 | glog.Error(err)
22 | panic(err)
23 | }
24 | idgenRPCCli = &IdgenRPCCli{
25 | conn: conn,
26 | }
27 | return
28 | }
29 |
30 | func (idgenRPCCli *IdgenRPCCli) GetUUID(getUUIDReq *rpc.Snowflake_NullRequest) (res *rpc.Snowflake_UUID, err error) {
31 | i := rpc.NewIDGenServerRPCClient(idgenRPCCli.conn)
32 | if res, err = i.GetUUID(context.Background(), getUUIDReq); err != nil {
33 | glog.Error(err)
34 | }
35 | return
36 | }
37 |
--------------------------------------------------------------------------------
/server/register/rpc/rpc_client.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/server/register/rpc/client"
6 | )
7 |
8 | type RPCClient struct {
9 | Idgen *client.IdgenRPCCli
10 | }
11 |
12 | func NewRPCClient() (c *RPCClient, err error) {
13 | idgen, err := client.NewIdgenRPCCli()
14 | if err != nil {
15 | glog.Error(err)
16 | return
17 | }
18 | c = &RPCClient{
19 | Idgen: idgen,
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/service/idgen/README.md:
--------------------------------------------------------------------------------
1 | 分布式uuid/自增ID生成器
--------------------------------------------------------------------------------
/service/idgen/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | )
8 |
9 | var (
10 | confPath string
11 | Conf *Config
12 | Etcd *commconf.Etcd
13 | )
14 |
15 | type Config struct {
16 | *commconf.CommConf
17 | configFile string
18 | RPCServer *commconf.RPCServer
19 | ServiceDiscoveryServer *commconf.ServiceDiscoveryServer
20 | Etcd *commconf.Etcd
21 | }
22 |
23 | func init() {
24 | flag.StringVar(&confPath, "conf", "./idgen.toml", "config path")
25 | }
26 |
27 | func Init() (err error) {
28 | _, err = toml.DecodeFile(confPath, &Conf)
29 | return
30 | }
31 |
--------------------------------------------------------------------------------
/service/idgen/dao/dao.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/service/idgen/conf"
6 | )
7 |
8 | type Dao struct {
9 | Etcd *Etcd
10 | }
11 |
12 | func NewDao() (dao *Dao, err error) {
13 | e, err := NewEtcd(conf.Conf.Etcd)
14 | if err != nil {
15 | glog.Error(err)
16 | return
17 | }
18 | dao = &Dao{
19 | Etcd: e,
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/service/idgen/dao/etcd.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/coreos/etcd/clientv3"
5 | "github.com/golang/glog"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | "time"
8 | )
9 |
10 | type Etcd struct {
11 | EtcCli *clientv3.Client
12 | rootPath string
13 | }
14 |
15 | func NewEtcd(c *commconf.Etcd) (etcd *Etcd, err error) {
16 | var etcdClient *clientv3.Client
17 | cfg := clientv3.Config{
18 | Endpoints: c.Addrs,
19 | DialTimeout: time.Duration(c.Timeout),
20 | }
21 | if etcdClient, err = clientv3.New(cfg); err != nil {
22 | glog.Error("Error: cannot connec to etcd:", err)
23 | return
24 | }
25 | etcd = &Etcd{
26 | EtcCli: etcdClient,
27 | rootPath: c.Root,
28 | }
29 | return
30 | }
31 |
--------------------------------------------------------------------------------
/service/idgen/idgen.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/service/idgen/conf"
7 | "github.com/oikomi/FishChatServer2/service/idgen/rpc"
8 | )
9 |
10 | func init() {
11 | flag.Set("alsologtostderr", "true")
12 | flag.Set("log_dir", "false")
13 | }
14 |
15 | func main() {
16 | flag.Parse()
17 | if err := conf.Init(); err != nil {
18 | glog.Error("conf.Init() error: ", err)
19 | panic(err)
20 | }
21 | rpc.RPCServerInit()
22 | }
23 |
--------------------------------------------------------------------------------
/service/idgen/idgen.toml:
--------------------------------------------------------------------------------
1 | # idgen conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/idgen.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:31000"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "idgen"
12 | rpcAddr = "127.0.0.1:31000"
13 | etcdAddr = "localhost:2379"
14 | interval = "2s"
15 | ttl = "10s"
16 |
17 | [etcd]
18 | root = "/service/idgen/"
19 | addrs = ["127.0.0.1:2379"]
20 | timeout = "1s"
21 |
22 |
--------------------------------------------------------------------------------
/service/idgen/idgen_31001.toml:
--------------------------------------------------------------------------------
1 | # idgen conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/idgen.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:31001"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "idgen"
12 | rpcAddr = "127.0.0.1:31001"
13 | etcdAddr = "localhost:2379"
14 | interval = "2s"
15 | ttl = "10s"
16 |
17 | [etcd]
18 | root = "/service/idgen/"
19 | addrs = ["127.0.0.1:2379"]
20 | timeout = "1s"
21 |
22 |
--------------------------------------------------------------------------------
/service/idgen/idgen_31002.toml:
--------------------------------------------------------------------------------
1 | # idgen conf
2 |
3 | ver = "1.0.0"
4 | logPath = "/tmp/idgen.log"
5 |
6 | [rpcServer]
7 | proto = "tcp"
8 | addr = "0.0.0.0:31002"
9 |
10 | [serviceDiscoveryServer]
11 | serviceName = "idgen"
12 | rpcAddr = "127.0.0.1:31002"
13 | etcdAddr = "localhost:2379"
14 | interval = "2s"
15 | ttl = "10s"
16 |
17 | [etcd]
18 | root = "/service/idgen/"
19 | addrs = ["127.0.0.1:2379"]
20 | timeout = "1s"
21 |
22 |
--------------------------------------------------------------------------------
/service/idgen/rpc/rpc_server_test.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | // import (
4 | // "fmt"
5 | // "github.com/oikomi/FishChatServer2/protocol/rpc"
6 | // "golang.org/x/net/context"
7 | // "google.golang.org/grpc"
8 | // "testing"
9 | // )
10 |
11 | // const (
12 | // address = "localhost:31000"
13 | // test_key = "test-uuid"
14 | // )
15 |
16 | // func TestSnowflake(t *testing.T) {
17 | // // Set up a connection to the server.
18 | // conn, err := grpc.Dial(address, grpc.WithInsecure())
19 | // if err != nil {
20 | // t.Fatalf("did not connect: %v", err)
21 | // }
22 | // defer conn.Close()
23 | // c := rpc.NewIDGenServerRPCClient(conn)
24 | // // Contact the server and print out its response.
25 | // r, err := c.Next(context.Background(), &rpc.Snowflake_Key{Name: test_key})
26 | // if err != nil {
27 | // t.Fatalf("could not get next value: %v", err)
28 | // }
29 | // fmt.Println(r.Value)
30 | // t.Log(r.Value)
31 | // }
32 |
33 | // func BenchmarkSnowflake(b *testing.B) {
34 | // // Set up a connection to the server.
35 | // conn, err := grpc.Dial(address, grpc.WithInsecure())
36 | // if err != nil {
37 | // b.Fatalf("did not connect: %v", err)
38 | // }
39 | // defer conn.Close()
40 | // c := rpc.NewIDGenServerRPCClient(conn)
41 | // for i := 0; i < b.N; i++ {
42 | // // Contact the server and print out its response.
43 | // r, err := c.Next(context.Background(), &rpc.Snowflake_Key{Name: test_key})
44 | // if err != nil {
45 | // b.Fatalf("could not get next value: %v", err)
46 | // }
47 | // fmt.Println(r.Value)
48 | // }
49 | // }
50 |
51 | // func TestSnowflakeUUID(t *testing.T) {
52 | // // Set up a connection to the server.
53 | // conn, err := grpc.Dial(address, grpc.WithInsecure())
54 | // if err != nil {
55 | // t.Fatalf("did not connect: %v", err)
56 | // }
57 | // defer conn.Close()
58 | // c := rpc.NewIDGenServerRPCClient(conn)
59 |
60 | // // Contact the server and print out its response.
61 | // r, err := c.GetUUID(context.Background(), &rpc.Snowflake_NullRequest{})
62 | // if err != nil {
63 | // t.Fatalf("could not get next value: %v", err)
64 | // }
65 | // fmt.Println(r.Uuid)
66 | // t.Logf("%b", r.Uuid)
67 | // }
68 |
69 | // func BenchmarkSnowflakeUUID(b *testing.B) {
70 | // // Set up a connection to the server.
71 | // conn, err := grpc.Dial(address, grpc.WithInsecure())
72 | // if err != nil {
73 | // b.Fatalf("did not connect: %v", err)
74 | // }
75 | // defer conn.Close()
76 | // c := rpc.NewIDGenServerRPCClient(conn)
77 | // for i := 0; i < b.N; i++ {
78 | // // Contact the server and print out its response.
79 | // r, err := c.GetUUID(context.Background(), &rpc.Snowflake_NullRequest{})
80 | // if err != nil {
81 | // b.Fatalf("could not get uuid: %v", err)
82 | // }
83 | // fmt.Println(r.Uuid)
84 | // }
85 | // }
86 |
--------------------------------------------------------------------------------
/service_discovery/etcd/resolver.go:
--------------------------------------------------------------------------------
1 | package etcd
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | etcd "github.com/coreos/etcd/clientv3"
7 | // "github.com/golang/glog"
8 | "github.com/golang/glog"
9 | "google.golang.org/grpc/naming"
10 | "strings"
11 | "time"
12 | )
13 |
14 | // EtcdResolver is the implementaion of grpc.naming.Resolver
15 | type EtcdResolver struct {
16 | ServiceName string // service name to resolve
17 | }
18 |
19 | // NewResolver return EtcdResolver with service name
20 | func NewResolver(serviceName string) *EtcdResolver {
21 | return &EtcdResolver{ServiceName: serviceName}
22 | }
23 |
24 | // Resolve to resolve the service from etcd, target is the dial address of etcd
25 | // target example: "http://127.0.0.1:2379;http://127.0.0.1:12379;http://127.0.0.1:22379"
26 | func (er *EtcdResolver) Resolve(target string) (naming.Watcher, error) {
27 | glog.Info("Resolve")
28 | if er.ServiceName == "" {
29 | return nil, errors.New("service_discovery: no service name provided")
30 | }
31 | // generate etcd client, return if error
32 | endpoints := strings.Split(target, ",")
33 | conf := etcd.Config{
34 | Endpoints: endpoints,
35 | DialTimeout: time.Second,
36 | }
37 | client, err := etcd.New(conf)
38 | if err != nil {
39 | return nil, fmt.Errorf("service_discovery: creat etcd error: %s", err.Error())
40 | }
41 | // Return EtcdWatcher
42 | watcher := &EtcdWatcher{
43 | er: er,
44 | ec: client,
45 | }
46 | return watcher, nil
47 | }
48 |
--------------------------------------------------------------------------------
/service_discovery/etcd/watcher.go:
--------------------------------------------------------------------------------
1 | package etcd
2 |
3 | import (
4 | "fmt"
5 | etcd "github.com/coreos/etcd/clientv3"
6 | "github.com/golang/glog"
7 | . "github.com/oikomi/FishChatServer2/service_discovery/lib"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc/naming"
10 | )
11 |
12 | // prefix is the root Dir of services in etcd
13 | var Prefix = "im"
14 |
15 | // EtcdWatcher is the implementaion of grpc.naming.Watcher
16 | type EtcdWatcher struct {
17 | // er: EtcdResolver
18 | er *EtcdResolver
19 | // ec: Etcd Client
20 | ec *etcd.Client
21 | // addrs is the service addrs cache
22 | addrs []string
23 | isInitialized bool
24 | }
25 |
26 | // Close do nothing
27 | func (ew *EtcdWatcher) Close() {
28 | }
29 |
30 | // Next to return the updates
31 | func (ew *EtcdWatcher) Next() ([]*naming.Update, error) {
32 | // key is the etcd key/value dir to watch
33 | key := fmt.Sprintf("/%s/%s", Prefix, ew.er.ServiceName)
34 | // ew.addrs is nil means it is intially called
35 | if ew.addrs == nil {
36 | // query addresses from etcd
37 | resp, _ := ew.ec.Get(context.Background(), key, etcd.WithPrefix())
38 | addrs, empty := extractAddrs(resp)
39 | dropEmptyDir(ew.ec, empty)
40 | // addrs is not empty, return the updates
41 | // addrs is empty, should to watch new data
42 | if len(addrs) != 0 {
43 | ew.addrs = addrs
44 | return GenUpdates([]string{}, addrs), nil
45 | }
46 | }
47 | for {
48 | // generate etcd Watcher
49 | rch := ew.ec.Watch(context.Background(), key, etcd.WithPrefix())
50 | for wresp := range rch {
51 | for _, ev := range wresp.Events {
52 | // glog.Info(ev.Type, string(ev.Kv.Key), string(ev.Kv.Value))
53 | if ev.Type.String() == "EXPIRE" {
54 | return []*naming.Update{{Op: naming.Delete, Addr: string(ev.Kv.Value)}}, nil
55 | } else if ev.Type.String() == "PUT" {
56 | return []*naming.Update{{Op: naming.Add, Addr: string(ev.Kv.Value)}}, nil
57 | } else if ev.Type.String() == "DELETE" {
58 | return []*naming.Update{{Op: naming.Delete, Addr: string(ev.Kv.Value)}}, nil
59 | }
60 | }
61 | }
62 | }
63 | }
64 |
65 | // helper function to extract addrs rom etcd response
66 | func extractAddrs(resp *etcd.GetResponse) (addrs, empty []string) {
67 | addrs = []string{}
68 | empty = []string{}
69 | if resp == nil || resp.Kvs == nil || len(resp.Kvs) == 0 {
70 | return addrs, empty
71 | }
72 | for _, v := range resp.Kvs {
73 | addr := ""
74 | what := v.Key[len(v.Key)-4 : len(v.Key)]
75 | if string(what) == "addr" {
76 | addr = string(v.Value)
77 | }
78 | if addr != "" {
79 | addrs = append(addrs, addr)
80 | }
81 | }
82 | glog.Info(addrs)
83 | return addrs, empty
84 | }
85 |
86 | func dropEmptyDir(keyapi *etcd.Client, empty []string) {
87 | if keyapi == nil || len(empty) == 0 {
88 | return
89 | }
90 | for _, key := range empty {
91 | _, err := keyapi.Delete(context.Background(), key)
92 | if err != nil {
93 | glog.Error("service_discovery: delete empty service dir error: ", err.Error())
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/service_discovery/lib/lib.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "google.golang.org/grpc/naming"
5 | )
6 |
7 | func GenUpdates(a, b []string) []*naming.Update {
8 | updates := []*naming.Update{}
9 |
10 | deleted := diff(a, b)
11 | for _, addr := range deleted {
12 | update := &naming.Update{Op: naming.Delete, Addr: addr}
13 | updates = append(updates, update)
14 | }
15 |
16 | added := diff(b, a)
17 | for _, addr := range added {
18 | update := &naming.Update{Op: naming.Add, Addr: addr}
19 | updates = append(updates, update)
20 | }
21 | return updates
22 | }
23 |
24 | // diff(a, b) = a - a(n)b
25 | func diff(a, b []string) []string {
26 | d := make([]string, 0)
27 | for _, va := range a {
28 | found := false
29 | for _, vb := range b {
30 | if va == vb {
31 | found = true
32 | break
33 | }
34 | }
35 |
36 | if !found {
37 | d = append(d, va)
38 | }
39 | }
40 | return d
41 | }
42 |
--------------------------------------------------------------------------------
/service_discovery/lib/lib_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestDiff(t *testing.T) {
8 | a := []string{"1", "2", "3", "4"}
9 | b := []string{"3", "4", "5", "6"}
10 |
11 | c := diff(a, b)
12 | d := diff(b, a)
13 |
14 | if len(c) != 2 || c[0] != "1" || c[1] != "2" {
15 | t.Errorf("diff(a-b) error, get=%v", c)
16 | }
17 |
18 | if len(d) != 2 || d[0] != "5" || d[1] != "6" {
19 | t.Errorf("diff(b-a) error, get=%v", d)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/trash/es_job/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | )
8 |
9 | var (
10 | confPath string
11 | Conf *Config
12 | )
13 |
14 | type Config struct {
15 | *commconf.CommConf
16 | KafkaConsumer *commconf.KafkaConsumer
17 | }
18 |
19 | func init() {
20 | flag.StringVar(&confPath, "conf", "./es_job.toml", "config path")
21 | }
22 |
23 | func Init() (err error) {
24 | _, err = toml.DecodeFile(confPath, &Conf)
25 | return
26 | }
27 |
--------------------------------------------------------------------------------
/trash/es_job/es_job.toml:
--------------------------------------------------------------------------------
1 |
2 | ver = "1.0.0"
3 | logPath = "/tmp/es-job.log"
4 |
5 | [kafkaConsumer]
6 | group = "es-job"
7 | topics = ["msg_server_producer"]
8 | offset = true
9 | [kafkaConsumer.zookeeper]
10 | root = "/brokers"
11 | addrs = ["127.0.0.1:2181"]
12 | timeout = "1s"
13 |
--------------------------------------------------------------------------------
/trash/es_job/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/jobs/msg_job/conf"
7 | "github.com/oikomi/FishChatServer2/jobs/msg_job/service"
8 | "os"
9 | "os/signal"
10 | "syscall"
11 | )
12 |
13 | func init() {
14 | flag.Set("alsologtostderr", "true")
15 | flag.Set("log_dir", "false")
16 | }
17 |
18 | var (
19 | s *service.Service
20 | )
21 |
22 | func main() {
23 | flag.Parse()
24 | if err := conf.Init(); err != nil {
25 | glog.Error(err)
26 | panic(err)
27 | }
28 | s = service.New(conf.Conf)
29 | // signalHandler()
30 | for {
31 |
32 | }
33 | }
34 |
35 | func signalHandler() {
36 | var (
37 | err error
38 | ch = make(chan os.Signal, 1)
39 | )
40 | signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGSTOP)
41 | for {
42 | si := <-ch
43 | switch si {
44 | case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGINT:
45 | glog.Info("get a signal %s, stop the consume process", si.String())
46 | if err = s.Close(); err != nil {
47 | glog.Error("close consumer error :", err)
48 | }
49 | s.Wait()
50 | return
51 | case syscall.SIGHUP:
52 | default:
53 | return
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/trash/es_job/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/common/dao/kafka"
7 | "github.com/oikomi/FishChatServer2/jobs/msg_job/conf"
8 | "sync"
9 | "time"
10 | )
11 |
12 | var (
13 | _module = "msg_job"
14 | _add = "add"
15 | )
16 |
17 | // action the message struct of kafka
18 | type action struct {
19 | Action string `json:"action"`
20 | }
21 |
22 | type Service struct {
23 | c *conf.Config
24 | waiter *sync.WaitGroup
25 | consumer *kafka.Consumer
26 | }
27 |
28 | func New(c *conf.Config) (s *Service) {
29 | s = &Service{
30 | c: c,
31 | waiter: new(sync.WaitGroup),
32 | consumer: kafka.NewConsumer(c.KafkaConsumer),
33 | }
34 | for s.consumer.ConsumerGroup == nil {
35 | time.Sleep(time.Second)
36 | }
37 | for i := 0; i < 8; i++ {
38 | glog.Info("start proc")
39 | go s.consumeproc()
40 | }
41 | go s.errproc()
42 | return
43 | }
44 |
45 | func (s *Service) consumeproc() {
46 | s.waiter.Add(1)
47 | defer s.waiter.Done()
48 | for {
49 | msg, ok := <-s.consumer.ConsumerGroup.Messages()
50 | if !ok {
51 | glog.Info("consumeproc exit")
52 | return
53 | }
54 | if msg.Topic != s.c.KafkaConsumer.Topics[0] {
55 | continue
56 | }
57 | act := &action{}
58 | if err := json.Unmarshal(msg.Value, act); err != nil {
59 | glog.Error("json.Unmarshal() error ", err)
60 | continue
61 | }
62 | if act.Action != _add {
63 | continue
64 | }
65 | // aid, err := strconv.ParseInt(string(msg.Key), 10, 64)
66 | // if err == nil {
67 | // ctx := context.Background()
68 | // now := time.Now()
69 | // if err := s.arcRPC.AddMoment(trace.NewContext(ctx, t), &model.ArgAid{Aid: aid}); err != nil {
70 | // if s.elk != nil {
71 | // tmsub := time.Now().Sub(now)
72 | // s.elk.Error(t.ID, "moment-job", err.Error(), tmsub.Nanoseconds())
73 | // }
74 | // log.Error("moment add(%d) error(%v)", aid, err)
75 | // }
76 | // }
77 | s.consumer.ConsumerGroup.CommitUpto(msg)
78 | }
79 | }
80 |
81 | func (s *Service) errproc() {
82 | errs := s.consumer.ConsumerGroup.Errors()
83 | for {
84 | err, ok := <-errs
85 | if !ok {
86 | glog.Info("errproc exit")
87 | return
88 | }
89 | glog.Error(err)
90 | // glog.Error("topic(%s) partition(%d) error(%v)", err.Topic, err.Partition, err.Err)
91 | }
92 | }
93 |
94 | func (s *Service) Close() error {
95 | return s.consumer.Close()
96 | }
97 |
98 | func (s *Service) Wait() {
99 | s.waiter.Wait()
100 | }
101 |
--------------------------------------------------------------------------------
/trash/msg_job/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "flag"
5 | "github.com/BurntSushi/toml"
6 | commconf "github.com/oikomi/FishChatServer2/common/conf"
7 | )
8 |
9 | var (
10 | confPath string
11 | Conf *Config
12 | )
13 |
14 | type Config struct {
15 | *commconf.CommConf
16 | RPCClient *RPCClient
17 | KafkaConsumer *commconf.KafkaConsumer
18 | MongoDB *MongoDB
19 | Etcd *commconf.Etcd
20 | }
21 |
22 | type RPCClient struct {
23 | AccessClient *commconf.ServiceDiscoveryClient
24 | }
25 |
26 | type MongoDB struct {
27 | *commconf.MongoDB
28 | OfflineMsgCollection string
29 | }
30 |
31 | func init() {
32 | flag.StringVar(&confPath, "conf", "./msg_job.toml", "config path")
33 | }
34 |
35 | func Init() (err error) {
36 | _, err = toml.DecodeFile(confPath, &Conf)
37 | return
38 | }
39 |
--------------------------------------------------------------------------------
/trash/msg_job/conf_discovery/conf_discovery.go:
--------------------------------------------------------------------------------
1 | package conf_discovery
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/conf_discovery/etcd"
6 | "github.com/oikomi/FishChatServer2/jobs/msg_job/conf"
7 | "time"
8 | )
9 |
10 | var (
11 | AccessServerList map[string]*etcd.Member
12 | )
13 |
14 | func loadAccessServerProc(master *etcd.Master) {
15 | for {
16 | // glog.Info("loadAccessServerProc")
17 | AccessServerList = master.Members()
18 | time.Sleep(time.Second * 5)
19 | }
20 | }
21 |
22 | func ConfDiscoveryProc() {
23 | glog.Info("ConfDiscoveryProc")
24 | master, err := etcd.NewMaster(conf.Conf.Etcd)
25 | if err != nil {
26 | glog.Error("Error: cannot connect to etcd:", err)
27 | panic(err)
28 | }
29 | go loadAccessServerProc(master)
30 | master.WatchWorkers()
31 | }
32 |
--------------------------------------------------------------------------------
/trash/msg_job/dao/dao.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/golang/glog"
5 | )
6 |
7 | type Dao struct {
8 | MongoDB *MongoDB
9 | Kafka *Kafka
10 | }
11 |
12 | func NewDao() (dao *Dao, err error) {
13 | m, err := NewMongoDB()
14 | if err != nil {
15 | glog.Error(err)
16 | return
17 | }
18 | dao = &Dao{
19 | MongoDB: m,
20 | Kafka: NewKafka(),
21 | }
22 | return
23 | }
24 |
--------------------------------------------------------------------------------
/trash/msg_job/dao/kafka.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/oikomi/FishChatServer2/common/dao/kafka"
5 | "github.com/oikomi/FishChatServer2/jobs/msg_job/conf"
6 | )
7 |
8 | type Kafka struct {
9 | Consumer *kafka.Consumer
10 | }
11 |
12 | func NewKafka() (k *Kafka) {
13 | consumer := kafka.NewConsumer(conf.Conf.KafkaConsumer)
14 | k = &Kafka{
15 | Consumer: consumer,
16 | }
17 | return
18 | }
19 |
--------------------------------------------------------------------------------
/trash/msg_job/dao/mongodb.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/common/dao/mongodb"
6 | commmodel "github.com/oikomi/FishChatServer2/common/model"
7 | "github.com/oikomi/FishChatServer2/jobs/msg_job/conf"
8 | )
9 |
10 | type MongoDB struct {
11 | m *mongodb.MongoDB
12 | }
13 |
14 | func NewMongoDB() (mdb *MongoDB, err error) {
15 | m, err := mongodb.NewMongoDB(conf.Conf.MongoDB.MongoDB)
16 | if err != nil {
17 | glog.Error(err)
18 | }
19 | mdb = &MongoDB{
20 | m: m,
21 | }
22 | return
23 | }
24 |
25 | func (m *MongoDB) StoreOfflineMsg(msg *commmodel.OfflineMsg) (err error) {
26 | c := m.m.Session.DB(conf.Conf.MongoDB.DB).C(conf.Conf.MongoDB.OfflineMsgCollection)
27 | if err = c.Insert(msg); err != nil {
28 | glog.Error(err)
29 | }
30 | return
31 | }
32 |
--------------------------------------------------------------------------------
/trash/msg_job/msg_job.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/jobs/msg_job/conf"
7 | "github.com/oikomi/FishChatServer2/jobs/msg_job/conf_discovery"
8 | "github.com/oikomi/FishChatServer2/jobs/msg_job/service"
9 | "os"
10 | "os/signal"
11 | "syscall"
12 | )
13 |
14 | func init() {
15 | flag.Set("alsologtostderr", "true")
16 | flag.Set("log_dir", "false")
17 | }
18 |
19 | var (
20 | s *service.Service
21 | )
22 |
23 | func main() {
24 | flag.Parse()
25 | if err := conf.Init(); err != nil {
26 | glog.Error(err)
27 | panic(err)
28 | }
29 | go conf_discovery.ConfDiscoveryProc()
30 | s = service.New(conf.Conf)
31 | signalHandler()
32 | }
33 |
34 | func signalHandler() {
35 | var (
36 | err error
37 | ch = make(chan os.Signal, 1)
38 | )
39 | signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGSTOP)
40 | for {
41 | si := <-ch
42 | switch si {
43 | case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGINT:
44 | glog.Infof("get a signal %s, stop the consume process", si.String())
45 | if err = s.Close(); err != nil {
46 | glog.Error("close consumer error :", err)
47 | }
48 | s.Wait()
49 | return
50 | case syscall.SIGHUP:
51 | default:
52 | return
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/trash/msg_job/msg_job.toml:
--------------------------------------------------------------------------------
1 |
2 | ver = "1.0.0"
3 | logPath = "/tmp/msg-job.log"
4 |
5 | [rpcClient]
6 | [rpcClient.accessClient]
7 | serviceName = "access"
8 | etcdAddr = "localhost:2379"
9 | balancer = "rr"
10 |
11 | [kafkaConsumer]
12 | group = "msg-job"
13 | topics = ["logic_producer_p2p"]
14 | offset = false
15 | [kafkaConsumer.zookeeper]
16 | root = "/brokers"
17 | addrs = ["127.0.0.1:2181"]
18 | timeout = "5s"
19 |
20 | [mongoDB]
21 | addrs = "127.0.0.1:27017"
22 | db = "im"
23 | dialTimeout = "1s"
24 | offlineMsgCollection = "offline_msg"
25 |
26 | [etcd]
27 | root = "/server/access_server_msgjob/"
28 | addrs = ["127.0.0.1:2379"]
29 | timeout = "1s"
--------------------------------------------------------------------------------
/trash/msg_job/rpc/client/access_server.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "errors"
5 | "github.com/golang/glog"
6 | "github.com/oikomi/FishChatServer2/jobs/msg_job/conf_discovery"
7 | "github.com/oikomi/FishChatServer2/protocol/rpc"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | "time"
11 | )
12 |
13 | type AccessServerRPCCli struct {
14 | conns map[string]*grpc.ClientConn
15 | }
16 |
17 | func NewAccessServerRPCCli() (accessServerRPCCli *AccessServerRPCCli, err error) {
18 | glog.Info("NewAccessServerRPCCli")
19 | var accessServerList []string
20 | conns := make(map[string]*grpc.ClientConn)
21 | for {
22 | if len(conf_discovery.AccessServerList) <= 0 {
23 | glog.Info("len(conf_discovery.AccessServerList) <= 0")
24 | time.Sleep(time.Second * 5)
25 | } else {
26 | glog.Info(conf_discovery.AccessServerList)
27 | for _, v := range conf_discovery.AccessServerList {
28 | accessServerList = append(accessServerList, v.IP)
29 | }
30 | for _, accessServer := range accessServerList {
31 | conn, err := grpc.Dial(accessServer, grpc.WithInsecure())
32 | if err != nil {
33 | glog.Error(err)
34 | }
35 | conns[accessServer] = conn
36 | }
37 | accessServerRPCCli = &AccessServerRPCCli{
38 | conns: conns,
39 | }
40 | break
41 | }
42 | }
43 | go accessServerRPCCli.connProc()
44 | return
45 | }
46 |
47 | func (accessServerRPCCli *AccessServerRPCCli) connProc() {
48 | var accessServerList []string
49 | conns := make(map[string]*grpc.ClientConn)
50 | for {
51 | for _, v := range conf_discovery.AccessServerList {
52 | accessServerList = append(accessServerList, v.IP)
53 | }
54 | for _, accessServer := range accessServerList {
55 | conn, err := grpc.Dial(accessServer, grpc.WithInsecure())
56 | if err != nil {
57 | glog.Error(err)
58 | }
59 | conns[accessServer] = conn
60 | }
61 | accessServerRPCCli.conns = conns
62 | time.Sleep(time.Second * 10)
63 | }
64 | }
65 |
66 | // FIXME can not use rr
67 | func (accessServerRPCCli *AccessServerRPCCli) SendP2PMsgFromJob(sendP2PMsgReq *rpc.ASSendP2PMsgFromJobReq) (res *rpc.ASSendP2PMsgFromJobRes, err error) {
68 | if conn, ok := accessServerRPCCli.conns[sendP2PMsgReq.AccessServerAddr]; ok {
69 | a := rpc.NewAccessServerRPCClient(conn)
70 | if res, err = a.SendP2PMsgFromJob(context.Background(), sendP2PMsgReq); err != nil {
71 | glog.Error(err)
72 | }
73 | } else {
74 | err = errors.New("no access server")
75 | }
76 | return
77 | }
78 |
--------------------------------------------------------------------------------
/trash/msg_job/rpc/rpc_client.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/oikomi/FishChatServer2/jobs/msg_job/rpc/client"
6 | )
7 |
8 | type RPCClient struct {
9 | AccessServer *client.AccessServerRPCCli
10 | }
11 |
12 | func NewRPCClient() (c *RPCClient, err error) {
13 | accessServer, err := client.NewAccessServerRPCCli()
14 | if err != nil {
15 | glog.Error(err)
16 | return
17 | }
18 | c = &RPCClient{
19 | AccessServer: accessServer,
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------