基于hprose-golang创建RPC微服务
官网:https://hprose.com/ 本文将讲解如何使用
使用Go实现服务端初始化git初始化: git init echo "main" >> .gitignore echo "# hprose-sample" >> README.md 项目使用 go mod init sample 最终项目目录结构一览: ├── config │?? └── rd.ini ├── dao ├── main.go ├── model └── util ├── config.go └── state.go ├── service │?? └── sample.go ├── go.mod ├── go.sum ├── client_test.go ├── README.md ├── php ├── logs golang写微服务的好处就是我们可以按照自己理想的目录结构写代码,而无需关注代码 配置项我们使用 项目地址:https://github.com/go-ini/ini 这个库使用起来很简单,文档完善。有2种用法,一种是直接加载配置文件,一种是将配置映射到结构体,使用面向对象的方法获取配置。这里我们采用第二种方案。 首先在 ListenAddr = 0.0.0.0:8080 [Mysql] Host = localhost Port = 3306 User = root Password = Database = sample [Redis] Host = localhost Port = 6379 Auth = 编写 package util import "github.com/go-ini/ini" type MysqlCfg struct{ Host string Port int32 User string Password string Database string } type RedisCfg struct{ Host string Port int32 Auth string } type Config struct { ListenAddr string Mysql MysqlCfg Redis RedisCfg } //全局变量 var Cfg Config //加载配置 func InitConfig(ConfigFile string) error { return ini.MapTo(Cfg,ConfigFile) } main.go这里我们需要实现项目初始化、服务注册到RPC并启动一个TCP server。 package main import ( "flag" "fmt" "github.com/hprose/hprose-golang/rpc" "sample/service" "sample/util" ) func hello(name string) string { return "Hello " + name + "!" } func main() { //解析命令行参数 configFile := flag.String("c","config/rd.ini","config file") flag.Parse() err := util.InitConfig(*configFile) if err != nil { fmt.Printf("load config file fail,err:%vn",err) return } fmt.Printf("server is running at %sn",util.Cfg.ListenAddr) //tcp,推荐 server := rpc.NewTCPServer("tcp4://" + util.Cfg.ListenAddr + "/") //注册func server.AddFunction("hello",hello) //注册struct,命名空间是Sample server.AddInstanceMethods(&service.SampleService{},rpc.Options{NameSpace: "Sample"}) err = server.Start() if err != nil { fmt.Printf("start server fail,err) return } } 我们看到,RPC里注册了一个函数
其中
package service import ( "sample/model" "sample/util" ) //定义服务 type SampleService struct { } //服务里的方法 func (this *SampleService) GetUserInfo(uid int64) util.State { var state util.State if uid <= 0 { return state.SetErrCode(1001).SetErrMsg("uid不正确").End() } var user model.User user.Id = uid user.Name = "test" return state.SetData(user).End() } 日志作为一个线上项目,我们需要在业务代码里打印一些日志辅助我们排查问题。日志这里直接使用 package util import ( "errors" "fmt" "github.com/astaxie/beego/logs" ) var Logger *logs.BeeLogger func InitLog() error { Logger = logs.NewLogger(10) err := Logger.SetLogger(logs.AdapterMultiFile,fmt.Sprintf(`{"filename":"/work/git/hprose-sample/logs/main.log","daily":true,"maxdays":7,"rotate":true}`)) if err != nil { return errors.New("init beego log error:" + err.Error()) } Logger.Async(1000) return nil } 这里定义里全局变量
使用示例: if uid <= 0 { util.Logger.Debug("uid error. uid:%d",uid) } Go测试用例每个项目都应该写测试用例。下面的用例里,我们将测试上面注册的服务是否正常。 package main import ( "github.com/hprose/hprose-golang/rpc" "sample/util" "testing" ) //stub:申明服务里拥有的方法 type clientStub struct { Hello func(string) string GetUserInfo func(uid int64) util.State } //获取一个客户端 func GetClient() *rpc.TCPClient { return rpc.NewTCPClient("tcp4://127.0.0.1:8050") } //测试服务里的方法 func TestSampleService_GetUserInfo(t *testing.T) { client := GetClient() defer client.Close() var stub clientStub client.UseService(&stub,"Sample") //使用命名空间 rep := stub.GetUserInfo(10001) if rep.ErrCode > 0 { t.Error(rep.ErrMsg) } else { t.Log(rep.Data) } } //测试普通方法 func TestHello(t *testing.T) { client := GetClient() defer client.Close() var stub clientStub client.UseService(&stub) rep := stub.Hello("func") if rep == "" { t.Error(rep) } else { t.Log(rep) } } 运行: $ go test -v === RUN TestSampleService_GetUserInfo --- PASS: TestSampleService_GetUserInfo (0.00s) client_test.go:31: map[name:test id:10001] === RUN TestHello --- PASS: TestHello (0.00s) client_test.go:47: Hello func! PASS ok sample 0.016s PHP调用php-client需要先下载 composer config repo.packagist composer https://packagist.phpcomposer.com composer require "hprose/hprose:^2.0"
<?php include "vendor/autoload.php"; try{ $TcpServerAddr = "tcp://127.0.0.1:8050"; $client = HproseSocketClient::create($TcpServerAddr,false); $service = $client->useService('','Sample'); $rep = $service->GetUserInfo(10); print_r($rep); } catch (Exception $e){ echo $e->getMessage(); } 运行: $ php php/client.php stdClass Object ( [errCode] => 0 [errMsg] => [data] => stdClass Object ( [id] => 10 [name] => test ) ) 实际使用时最好对该处调用的代码做进一步的封装,例如实现异常捕获、返回码转换、日志打印等等。 编写codetips
hprose 不支持一键生成各语言的客户端代码(没有IDL支持),在写代码的时候PHP编译器没法提示。我们可以写一个类或者多个类,主要是Model类和Service类:
示例: class SampleService { /** * 获取用户信息 * @param int $uid * @return State */ public function GetUserInfo(int $uid): State { } } 调用的地方(请使用phpStorm查看提示效果): /** * @return SampleService * @throws Exception */ function getClient() { $TcpServerAddr = "tcp://127.0.0.1:8050"; $client = HproseSocketClient::create($TcpServerAddr,'Sample'); return $service; } try { $client = getClient(); $rep = $client->GetUserInfo(10); echo $rep->errCode . PHP_EOL; print_r($rep); } catch (Exception $e) { echo $e->getMessage(); } 方法 部署线上微服务需要后台长期稳定运行,可以使用 如果还没有安装,请餐参考:Supervisor使用教程。 新增一个常驻任务,需要新建配置。 以上述sample为例,新建配置: [program:go_hprose_sample] command=/usr/local/bin/go /work/git/hprose-sample/main priority=999 ; the relative start priority (default 999) autostart=true ; start at supervisord start (default: true) autorestart=true ; retstart at unexpected quit (default: true) startsecs=10 ; number of secs prog must stay running (def. 10) startretries=3 ; max # of serial start failures (default 3) exitcodes=0,2 ; 'expected' exit codes for process (default 0,2) stopsignal=QUIT ; signal used to kill process (default TERM) stopwaitsecs=10 ; max num secs to wait before SIGKILL (default 10) user=root ; setuid to this UNIX account to run the program log_stdout=true log_stderr=true ; if true,log program stderr (def false) logfile=/work/git/hprose-sample/logs/supervisor/go_hprose_sample.log logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) logfile_backups=10 ; # of logfile backups (default 10) stdout_logfile_maxbytes=20MB ; stdout 日志文件大小,默认 50MB stdout_logfile_backups=20 ; stdout 日志文件备份数 stdout_logfile=/work/git/hprose-sample/logs/supervisor/go_hprose_sample.stdout.log
然后启动任务: supervisorctl reread supervisorctl update supervisorctl start go_hprose_sample 线上部署最少要2台机器,组成负载均衡。这样当升级的时候,可以一台一台的上线,避免服务暂停。 Hprose 总结优点:
缺点:
参考1、Supervisor使用教程 - 飞鸿影 - 博客园 https://www.cnblogs.com/52fhy/p/10161253.html 2、Home · hprose/hprose-golang Wiki https://github.com/hprose/hprose-golang/wiki 3、go-ini/ini: 超赞的 Go 语言 INI 文件操作 https://ini.unknwon.io/ 4、golang中os/exec包用法 https://www.cnblogs.com/vijayfly/p/6102470.html (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |