远程进程调用(Remote Procedure Call,RPC)是构建分布式系统时所使用的一种常见组件。它使得客户端可以像进行本地调用一样进行远程的过程调用,即客户端可以忽略消息传递的细节,而专注于过程调用的效果。
Portal RPC是Lustre 的RPC组件,它构建于LNET之上,提供客户端和服务器之间进行消息通信的接口。
2.1 消息通信连接的建立
进行Portal RPC通信的是位于服务器和客户端的两个消息终端。服务器上的消息终端称为出口端(export),而客户端上的消息中断称为入口端(import)。
入口端是通过类型为obd_import的对象描述的。这个对象由class_new_import()函数新建。MGC、MDC、OSC的启动函数mgc_setup()、mdc_setup()和osc_setup(),都会调用一个通用的函数client_obd_setup()。在这个函数里,将会调用class_new_import()函数为该客户端新建一个入口端。
在客户端新建了入口端之后,为了完成与服务器的连接,会紧接着调用client_connect_import()函数向对应服务器发送一个请求消息。请求消息由类型为ptlrpc_request的对象描述,client_connect_import()函数小心地设置它的rq_import字段,这样当回复被接收到后,就可以知道该回复是属于哪个入口端了。
与入口端相对的出口端使用类型为obd_export的对象来描述。在服务端,对来自每个客户端的连接,都有一个对应的出口对象。这个出口对象并不在事先创建好,而是在接收到客户端连接请求时新建。
MGS、MDT、OST的在处理连接请求时都会调用通用的target_handle_connect()函数。在这个函数中,因为存在客户端的连接恢复等情况,因此需要进行复杂的处理。在此,我们只分析最简单的情形,即服务端首次接收到来自该客户端的连接请求。在这种情况下,该函数通过obd_connect()这个通用的封装函数,调用各服务定制的连接函数,如mgs_connect、mdt_obd_connect和filter_connect。这些函数又会调用通用的class_connect()函数,最终通过class_new_export()函数新建一个出口端。这样,在连接请求的处理过程中,该入口端对应的出口端就被创建完成了。随后,target_handle_connect()函数将会把回复消息的句柄通过lustre_msg_set_handle()设置为出口端的句柄。
当服务端的这个连接回复消息被客户端接收到时,作为在client_connect_import()中被注册的处理函数,ptlrpc_connect_interpret()将被调用。这个函数使用lustre_msg_get_handle()函数从回复消息中取得服务端设置的句柄,作为远端句柄(remotehandle)放在obd_import对象的imp_remote_handle字段中。自此以后,客户端通过ptl_send_rpc()函数发送消息时,都用lustre_msg_set_handle()函数把消息的句柄设置为远端句柄的值。这样,在服务端接收到在请求消息后,请求处理函数ptlrpc_server_handle_req_in()可以从消息中获得这个句柄,并由此取得对应的出口端。
2.2 消息的发送与接收
Portal RPC既支持同步的消息通信也支持异步的消息通信。在同步消息通信中,请求发送进程挂起直至消息的回复接受完成。在异步消息通信中,请求发送者不等待回复,而交由Portal RPC的守护进程处理回复。通信可以以单一消息或消息集合的方式进行。
下表给出了可以用来发送消息的一些函数。
表 RPC发送函数列表
函数 |
描述 |
ptl_send_rpc() |
发送RPC的一种简单形式,不等待回复,在失败发生时也不重发。这不是一个发送RPC的优选方法。 |