服务器端开发

前面两章已经看到了简单的服务器的开发,接下来的两章我们将了解的服务器和客户端更详细的设置和开发。

服务器提供了几种启动服务器的方法:

  1. func (s *Server) Serve(network, address string)
  2. func (s *Server) ServeByHTTP(ln net.Listener, rpcPath string)
  3. func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request)
  4. func (s *Server) ServeListener(ln net.Listener)
  5. func (s *Server) ServeTLS(network, address string, config *tls.Config)
  6. func (s *Server) Start(network, address string)
  7. func (s *Server) StartTLS(network, address string, config *tls.Config)

ServeXXX方法的方法会阻塞当前的goroutine,如果不想阻塞当前的goroutine,可以调用StartXXX方法。

一些例子:

  1. ln, _ := net.Listen("tcp", "127.0.0.1:0")
  2. go s.ServeByHTTP(ln, "foo")
  1. server.Start("tcp", "127.0.0.1:0")

RegisterName用来注册服务,可以指定服务名和它的元数据。

  1. func (s *Server) RegisterName(name string, service interface{}, metadata ...string)

另外Server还提供其它几个方法。NewServer用来创建一个新的Server对象。

  1. func NewServer() *Server

Address返回Server监听的地址。如果你设置的时候设置端口为0,则Go会选择一个合适的端口号作为监听的端口号,通过这个方法可以返回实际的监听地址和端口。

  1. func (s *Server) Address() string

Close则关闭监听,停止服务。

  1. func (s *Server) Close() error

Auth提供一个身份验证的方法,它在你需要实现服务权限设置的时候很有用。
客户端会将一个身份验证的token传给服务器,但是rpcx并不限制你采用何种验证方式,普通的用户名+密码的明文也可以,OAuth2也可以,只要客户端和服务器端协商好一致的验证方式即可。
rpcx会将解析的验证token,服务名称以及额外的信息传给下面的设置的方法AuthorizationFunc,你需要实现这个方法。比如通过OAuth2的方式,解析出access_token,然后检查这个access_toekn是否对这个服务有授权。

  1. func (s *Server) Auth(fn AuthorizationFunc) error

我们可以看一个例子,服务器的代码如下:

  1. func main() {
  2. server := rpcx.NewServer()
  3. fn := func(p *rpcx.AuthorizationAndServiceMethod) error {
  4. if p.Authorization != "0b79bab50daca910b000d4f1a2b675d604257e42" || p.Tag != "Bearer" {
  5. fmt.Printf("error: wrong Authorization: %s, %s\n", p.Authorization, p.Tag)
  6. return errors.New("Authorization failed ")
  7. }
  8. fmt.Printf("Authorization success: %+v\n", p)
  9. return nil
  10. }
  11. server.Auth(fn)
  12. server.RegisterName("Arith", new(Arith)) server.Serve("tcp", "127.0.0.1:8972")}

这个简单的例子演示了只有用户使用”Bear 0b79bab50daca910b000d4f1a2b675d604257e42” acces_token 访问时才提供服务,否则报错。

我们可以看一下客户端如何设置这个access_token:

  1. func main() {
  2. s := &rpcx.DirectClientSelector{Network: "tcp", Address: "127.0.0.1:8972", DialTimeout: 10 * time.Second}
  3. client := rpcx.NewClient(s)
  4. //add Authorization info
  5. err := client.Auth("0b79bab50daca910b000d4f1a2b675d604257e42_ABC", "Bearer")
  6. if err != nil {
  7. fmt.Printf("can't add auth plugin: %#v\n", err)
  8. }
  9. args := &Args{7, 8}
  10. var reply Reply
  11. err = client.Call("Arith.Mul", args, &reply)
  12. if err != nil {
  13. fmt.Printf("error for Arith: %d*%d, %v \n", args.A, args.B, err)
  14. } else {
  15. fmt.Printf("Arith: %d*%d=%d \n", args.A, args.B, reply.C)
  16. }
  17. client.Close()
  18. }

客户端很简单,调用Auth方法设置access_token和token_type(optional)即可。

rpcx创建了一个缺省的Server,并提供了一些辅助方法来暴露Server的方法,因此你也可以直接调用rpcx.XXX方法来调用缺省的Server的方法。

Server是一个struct类型,它还包含一些有用的字段:

  1. type Server struct {
  2. ServerCodecFunc ServerCodecFunc //PluginContainer must be configured before starting and Register plugins must be configured before invoking RegisterName method
  3. PluginContainer IServerPluginContainer
  4. //Metadata describes extra info about this service, for example, weight, active status Metadata string
  5. Timeout time.Duration
  6. ReadTimeout time.Duration
  7. WriteTimeout time.Duration
  8. // contains filtered or unexported fields
  9. }

ServerCodecFunc用来设置序列化方法。
PluginContainer是插件容器,服务器端设置的插件都要加入到这个容器之中,比如注册中心、日志、监控、限流等等。
你还可以设置超时的值。超时的值很容易被忽视,但是在实际的开发应用中却非常的有用和必要。因为经常会遇到网络的一些意外的状况,如果不设置超时,很容易导致服务器性能的下降。