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