first commit
This commit is contained in:
27
pkg/websocket/service/component.go
Normal file
27
pkg/websocket/service/component.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
)
|
||||
|
||||
type Component interface {
|
||||
Init()
|
||||
OnSessionClose(*peer.Session) bool
|
||||
OnRequestFinished(*peer.Session, string, any, string, time.Duration)
|
||||
}
|
||||
|
||||
type ComponentBase struct{}
|
||||
|
||||
func (c *ComponentBase) Init() {
|
||||
|
||||
}
|
||||
|
||||
func (c *ComponentBase) OnSessionClose(session *peer.Session) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *ComponentBase) OnRequestFinished(session *peer.Session, router string, req any, errMsg string, delta time.Duration) {
|
||||
|
||||
}
|
33
pkg/websocket/service/method.go
Normal file
33
pkg/websocket/service/method.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
||||
typeOfBytes = reflect.TypeOf(([]byte)(nil))
|
||||
typeOfSession = reflect.TypeOf(peer.NewSession(nil))
|
||||
)
|
||||
|
||||
// 方法检测
|
||||
func isHandlerMethod(method reflect.Method) bool {
|
||||
mt := method.Type
|
||||
if method.PkgPath != "" {
|
||||
return false
|
||||
}
|
||||
if mt.NumIn() != 3 {
|
||||
return false
|
||||
}
|
||||
if mt.NumOut() != 2 {
|
||||
return false
|
||||
}
|
||||
if t1 := mt.In(1); t1.Kind() != reflect.Ptr || t1 != typeOfSession {
|
||||
return false
|
||||
}
|
||||
if (mt.In(2).Kind() != reflect.Ptr && mt.In(2) != typeOfBytes) || mt.Out(1) != typeOfError || mt.Out(0).Kind() != reflect.Ptr {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
98
pkg/websocket/service/service.go
Normal file
98
pkg/websocket/service/service.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/util"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
Receiver reflect.Value // 值
|
||||
Method reflect.Method // 方法
|
||||
Type reflect.Type // 类型
|
||||
IsRawArg bool // 数据是否需要序列化
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
Name string // 服务名
|
||||
Type reflect.Type // 服务类型
|
||||
Receiver reflect.Value // 服务值
|
||||
Handlers map[string]*Handler // 注册的方法列表
|
||||
Component Component
|
||||
}
|
||||
|
||||
func NewService(comp Component) *Service {
|
||||
s := &Service{
|
||||
Type: reflect.TypeOf(comp),
|
||||
Receiver: reflect.ValueOf(comp),
|
||||
Component: comp,
|
||||
}
|
||||
s.Name = strings.ToLower(reflect.Indirect(s.Receiver).Type().Name())
|
||||
//调用初始化方法
|
||||
s.Component.Init()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Service) SuitableHandlerMethods(typ reflect.Type) map[string]*Handler {
|
||||
methods := make(map[string]*Handler)
|
||||
for m := 0; m < typ.NumMethod(); m++ {
|
||||
method := typ.Method(m)
|
||||
mt := method.Type
|
||||
mn := method.Name
|
||||
if isHandlerMethod(method) {
|
||||
raw := false
|
||||
if mt.In(2) == typeOfBytes {
|
||||
raw = true
|
||||
}
|
||||
mn = strings.ToLower(mn)
|
||||
methods[mn] = &Handler{Method: method, Type: mt.In(2), IsRawArg: raw}
|
||||
}
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
func (s *Service) ExtractHandler() error {
|
||||
typeName := reflect.Indirect(s.Receiver).Type().Name()
|
||||
if typeName == "" {
|
||||
return errors.New("no service name for type " + s.Type.String())
|
||||
}
|
||||
if !util.IsExported(typeName) {
|
||||
return errors.New("type " + typeName + " is not exported")
|
||||
}
|
||||
s.Handlers = s.SuitableHandlerMethods(s.Type)
|
||||
for i := range s.Handlers {
|
||||
s.Handlers[i].Receiver = s.Receiver
|
||||
}
|
||||
if reflect.Indirect(s.Receiver).NumField() > 0 {
|
||||
filedNum := reflect.Indirect(s.Receiver).NumField()
|
||||
for i := 0; i < filedNum; i++ {
|
||||
ty := reflect.Indirect(s.Receiver).Field(i).Type().Name()
|
||||
if ty == ChildName {
|
||||
h := s.SuitableHandlerMethods(reflect.Indirect(s.Receiver).Field(i).Elem().Type())
|
||||
for ih, v := range h {
|
||||
s.Handlers[ih] = v
|
||||
s.Handlers[ih].Receiver = reflect.Indirect(s.Receiver).Field(i).Elem()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(s.Handlers) == 0 {
|
||||
str := "service: "
|
||||
method := s.SuitableHandlerMethods(reflect.PtrTo(s.Type))
|
||||
if len(method) != 0 {
|
||||
str = "type " + s.Name + " has no exported methods of suitable type (hint: pass a pointer to value of that type)"
|
||||
} else {
|
||||
str = "type " + s.Name + " has no exported methods of suitable type"
|
||||
}
|
||||
return errors.New(str)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) OnSessionClose(session *peer.Session) bool {
|
||||
return s.Component.OnSessionClose(session)
|
||||
}
|
59
pkg/websocket/service/service_manager.go
Normal file
59
pkg/websocket/service/service_manager.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
_ "git.bvbej.com/bvbej/base-golang/pkg/websocket/codec/json"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
serviceLogger *zap.Logger
|
||||
|
||||
RegisteredServiceList = make(map[string]*Service) // all registered service
|
||||
RouterCodec codec.Codec
|
||||
)
|
||||
|
||||
const ChildName = "Hbase"
|
||||
|
||||
type Hbase any
|
||||
|
||||
func RegisterService(logger *zap.Logger, comp ...Component) {
|
||||
serviceLogger = logger
|
||||
for _, v := range comp {
|
||||
s := NewService(v)
|
||||
if _, ok := RegisteredServiceList[s.Name]; ok {
|
||||
serviceLogger.Sugar().Errorf("service: service already defined: %s", s.Name)
|
||||
}
|
||||
if err := s.ExtractHandler(); err != nil {
|
||||
serviceLogger.Sugar().Errorf("service: extract handler function failed: %v", err)
|
||||
}
|
||||
RegisteredServiceList[s.Name] = s
|
||||
for name, handler := range s.Handlers {
|
||||
router := fmt.Sprintf("%s.%s", s.Name, name)
|
||||
//注册消息 用于解码
|
||||
codec.RegisterMessage(router, handler.Type)
|
||||
serviceLogger.Sugar().Debugf("service: router %s param %s registed", router, handler.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SetCodec(name string) error {
|
||||
RouterCodec = codec.GetCodec(name)
|
||||
if RouterCodec == nil {
|
||||
return fmt.Errorf("service: codec %s not registered", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Send(session *peer.Session, router string, data any) error {
|
||||
if RouterCodec == nil {
|
||||
return fmt.Errorf("service: codec not set")
|
||||
}
|
||||
rb, err := RouterCodec.Marshal(router, data, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service: %v", err)
|
||||
}
|
||||
return session.Conn.Send(rb)
|
||||
}
|
102
pkg/websocket/service/session_callback.go
Normal file
102
pkg/websocket/service/session_callback.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
)
|
||||
|
||||
type callBackEntity struct{}
|
||||
|
||||
func GetSessionManager() *peer.SessionManager {
|
||||
return peer.NewSessionMgr(&callBackEntity{})
|
||||
}
|
||||
|
||||
func (cb *callBackEntity) OnClosed(session *peer.Session) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Println(fmt.Sprintf("OnClosed: session:%d err:%v", session.Conn.ID(), err))
|
||||
}
|
||||
}()
|
||||
for _, v := range RegisteredServiceList {
|
||||
if ok := v.OnSessionClose(session); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cb *callBackEntity) OnReceive(session *peer.Session, msg []byte) error {
|
||||
_, msgPack, err := RouterCodec.Unmarshal(msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("onreceive: %v", err)
|
||||
}
|
||||
router, ok := msgPack.Router.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("onreceive: invalid router:%v", msgPack.Router)
|
||||
}
|
||||
routerArr := strings.Split(router, ".")
|
||||
if len(routerArr) != 2 {
|
||||
return fmt.Errorf("onreceive: invalid router:%s", msgPack.Router)
|
||||
}
|
||||
s, ok := RegisteredServiceList[routerArr[0]]
|
||||
if !ok {
|
||||
return fmt.Errorf("onreceive: function not registed router:%s err:%v", msgPack.Router, err)
|
||||
}
|
||||
h, ok := s.Handlers[routerArr[1]]
|
||||
if !ok {
|
||||
return fmt.Errorf("onreceive: function not registed router:%s err:%v", msgPack.Router, err)
|
||||
}
|
||||
t1 := time.Now()
|
||||
|
||||
var args = []reflect.Value{h.Receiver, reflect.ValueOf(session), reflect.ValueOf(msgPack.DataPtr)}
|
||||
var res any
|
||||
var rb []byte
|
||||
res, err = CallHandlerFunc(h.Method, args)
|
||||
if res != nil && !reflect.ValueOf(res).IsNil() {
|
||||
rb, err = RouterCodec.Marshal(router, res, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service: %v", err)
|
||||
}
|
||||
err = session.Conn.Send(rb)
|
||||
if err != nil {
|
||||
serviceLogger.Sugar().Warnf("warn! service send msg failed router:%s err:%v", router, err)
|
||||
}
|
||||
} else {
|
||||
rb, err = RouterCodec.Marshal(router, nil, err)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service: %v", err)
|
||||
}
|
||||
err = session.Conn.Send(rb)
|
||||
if err != nil {
|
||||
serviceLogger.Sugar().Warnf("warn! service send msg failed router:%s err:%v", router, err)
|
||||
}
|
||||
}
|
||||
var errs string
|
||||
if err != nil {
|
||||
errs = err.Error()
|
||||
}
|
||||
dt := time.Since(t1)
|
||||
go s.Component.OnRequestFinished(session, router, RouterCodec.ToString(msgPack.DataPtr), errs, dt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallHandlerFunc(foo reflect.Method, args []reflect.Value) (retValue any, retErr error) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Println(fmt.Sprintf("CallHandlerFunc: %v", err))
|
||||
retValue = nil
|
||||
retErr = fmt.Errorf("CallHandlerFunc: call method pkg:%s method:%s err:%v", foo.PkgPath, foo.Name, err)
|
||||
}
|
||||
}()
|
||||
if ret := foo.Func.Call(args); len(ret) > 0 {
|
||||
var err error = nil
|
||||
if r1 := ret[1].Interface(); r1 != nil {
|
||||
err = r1.(error)
|
||||
}
|
||||
return ret[0].Interface(), err
|
||||
}
|
||||
return nil, fmt.Errorf("CallHandlerFunc: call method pkg:%s method:%s", foo.PkgPath, foo.Name)
|
||||
}
|
Reference in New Issue
Block a user