Skip to content

Commit

Permalink
examples: add cluster example (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
lonng committed Jun 30, 2019
1 parent 898c1a2 commit 9138e2a
Show file tree
Hide file tree
Showing 14 changed files with 1,356 additions and 0 deletions.
18 changes: 18 additions & 0 deletions examples/cluster/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Nano cluster example

## About this example



## How to run the example?

```shell
cd examples/cluster
go build

# run master server
./cluster master
./cluster chat --listen "127.0.0.1:34580"
./cluster gate --listen "127.0.0.1:34570" --gate-address "127.0.0.1:34590"
```

19 changes: 19 additions & 0 deletions examples/cluster/chat/chat_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package chat

import (
"github.com/lonng/nano/component"
"github.com/lonng/nano/session"
"github.com/pingcap/errors"
)

type RoomService struct {
component.Base
}

func newRoomService() *RoomService {
return &RoomService{}
}

func (cs *RoomService) JoinTopic(s *session.Session, msg []byte) error {
return errors.Errorf("not implement")
}
14 changes: 14 additions & 0 deletions examples/cluster/chat/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package chat

import "github.com/lonng/nano/component"

var (
// All services in master server
Services = &component.Components{}

roomService = newRoomService()
)

func init() {
Services.Register(roomService)
}
34 changes: 34 additions & 0 deletions examples/cluster/gate/gate_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package gate

import (
"github.com/lonng/nano/component"
"github.com/lonng/nano/examples/cluster/protocol"
"github.com/lonng/nano/session"
"github.com/pingcap/errors"
)

type BindService struct {
component.Base
nextGateUid int64
}

func newBindService() *BindService {
return &BindService{}
}

type LoginRequest struct {
Nickname string `json:"nickname"`
}

func (bs *BindService) Login(s *session.Session, msg *LoginRequest) error {
bs.nextGateUid++
uid := bs.nextGateUid
return s.RPC("TopicService.NewUser", &protocol.NewUserRequest{
Nickname: msg.Nickname,
GateUid: uid,
})
}

func (bs *BindService) BindChatServer(s *session.Session, msg []byte) error {
return errors.Errorf("not implement")
}
14 changes: 14 additions & 0 deletions examples/cluster/gate/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package gate

import "github.com/lonng/nano/component"

var (
// All services in master server
Services = &component.Components{}

bindService = newBindService()
)

func init() {
Services.Register(bindService)
}
171 changes: 171 additions & 0 deletions examples/cluster/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package main

import (
"log"
"net/http"
"os"
"path/filepath"
"runtime"

"github.com/lonng/nano"
"github.com/lonng/nano/examples/cluster/chat"
"github.com/lonng/nano/examples/cluster/gate"
"github.com/lonng/nano/examples/cluster/master"
"github.com/lonng/nano/serialize/json"
"github.com/pingcap/errors"
"github.com/urfave/cli"
)

func main() {
app := cli.NewApp()
app.Name = "NanoClusterDemo"
app.Author = "Lonng"
app.Email = "[email protected]"
app.Description = "Nano cluster demo"
app.Commands = []cli.Command{
{
Name: "master",
Flags: []cli.Flag{
cli.StringFlag{
Name: "listen,l",
Usage: "Master service listen address",
Value: "127.0.0.1:34567",
},
},
Action: runMaster,
},
{
Name: "gate",
Flags: []cli.Flag{
cli.StringFlag{
Name: "master",
Usage: "master server address",
Value: "127.0.0.1:34567",
},
cli.StringFlag{
Name: "listen,l",
Usage: "Gate service listen address",
Value: "",
},
cli.StringFlag{
Name: "gate-address",
Usage: "Client connect address",
Value: "",
},
},
Action: runGate,
},
{
Name: "chat",
Flags: []cli.Flag{
cli.StringFlag{
Name: "master",
Usage: "master server address",
Value: "127.0.0.1:34567",
},
cli.StringFlag{
Name: "listen,l",
Usage: "Chat service listen address",
Value: "",
},
},
Action: runChat,
},
}
log.SetFlags(log.LstdFlags | log.Lshortfile)
if err := app.Run(os.Args); err != nil {
log.Fatalf("Startup server error %+v", err)
}
}

func srcPath() string {
_, file, _, _ := runtime.Caller(0)
return filepath.Dir(file)
}

func runMaster(args *cli.Context) error {
listen := args.String("listen")
if listen == "" {
return errors.Errorf("master listen address cannot empty")
}

webDir := filepath.Join(srcPath(), "master", "web")
log.Println("Nano master server web content directory", webDir)
log.Println("Nano master listen address", listen)
log.Println("Open http://127.0.0.1:12345/web/ in browser")

http.Handle("/web/", http.StripPrefix("/web/", http.FileServer(http.Dir(webDir))))
go func() {
if err := http.ListenAndServe(":12345", nil); err != nil {
panic(err)
}
}()

// Startup Nano server with the specified listen address
nano.Listen(listen,
nano.WithMaster(),
nano.WithComponents(master.Services),
nano.WithSerializer(json.NewSerializer()),
nano.WithDebugMode(),
)

return nil
}

func runGate(args *cli.Context) error {
listen := args.String("listen")
if listen == "" {
return errors.Errorf("gate listen address cannot empty")
}

masterAddr := args.String("master")
if listen == "" {
return errors.Errorf("master address cannot empty")
}

gateAddr := args.String("gate-address")
if gateAddr == "" {
return errors.Errorf("gate address cannot empty")
}

log.Println("Current server listen address", listen)
log.Println("Current gate server address", gateAddr)
log.Println("Remote master server address", masterAddr)

// Startup Nano server with the specified listen address
nano.ListenWS(listen,
nano.WithAdvertiseAddr(masterAddr),
nano.WithClientAddr(gateAddr),
nano.WithComponents(gate.Services),
nano.WithSerializer(json.NewSerializer()),
nano.WithCheckOriginFunc(func(_ *http.Request) bool { return true }),
nano.WithWSPath("/nano"),
nano.WithDebugMode(),
)
return nil
}

func runChat(args *cli.Context) error {
listen := args.String("listen")
if listen == "" {
return errors.Errorf("chat listen address cannot empty")
}

masterAddr := args.String("master")
if listen == "" {
return errors.Errorf("master address cannot empty")
}

log.Println("Current chat server listen address", listen)
log.Println("Remote master server address", masterAddr)

// Startup Nano server with the specified listen address
nano.Listen(listen,
nano.WithAdvertiseAddr(masterAddr),
nano.WithComponents(chat.Services),
nano.WithSerializer(json.NewSerializer()),
nano.WithDebugMode(),
)

return nil
}
1 change: 1 addition & 0 deletions examples/cluster/master/http_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package master
16 changes: 16 additions & 0 deletions examples/cluster/master/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package master

import "github.com/lonng/nano/component"

var (
// All services in master server
Services = &component.Components{}

// Topic service
topicService = newTopicService()
// ... other services
)

func init() {
Services.Register(topicService)
}
62 changes: 62 additions & 0 deletions examples/cluster/master/topic_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package master

import (
"fmt"

"github.com/lonng/nano"
"github.com/lonng/nano/component"
"github.com/lonng/nano/examples/cluster/protocol"
"github.com/lonng/nano/session"
"github.com/pingcap/errors"
)

type User struct {
session *session.Session
nickname string
gateId int64
masterId int64
}

type TopicService struct {
component.Base
nextUid int64
users map[int64]*User
group *nano.Group
}

func newTopicService() *TopicService {
return &TopicService{
users: map[int64]*User{},
group: nano.NewGroup("all-users"),
}
}

func (ts *TopicService) NewUser(s *session.Session, msg *protocol.NewUserRequest) error {
// exists users

ts.nextUid++
uid := ts.nextUid
user := &User{
session: s,
nickname: msg.Nickname,
gateId: msg.GateUid,
masterId: uid,
}
ts.users[uid] = user

broadcast := &protocol.NewUserBroadcast{
Content: fmt.Sprintf("User user join: %v", msg.Nickname),
}
if err := ts.group.Broadcast("onNewUser", broadcast); err != nil {
return err
}
return ts.group.Add(s)
}

type OpenTopicRequest struct {
Name string `json:"name"`
}

func (ts *TopicService) OpenTopic(s *session.Session, msg *OpenTopicRequest) error {
return errors.Errorf("not implemented: %v", msg)
}
Loading

0 comments on commit 9138e2a

Please sign in to comment.