├── .gitignore ├── README.md ├── cmd ├── gen.go └── root.go ├── config ├── config.go └── config.json ├── example ├── Item.thrift └── Product.thrift ├── ezrpc ├── client.go ├── middlewares │ └── request_timer.go └── server.go ├── global └── variables.go ├── langs ├── base_gen.go ├── csharp │ ├── gen.go │ └── template.go ├── generator.go ├── go │ ├── gen_go.go │ ├── namespace_test.go │ └── template.go └── utils.go ├── main.go ├── makefile └── tmpl ├── bindata.go ├── csharp └── server.gocs └── golang └── server.gogo /.gitignore: -------------------------------------------------------------------------------- 1 | gen_*.go 2 | .vscode/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ezrpc 2 | 3 | ezrpc is a `micro service` framework for server side rpc communication. 4 | 5 | It's based on [nats](http://nats.io/) and [thrift](https://github.com/samuel/go-thrift), using code-gen approach, supporting Go & .net(C#). 6 | 7 | # Service Definition 8 | 9 | ```thrift 10 | service Category { 11 | list GetIDs(1:i32 offset, 2:i32 limit), 12 | } 13 | ``` 14 | 15 | # Usage 16 | 17 | 1 Generate language specified source files by `thrift` IDL 18 | 19 | * C# 20 | 21 | thrift --gen csharp -o ./sample/ ./sample/HelloWorld.thrift 22 | 23 | * Go 24 | 25 | generator ./sample/HelloWorld.thrift` ./sample/ 26 | 27 | 2 Genrate source files which will be used for subscribing NATS messages 28 | 29 | * C# 30 | 31 | ./ezrpc gen -l csharp -i ./sample/HelloWorld.thrift -o ./sample 32 | 33 | * Go 34 | 35 | ./ezrpc gen -l go -i ./sample/HelloWorld.thrift -o ./sample 36 | -------------------------------------------------------------------------------- /cmd/gen.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/Wuvist/go-thrift/parser" 10 | "github.com/ezbuy/ezrpc/global" 11 | "github.com/ezbuy/ezrpc/langs" 12 | _ "github.com/ezbuy/ezrpc/langs/csharp" // fullfill langs 13 | _ "github.com/ezbuy/ezrpc/langs/go" 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | // genCmd represents the gen command 18 | var genCmd = &cobra.Command{ 19 | Use: "gen", 20 | Short: "generate ezrpc server/client code", 21 | Run: func(cmd *cobra.Command, args []string) { 22 | if lang == "" { 23 | fmt.Println("-l language must be specified") 24 | return 25 | } 26 | 27 | if input == "" { 28 | fmt.Println("-i input thrift file must be specified") 29 | return 30 | } 31 | 32 | if output == "" { 33 | fmt.Println("-o output path must be specified") 34 | return 35 | } 36 | 37 | // initialize global variables here 38 | 39 | a, err := filepath.Abs(input) 40 | if err != nil { 41 | log.Fatalf("failed to get absoulte path of input file %q: %s", input, err.Error()) 42 | } 43 | 44 | global.InputFile = a 45 | global.IsGenSrvRecursive = genSrvRecursive 46 | 47 | p := &parser.Parser{} 48 | parsedThrift, _, err := p.ParseFile(input) 49 | if err != nil { 50 | fmt.Fprintf(os.Stderr, "%s\n", err.Error()) 51 | os.Exit(2) 52 | } 53 | 54 | if generator, ok := langs.Langs[lang]; ok { 55 | generator.Generate(output, parsedThrift) 56 | } else { 57 | fmt.Printf("lang %s is not supported\n", lang) 58 | fmt.Println("Supported language options are:") 59 | for key := range langs.Langs { 60 | fmt.Printf("\t%s\n", key) 61 | } 62 | } 63 | }, 64 | } 65 | 66 | var lang, input, output string 67 | 68 | // when a thrift includes other thrifts, if we generate all service as server, 69 | // we may encounter problem such as compile error due to lack of structs. 70 | // so in the minimum, we should only generate server & structs specified to the thrift itself 71 | var genSrvRecursive bool 72 | 73 | func init() { 74 | genCmd.PersistentFlags().StringVarP(&lang, "lang", "l", "", "language: go | csharp") 75 | genCmd.PersistentFlags().StringVarP(&input, "input", "i", "", "input file") 76 | genCmd.PersistentFlags().StringVarP(&output, "output", "o", "", "output path") 77 | genCmd.PersistentFlags().BoolVarP(&genSrvRecursive, "srvRecursive", "R", true, "recursivly generate or not") 78 | 79 | RootCmd.AddCommand(genCmd) 80 | } 81 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | var cfgFile string 12 | 13 | // RootCmd represents the base command when called without any subcommands 14 | var RootCmd = &cobra.Command{ 15 | Use: "ezrpc", 16 | Short: "ezrpc is a micro service framework for server side rpc communication.", 17 | // Uncomment the following line if your bare application 18 | // has an action associated with it: 19 | // Run: func(cmd *cobra.Command, args []string) { }, 20 | } 21 | 22 | // Execute adds all child commands to the root command sets flags appropriately. 23 | // This is called by main.main(). It only needs to happen once to the rootCmd. 24 | func Execute() { 25 | if err := RootCmd.Execute(); err != nil { 26 | fmt.Println(err) 27 | os.Exit(-1) 28 | } 29 | } 30 | 31 | func init() { 32 | cobra.OnInitialize(initConfig) 33 | 34 | } 35 | 36 | // initConfig reads in config file and ENV variables if set. 37 | func initConfig() { 38 | if cfgFile != "" { // enable ability to specify config file via flag 39 | viper.SetConfigFile(cfgFile) 40 | } 41 | 42 | viper.SetConfigName(".ezrpc") // name of config file (without extension) 43 | viper.AddConfigPath("$HOME") // adding home directory as first search path 44 | viper.AutomaticEnv() // read in environment variables that match 45 | 46 | // If a config file is found, read it in. 47 | if err := viper.ReadInConfig(); err == nil { 48 | fmt.Println("Using config file:", viper.ConfigFileUsed()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | 7 | "github.com/ezbuy/statsd" 8 | ) 9 | 10 | type config struct { 11 | Statsd *statsd.Config `json:"statsd"` 12 | } 13 | 14 | // Config is global config 15 | var Config *config 16 | 17 | // InitConfig load config from json file 18 | func InitConfig(path string) error { 19 | buf, err := ioutil.ReadFile(path) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | Config = new(config) 25 | if err := json.Unmarshal(buf, Config); err != nil { 26 | return err 27 | } 28 | 29 | // init dependencies 30 | statsd.Setup(Config.Statsd) 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "statsd":{ 3 | "host": "192.168.199.61", 4 | "port": 8125, 5 | "project": "ezrpc", 6 | "enable": true, 7 | "sample_rate": 1 8 | } 9 | } -------------------------------------------------------------------------------- /example/Item.thrift: -------------------------------------------------------------------------------- 1 | namespace go item 2 | 3 | service Item { 4 | void SayHi(); 5 | } 6 | -------------------------------------------------------------------------------- /example/Product.thrift: -------------------------------------------------------------------------------- 1 | namespace go product 2 | namespace csharp Zen.DataAccess.Product 3 | 4 | include "Item.thrift" 5 | 6 | struct TShippingFee { 7 | 1:required string warehouse; //仓库 8 | 2:required double fee; //运费 9 | 3:required double localFee; //汇率转换后的运费 10 | } 11 | 12 | struct TProduct { 13 | 1:optional i64 cid; //淘宝商品分类id 14 | 2:optional string vendorName; //卖家名 15 | 3:optional string productName; //商品名 16 | 4:optional double unitPrice; //单价 17 | 5:optional double shippingFee; //运费 18 | 6:optional string productUrl; //商品url 19 | 7:optional string productImage; //商品图片 20 | 8:optional string originCode; //采购国家 21 | 9:optional string shopName; //店铺名 22 | 10:optional string location; //卖家地址 23 | 11:optional string aroundwWarehouse; //附近仓库 24 | 12:optional bool isShippingFee; //是否免国内运费 25 | 13:optional i32 favoritesItemId; //收藏商品id 26 | 14:optional i32 favoriteCatId; //收藏分类id 27 | 15:optional string specialHandlingFeeMessage; //美国商品手续费描述 28 | 16:optional double specialHandlingFeePercent; //美国商品手续费 29 | 17:optional list propertyNames; //商品规格描述 30 | 18:optional list shippingFees; //各仓库运费 31 | 19:optional list descriptionImages; //商品详细图片 32 | 20:optional list characteristicGroups; //可选sku列表 33 | 21:optional list skus; //商品sku列表 34 | 22:optional list itemImgs; 35 | 23:optional bool isEZBuy; //是否是特殊商品 36 | 24:optional string priceSymbol; //货币符号 37 | 25:optional string localUnitPrice; //汇率转换后的价格 38 | 26:optional double localShipmentFee; //汇率转换后的运费 39 | 27:optional string errMsg; //错误信息 40 | } 41 | 42 | struct TProductExtension { 43 | 1:optional i64 cid; //淘宝商品分类id 44 | 2:required string vendorName; //卖家名 45 | 3:required string productName; //商品名 46 | 4:required double unitPrice; //单价 47 | 5:required double shippingFee; //运费 48 | 6:required string productUrl; //商品url 49 | 7:required string productImage; //商品图片 50 | 8:required string originCode; //采购国家 51 | 9:required string shopName; //店铺名 52 | 10:required string location; //卖家地址 53 | 11:required string aroundwWarehouse; //附近仓库 54 | 12:required bool isShippingFee; //是否免国内运费 55 | 13:required i32 favoritesItemId; //收藏商品id 56 | 14:required i32 favoriteCatId; //收藏分类id 57 | 15:required string specialHandlingFeeMessage; //美国商品手续费描述 58 | 16:required double specialHandlingFeePercent; //美国商品手续费 59 | 17:optional list propertyNames; //商品规格描述 60 | 18:required list shippingFees; //各仓库运费 61 | 19:optional list descriptionImages; //商品详细图片 62 | 20:optional list characteristicGroups; //可选sku列表 63 | 21:optional list skus; //商品sku列表 64 | 22:optional list itemImgs; 65 | 23:optional bool isEZBuy; //是否是特殊商品 66 | 24:required string priceSymbol; //货币符号 67 | 25:required string localUnitPrice; //汇率转换后的价格 68 | 26:required double localShipmentFee; //汇率转换后的运费 69 | 27:optional string errMsg; //错误信息 70 | 28:optional string eta; //ETA时间范围 71 | 29:optional bool displayShippingIcon; //是否 显示 icon(特殊运输方式要显示icon) 72 | 30:optional string altProductName; //商品英文名称 73 | 31:optional list altCharacteristicGroups; //可选英文sku列表 74 | 32:required bool primeAvailable; //是否支持prime模式购买 75 | } 76 | 77 | 78 | struct TSimpleProduct { 79 | 1:required string productName; //商品名 80 | 2:required string productUrl; //商品url 81 | 3:required string productImage; //商品图片 82 | 4:required string originCode; //采购国家 83 | 5:required bool isEZBuy; //是否是特殊商品 84 | 6:required string localUnitPrice; //汇率转换后的价格 85 | } 86 | 87 | struct TSku { 88 | 1:required string price; //价格 89 | 2:required string properties; //sku组合编号 90 | 3:required string propertiesName;//sku名字 91 | 4:required i64 quantity; //数量 92 | 5:required i64 skuId; //sku Id 93 | 6:required i64 skuSpecId; 94 | 7:required string status; 95 | 8:required i64 withHoldQuantity; 96 | } 97 | 98 | struct TCharacteristic { 99 | 1:required string propkey; //标识id 100 | 2:required string actualValue; //实际值 101 | 3:required string remark; //描述 102 | 4:required string imageUrl; //图片url 103 | 5:required bool isSelected; //是否选中 104 | } 105 | 106 | struct TCharacteristicGroup { 107 | 1:required string name; //标识id 108 | 2:required list characteristics; 109 | } 110 | 111 | struct TProductReviewDetail { 112 | 1: required i32 id; // 商品评论的id,默认为0 113 | 2: required string productUrl; // 商品url 114 | 3: required i32 userId; // 用户id 115 | 4: required i32 rating; // 满意度 116 | 5: optional i32 helpfulCount; // 此条评论的采纳数 117 | 6: required string comment; // 商品评论内容 118 | 7: optional string pictures; // `图片key`数组,即客户端可以自行计算出所需的任意规格的图片url 119 | 8: required bool setHelpful; //用户是否设置过helpful 120 | 9: required string nickName; //用户昵称 121 | 10:required string headPortraits; //用户头像 122 | 11:optional string sku; //商品sku 123 | 12:required string createDate; //创建日期 124 | } 125 | 126 | struct TProductReviewCount { 127 | 1: required i32 all; // 某个商品所有评论的个数 128 | 2: required i32 hasPhoto; // 某个商品含有图片评论的个数 129 | } 130 | 131 | struct SearchFilterField { 132 | 1: required string name; 133 | 2: required i32 productCount; 134 | } 135 | 136 | struct SearchFilter { 137 | 1: required string name; 138 | 2: required list fields; 139 | } 140 | 141 | struct SearchFilterCond { 142 | 1: required string filterName; 143 | 2: required string fieldName; 144 | } 145 | 146 | struct SearchSortCond { 147 | 1: required string sort; 148 | 2: required bool isDesc; 149 | } 150 | 151 | struct SearchResult { 152 | 1: list products; 153 | 2: list sorts; 154 | 3: list filters; 155 | } 156 | 157 | exception TwitterUnavailable { 158 | 1: string message; 159 | } 160 | 161 | service Product { 162 | TProduct GetProductDetail(1:string productUrl, 2:string purchaseSource) throws (1:TwitterUnavailable cond); 163 | oneway void Ping(); 164 | 165 | # broadcast 166 | oneway void OnExchangeUpdate(); 167 | oneway void OnCacheEvict(1:string arg); 168 | 169 | # direct 170 | TProduct DirectGetProductDetail(1:string productUrl, 2:string purchaseSource) throws (1:TwitterUnavailable cond); 171 | oneway void DirectOnCacheEvict(); 172 | } 173 | -------------------------------------------------------------------------------- /ezrpc/client.go: -------------------------------------------------------------------------------- 1 | package ezrpc 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "strings" 7 | "time" 8 | 9 | "github.com/Wuvist/go-thrift/thrift" 10 | "github.com/nats-io/nats" 11 | ) 12 | 13 | type OnewayRequest interface { 14 | Oneway() bool 15 | } 16 | 17 | type Client struct { 18 | cfg *Config 19 | Conn *nats.Conn 20 | Service string 21 | DirectKey string 22 | } 23 | 24 | func NewClient(service string, conn *nats.Conn) *Client { 25 | return NewClientEx(&Config{}, service, conn) 26 | } 27 | 28 | type Config struct { 29 | Timeout time.Duration 30 | Reties int 31 | } 32 | 33 | func (cfg *Config) init() { 34 | if cfg.Timeout <= 0 { 35 | cfg.Timeout = 10 * time.Second 36 | } 37 | } 38 | 39 | func NewFastRetryClient(service string, conn *nats.Conn) *Client { 40 | return NewClientEx(&Config{ 41 | Timeout: 2 * time.Second, 42 | Reties: 3, 43 | }, service, conn) 44 | } 45 | 46 | func NewClientEx(cfg *Config, service string, conn *nats.Conn) *Client { 47 | cfg.init() 48 | return &Client{ 49 | Service: service, 50 | Conn: conn, 51 | cfg: cfg, 52 | } 53 | 54 | } 55 | 56 | func NewClientTimeout(service string, timeout time.Duration, conn *nats.Conn) *Client { 57 | return NewClientEx(&Config{ 58 | Timeout: timeout, 59 | }, service, conn) 60 | } 61 | 62 | func (c *Client) Call(method string, request interface{}, response interface{}) error { 63 | buf := &bytes.Buffer{} 64 | w := thrift.NewCompactProtocolWriter(buf) 65 | thrift.EncodeStruct(w, request) 66 | 67 | // 认为客户端 UNTIL 类请求是超长超时的请求 68 | timeout := c.cfg.Timeout 69 | if strings.HasPrefix(method, "UNTIL") { 70 | if c.cfg.Timeout < time.Hour { 71 | timeout = time.Hour 72 | } 73 | 74 | method = method[5:] 75 | } 76 | 77 | var subject string 78 | if strings.HasPrefix(method, "Direct") { 79 | if c.DirectKey == "" { 80 | return errors.New("client DirectKey is empty") 81 | } 82 | subject = c.DirectKey + "." + c.Service + "." + method 83 | } else { 84 | subject = c.Service + "." + method 85 | 86 | if strings.HasPrefix(method, "On") { 87 | if onewayReq, ok := request.(OnewayRequest); ok && onewayReq != nil && onewayReq.Oneway() { 88 | subject = "On." + subject 89 | } 90 | } 91 | } 92 | 93 | // 认为客户端实现过程中 broadcast 类的请求始终 reponse == nil 94 | if response == nil { 95 | return c.Conn.Publish(subject, buf.Bytes()) 96 | } 97 | 98 | retryTime := c.cfg.Reties 99 | retry: 100 | msg, err := c.Conn.Request(subject, buf.Bytes(), timeout) 101 | if err == nats.ErrTimeout { 102 | if retryTime > 0 { 103 | time.Sleep(100 * time.Millisecond) 104 | retryTime-- 105 | goto retry 106 | } 107 | } 108 | if err != nil { 109 | return err 110 | } 111 | 112 | r := thrift.NewCompactProtocolReader(bytes.NewReader(msg.Data)) 113 | 114 | return thrift.DecodeStruct(r, response) 115 | } 116 | -------------------------------------------------------------------------------- /ezrpc/middlewares/request_timer.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/ezbuy/ezrpc/ezrpc" 8 | "github.com/nats-io/nats" 9 | ) 10 | 11 | func RequestTimer() ezrpc.MsgMiddleware { 12 | 13 | return func(h nats.MsgHandler) nats.MsgHandler { 14 | return func(msg *nats.Msg) { 15 | start := time.Now() 16 | 17 | h(msg) 18 | 19 | fmt.Printf("REQ %s : %s\n", msg.Subject, time.Now().Sub(start)) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ezrpc/server.go: -------------------------------------------------------------------------------- 1 | package ezrpc 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/signal" 7 | "sync" 8 | "syscall" 9 | "time" 10 | 11 | "github.com/nats-io/nats" 12 | ) 13 | 14 | type MsgMiddleware func(h nats.MsgHandler) nats.MsgHandler 15 | 16 | type Daemon struct { 17 | mutex sync.RWMutex 18 | msgWg sync.WaitGroup 19 | 20 | timeout time.Duration 21 | 22 | conn *nats.Conn 23 | subscriptions []*nats.Subscription 24 | middelwares []MsgMiddleware 25 | 26 | exit chan bool 27 | } 28 | 29 | func NewDaemon(opts nats.Options, middlewares ...MsgMiddleware) (*Daemon, error) { 30 | conn, err := opts.Connect() 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | return NewDaemonWithConn(conn, middlewares...), nil 36 | } 37 | 38 | func NewDaemonWithConn(conn *nats.Conn, middlewares ...MsgMiddleware) *Daemon { 39 | server := &Daemon{ 40 | timeout: 30 * time.Second, 41 | conn: conn, 42 | subscriptions: make([]*nats.Subscription, 0), 43 | middelwares: middlewares, 44 | exit: make(chan bool, 1), 45 | } 46 | 47 | return server 48 | } 49 | 50 | func (this *Daemon) Use(middlewares ...MsgMiddleware) { 51 | this.mutex.Lock() 52 | this.middelwares = append(this.middelwares, middlewares...) 53 | this.mutex.Unlock() 54 | } 55 | 56 | func (this *Daemon) Subscribe(subject string, h nats.MsgHandler) error { 57 | h = this.buildMsgHandler(h) 58 | 59 | sub, err := this.conn.Subscribe(subject, func(msg *nats.Msg) { 60 | go h(msg) 61 | }) 62 | 63 | if err != nil { 64 | return err 65 | } 66 | 67 | this.addSubscription(sub) 68 | 69 | return nil 70 | } 71 | 72 | func (this *Daemon) QueueSubscribe(subject, queue string, h nats.MsgHandler) error { 73 | h = this.buildMsgHandler(h) 74 | 75 | sub, err := this.conn.QueueSubscribe(subject, queue, func(msg *nats.Msg) { 76 | go h(msg) 77 | }) 78 | 79 | if err != nil { 80 | return err 81 | } 82 | 83 | this.addSubscription(sub) 84 | 85 | return nil 86 | } 87 | 88 | func (this *Daemon) Run() { 89 | fmt.Fprintln(os.Stdout, "nats msg server running") 90 | go this.handleSignal() 91 | 92 | <-this.exit 93 | 94 | this.conn.Close() 95 | 96 | fmt.Fprintln(os.Stdout, "nats msg server stopped") 97 | } 98 | 99 | func (this *Daemon) Stop() { 100 | this.unsubscribeAll() 101 | 102 | go this.waitForHandlers() 103 | go this.waitForTimeout() 104 | 105 | } 106 | 107 | func (this *Daemon) waitForHandlers() { 108 | this.msgWg.Wait() 109 | 110 | fmt.Fprintln(os.Stdout, "nats msg server: all handlers finished") 111 | this.exit <- true 112 | } 113 | 114 | func (this *Daemon) waitForTimeout() { 115 | timer := time.NewTimer(this.timeout) 116 | <-timer.C 117 | 118 | fmt.Fprintln(os.Stderr, "nats msg server: timeout before all handlers finish") 119 | this.exit <- true 120 | } 121 | 122 | func (this *Daemon) handleSignal() { 123 | c := make(chan os.Signal, 1) 124 | signal.Notify(c, syscall.SIGTERM) 125 | 126 | <-c 127 | this.Stop() 128 | } 129 | 130 | func (this *Daemon) unsubscribeAll() { 131 | this.mutex.Lock() 132 | 133 | subs := this.subscriptions 134 | this.subscriptions = []*nats.Subscription{} 135 | 136 | this.mutex.Unlock() 137 | 138 | for _, one := range subs { 139 | one.Unsubscribe() 140 | } 141 | } 142 | 143 | func (this *Daemon) addSubscription(sub *nats.Subscription) { 144 | fmt.Printf("SUB %q\n", sub.Subject) 145 | 146 | this.mutex.Lock() 147 | 148 | this.subscriptions = append(this.subscriptions, sub) 149 | 150 | this.mutex.Unlock() 151 | } 152 | 153 | func (this *Daemon) buildMsgHandler(h nats.MsgHandler) nats.MsgHandler { 154 | handler := func(msg *nats.Msg) { 155 | this.msgWg.Add(1) 156 | defer this.msgWg.Done() 157 | 158 | h(msg) 159 | } 160 | 161 | this.mutex.RLock() 162 | mws := this.middelwares 163 | this.mutex.RUnlock() 164 | 165 | if len(mws) == 0 { 166 | return handler 167 | } 168 | 169 | for i := len(mws) - 1; i >= 0; i -= 1 { 170 | handler = mws[i](handler) 171 | } 172 | 173 | return handler 174 | } 175 | -------------------------------------------------------------------------------- /global/variables.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | var ( 4 | IsGenSrvRecursive bool 5 | InputFile string 6 | ) 7 | -------------------------------------------------------------------------------- /langs/base_gen.go: -------------------------------------------------------------------------------- 1 | package langs 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "os" 8 | "strings" 9 | 10 | "github.com/Wuvist/go-thrift/parser" 11 | ) 12 | 13 | type BaseGen struct { 14 | Lang string 15 | Namespace string 16 | Thrifts map[string]*parser.Thrift 17 | } 18 | 19 | func (g *BaseGen) Init(lang string, parsedThrift map[string]*parser.Thrift) { 20 | g.Lang = lang 21 | g.Thrifts = parsedThrift 22 | g.CheckNamespace() 23 | 24 | if err := g.checkMethodName(); err != nil { 25 | log.Fatalf("error: %s", err.Error()) 26 | } 27 | } 28 | 29 | func (g *BaseGen) CheckNamespace() { 30 | for _, thrift := range g.Thrifts { 31 | for lang, namepace := range thrift.Namespaces { 32 | if lang == g.Lang { 33 | g.Namespace = namepace 34 | return 35 | } 36 | } 37 | } 38 | 39 | fmt.Fprintf(os.Stderr, "Namespace not found for: %s\n", g.Lang) 40 | os.Exit(2) 41 | } 42 | 43 | func (g *BaseGen) checkMethodName() error { 44 | for _, thrift := range g.Thrifts { 45 | for _, s := range thrift.Services { 46 | for _, m := range s.Methods { 47 | if m.Name[:1] == strings.ToUpper(m.Name[:1]) { 48 | continue 49 | } 50 | 51 | return errors.New("the first letter of thrift service methods should be capitial") 52 | } 53 | } 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /langs/csharp/gen.go: -------------------------------------------------------------------------------- 1 | package csharp 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/Wuvist/go-thrift/parser" 10 | "github.com/ezbuy/ezrpc/langs" 11 | ) 12 | 13 | var lang = "csharp" 14 | 15 | type gen struct { 16 | langs.BaseGen 17 | } 18 | 19 | func (g *gen) Generate(output string, parsedThrift map[string]*parser.Thrift) { 20 | g.BaseGen.Init(lang, parsedThrift) 21 | 22 | path, err := filepath.Abs(output) 23 | if err != nil { 24 | log.Fatalf("failed to get absolute path of [%s]", output) 25 | } 26 | 27 | for _, parsed := range parsedThrift { 28 | ns := getNamespace(parsed) 29 | 30 | // make output dir 31 | d := filepath.Join(path, filepath.Join(strings.Split(ns, ".")...)) 32 | if err := os.MkdirAll(d, 0755); err != nil { 33 | log.Fatalf("failed to make output dir [%s]", d) 34 | } 35 | 36 | // write file 37 | for n, s := range parsed.Services { 38 | f := filepath.Join(d, "gen_"+n+"_server.cs") 39 | data := &tmpldata{Namespace: ns, Service: s} 40 | 41 | if err := writefile(f, data); err != nil { 42 | log.Fatalf("failed to write file [%s]: %s", f, err.Error()) 43 | } 44 | } 45 | } 46 | } 47 | 48 | func getNamespace(t *parser.Thrift) string { 49 | if namespace, ok := t.Namespaces[lang]; ok { 50 | return namespace 51 | } 52 | return "" 53 | } 54 | 55 | func init() { 56 | langs.Langs[lang] = &gen{} 57 | } 58 | -------------------------------------------------------------------------------- /langs/csharp/template.go: -------------------------------------------------------------------------------- 1 | package csharp 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "strings" 7 | "text/template" 8 | 9 | "github.com/Wuvist/go-thrift/parser" 10 | "github.com/ezbuy/ezrpc/langs" 11 | "github.com/ezbuy/ezrpc/tmpl" 12 | ) 13 | 14 | var tpl *template.Template 15 | 16 | type tmpldata struct { 17 | Namespace string 18 | Service *parser.Service 19 | } 20 | 21 | func writefile(f string, d *tmpldata) error { 22 | file, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 23 | if err != nil { 24 | return err 25 | } 26 | defer file.Close() 27 | 28 | return tpl.ExecuteTemplate(file, "ezrpc/csharp", d) 29 | } 30 | 31 | func init() { 32 | tpl = template.New("ezrpc/csharp") 33 | 34 | funcs := template.FuncMap{ 35 | "ToLower": strings.ToLower, 36 | "Utils": langs.Utils, 37 | } 38 | tpl.Funcs(funcs) 39 | 40 | files := []string{ 41 | "tmpl/csharp/server.gocs", 42 | } 43 | 44 | for _, f := range files { 45 | data, err := tmpl.Asset(f) 46 | if err != nil { 47 | log.Fatalf("failed to get template [%s]: %s", f, err.Error()) 48 | } 49 | 50 | if _, err := tpl.Parse(string(data)); err != nil { 51 | log.Fatalf("failed to parse template [%s]: %s", f, err.Error()) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /langs/generator.go: -------------------------------------------------------------------------------- 1 | package langs 2 | 3 | import "github.com/Wuvist/go-thrift/parser" 4 | 5 | type ApiGen interface { 6 | Generate(output string, parsedThrift map[string]*parser.Thrift) 7 | } 8 | 9 | var Langs = make(map[string]ApiGen) 10 | -------------------------------------------------------------------------------- /langs/go/gen_go.go: -------------------------------------------------------------------------------- 1 | package gogen 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/Wuvist/go-thrift/parser" 10 | "github.com/ezbuy/ezrpc/global" 11 | "github.com/ezbuy/ezrpc/langs" 12 | ) 13 | 14 | const langName = "go" 15 | 16 | type GoGen struct { 17 | langs.BaseGen 18 | } 19 | 20 | func getNamespace(namespaces map[string]string) string { 21 | if namespace, ok := namespaces[langName]; ok { 22 | return namespace 23 | } 24 | 25 | return "" 26 | } 27 | 28 | func genNamespace(namespace string) (string, string) { 29 | var path string 30 | if strings.Contains(namespace, "..") { 31 | path = strings.Replace(namespace, "..", "/", -1) 32 | } else { 33 | path = strings.Replace(namespace, ".", "/", -1) 34 | } 35 | 36 | pkgName := filepath.Base(path) 37 | return path, pkgName 38 | } 39 | 40 | func panicWithErr(format string, msg ...interface{}) { 41 | panic(fmt.Errorf(format, msg...)) 42 | } 43 | 44 | type ServerData struct { 45 | Namespace string 46 | Service *parser.Service 47 | } 48 | 49 | func (d ServerData) HasBroadcastMethod() bool { 50 | for _, m := range d.Service.Methods { 51 | if langs.Utils().IsBroadcastMethod(m) { 52 | return true 53 | } 54 | } 55 | 56 | return false 57 | } 58 | 59 | func (d ServerData) HasNormalMsgMethod() bool { 60 | for _, m := range d.Service.Methods { 61 | if langs.Utils().IsNormalMethod(m) { 62 | return true 63 | } 64 | } 65 | 66 | return false 67 | } 68 | 69 | func (this *GoGen) Generate(output string, parsedThrift map[string]*parser.Thrift) { 70 | this.BaseGen.Init(langName, parsedThrift) 71 | 72 | outputPath, err := filepath.Abs(output) 73 | if err != nil { 74 | panicWithErr("fail to get absolute path for %q", output) 75 | } 76 | 77 | outputPackageDirs := make([]string, 0, len(parsedThrift)) 78 | 79 | fmt.Println("##### Parsing:") 80 | for filename, parsed := range parsedThrift { 81 | if !global.IsGenSrvRecursive && filename != global.InputFile { 82 | continue 83 | } 84 | 85 | fmt.Printf("%s\n", filename) 86 | namespace := getNamespace(parsed.Namespaces) 87 | importPath, _ := genNamespace(namespace) 88 | 89 | pkgs := strings.Split(namespace, ".") 90 | pkg := pkgs[len(pkgs)-1] 91 | 92 | // make output dir 93 | pkgDir := filepath.Join(outputPath, importPath) 94 | if err := os.MkdirAll(pkgDir, 0755); err != nil { 95 | panicWithErr("fail to make package directory %s", pkgDir) 96 | } 97 | 98 | outputPackageDirs = append(outputPackageDirs, pkgDir) 99 | 100 | // write file 101 | for name, service := range parsed.Services { 102 | fname := filepath.Join(pkgDir, "gen_"+name+"_server.go") 103 | data := ServerData{ 104 | Namespace: pkg, 105 | Service: service, 106 | } 107 | if err := outputFile(fname, "server", data); err != nil { 108 | panicWithErr("fail to write defines file %q : %s", fname, err) 109 | } 110 | } 111 | 112 | } 113 | } 114 | 115 | func init() { 116 | langs.Langs[langName] = &GoGen{} 117 | } 118 | -------------------------------------------------------------------------------- /langs/go/namespace_test.go: -------------------------------------------------------------------------------- 1 | package gogen 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGenNamespace(t *testing.T) { 8 | cases := []struct { 9 | namespace string 10 | path string 11 | pkgName string 12 | }{ 13 | { 14 | "github.com..ezbuy..ezrpc..example", 15 | "github.com/ezbuy/ezrpc/example", 16 | "example", 17 | }, 18 | { 19 | "github.com.ezbuy.ezrpc.example", 20 | "github/com/ezbuy/ezrpc/example", 21 | "example", 22 | }, 23 | } 24 | 25 | for _, one := range cases { 26 | path, pkgName := genNamespace(one.namespace) 27 | 28 | if path != one.path { 29 | t.Errorf("expected path: %s, got %s", one.path, path) 30 | } 31 | 32 | if pkgName != one.pkgName { 33 | t.Errorf("expected package name: %s, got %s", one.pkgName, pkgName) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /langs/go/template.go: -------------------------------------------------------------------------------- 1 | package gogen 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "text/template" 7 | 8 | "github.com/ezbuy/ezrpc/langs" 9 | "github.com/ezbuy/ezrpc/tmpl" 10 | ) 11 | 12 | var tpl *template.Template 13 | 14 | func Tpl() *template.Template { 15 | return tpl 16 | } 17 | 18 | func init() { 19 | tpl = template.New("ezrpc/golang") 20 | funcMap := template.FuncMap{ 21 | "ToLower": strings.ToLower, 22 | "Utils": langs.Utils, 23 | } 24 | tpl.Funcs(funcMap) 25 | files := []string{ 26 | "tmpl/golang/server.gogo", 27 | } 28 | 29 | for _, filename := range files { 30 | data, err := tmpl.Asset(filename) 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | if _, err = tpl.Parse(string(data)); err != nil { 36 | panic(err) 37 | } 38 | } 39 | } 40 | 41 | func outputFile(path string, tplName string, data interface{}) error { 42 | file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | defer file.Close() 48 | 49 | return tpl.ExecuteTemplate(file, tplName, data) 50 | } 51 | -------------------------------------------------------------------------------- /langs/utils.go: -------------------------------------------------------------------------------- 1 | package langs 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/Wuvist/go-thrift/parser" 7 | ) 8 | 9 | type Util struct { 10 | } 11 | 12 | func Utils() *Util { 13 | return _util 14 | } 15 | 16 | var _util *Util 17 | 18 | func (u *Util) IsNormalMethod(m *parser.Method) bool { 19 | return !u.IsBroadcastMethod(m) && !u.IsDirectMethod(m) 20 | } 21 | 22 | func (u *Util) IsBroadcastMethod(m *parser.Method) bool { 23 | return m.Oneway && strings.HasPrefix(m.Name, "On") 24 | } 25 | 26 | func (u *Util) HasDirectMethod(s *parser.Service) bool { 27 | for _, m := range s.Methods { 28 | if u.IsDirectMethod(m) { 29 | return true 30 | } 31 | } 32 | return false 33 | } 34 | 35 | func (u *Util) IsDirectMethod(m *parser.Method) bool { 36 | return strings.HasPrefix(m.Name, "Direct") 37 | } 38 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/ezbuy/ezrpc/cmd" 4 | 5 | //go:generate go get github.com/jteeuwen/go-bindata/... 6 | //go:generate go-bindata -o ./tmpl/bindata.go -ignore bindata.go -pkg tmpl tmpl/golang 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | init: 4 | # go client 5 | go get github.com/nats-io/nats 6 | # statsd 7 | go get github.com/ezbuy/statsd 8 | go get github.com/jteeuwen/go-bindata/... 9 | 10 | buildtpl: 11 | go-bindata -o tmpl/bindata.go -ignore bindata.go -pkg tmpl tmpl/... 12 | 13 | gencsharp: buildtpl 14 | go build -o exe 15 | ./exe gen -l csharp -i example/Product.thrift -o ./gencsharp 16 | rm exe 17 | 18 | gengo: buildtpl 19 | go build -o exe 20 | ./exe gen -l go -i example/Product.thrift -o ./example 21 | rm exe 22 | 23 | gen: gencsharp gengo 24 | 25 | clean: 26 | rm -rf ./gengo 27 | rm -rf ./gencsharp 28 | -------------------------------------------------------------------------------- /tmpl/bindata.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-bindata. 2 | // sources: 3 | // tmpl/.DS_Store 4 | // tmpl/csharp/server.gocs 5 | // tmpl/golang/server.gogo 6 | // DO NOT EDIT! 7 | 8 | package tmpl 9 | 10 | import ( 11 | "bytes" 12 | "compress/gzip" 13 | "fmt" 14 | "io" 15 | "io/ioutil" 16 | "os" 17 | "path/filepath" 18 | "strings" 19 | "time" 20 | ) 21 | 22 | func bindataRead(data []byte, name string) ([]byte, error) { 23 | gz, err := gzip.NewReader(bytes.NewBuffer(data)) 24 | if err != nil { 25 | return nil, fmt.Errorf("Read %q: %v", name, err) 26 | } 27 | 28 | var buf bytes.Buffer 29 | _, err = io.Copy(&buf, gz) 30 | clErr := gz.Close() 31 | 32 | if err != nil { 33 | return nil, fmt.Errorf("Read %q: %v", name, err) 34 | } 35 | if clErr != nil { 36 | return nil, err 37 | } 38 | 39 | return buf.Bytes(), nil 40 | } 41 | 42 | type asset struct { 43 | bytes []byte 44 | info os.FileInfo 45 | } 46 | 47 | type bindataFileInfo struct { 48 | name string 49 | size int64 50 | mode os.FileMode 51 | modTime time.Time 52 | } 53 | 54 | func (fi bindataFileInfo) Name() string { 55 | return fi.name 56 | } 57 | func (fi bindataFileInfo) Size() int64 { 58 | return fi.size 59 | } 60 | func (fi bindataFileInfo) Mode() os.FileMode { 61 | return fi.mode 62 | } 63 | func (fi bindataFileInfo) ModTime() time.Time { 64 | return fi.modTime 65 | } 66 | func (fi bindataFileInfo) IsDir() bool { 67 | return false 68 | } 69 | func (fi bindataFileInfo) Sys() interface{} { 70 | return nil 71 | } 72 | 73 | var _tmplDs_store = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x98\xc1\x4a\xc4\x30\x10\x86\xff\x89\x45\x02\x5e\x72\xf4\x98\x57\xf0\x0d\xc2\xb2\x3e\xc1\xbe\x80\x82\xd0\xcb\xd2\x82\xa0\xe7\x9c\x7c\x2e\x1f\xcd\x86\xf9\x45\xb1\x2d\xd4\x53\xc5\xfd\x3f\x08\xdf\xc2\xce\x4c\xdb\x4b\x26\x13\x00\x76\x78\x79\xba\x03\xd2\xf4\x33\xc2\x8d\x37\x2c\x12\xb9\x66\x04\xda\x7c\x4d\x35\x06\xf4\xaf\xa7\xe7\xe1\x3c\x0e\xfd\x72\xad\x19\x2d\xf7\x1a\x3d\x46\x9c\xf1\xf8\x23\xdf\x36\xd6\x10\x42\x08\x21\xc4\x76\xd8\x5f\xe3\xcd\xbe\xaf\x21\x84\xf8\x83\xb4\xfd\x21\xd3\x85\xae\x6e\xe3\xff\x81\xee\xbe\xe5\x24\x3a\xd3\x85\xae\x6e\x63\x5c\xa0\x3b\x3a\xd2\x89\xce\x74\xa1\xab\x9b\x9b\x96\x71\xf8\x30\x3e\xd9\x38\xa1\x58\xa2\x33\x5d\x7e\xf9\xd1\x42\x5c\x08\x57\xae\xd4\xfa\xff\x3d\x56\xe7\x7f\x21\xc4\x3f\xc6\xba\xe3\xe9\x78\xc0\xfa\x85\x5b\xeb\xb5\x79\x5a\x0f\x8c\x79\xff\x4c\x5c\x39\x08\x04\xbf\x30\xbc\xc5\x57\x5c\xa6\x0b\x5d\xdd\x3a\x0c\x08\xb1\x07\x1f\x01\x00\x00\xff\xff\x39\x97\xc4\xee\x04\x18\x00\x00") 74 | 75 | func tmplDs_storeBytes() ([]byte, error) { 76 | return bindataRead( 77 | _tmplDs_store, 78 | "tmpl/.DS_Store", 79 | ) 80 | } 81 | 82 | func tmplDs_store() (*asset, error) { 83 | bytes, err := tmplDs_storeBytes() 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | info := bindataFileInfo{name: "tmpl/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1467776856, 0)} 89 | a := &asset{bytes: bytes, info: info} 90 | return a, nil 91 | } 92 | 93 | var _tmplCsharpServerGocs = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xcc\x54\xdf\x8b\xe3\x36\x10\x7e\xb6\xff\x8a\x21\x2c\xd4\x0e\x39\xa5\xcf\x17\xee\x61\x6f\x2f\xb4\x0b\xcd\xdd\x72\x49\x29\xec\xcb\xa2\xc8\x93\x44\xad\x2d\x19\x49\xce\xae\x1b\xfc\xbf\x77\x24\xcb\xf9\xd1\x2c\xed\xb6\x50\x7a\x21\x24\x96\xf4\xcd\x37\x9a\x99\xef\xf3\xe1\x70\xe3\xaa\xba\x2c\xb8\xe3\xf0\xfe\x03\x30\x78\xd7\x75\xe9\x74\x3c\x4e\x61\x0c\xab\x9d\xb4\xb0\x91\x25\x02\xfd\xf3\xc6\xe9\x2d\x2a\x34\xdc\x61\x01\xeb\x16\xf0\x77\x53\x0b\x0f\xfb\xa4\xd5\x77\x0e\xc4\x8e\xab\x2d\x42\xc5\x55\xc3\xcb\xb2\xa5\x83\x69\x9a\x36\x56\xaa\x2d\xcc\x1f\xbf\x3e\xdc\xb1\xcf\xdc\xd9\x59\xdc\xf9\x7c\xbb\x5a\xb2\xbb\x52\xa2\x72\xc3\xd6\xb2\xb5\x0e\xab\x61\xb5\xda\x19\xb9\x71\x6c\x65\xb8\xb2\xb5\x36\x84\x4a\x15\xaf\xd0\xd6\x5c\x20\x1c\x0e\x44\x16\x17\x5d\x07\x87\x34\xb1\x8e\x3b\x29\x40\x94\xdc\x5a\x7f\xbc\x44\xb3\x97\x02\x03\xac\xeb\xe2\xca\x03\x93\xe9\x14\x1e\x8c\x16\x68\xad\x36\xbe\xac\x8b\x92\xe2\x42\x1b\x02\xd6\x46\xee\x69\x1f\x22\xf5\x15\x29\x3b\xd1\xd4\xc3\xd3\xd3\xec\x3a\xf0\xfe\x4e\x2b\x85\xc2\x49\xad\x40\xd0\x23\x61\x3c\xa8\x59\x97\x74\x18\x31\x7b\x2d\x0b\x90\x4a\xba\xec\x4d\x69\x26\x97\xa4\x79\x28\x2c\x39\xdd\x02\x3e\x9c\xb0\xfe\x46\x49\xc8\x4b\xbb\x22\xe4\x4e\x04\x5b\x36\x6b\x2b\x8c\x5c\xe3\xad\x6d\x95\xc8\x46\x5f\x14\xbb\xce\x3c\x1e\x4d\x40\xab\x8f\x46\xf3\x42\x70\xeb\xf2\xd9\xeb\xb1\xaf\x07\x8e\x82\x3c\x02\xc3\xc2\x6e\x43\x6c\x97\x5e\x77\x27\x54\x1e\x20\x99\x5e\xff\x4a\x15\x81\x45\x55\x20\x95\x48\x5b\x3f\x72\x55\x94\x68\xe6\x7b\x92\xc9\xad\xd9\x5a\xc0\x58\xea\x9e\x1b\x70\x5e\x1a\x54\x93\xc2\xe7\x2b\xb5\xb0\xd5\x02\x2b\x6d\xda\x8f\xcd\x66\x83\x26\x43\xb6\xa0\x5e\xf0\x2d\xb2\x4f\x24\xf3\xbe\x0e\x4f\x21\xa9\x4b\x2e\x52\xcc\x1f\xa9\xd1\x4e\x67\x81\x36\x9f\x01\xe9\x44\x2a\x0f\x74\xa6\xed\x93\x42\x72\xc1\x0a\xeb\x66\x13\x98\x80\x3e\x31\x1a\xb4\x67\x3c\xee\x52\x09\x50\xd9\x6d\xdf\xf4\xc4\x3e\x4b\x27\x76\x70\x76\x1b\xea\xa4\x2f\x39\x16\x95\x1c\x0e\xef\xc0\x04\x13\xdd\x78\xad\x4f\xe0\xa6\x42\xb7\xd3\x45\xf0\xe5\xd0\xe2\x45\xd8\xb2\x64\xd2\x21\x44\x6e\x40\x51\x19\xd9\xcf\x4e\x96\x96\xdd\xdb\xe3\xc0\x7a\xe8\xc0\x92\xc7\x10\x3a\x41\x18\x9d\xd9\xfe\xcf\xd3\xa3\x23\x15\x9e\x46\xef\x43\xc0\x90\x24\xf2\xb0\x2f\xd4\x2e\xde\x46\xb6\x33\xd5\x9d\x02\x9f\xa2\x66\xb3\xef\x27\x7d\x8f\x27\xa0\x9a\xb2\xec\x1b\xdf\xf3\x61\x69\x71\xa0\xa0\x46\xbe\x6d\x8e\x03\x81\x7e\x65\x6e\x44\xd2\x4f\x4d\x37\x2e\xfd\x47\x57\x0b\x6c\x79\x1c\x52\x42\xf3\x8a\xcc\x5e\x93\x43\x46\xda\x1d\xa6\x45\xa7\xa7\x09\x7e\xc5\xba\x6c\xcf\x30\x5e\x5f\x04\xa0\xcb\xb0\x1f\xd0\x9d\xae\xdd\x23\xb2\xec\x3e\x78\x26\x3a\xa8\xf6\xf6\xcd\x7b\xbd\xe7\xec\xe4\x68\xf6\xe0\x5f\x0e\x76\x97\x55\xd1\x38\x43\xcf\x54\x71\x6c\x99\x41\xfe\xdb\x2c\xbd\x3e\xb9\x5c\x85\xdf\x0e\x04\x0f\xc2\x9b\xbf\x08\x0c\x39\x01\x5f\x06\xcd\x51\x56\xab\x4b\x64\xbf\x18\xe9\xf0\x27\xa9\x30\xc3\x17\xb6\xd2\x4b\x67\xe8\x2d\x9c\xe5\x7d\xfa\xee\xaf\xcd\x7b\x14\xdc\x37\x6f\xe1\xff\xd2\x83\x7f\x63\xbf\x0b\xf7\x85\xd7\xed\xdb\x0d\xf8\x2f\x3c\xf6\x7f\x08\x84\xbe\x5d\xfa\x47\x00\x00\x00\xff\xff\x43\xe4\x02\xdc\x50\x08\x00\x00") 94 | 95 | func tmplCsharpServerGocsBytes() ([]byte, error) { 96 | return bindataRead( 97 | _tmplCsharpServerGocs, 98 | "tmpl/csharp/server.gocs", 99 | ) 100 | } 101 | 102 | func tmplCsharpServerGocs() (*asset, error) { 103 | bytes, err := tmplCsharpServerGocsBytes() 104 | if err != nil { 105 | return nil, err 106 | } 107 | 108 | info := bindataFileInfo{name: "tmpl/csharp/server.gocs", size: 2128, mode: os.FileMode(420), modTime: time.Unix(1467776752, 0)} 109 | a := &asset{bytes: bytes, info: info} 110 | return a, nil 111 | } 112 | 113 | var _tmplGolangServerGogo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x58\x5f\x8f\xe2\x36\x10\x7f\x4e\x3e\x85\x1b\x5d\x7b\x09\x07\x46\x7b\x8f\x48\xfb\xd0\xbd\xab\xd4\x55\x75\xec\x76\xd9\x6a\x1f\x56\xfb\x10\x92\x01\xdc\x4d\x6c\x6a\x3b\x87\x68\xc4\x77\xef\x8c\x1d\x68\x80\xa8\xec\x5e\xff\x4b\x20\x91\xc4\xe3\xf1\xfc\xf7\x6f\x9c\xd4\x75\x0e\x33\x21\x81\x45\x06\xf4\x67\xd0\x11\x1b\x6c\x36\xe1\x32\xcd\x9e\xd3\x39\xb0\xba\xe6\xe3\xb4\x04\x83\x63\x40\x72\x5d\xbf\x21\x2e\x91\x01\x51\xd9\xe8\x92\xf1\x89\x1f\x3b\x36\xe4\x10\xe5\x52\x69\xcb\xe2\x30\x88\xa6\x6b\x0b\x26\xc2\x87\x42\xcd\xa3\x10\xef\x73\x61\x17\xd5\x94\x67\xaa\x1c\x3e\x54\x9f\x85\xb1\xc3\xb9\x1a\xd8\x85\x16\x33\x3b\xf4\xb7\x68\x9f\x0b\x7e\x9d\x56\x6b\xbc\xea\x65\xe6\xaf\x9d\xf3\xc6\xa6\xd6\xe4\x07\x53\x12\x69\x03\xa1\xdc\x3d\x0a\x93\x30\xb4\xeb\x25\xb0\x7b\xa7\x65\x8c\xb4\x7d\x4f\x36\x9b\x89\x73\x9e\x19\xab\xab\xcc\xb2\x3a\x0c\x1a\x02\xfe\x7a\xdd\xbc\x61\xf0\x41\x49\xc9\xdc\xaf\x47\x6a\x38\x8d\xc3\xe0\xa3\xd0\x90\xd9\x1f\x60\x4d\xc2\x84\x9c\x23\x25\x85\x52\x39\xce\x9e\xf3\x82\x7b\x42\xb8\x09\xc3\x59\x25\x33\x16\x1b\xd6\x3b\x65\x59\xc2\x94\xfc\x64\xe6\x71\x69\xe6\x8d\x36\x1c\x25\x68\x68\x5d\x0f\x98\x98\x31\xfe\x7d\x6a\xc6\x4a\x97\x69\x81\xf4\x4f\x60\x17\x2a\xc7\x6c\x04\x9a\x72\xe4\x63\xcb\xc7\xb0\xfa\xa0\x4a\xcc\xa4\xbd\xd5\xca\xaa\x4c\x15\x77\x90\xe6\xa0\x63\x97\x28\x9a\x6e\xc6\xa8\x03\x4d\xb4\x69\x82\x61\x0b\xcc\x4a\xd8\x6c\xc1\x88\x36\xa9\xa6\x3f\x83\x8b\x0e\x29\xd5\xa9\xc4\x02\x79\x53\x3a\x5d\x7b\xa5\xe0\xd5\x1b\x57\x2f\xce\xb8\x9f\xac\x28\x0c\xbf\xde\x1a\xe8\x57\x34\x2b\xc9\xca\x2c\x35\x58\x7f\x87\x8e\x73\x24\x78\x9e\xa6\xb8\xa2\xd1\x56\xe0\x96\x7e\x23\x61\x95\xae\x49\x44\xb0\x24\x13\xbe\x39\x94\x71\x28\xe2\x0e\x7e\xa9\xc0\xd8\x9a\x56\x80\x6e\x47\xe7\x23\x64\x2a\x87\x89\xcb\x7f\xac\xfb\x6c\x99\x20\x0b\xaa\x22\xae\xaf\x2e\x99\x14\x05\x55\x45\x10\x60\x31\xf3\x5b\xcc\xab\x9d\xc5\xd1\xe3\xf8\xdb\xfb\xc9\xd3\xe0\xf1\xa4\xe5\x4f\x2c\x77\xe2\x49\x9a\xd2\x23\xf6\xb5\x89\xfa\xf4\x4c\x3a\xb6\xa6\x5c\x32\xc3\x7d\xae\x8f\x96\xc7\x7f\xb1\x31\xcd\x7c\xb7\x35\x78\xf1\x3b\x8a\x5f\xcb\x4c\xc7\xa7\xd3\x82\xfb\xad\x92\x36\x4a\x5c\x72\xa0\x30\xe0\xf2\x61\x2f\x28\xb8\x8d\xa4\xb1\x5a\xc5\x54\x4c\x5f\x90\x25\x0d\xe6\x85\x6b\xcc\x52\x49\x03\xff\xbb\xd4\xf6\x19\xba\xf8\xcf\xe6\x77\x5a\xcd\x5c\x4c\xfd\xc6\xbf\xaa\x66\x33\xd0\x2e\x6e\xab\x3f\x84\x8b\x07\x2d\x2c\xc1\x45\x35\x23\x49\x0d\xdb\x77\xb2\x15\xdc\xd5\xce\x19\x8f\x86\xfc\xb6\x9a\x16\xc2\x2c\x1c\xa4\xdc\xc1\xb2\x58\xf7\x19\x2e\xe7\x57\xa4\x38\x76\xf8\x12\xd8\xf7\x47\x85\xb2\xab\xc0\x7b\x51\x22\x82\xbe\xa4\x06\xad\xe3\x44\x3f\xed\x05\xfe\xdf\x37\xc5\x28\xf3\x06\x84\x0e\x9f\x82\xf6\x00\x63\xb2\x07\x54\x08\xa5\x1e\xc4\x1b\xa4\xda\x02\xdb\xe6\xb5\x80\xed\xa5\x1c\x63\xf6\x9f\x86\xe5\xe0\x19\xd6\x05\x48\x92\x82\xb7\xd8\xf0\x5d\xd3\x49\xba\x10\xfb\xd1\xb3\xbf\xbb\x18\x3d\x7d\x39\x78\xef\x85\xe4\x0c\xde\x67\xf0\x3e\x83\xf7\x19\xbc\xff\x2d\xf0\x7e\x1d\x12\x4f\xc0\xee\x00\x32\x7e\xde\x9d\xcc\x1d\x14\xb7\xb0\x13\x53\x8d\x93\x8e\xe4\x0e\xe8\x04\x9f\x26\xd3\x62\x0a\xb4\xe8\x5d\xc4\x8f\x3c\xe9\xa1\xcd\x86\x6f\x81\x3e\x09\xdb\x56\xbe\xae\x55\x5c\x69\x95\xe6\x08\xa5\x1d\xdd\xa2\x75\xc2\xdf\x71\xfd\x57\x4f\xf8\x07\x06\x1e\xf7\x89\x1b\x79\x1c\xc5\x8e\x56\x71\x6e\x09\x7f\x5b\x4b\x78\x49\x06\xf6\xbb\xc2\x89\xb3\x93\xab\x73\xac\xad\xee\xe2\x8e\x45\xb9\x44\xef\x0e\xe6\xfa\x2c\xa3\x97\xe7\xdf\xdf\x9b\xfb\xac\x14\x79\x5e\xe0\x89\x80\x7a\x06\xe7\xdc\xbf\x29\xd3\xcb\xec\x8e\x9e\x9c\xde\x4a\x14\xc6\xdc\xbf\x6e\x63\x29\x78\x19\x68\x9a\xdf\xcf\x0f\xc2\x2e\x48\x57\x9c\x1d\x2a\x44\x7d\x6e\x27\x74\xd6\x9c\x97\x5c\x5f\xa3\x1f\x50\x82\xc4\x58\x0a\x25\x47\x8c\xfc\xa2\xe8\xfa\xcf\x26\x6e\xe5\x29\xeb\x28\xc7\xfe\x69\xc4\x4c\x1f\x07\x64\xcd\x88\x31\x17\x0c\x1a\x7b\x3b\x47\xcc\xbb\xd0\x0f\x5d\xfa\x86\x43\x96\x16\x05\x9b\x6e\xb7\x16\x2b\xc1\x98\x74\x8e\x61\x32\x0b\x55\x15\x39\x9b\x02\xab\x24\x6e\x6b\x26\xb7\x9f\x6a\xd8\xdb\x1b\xf9\x76\x1b\x89\x16\x8c\x75\x26\xdf\x81\x98\x2f\xd4\x16\x0e\x25\xbb\xe5\x3f\x56\x50\x41\x4b\x46\xa7\x80\xc8\x7f\x9f\x69\x49\x22\xf4\x42\xeb\x35\xd8\x4a\xcb\x86\xbc\x07\x90\xbf\x05\x00\x00\xff\xff\xd2\x50\x7d\xcd\x7a\x12\x00\x00") 114 | 115 | func tmplGolangServerGogoBytes() ([]byte, error) { 116 | return bindataRead( 117 | _tmplGolangServerGogo, 118 | "tmpl/golang/server.gogo", 119 | ) 120 | } 121 | 122 | func tmplGolangServerGogo() (*asset, error) { 123 | bytes, err := tmplGolangServerGogoBytes() 124 | if err != nil { 125 | return nil, err 126 | } 127 | 128 | info := bindataFileInfo{name: "tmpl/golang/server.gogo", size: 4730, mode: os.FileMode(420), modTime: time.Unix(1467777485, 0)} 129 | a := &asset{bytes: bytes, info: info} 130 | return a, nil 131 | } 132 | 133 | // Asset loads and returns the asset for the given name. 134 | // It returns an error if the asset could not be found or 135 | // could not be loaded. 136 | func Asset(name string) ([]byte, error) { 137 | cannonicalName := strings.Replace(name, "\\", "/", -1) 138 | if f, ok := _bindata[cannonicalName]; ok { 139 | a, err := f() 140 | if err != nil { 141 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) 142 | } 143 | return a.bytes, nil 144 | } 145 | return nil, fmt.Errorf("Asset %s not found", name) 146 | } 147 | 148 | // MustAsset is like Asset but panics when Asset would return an error. 149 | // It simplifies safe initialization of global variables. 150 | func MustAsset(name string) []byte { 151 | a, err := Asset(name) 152 | if err != nil { 153 | panic("asset: Asset(" + name + "): " + err.Error()) 154 | } 155 | 156 | return a 157 | } 158 | 159 | // AssetInfo loads and returns the asset info for the given name. 160 | // It returns an error if the asset could not be found or 161 | // could not be loaded. 162 | func AssetInfo(name string) (os.FileInfo, error) { 163 | cannonicalName := strings.Replace(name, "\\", "/", -1) 164 | if f, ok := _bindata[cannonicalName]; ok { 165 | a, err := f() 166 | if err != nil { 167 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) 168 | } 169 | return a.info, nil 170 | } 171 | return nil, fmt.Errorf("AssetInfo %s not found", name) 172 | } 173 | 174 | // AssetNames returns the names of the assets. 175 | func AssetNames() []string { 176 | names := make([]string, 0, len(_bindata)) 177 | for name := range _bindata { 178 | names = append(names, name) 179 | } 180 | return names 181 | } 182 | 183 | // _bindata is a table, holding each asset generator, mapped to its name. 184 | var _bindata = map[string]func() (*asset, error){ 185 | "tmpl/.DS_Store": tmplDs_store, 186 | "tmpl/csharp/server.gocs": tmplCsharpServerGocs, 187 | "tmpl/golang/server.gogo": tmplGolangServerGogo, 188 | } 189 | 190 | // AssetDir returns the file names below a certain 191 | // directory embedded in the file by go-bindata. 192 | // For example if you run go-bindata on data/... and data contains the 193 | // following hierarchy: 194 | // data/ 195 | // foo.txt 196 | // img/ 197 | // a.png 198 | // b.png 199 | // then AssetDir("data") would return []string{"foo.txt", "img"} 200 | // AssetDir("data/img") would return []string{"a.png", "b.png"} 201 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error 202 | // AssetDir("") will return []string{"data"}. 203 | func AssetDir(name string) ([]string, error) { 204 | node := _bintree 205 | if len(name) != 0 { 206 | cannonicalName := strings.Replace(name, "\\", "/", -1) 207 | pathList := strings.Split(cannonicalName, "/") 208 | for _, p := range pathList { 209 | node = node.Children[p] 210 | if node == nil { 211 | return nil, fmt.Errorf("Asset %s not found", name) 212 | } 213 | } 214 | } 215 | if node.Func != nil { 216 | return nil, fmt.Errorf("Asset %s not found", name) 217 | } 218 | rv := make([]string, 0, len(node.Children)) 219 | for childName := range node.Children { 220 | rv = append(rv, childName) 221 | } 222 | return rv, nil 223 | } 224 | 225 | type bintree struct { 226 | Func func() (*asset, error) 227 | Children map[string]*bintree 228 | } 229 | var _bintree = &bintree{nil, map[string]*bintree{ 230 | "tmpl": &bintree{nil, map[string]*bintree{ 231 | ".DS_Store": &bintree{tmplDs_store, map[string]*bintree{}}, 232 | "csharp": &bintree{nil, map[string]*bintree{ 233 | "server.gocs": &bintree{tmplCsharpServerGocs, map[string]*bintree{}}, 234 | }}, 235 | "golang": &bintree{nil, map[string]*bintree{ 236 | "server.gogo": &bintree{tmplGolangServerGogo, map[string]*bintree{}}, 237 | }}, 238 | }}, 239 | }} 240 | 241 | // RestoreAsset restores an asset under the given directory 242 | func RestoreAsset(dir, name string) error { 243 | data, err := Asset(name) 244 | if err != nil { 245 | return err 246 | } 247 | info, err := AssetInfo(name) 248 | if err != nil { 249 | return err 250 | } 251 | err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) 252 | if err != nil { 253 | return err 254 | } 255 | err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) 256 | if err != nil { 257 | return err 258 | } 259 | err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) 260 | if err != nil { 261 | return err 262 | } 263 | return nil 264 | } 265 | 266 | // RestoreAssets restores an asset under the given directory recursively 267 | func RestoreAssets(dir, name string) error { 268 | children, err := AssetDir(name) 269 | // File 270 | if err != nil { 271 | return RestoreAsset(dir, name) 272 | } 273 | // Dir 274 | for _, child := range children { 275 | err = RestoreAssets(dir, filepath.Join(name, child)) 276 | if err != nil { 277 | return err 278 | } 279 | } 280 | return nil 281 | } 282 | 283 | func _filePath(dir, name string) string { 284 | cannonicalName := strings.Replace(name, "\\", "/", -1) 285 | return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) 286 | } 287 | 288 | -------------------------------------------------------------------------------- /tmpl/csharp/server.gocs: -------------------------------------------------------------------------------- 1 | {{$tmpldata := . -}} 2 | /** 3 | * This file is autogenerated by ezrpc 4 | * Don't change manually 5 | */ 6 | 7 | using EZRPC.Nats; 8 | using NATS.Client; 9 | using System; 10 | using Thrift.Transport; 11 | 12 | namespace {{.Namespace}} { 13 | static class {{.Service.Name}}Service { 14 | // Processor is generated by generator 15 | private static {{.Service.Name}}.Processor processor_; 16 | private static IConnection conn_; 17 | 18 | public static void init({{.Service.Name}}.Processor processor, IConnection c) { 19 | processor_ = processor; 20 | conn_ = c; 21 | 22 | c.SubscribeAsync("On.{{.Service.Name}}.*", onBroadcast); 23 | c.SubscribeAsync("{{.Service.Name}}.*", "ezrpc", onMsg); 24 | } 25 | 26 | private static void onMsg(object sender, MsgHandlerEventArgs e) { 27 | var trans = new Thrift.Transport.TMemoryBuffer(e.Message.Data); 28 | var iprot = new EZProto(trans); // in 29 | try { 30 | TMemoryBuffer buf; 31 | EZProto oprot; 32 | Msg msg; 33 | 34 | switch (e.Message.Subject) { 35 | {{- range $name, $method := .Service.Methods}} 36 | {{- if not (Utils.IsBroadcastMethod $method)}} 37 | case "{{$tmpldata.Service.Name}}.{{$name}}": 38 | {{- if $method.Oneway}} 39 | processor_.{{$name}}_Process(0, iprot, null); 40 | {{- else}} 41 | buf = new Thrift.Transport.TMemoryBuffer(); 42 | oprot = new EZProto(buf); // out 43 | 44 | processor_.{{$name}}_Process(0, iprot, oprot); 45 | 46 | msg = new Msg(); 47 | msg.Subject = e.Message.Reply; 48 | msg.Data = buf.GetBuffer(); 49 | 50 | ((IAsyncSubscription)sender).Connection.Publish(msg); 51 | {{- end}} 52 | break; 53 | {{- end}} 54 | {{- end}} 55 | } 56 | } catch (Exception ex) { 57 | Console.WriteLine(ex.ToString()); 58 | } 59 | } 60 | 61 | private static void onBroadcast(object sender, MsgHandlerEventArgs e) { 62 | var trans = new Thrift.Transport.TMemoryBuffer(e.Message.Data); 63 | var iprot = new EZProto(trans); // in 64 | try { 65 | switch (e.Message.Subject) { 66 | {{- range $name, $method := .Service.Methods}} 67 | {{- if Utils.IsBroadcastMethod $method}} 68 | case "On.{{$tmpldata.Service.Name}}.{{$name}}": 69 | processor_.{{$name}}_Process(0, iprot, null); 70 | break; 71 | {{- end}} 72 | {{- end}} 73 | } 74 | } catch (Exception ex) { 75 | Console.WriteLine(ex.ToString()); 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tmpl/golang/server.gogo: -------------------------------------------------------------------------------- 1 | {{define "server" -}} 2 | package {{.Namespace}} 3 | {{$serviceName := .Service.Name}} 4 | import ( 5 | "bytes" 6 | "log" 7 | 8 | "github.com/Wuvist/go-thrift/thrift" 9 | "github.com/ezbuy/ezrpc/ezrpc" 10 | "github.com/ezbuy/statsd" 11 | "github.com/nats-io/nats" 12 | ) 13 | 14 | type ThriftNats{{$serviceName}}Server struct { 15 | Server *{{$serviceName}}Server 16 | Conn *nats.Conn 17 | DirectKey string 18 | Daemon *ezrpc.Daemon 19 | } 20 | 21 | func (s *ThriftNats{{$serviceName}}Server) onMsg(msg *nats.Msg) { 22 | {{- if .HasNormalMsgMethod}} 23 | r := thrift.NewCompactProtocolReader(bytes.NewReader(msg.Data)) 24 | 25 | switch msg.Subject { 26 | {{- range $method := .Service.Methods}} 27 | {{- if Utils.IsNormalMethod $method}} 28 | case "{{$serviceName}}.{{$method.Name}}": 29 | {{- if $method.Oneway}} 30 | p := &{{$serviceName}}{{$method.Name}}Request{} 31 | err := thrift.DecodeStruct(r, p) 32 | if err != nil { 33 | log.Printf("[NATS]-[{{$serviceName}}.{{$method.Name}}] decode error: %s", err) 34 | } 35 | err = s.Server.{{$method.Name}}(p) 36 | if err != nil { 37 | log.Printf("[NATS]-[{{$serviceName}}.{{$method.Name}}] service error: %s", err) 38 | } 39 | 40 | statsd.Incr("{{$serviceName}}.{{$method.Name}}.count") 41 | {{- else}} 42 | t1 := statsd.Now() 43 | 44 | p := &{{$serviceName}}{{$method.Name}}Request{} 45 | res := &{{$serviceName}}{{$method.Name}}Response{} 46 | err := thrift.DecodeStruct(r, p) 47 | if err != nil { 48 | log.Printf("[NATS]-[{{$serviceName}}.{{$method.Name}}] decode error: %s", err) 49 | } 50 | err = s.Server.{{$method.Name}}(p, res) 51 | if err != nil { 52 | log.Printf("[NATS]-[{{$serviceName}}.{{$method.Name}}] service error: %s", err) 53 | } 54 | 55 | buf := &bytes.Buffer{} 56 | w := thrift.NewCompactProtocolWriter(buf) 57 | thrift.EncodeStruct(w, res) 58 | s.Conn.Publish(msg.Reply, buf.Bytes()) 59 | 60 | t2 := statsd.Now() 61 | statsd.Timing("{{$serviceName}}.{{$method.Name}}.timing", t1, t2) 62 | {{- end}} 63 | {{- end}} 64 | {{- end}} 65 | } 66 | {{- end}} 67 | } 68 | 69 | {{- if Utils.HasDirectMethod .Service}} 70 | 71 | func (s *ThriftNats{{$serviceName}}Server) onDirect(msg *nats.Msg) { 72 | r := thrift.NewCompactProtocolReader(bytes.NewReader(msg.Data)) 73 | keylen := len(s.DirectKey) 74 | switch msg.Subject[keylen+1:] { 75 | {{- range $method := .Service.Methods}} 76 | {{- if Utils.IsDirectMethod $method}} 77 | case "{{$serviceName}}.{{$method.Name}}": 78 | {{- if $method.Oneway}} 79 | p := &{{$serviceName}}{{$method.Name}}Request{} 80 | err := thrift.DecodeStruct(r, p) 81 | if err != nil { 82 | log.Printf("[NATS]-[{{$serviceName}}.{{$method.Name}}] decode error: %s", err) 83 | } 84 | err = s.Server.{{$method.Name}}(p) 85 | if err != nil { 86 | log.Printf("[NATS]-[{{$serviceName}}.{{$method.Name}}] service error: %s", err) 87 | } 88 | 89 | statsd.Incr("{{$serviceName}}.{{$method.Name}}.count") 90 | {{- else}} 91 | t1 := statsd.Now() 92 | 93 | p := &{{$serviceName}}{{$method.Name}}Request{} 94 | res := &{{$serviceName}}{{$method.Name}}Response{} 95 | err := thrift.DecodeStruct(r, p) 96 | if err != nil { 97 | log.Printf("[NATS]-[{{$serviceName}}.{{$method.Name}}] decode error: %s", err) 98 | } 99 | err = s.Server.{{$method.Name}}(p, res) 100 | if err != nil { 101 | log.Printf("[NATS]-[{{$serviceName}}.{{$method.Name}}] service error: %s", err) 102 | } 103 | 104 | buf := &bytes.Buffer{} 105 | w := thrift.NewCompactProtocolWriter(buf) 106 | thrift.EncodeStruct(w, res) 107 | s.Conn.Publish(msg.Reply, buf.Bytes()) 108 | 109 | t2 := statsd.Now() 110 | statsd.Timing("{{$serviceName}}.{{$method.Name}}.timing", t1, t2) 111 | {{- end}} 112 | {{- end}} 113 | {{- end}} 114 | } 115 | } 116 | 117 | func (s *ThriftNats{{$serviceName}}Server) SetDirectKey(key string) { 118 | s.DirectKey = key 119 | s.Daemon.Subscribe(key+".{{$serviceName}}.*", s.onDirect) 120 | } 121 | {{- end}} 122 | 123 | func (s *ThriftNats{{$serviceName}}Server) onBroadcast(msg *nats.Msg) { 124 | {{- if .HasBroadcastMethod}} 125 | r := thrift.NewCompactProtocolReader(bytes.NewReader(msg.Data)) 126 | 127 | switch msg.Subject { 128 | {{- range $method := .Service.Methods}} 129 | {{- if Utils.IsBroadcastMethod $method}} 130 | case "On.{{$serviceName}}.{{$method.Name}}": 131 | p := &{{$serviceName}}{{$method.Name}}Request{} 132 | err := thrift.DecodeStruct(r, p) 133 | if err != nil { 134 | log.Printf("[NATS]-[{{$serviceName}}.{{$method.Name}}] decode error: %s", err) 135 | } 136 | err = s.Server.{{$method.Name}}(p) 137 | if err != nil { 138 | log.Printf("[NATS]-[{{$serviceName}}.{{$method.Name}}] service error: %s", err) 139 | } 140 | 141 | statsd.Incr("On.{{$serviceName}}.{{$method.Name}}.count") 142 | {{- end}} 143 | {{- end}} 144 | } 145 | {{- end}} 146 | } 147 | 148 | func New{{$serviceName}}Server(impl {{$serviceName}}, conn *nats.Conn, middlewares ...ezrpc.MsgMiddleware) *ThriftNats{{$serviceName}}Server { 149 | daemon := ezrpc.NewDaemonWithConn(conn, middlewares...) 150 | 151 | s := &{{$serviceName}}Server{Implementation: impl} 152 | 153 | server := &ThriftNats{{$serviceName}}Server{ 154 | Server: s, 155 | Conn: conn, 156 | Daemon: daemon, 157 | } 158 | 159 | // all broadcast messages should be under namespace 'On' 160 | daemon.Subscribe("On.{{$serviceName}}.*", server.onBroadcast) 161 | daemon.QueueSubscribe("{{$serviceName}}.*", "ezrpc", server.onMsg) 162 | 163 | return server 164 | } 165 | {{- end}} 166 | --------------------------------------------------------------------------------