1.. Licensed to the Apache Software Foundation (ASF) under one 2 or more contributor license agreements. See the NOTICE file 3 distributed with this work for additional information 4 regarding copyright ownership. The ASF licenses this file 5 to you under the Apache License, Version 2.0 (the 6 "License"); you may not use this file except in compliance 7 with the License. You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, 12 software distributed under the License is distributed on an 13 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 KIND, either express or implied. See the License for the 15 specific language governing permissions and limitations 16 under the License. 17 18.. include:: ../../common.defs 19 20.. highlight:: cpp 21.. default-domain:: cpp 22 23.. _developer-doc-rpc: 24 25.. |TServer| replace:: :program:`traffic_server` 26.. |TManager| replace:: :program:`traffic_manager` 27.. |TCtl| replace:: :program:`traffic_ctl` 28.. |LM| replace:: :class:`LocalManager` 29.. |PM| replace:: :class:`ProcessManager` 30 31RPC 32*** 33 34======== 35Overview 36======== 37 38In order for programs in different address spaces (remote clients and |TManager|) to communicate with |TServer|, there is a RPC mechanism in :ts:git:`mgmt/`. 39 40This is a simple serialization style RPC which runs over unix domain sockets. The following sections will detail the runtime structure, serialization mechanisms and how messages are passed from remote clients to |TServer|. 41 42 43================= 44Runtime Structure 45================= 46 47.. uml:: 48 :align: center 49 50 hide empty members 51 52 node "traffic_manager" 53 node "traffic_server" 54 55 [traffic_ctl] <-d-> [traffic_manager] : Remote RPC 56 [other remote clients] <-u-> [traffic_manager] : Remote RPC 57 [traffic_manager] <-r-> [traffic_server] : Local RPC 58 [traffic_server] <-r-> [plugin] : Hook 59 60|TManager| opens a unix domain socket to receive commands from remote clients. |TManager| also has a unix domain socket connection with |TServer| to communicate. 61 62=============== 63Message Passing 64=============== 65 66Sequence diagram for a command sent from |TCtl| to when it is received by a plugin. 67 68.. figure:: ../../uml/images/RPC-sequence-diagram.svg 69 70.. note:: 71 72 Currently a fire and forget model. traffic_manager sends out an asynchronous signal without any acknowledgment. It then proceeds to send a response itself. 73 74======================= 75Remote RPC vs Local RPC 76======================= 77 78The RPC API for remote clients, such as |TCtl|, etc, is different from the RPC API used between |TManager| and |TServer|. 79 80|TManager| acts like a bridge for remote clients to interact with |TServer|. Thus, it is currently impossible to do things like have |TCtl| directly send messages to |TServer|. Classes suffixed with "Remote", ie. :ts:git:`CoreAPIRemote.cc`, and classes suffixed with "Local", ie. :ts:git:`NetworkUtilsLocal.cc` are for remote and local clients, respectively. The following sections will note which set of RPC's are relevant. 81 82======================= 83Serialization Mechanism 84======================= 85 86.. class:: MgmtMarshall 87 88 This is the class used to marshall data objects. It provides functions to marshall and unmarshall data. Each data object is associated with a field. Fields are of :type:`MgmtMarshallType`: 89 90 - **MGMT_MARSHALL_INT** : 4 bytes. 91 - **MGMT_MARSHALL_LONG** : 8 bytes. 92 - **MGMT_MARSHALL_STRING** : 4 bytes to indicate the string size in bytes, followed by the entire string and NULL terminator. 93 - **MGMT_MARSHALL_DATA** : 4 byt es to indicate data size in bytes, followed by the entire data object. 94 95When data is actually sent over a connection it must first be serialized. This is the general serialization mechanism for RPC communication. 96 97Generally, for remote clients sending messages to |TServer|, the message is serialized using remote RPC APIs. The serialized message is sent to |TManager| and |TManager| then simply relays the message to |TServer|. |TServer| eventually unserializes the message. 98 99Details for how |TManager| and |TServer| communicate are documented in the next section. 100 101Marshalling: 102============ 103 104 .. function:: ssize_t mgmt_message_marshall(void *buf, size_t remain, const MgmtMarshallType *fields, unsigned count, ...) 105 106 Variable argument wrapper for ``mgmt_message_marshall_v``. Allows for different data types to be marshalled together as long as a field is specified for each data object. Arguments should be references to objects to be marshalled. 107 108 .. function:: ssize_t mgmt_message_marshall_v(void *buf, size_t remain, const MgmtMarshallType *fields, unsigned count, va_list ap) 109 110 This function goes through all the data objects and serializes them together into a buffer. Based on the field, the number of bytes is determined and if there is enough space, it is written into the buffer. 111 112Unmarshalling: 113============== 114 115 .. function:: ssize_t mgmt_message_parse(const void *buf, size_t len, const MgmtMarshallType *fields, unsigned count, ...) 116 117 Variable argument wrapper for ``mgmt_message_parse_v``. Reference to data object to store unmarshalled message needed for variable arguments. 118 119 .. function:: ssize_t mgmt_message_parse_v(const void *buf, size_t len, const MgmtMarshallType *fields, unsigned count, va_list ap) 120 121 This function parses all the serialized. Based on the field, the number of bytes to be read is determined and copied into a ``MgmtMarshallAnyPtr``. 122 123=================== 124Local Serialization 125=================== 126 127A RPC message is sent as a :class:`MgmtMessageHdr` followed by the serialized data in bytes. Serialization is very simple as the interface for communication between |TManager| and |TServer| only allows for :class:`MgmtMessageHdr`, which is a fixed size, and raw data in the form of :code:`char*` to be sent. A header specifies a :arg:`msg_id` and the :arg:`data_len`. On a read, the header is *always* first read. Based on the length of the data payload, the correct number of bytes is then read from the socket. On a write, the header is first populated and sent on the socket, followed by the raw data. 128 129.. class:: MgmtMessageHdr 130 131 .. member:: int msg_id 132 133 ID for the event or signal to be sent. 134 135 .. member:: int data_len 136 137 Length in bytes of the message. 138 139.. uml:: 140 141 |Write| 142 start 143 :populate msg_id; 144 :populate data_len; 145 |#LightGreen|Socket| 146 :send MgmtMessageHdr; 147 |Write| 148 :get the raw data; 149 note left : casts from\nchar* to void* 150 |Socket| 151 :send raw data; 152 |Read| 153 :different address space; 154 : ...\nsome time later; 155 |Socket| 156 :read MgmtMessageHdr; 157 |Read| 158 :get data_len; 159 |Socket| 160 :read data_len bytes; 161 |Read| 162 :choose callback based on msg_id\nand send raw data; 163 stop 164 165========================== 166RPC API for remote clients 167========================== 168 169:ts:git:`NetworkMessage.cc` provides a set of APIs for remote clients to communicate with |TManager|. 170 171|TManager| will then send a response with the return value. The return value only indicates if the request was successfully propagated to |TServer|. Thus, there is no way currently for |TServer| to send information back to remote clients. 172 173.. function:: TSMgmtError send_mgmt_request(const mgmt_message_sender &snd, OpType optype, ...) 174.. function:: TSMgmtError send_mgmt_request(int fd, OpType optype, ...) 175 176 Send a request from a remote client to |TManager|. 177 178.. function:: TSMgmtError send_mgmt_response(int fd, OpType optype, ...) 179 180 Send a response from |TManager| to remote client. 181 182.. function:: TSMgmtError send_mgmt_error(int fd, OpType optype, TSMgmtError error) 183 184 Send err from |TManager| to remote client. 185 186.. function:: TSMgmtError recv_mgmt_request(void *buf, size_t buflen, OpType optype, ...) 187 188 Read request from remote client. Used by |TManager|. 189 190.. function:: TSMgmtError recv_mgmt_response(void *buf, size_t buflen, OpType optype, ...) 191 192 Read response from |TManager|. Used by remote clients. 193 194Using Remote RPC API Example 195============================ 196 197This details how a remote client may use the :ts:git:`NetworkMessage.cc` interface to send messages to |TManager|. Leveraging :func:`send_mgmt_request` with a :class:`mgmt_message_sender` which specifies how *send* a message, a remote client can implement its own way to send messages to |TManager| to allow for things such as preprocessing messages. 198 199.. class:: mgmt_message_sender 200 201.. class:: mgmtapi_sender : public mgmt_message_sender 202 203 .. function:: TSMgmtError send(void *msg, size_t msglen) const override 204 205 Specifies how the message is to be sent. Can do things such as serialization or preprocessing based on parameters. 206 207.. code-block:: cpp 208 209 #define MGMTAPI_SEND_MESSAGE(fd, optype, ...) send_mgmt_request(mgmtapi_sender(fd), (optype), __VA_ARGS__) 210 211Now, using this macro, messages can easily be sent. For example: 212 213.. code-block:: cpp 214 215 TSMgmtError ret; 216 MgmtMarshallData reply = {nullptr, 0}; 217 218 // create and send request 219 ret = MGMTAPI_SEND_MESSAGE(main_socket_fd, op, &optype); 220 if (ret != TS_ERR_OKAY) { 221 goto done; 222 } 223 224 // get a response 225 ret = recv_mgmt_message(main_socket_fd, reply); 226 if (ret != TS_ERR_OKAY) { 227 goto done; 228 } 229 230==================================== 231RPC API for |TServer| and |TManager| 232==================================== 233 234.. figure:: ../../uml/images/RPC-states.svg 235 :align: center 236 237|LM| and |PM| follow similar workflows. A manager will poll the socket for any messages. If it is able to read a message, it will handle it based on the :arg:`msg_id` from the :class:`MgmtMessageHdr` and select a callback to run asynchoronously. The async callback will add a response, if any, to an outgoing event queue within the class. A manager will continue to poll and read on the socket as long as there are messages available. Two things can stop a manager from polling. 238 2391. there are no longer any messages on the socket for a *timeout* time period. 240 241#. an external event, for example, a |TCtl| command, triggers an ``eventfd`` to tell the manager to stop polling. In the case of an external event, the manager will finish reading anything on the socket, but it will not wait for any timeout period. So, if there are three pending events on the socket, all three will be processed and then immediately after, the manager will stop polling. 242 243Once a manager is done polling, it will begin to send out messages from it's outgoing event queue. It continues to do this until there are no more messages left. Note that as a manager is polling, it is unable to write anything on the socket itself so the number of messages read will always be exhaustive. 244 245.. class:: BaseManager 246 247LocalManager 248============ 249 250.. class:: LocalManager : public BaseManager 251 252 This class is used by |TManager| to communicate with |TServer| 253 254 .. function:: void pollMgmtProcessServer() 255 256 This function watches the socket and handles incoming messages from processes. Used in the main event loop of |TManager|. 257 258 .. function:: void sendMgmtMsgToProcesses(MgmtMessageHdr *mh) 259 260 This function is used by |TManager| to process the messages based on :arg:`msg_id`. It then sends the message to |TServer| over sockets. 261 262ProcessManager 263============== 264 265.. class:: ProcessManager : public BaseManager 266 267 This class is used by |TServer| to communicate with |TManager| 268 269 .. function:: int pollLMConnection() 270 271 This function periodically polls the socket to see if there are any messages from the |LM|. 272 273 .. function:: void signalManager (int msg_id, const char *data_raw, int data_len) 274 275 This function sends messages to the |LM| using sockets. 276 277.. note:: 278 279 1. Both :func:`LocalManager::pollMgmtProcessServer` and :func:`ProcessManager::pollLMConnection` actually use ``select``, not ``poll`` or ``epoll``, underneath. 280