NSCA bottleneck / NSCA Timestamp
Andreas Ericsson
ae at op5.se
Fri Nov 23 11:43:15 CET 2007
Thomas Guyot-Sionnest wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Thomas Guyot-Sionnest wrote:
>> * Or did you have another idea to do this? Tabs are currently allowed in
>> the last field for service checks so the number of separators isn't an
>> option, even when adding two new fields. Perhaps the position of the
>> return code + two new fields, but then a service named "0" trough "3"
>> would mess the thing up.
>
> Oh, I overlooked a bit too much the protocol (I thought the data was
> sent as-it but I believe I actually got confused reading client code
> thinking it was server-side code).
>
> Solution #1:
> Since the server only read sizeof(receive_packet) bytes, we should
> normally be able to add things to the struct without breaking anything
> (older server will just ignore it).
>
> Solution #2:
> Each packet sent have a version number, so we can create new packet
> formats with different version (older servers will discard non-matching
> version). I'm not an expert in network protocols but I guess we should
> do something like this:
>
> typedef struct data_packet_struct{
> int16_t packet_version;
> u_int32_t crc32_value;
> u_int32_t timestamp;
> int16_t return_code;
> uint16_t count; /* bytes of data */
> char payload[MAX_PACKET_SIZE];
> }data_packet;
>
> typedef struct nsca_message_v3_struct{
> int16_t return_code;
> char host_name[MAX_HOSTNAME_LENGTH];
> char svc_description[MAX_DESCRIPTION_LENGTH];
> char plugin_output[MAX_PLUGINOUTPUT_LENGTH];
> }nsca_message_v3;
>
This is bad. Look up modbus for how to write header + body part
messages which allows for near-unlimited protocol extensions.
With this way of doing things, you're limiting yourself to a
definitive size of each host/service. It makes for (a little)
easier coding, but it's a useless limitation to put on people.
A header should (basically) consist of something like this:
struct proto_header {
u_int32_t proto_version;
u_int32_t pkt_type;
u_int32_t body_len;
char pad[DESIRED_HEADER_SIZE - (sizeof(u_int32_t) * 3);
};
where DESIRED_HEADER_SIZE is usually 32 or 64, for alignment purposes.
It's usually desirable to have protocol_version be 32 bits wide as
well, also for alignment reasons (no modern architecture aligns on
2 byte boundaries).
Then you just invent packets as you go along and can handle them all
in a single switch() statement (to decide which function *really*
should take care of them) or, if performance is really critical,
you do it with a list such as this:
---%<---%<---%<---
#define MAX_PKT_TYPE 3
int (*handle_pkt[MAX_PKT_TYPE_HANDLED])(void *buf, size_t len) = {
handle_pkt0,
handle_pkt1,
handle_pkt2,
};
if (hdr.pkt_type > MAX_PKT_TYPE)
unsupported_packet();
handle_pkt[hdr.pkt_type](packet_body, len);
---%<---%<---%<---
which will incur exactly one branch and one memory lookup per
packet. Note that this is really a micro-optimization though.
For anything but embedded systems, the switch() statement will
almost certainly be a better choice, providing better readability
at the expense of binary footprint size and memory usage.
--
Andreas Ericsson andreas.ericsson at op5.se
OP5 AB www.op5.se
Tel: +46 8-230225 Fax: +46 8-230231
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
More information about the Developers
mailing list