fix
This commit is contained in:
111
pkg/rpc/client.go
Normal file
111
pkg/rpc/client.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"jiacrontab/pkg/proto"
|
||||
"net"
|
||||
"net/rpc"
|
||||
"time"
|
||||
|
||||
"github.com/iwannay/log"
|
||||
)
|
||||
|
||||
const (
|
||||
diaTimeout = 5 * time.Second
|
||||
callTimeout = 1 * time.Minute
|
||||
pingDuration = 3 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRpc = errors.New("rpc is not available")
|
||||
ErrRpcTimeout = errors.New("rpc call timeout")
|
||||
ErrRpcCancel = errors.New("rpc call cancel")
|
||||
ErrShutdown = rpc.ErrShutdown
|
||||
)
|
||||
|
||||
type ClientOptions struct {
|
||||
Network string
|
||||
Addr string
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
*rpc.Client
|
||||
options ClientOptions
|
||||
quit chan struct{}
|
||||
err error
|
||||
}
|
||||
|
||||
func Dial(options ClientOptions) (c *Client) {
|
||||
c = &Client{}
|
||||
c.options = options
|
||||
c.dial()
|
||||
c.quit = make(chan struct{}, 100)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) dial() (err error) {
|
||||
conn, err := net.DialTimeout(c.options.Network, c.options.Addr, diaTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Client = rpc.NewClient(conn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Call(serviceMethod string, ctx context.Context, args interface{}, reply interface{}) error {
|
||||
if serviceMethod != PingService && serviceMethod != RegisterService {
|
||||
log.Info("rpc call", c.options.Addr, serviceMethod)
|
||||
}
|
||||
|
||||
if c.Client == nil {
|
||||
return ErrRpc
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ErrRpcCancel
|
||||
case call := <-c.Client.Go(serviceMethod, args, reply, make(chan *rpc.Call, 1)).Done:
|
||||
return call.Error
|
||||
case <-time.After(callTimeout):
|
||||
return ErrRpcTimeout
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Error() error {
|
||||
return c.err
|
||||
}
|
||||
|
||||
func (c *Client) Close() {
|
||||
c.quit <- struct{}{}
|
||||
}
|
||||
|
||||
func (c *Client) Ping(serviceMethod string) {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
for {
|
||||
select {
|
||||
case <-c.quit:
|
||||
goto closed
|
||||
default:
|
||||
}
|
||||
if c.Client != nil && c.err == nil {
|
||||
if err = c.Call(serviceMethod, context.TODO(), &proto.EmptyArgs{}, &proto.EmptyReply{}); err != nil {
|
||||
c.err = err
|
||||
c.Client.Close()
|
||||
log.Infof("client.Call(%s, args, reply) error (%v) \n", serviceMethod, err)
|
||||
}
|
||||
} else {
|
||||
if err = c.dial(); err == nil {
|
||||
c.err = nil
|
||||
log.Info("client reconnet ", c.options.Addr)
|
||||
}
|
||||
}
|
||||
time.Sleep(pingDuration)
|
||||
}
|
||||
closed:
|
||||
log.Info("rpc quited", c.options.Addr)
|
||||
if c.Client != nil {
|
||||
c.Client.Close()
|
||||
}
|
||||
}
|
||||
66
pkg/rpc/client_test.go
Normal file
66
pkg/rpc/client_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"jiacrontab/pkg/proto"
|
||||
"log"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Logic struct {
|
||||
}
|
||||
|
||||
func (l *Logic) Ping(args *proto.EmptyArgs, reply *proto.EmptyReply) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Logic) Say(args string, reply *string) error {
|
||||
|
||||
*reply = "hello boy"
|
||||
time.Sleep(100 * time.Second)
|
||||
return nil
|
||||
}
|
||||
func TestCall(t *testing.T) {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
|
||||
done <- struct{}{}
|
||||
|
||||
log.Println("start server")
|
||||
err := listen(":6478", &Logic{})
|
||||
if err != nil {
|
||||
t.Fatal("server error:", err)
|
||||
}
|
||||
}()
|
||||
<-done
|
||||
time.Sleep(5 * time.Second)
|
||||
// 等待server启动
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
|
||||
defer wg.Done()
|
||||
var ret string
|
||||
// var args string
|
||||
err := Call(":6478", "Logic.Say", "", &ret)
|
||||
if err != nil {
|
||||
log.Println(i, "error:", err)
|
||||
}
|
||||
t.Log(i, ret)
|
||||
}(i)
|
||||
|
||||
}
|
||||
|
||||
go func() {
|
||||
t.Log("listen :6060")
|
||||
t.Log(http.ListenAndServe(":6060", nil))
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
log.Println("end")
|
||||
time.Sleep(2 * time.Minute)
|
||||
}
|
||||
86
pkg/rpc/clients.go
Normal file
86
pkg/rpc/clients.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/rpc"
|
||||
"sync"
|
||||
|
||||
"github.com/iwannay/log"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultClients *clients
|
||||
PingService = "Srv.Ping"
|
||||
RegisterService = "Srv.Register"
|
||||
)
|
||||
|
||||
type clients struct {
|
||||
lock sync.RWMutex
|
||||
clients map[string]*Client
|
||||
}
|
||||
|
||||
func (c *clients) get(addr string) *Client {
|
||||
var (
|
||||
cli *Client
|
||||
ok bool
|
||||
op ClientOptions
|
||||
)
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if cli, ok = c.clients[addr]; ok {
|
||||
return cli
|
||||
}
|
||||
op.Network = "tcp4"
|
||||
op.Addr = addr
|
||||
cli = Dial(op)
|
||||
c.clients[addr] = cli
|
||||
go cli.Ping(PingService)
|
||||
|
||||
return cli
|
||||
}
|
||||
|
||||
func (c *clients) del(addr string) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if cli, ok := c.clients[addr]; ok {
|
||||
cli.Close()
|
||||
}
|
||||
delete(c.clients, addr)
|
||||
}
|
||||
|
||||
func Call(addr string, serviceMethod string, args interface{}, reply interface{}) error {
|
||||
err := defaultClients.get(addr).Call(serviceMethod, context.TODO(), args, reply)
|
||||
if err == rpc.ErrShutdown {
|
||||
log.Debug("rpc remove", addr)
|
||||
Del(addr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func CallCtx(addr string, serviceMethod string, ctx context.Context, args interface{}, reply interface{}) error {
|
||||
err := defaultClients.get(addr).Call(serviceMethod, ctx, args, reply)
|
||||
if err == rpc.ErrShutdown {
|
||||
log.Debug("rpc remove", addr)
|
||||
Del(addr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func Del(addr string) {
|
||||
if defaultClients != nil {
|
||||
defaultClients.del(addr)
|
||||
}
|
||||
}
|
||||
|
||||
func DelNode(addr string) {
|
||||
if defaultClients != nil {
|
||||
defaultClients.del(addr)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
defaultClients = &clients{
|
||||
clients: make(map[string]*Client),
|
||||
}
|
||||
}
|
||||
41
pkg/rpc/server.go
Normal file
41
pkg/rpc/server.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"github.com/iwannay/log"
|
||||
"net"
|
||||
"net/rpc"
|
||||
)
|
||||
|
||||
// listen Start rpc server
|
||||
func listen(addr string, srcvr ...interface{}) error {
|
||||
var err error
|
||||
for _, v := range srcvr {
|
||||
if err = rpc.Register(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp4", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
log.Info("listen rpc", addr, "close")
|
||||
if err := l.Close(); err != nil {
|
||||
log.Infof("listen.Close() error(%v)", err)
|
||||
}
|
||||
}()
|
||||
|
||||
rpc.Accept(l)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListenAndServe run rpc server
|
||||
func ListenAndServe(addr string, srcvr ...interface{}) {
|
||||
log.Info("rpc server listen:", addr)
|
||||
err := listen(addr, srcvr...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user