调用中间件
调用中间件的形式为:
- func(
- name string,
- args []reflect.Value,
- context Context,
- next NextInvokeHandler) (results []reflect.Value, err error) {
- ...
- results, err = next(name, args, context)
- ...
- return results, err
- }
name
是调用的远程函数/方法名。
args
是调用参数,引用传递的数组。
context
是调用上下文对象。
next
表示下一个中间件。通过调用 next
将各个中间件串联起来。
在调用 next
之前的操作在调用发生前执行,在调用 next
之后的操作在调用发生后执行,如果你不想修改返回结果,你应该将 next
的返回值作为该中间件的返回值返回。
如果在调用 next
之前的操作中发生 panic,则 next
和后面的代码都不会被执行而是直接返回上一层,panic 信息作为 error 结果返回给上一层中间件。
调用中间件可以是普通函数/方法,也可以是匿名函数。
跟踪调试
我们来看一个例子:
- package main
- import (
- "fmt"
- "reflect"
- "github.com/hprose/hprose-golang/rpc"
- )
- func logInvokeHandler(
- name string,
- args []reflect.Value,
- context rpc.Context,
- next rpc.NextInvokeHandler) (results []reflect.Value, err error) {
- fmt.Printf("%s(%v) = ", name, args)
- results, err = next(name, args, context)
- fmt.Printf("%v %v\r\n", results, err)
- return
- }
- func hello(name string) string {
- return "Hello " + name + "!"
- }
- type HelloService struct {
- Hello func(string) (string, error)
- Hi func(string) error
- }
- func main() {
- server := rpc.NewTCPServer("")
- server.AddInvokeHandler(logInvokeHandler)
- server.AddFunction("hello", hello)
- server.Handle()
- client := rpc.NewClient(server.URI())
- var helloService *HelloService
- client.UseService(&helloService)
- helloService.Hello("World")
- helloService.Hi("World")
- client.Close()
- server.Close()
- }
运行该程序,我们会看到如下结果:
Hello([World]) = [Hello World!] <nil>
Hi([<interface {} Value>]) = [] Can't find this method Hi
如果把:
- server.AddInvokeHandler(logInvokeHandler)
去掉,在创建客户端之后,加上下面这句:
- client.AddInvokeHandler(logInvokeHandler)
再运行该程序,我们会看到如下输出:
Hello([World]) = [Hello World!] <nil>
Hi([World]) = [] Can't find this method Hi
我们会发现,关于 Hello
方法调用的输出是一样的,关于 Hi
方法的调用,参数上不太一样。原因是客户端的参数总是跟调用时带入的参数值类型一致的,而在服务器端的参数类型是跟服务器端服务函数/方法的参数定义类型一致,而 Hi
方法在服务器并没有定义,因此,在反序列化时,是按照 interface{}
类型反序列化的,所以在服务器端看到的是一个 interface{}
类型的值。