Batch calls with multicall
Это содержимое пока не доступно на вашем языке.
system.multicall packs many API calls into one HTTP request. Use it for bulk jobs such as adding hundreds of DIDs or reading many accounts: you save one HTTPS round trip and one digest handshake per call. In a test with 1000 addDID calls, one by one took 5 min 20 s and multicall took 1 min 39 s, about three times faster.
All calls in a batch run as the authenticated customer, one after another, in the order you send them. Each call succeeds or fails on its own: a fault in one call does not stop the others and does not undo the calls before it.
Request
Section titled “Request”system.multicall takes one parameter, an array. Each element is a struct with two members:
| Name | Type | Description |
|---|---|---|
methodName |
string | The method to call, for example getAccountInfo |
params |
array | The parameters of that method. API methods take one struct, so this is an array of one struct |
<?xml version="1.0"?><methodCall> <methodName>system.multicall</methodName> <params> <param> <value><array><data> <value><struct> <member><name>methodName</name><value><string>getAccountInfo</string></value></member> <member><name>params</name><value><array><data> <value><struct> <member><name>i_account</name><value><int>3092</int></value></member> </struct></value> </data></array></value></member> </struct></value> <value><struct> <member><name>methodName</name><value><string>getAccountInfo</string></value></member> <member><name>params</name><value><array><data> <value><struct> <member><name>i_account</name><value><int>-1</int></value></member> </struct></value> </data></array></value></member> </struct></value> </data></array></value> </param> </params></methodCall>Response
Section titled “Response”The response is an array with one element per call, in the same order:
- a call that succeeded returns an
arrayof one element, the method’s usual resultstruct; - a call that failed returns a
structwithfaultCodeandfaultString, the same members as a fault.
<?xml version="1.0"?><methodResponse> <params> <param> <value><array><data> <value><array><data> <value><struct> <member><name>result</name><value><string>OK</string></value></member> <member><name>i_account</name><value><int>3092</int></value></member> <member><name>username</name><value><string>1001</string></value></member> </struct></value> </data></array></value> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>…</string></value></member> </struct></value> </data></array></value> </param> </params></methodResponse>If the batch itself is malformed, for example when the parameter is not an array, the whole request returns one fault instead.
Python
Section titled “Python”The call() helper of the overview wraps one result struct, and xmlrpc.client.MultiCall needs a ServerProxy, which has no digest authentication. This function sends the batch with requests and turns every element of the response into a result or an xmlrpc.client.Fault:
import xmlrpc.client
import requests
from flysip_api import AUTH, URL # the helper module from the overview
def multicall(calls: list[tuple[str, dict]], timeout: int = 300) -> list: """Send (method, params) pairs in one request.
Returns one item per call, in order: the result struct of the call, or an xmlrpc.client.Fault for a call that failed. """ batch = [{"methodName": method, "params": [params]} for method, params in calls] body = xmlrpc.client.dumps((batch,), "system.multicall", allow_none=True) resp = requests.post(URL, data=body.encode(), auth=AUTH, headers={"Content-Type": "text/xml"}, timeout=timeout) resp.raise_for_status() # Raises Fault if the batch as a whole was rejected. results = xmlrpc.client.loads(resp.content, use_builtin_types=True)[0][0] return [ xmlrpc.client.Fault(r["faultCode"], r["faultString"]) if isinstance(r, dict) else r[0] for r in results ]Read three accounts, one of which does not exist:
calls = [ ("getAccountInfo", {"i_account": 3092}), ("getAccountInfo", {"i_account": -1}), # fails ("getAccountInfo", {"username": "1001"}),]for (method, params), res in zip(calls, multicall(calls)): if isinstance(res, xmlrpc.client.Fault): print(method, params, "fault", res.faultCode, res.faultString) else: print(method, params, res["username"])Add 1000 DIDs in batches of 200, so that each request finishes well within the timeout:
dids = [f"1206555{n:04d}" for n in range(1000, 2000)]for start in range(0, len(dids), 200): chunk = dids[start:start + 200] results = multicall([("addDID", {"did": d, "incoming_did": d}) for d in chunk]) for did, res in zip(chunk, results): if isinstance(res, xmlrpc.client.Fault): print(f"{did}: fault {res.faultCode} {res.faultString}") else: print(f"{did}: i_did {res['i_did']}")The phpxmlrpc library has multicall() on its client. Pass false as the fourth argument so that it sends a real system.multicall instead of falling back to one request per call. It returns one response object per call:
<?phpinclude 'xmlrpc/xmlrpc.inc'; // the phpxmlrpc library
$cli = new xmlrpc_client('https://panel.example.com/xmlapi/xmlapi');$cli->setCredentials('acme-web', 'API-PASSWORD', CURLAUTH_DIGEST);$cli->request_charset_encoding = 'UTF-8';$cli->return_type = 'phpvals';
$msgs = [];foreach ([3092, -1, 3093] as $i_account) { $msgs[] = new xmlrpcmsg('getAccountInfo', [new xmlrpcval([ 'i_account' => new xmlrpcval($i_account, 'int'), ], 'struct')]);}
$responses = $cli->multicall($msgs, 60, '', false);foreach ($responses as $r) { if ($r->faultCode()) { error_log('Fault ' . $r->faultCode() . ': ' . $r->faultString()); } else { $v = $r->value(); print $v['username'] . "\n"; }}Related
Section titled “Related”- API overview: request format, faults and the
call()helper - Python client
- Manage DIDs:
addDID - Get account information