├── .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 | --------------------------------------------------------------------------------