protocol-evolution.adoc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. = GPSD-NG: A Case Study in Application Protocol Evolution =
  2. :description: A case study in the evolution of the gpsd protocol
  3. :keywords: GPSD, protocol, evolution
  4. Eric S. Raymond <esr@thyrsus.com>
  5. v1.5.2, January 2016
  6. This document is mastered in asciidoc format. If you are reading it in HTML,
  7. you can find the original at the GPSD project website.
  8. == Introduction ==
  9. GPSD is a service daemon that collects data from serial and USB GPS
  10. sensors attached to a host machine and presents it in a
  11. simple-to-parse form on TCP/IP port 2947. This is a less trivial task
  12. than it sounds, because GPS sensor interfaces are both highly variable
  13. and really badly designed (see http://esr.ibiblio.org/?p=801[Why GPSes
  14. suck, and what to do about it] for a description of NMEA 0183 and
  15. other horrors).
  16. In this paper, however, we will be ignoring all the dodgy stuff that
  17. goes on at GPSD's back end to concentrate on what happens at the front
  18. - the request-response protocol through which client programs get
  19. access to the information that GPSD acquires from its devices and
  20. internal computations.
  21. The GPSD request-response protocol is entering its third generation of
  22. design, and I think the way it has evolved spotlights some interesting
  23. design issues and long-term trends in the design of network protocols
  24. in general. To anticipate, these trends are: (1) changing tradeoffs
  25. of bandwidth economy versus extensibility and explicitness, (2) a
  26. shift from lockstep conversational interfaces to event streams, (3)
  27. changes in the "sweet spot" of protocol designs due to increasing use
  28. of scripting languages, and (4) protocols built on metaprotocols.
  29. Carrying these trends forward may even give us a bit of a glimpse at
  30. the future of application-protocol design.
  31. == The first version: a simple conversational protocol ==
  32. The very first version of GPSD, back in the mid-1990s, handled
  33. NMEA GPSes only and was designed with a dead-simple request-response
  34. protocol. To get latitude and longitude out of it, you'd connect
  35. to port 2947 and have a conversation that looked like this:
  36. -------------------------------------------------------------------------
  37. -> P
  38. <- GPSD,P=4002.1207 07531.2540
  39. -------------------------------------------------------------------------
  40. That is GPSD reporting, the only way it could in the earliest protocol
  41. version, that I'm at latitude about 40 north and 75 west.
  42. If you are a mathematician or a physicist, you're probably noticing
  43. some things missing in this report. Like a timestamp, and a circular
  44. error estimate, and an altitude. In fact, it was possible to get some
  45. these data using the old protocol. You could make a compound request
  46. like this:
  47. -------------------------------------------------------------------------
  48. -> PAD
  49. <- GPSD,P=4002.1207 07531.2540,A=351.27,D=2009:07:11T11:16Z
  50. -------------------------------------------------------------------------
  51. For some devices (not all) you could add E and get error estimates.
  52. Other data such as course and rate of climb/sink might be available
  53. via other single-letter commands. I say "might be" because in those
  54. early days gpsd didn't attempt to compute error estimates or velocities
  55. if the GPS didn't explicitly supply them. I fixed that, later, but
  56. this essay is about protocol design so I'm going to ignore all the
  57. issues associated with the implementation for the rest of the discussion.
  58. The version 1 protocol is squarely in the tradition of classic textual
  59. Internet protocols, even though it doesn't look much like (say) SMTP
  60. transactions - requests are simple to emit and responses are easy to
  61. parse. It was clearly designed with the more specific goal of
  62. minimizing traffic volume between the daemon and its clients. It
  63. accomplishes that goal quite well.
  64. == The second version: from conversational to streaming ==
  65. However, when I started work on it it in 2004 there was already
  66. pressure from the existing userbase to change at least one of the
  67. protocol's major assumptions - that is, that the client would poll
  68. whenever it wanted data. It's usually more convenient to be able to
  69. say to the daemon "Speak!" and have it stream TPV
  70. (time/position/velocity) reports back at you at the sensor's sampling
  71. rate (usually once per second). Especially when, as with GPSD, you
  72. have a client library that can spin in a thread picking up the updates
  73. and dropping them in a struct somewhere that you specify.
  74. This was the first major feature I implemented. I called it "watcher
  75. mode", and it required me to add two commands to the protocol. There
  76. were already so many single-shot commands defined that we were close
  77. to running out of letters for new ones; I was able to grab "W" for the
  78. command that enables or disables watcher mode, but was left with the
  79. not-exactly-intuitive "O" for the streaming TPV report format. Here's
  80. how it looks:
  81. -------------------------------------------------------------------------
  82. -> W=1
  83. <- GPSD,W=1
  84. <- GPSD,O=MID2 1118327700.280 0.005 46.498339529 7.567392712 1342.392 36.000 32.321 10.3787 0.091 -0.085 ? 38.66 ? 3
  85. <- GPSD,O=MID2 1118327701.280 0.005 46.498339529 7.567392712 1342.392 48.000 32.321 10.3787 0.091 -0.085 ? 50.67 ? 3
  86. <- GPSD,O=MID2 1118327702.280 0.005 46.498345996 7.567394427 1341.710 36.000 32.321 10.3787 0.091 -0.085 ? 38.64 ? 3
  87. <- GPSD,O=MID2 1118327703.280 0.005 46.498346855 7.567381517 1341.619 48.000 32.321 10.3787 0.091 -0.085 ? 50.69 ? 3
  88. <- GPSD,Y=MID4 1118327704.280 8:23 6 84 0 0:28 7 160 0 0:8 66 189 45 1:29 13 273 0 0:10 51 304 0 0:4 15 199 34 1:2 34 241 41 1:27 71 76 42 1:
  89. <- GPSD,O=MID2 1118327704.280 0.005 46.498346855 7.567381517 1341.619 48.000 32.321 10.3787 0.091 -0.085 ? ? ? 3
  90. -> W=0
  91. <- GPSD,W=0
  92. -------------------------------------------------------------------------
  93. The fields in the O report are tag (an indication of the device
  94. sentence that produced this report), time, time error estimate,
  95. longitude, latitude, altitude, horizontal error estimate, vertical
  96. error estimate, course, speed, climb/sink, error estimates for
  97. those last three fields, and mode (an indication of fix quality). If
  98. you care about issues like reporting units, read the documentation.
  99. The 'Y' report is a satellite skyview, giving right-ascension,
  100. declination, and signal quality for each of the visible satellites.
  101. GPSes usually report this every five cycles (seconds).
  102. The 'W', 'O' and 'Y' sentences, together, effectively constituted
  103. version 2 of the protocol - designed for streaming use. The other
  104. single-shot commands, though still supported, rapidly became
  105. obsolescent.
  106. Attentive readers may wonder why I designed a novel 'O' format rather
  107. that writing the watcher-mode command so that it could specify a
  108. compound report format (like PADE) every second. Part of the answer
  109. is, again, that we were running out of letters to associate with new
  110. data fields like the error estimates. I wanted to use up as little of
  111. the remaining namespace as I could get away with.
  112. Another reason is, I think, that I was still half-consciously thinking
  113. of bit bandwidth as a scarce resource to be conserved. I had a bias
  114. against designs that would associate "extra" name tags with the
  115. response fields ("A=351.27") even though the longest tagged response
  116. GPSD could be expected to generate would still be shorter than a
  117. single Ethernet packet (1509 bytes).
  118. == Pressure builds for a redesign ==
  119. Along about 2006, despite my efforts to conserve the remaining
  120. namespace, we ran out of letters completely. As the PADE example
  121. shows, the protocol parser interprets command words letter
  122. by letter, so trying to wedge longer commands in by simple
  123. fiat wouldn't work. Recruiting non-letter characters as
  124. command characters would have been ugly and only postponed
  125. the problem a bit, not solved it.
  126. 'H' is actually still left, but at the time I believed we couldn't
  127. commit the last letter (whatever it was) because we'd need it as an
  128. inline switch to a new protocol. I started feeling pressure to
  129. actually design a new protocol. Besides running out of command
  130. namespace in the old one, a couple of things were happening that
  131. implied we'd need to define new commands.
  132. What had used up the last of the command namespace was multi-device
  133. support. Originally, GPSD could only monitor one GPS at a time. I
  134. re-engineered it so it could monitor multiple GPSes, with GPS streams
  135. available as data channels to which a client could connect one at a
  136. time. I was thinking about use cases like this one: spot two GPSes on
  137. either end of an oil tanker, use the position delta as a check on
  138. reported true course.
  139. (For those of you wondering, this wasn't the huge job it may sound
  140. like. I had carefully structured GPSD as a relatively small (about
  141. 5.5 KLOC) networking and dispatcher top-level calling a 30 KLOC driver
  142. and services library, all of which was designed from the get-go to use
  143. re-entrant structures. Thus, only the top layer needed to change, and
  144. at that only about 1 KLOC of it actually did. Building the test
  145. framework to verify the multi-device code in action was a bigger job.)
  146. Note that the "one at a time" limitation was imposed by the
  147. protocol design, notably the fact that the 'O' record didn't contain
  148. the name of the device it was reporting from. Thus, GPSD could not
  149. mix reports from different devices without effectively discarding
  150. information about where they had come from.
  151. Though I had just barely managed to cram in multi-GPS support without
  152. overrunning the available command space, we were starting to look at
  153. monitoring multiple *kinds* of devices in one session - RTCM2
  154. correction sources and NTRIP were the first examples. (These are both
  155. protocols that support
  156. http://www.esri.com/news/arcuser/0103/differential1of2.html[differential
  157. GPS correction].) My chief lieutenant was muttering about making GPSD
  158. report raw pseudorange data from the sensors that allow you to get at
  159. that. It was abundantly clear that broadening GPSD's scope was going
  160. to require command-set extensions.
  161. Even though I love designing application protocols only a little bit
  162. less than I love designing domain-specific minilanguages, I dragged my
  163. feet on tackling the GPSD-NG redesign for three years. I had a strong
  164. feeling that I didn't understand the problem space well enough, and
  165. that jumping into the effort prematurely might lock in some mistakes
  166. that I would come to gravely regret later on.
  167. == JSON and the AISonauts ==
  168. What finally got me off the dime in early 2009 were two developments - the
  169. push of AIS and the pull of JSON.
  170. AIS is the marine http://www.navcen.uscg.gov/enav/ais/[Automatic
  171. Identification System]. All the open-source implementations of AIS
  172. packet decoding I could find were sketchy, incomplete, and not at a
  173. quality level I was comfortable with. It quickly became apparent that
  174. this was due to a paucity of freely available public information about
  175. the applicable standards.
  176. http://esr.ibiblio.org/?p=888[I fixed that problem] - but having done
  177. so, I was faced with the problem of just how GPSD is supposed to
  178. report AIS data packets to clients in a way that can't be confused
  179. with GPS data. This brought the GPSD-NG design problem to the front
  180. burner again.
  181. Fortunately, my AIS-related research also led me to discover
  182. http://www.json.org/[JSON], aka JavaScript Object Notation. And JSON
  183. is *really nifty*, one of those ideas that seem so simple and
  184. powerful and obvious once you've seen it that you wonder why it wasn't
  185. invented sooner.
  186. In brief, JSON is a lightweight and human-readable way to serialize
  187. data structures equivalent to Python dictionaries, with attributes
  188. that can be numbers, strings, booleans, nested dictionary objects,
  189. or variable-extent lists of any of these things.
  190. == GPSD-NG is born ==
  191. I had played with several different protocol design possibilities
  192. between 2006 and 2009, but none of them really felt right. My
  193. breakthrough moment in the GPSD-NG design came when I thought this:
  194. "Suppose all command arguments to GPSD-NG commands, and their
  195. responses, were self-describing JSON objects?"
  196. In particular, the equivalent of the 'O' report shown above looks like
  197. this in GPSD-NG (with some whitespace added to avoid hard-to-read
  198. linewraps):
  199. -------------------------------------------------------------------------
  200. {"class":"TPV","tag":"MID50","device":"/dev/pts/1",
  201. "time":"2005-06-09T14:35:11.79",
  202. "ept":0.005,"lat":46.498333338,"lon":7.567392712,"alt":1341.667,
  203. "eph":48.000,"epv":32.321,"track":60.9597,"speed":0.161,"climb":-0.074,
  204. "eps":50.73,"mode":3}
  205. -------------------------------------------------------------------------
  206. To really appreciate what you can do with object-valued attributes,
  207. however, consider this JSON equivalent of a 'Y' record. The skyview
  208. is a sublist of objects, one per satellite in view:
  209. -------------------------------------------------------------------------
  210. {"class":"SKY","tag":"MID2","device":"/dev/pts/1",
  211. "time":"2005-06-09T14:35:11.79",
  212. "reported":8,"satellites":[
  213. {"PRN":23,"el":6,"az":84,"ss":0,"used":false},
  214. {"PRN":28,"el":7,"az":160,"ss":0,"used":false},
  215. {"PRN":8,"el":66,"az":189,"ss":40,"used":true},
  216. {"PRN":29,"el":13,"az":273,"ss":0,"used":false},
  217. {"PRN":10,"el":51,"az":304,"ss":36,"used":true},
  218. {"PRN":4,"el":15,"az":199,"ss":27,"used":false},
  219. {"PRN":2,"el":34,"az":241,"ss":36,"used":true},
  220. {"PRN":27,"el":71,"az":76,"ss":43,"used":true}
  221. ]}
  222. -------------------------------------------------------------------------
  223. (Yes, those "el" and "az" attributes are elevation and azimuth. "PRN"
  224. is the satellite ID; "ss" is signal strength in decibels, and "used"
  225. is a flag indicating whether the satellite was used in the current solution."
  226. These are rather more verbose than the 'O' or 'Y' records, but have several
  227. compensating advantages:
  228. * Easily extensible. If we need to add more fields, we just add named
  229. attributes. This is especially nice because...
  230. * Fields with undefined values can be omitted. This means extension
  231. fields don't weigh down the response format when we aren't using them.
  232. * It's explicit. Much easier to read with eyeball than the corresponding
  233. 'O' record.
  234. * It includes the name of the device reporting the fix. This opens up
  235. some design possibilities I will discuss in more detail in a bit.
  236. * It includes, up front, a "class" tag that tells client software what it
  237. is, which can be used to drive a parse.
  238. My first key decision was that these benefits are a good trade for the
  239. increased verbosity. I had to wrestle with this a bit; I've been
  240. programming a long time, and (as I mentioned previously) have reflexes
  241. from elder days that push me to equate "good" with "requiring minimum
  242. computing power and bandwidth". I reminded myself that it's 2009 and
  243. machine resources are cheap; readability and extensibility are the goals
  244. to play for.
  245. Once I had decided that, though, there remained another potential
  246. blocker. The implementation language of gpsd and its principal client
  247. library is C. There are lots of open-source JSON parsers in C out
  248. there, but they all have the defect of requiring malloc(3) and handing
  249. back a dynamic data structure that you then have to pointer-walk at
  250. runtime.
  251. This is a problem, because one of my design rules for gpsd is no use
  252. of malloc. Memory leaks in long-running service daemons are bad things;
  253. using only static, fixed-extent data structures is a brutally effective
  254. strategy for avoiding them. Note, this is only possible because the maximum
  255. size of the packets gpsd sees is fairly small, and its algorithms are O(1)
  256. in memory utilization.
  257. "Um, wait..." I hear you asking "...why accept that constraint when
  258. gpsd hasn't had a requirement to parse JSON yet, just emit it as
  259. responses?" Because I fully expected gpsd to have to parse structured
  260. JSON arguments for commands. Here's an example, which I'll explain fully
  261. later but right now just hint at the (approximate) GPSD-NG equivalent
  262. of a 'W+R+' command.
  263. -------------------------------------------------------------------------
  264. ?WATCH={"raw":1,nmea:true}
  265. -------------------------------------------------------------------------
  266. Even had I not anticipated parsing JSON arguments in gpsd, I try to
  267. limit malloc use in the client libraries as well. Before the
  268. new-protocol implementation the client library only used two calloc(3)
  269. calls, in very careful ways. Now they use none at all.
  270. So my next challenge was to write and verify a tiny JSON parser that
  271. is driven by sets of fixed-extent structures - they tell it what shape
  272. of data to expect and at which static locations to drop the actual
  273. parsed data; if the shape does not match what's expected, error out.
  274. Fortunately, I am quite good at this sort of hacking - the
  275. result, after a day and a half of work, fit in 310 LOC including
  276. comments (but not including 165 LOC of unit-test code).
  277. == Un-channeling: the power ==
  278. Both gpsd and its C client library could now count on parsing JSON;
  279. that gave me my infrastructure. And an extremely strong one, too;
  280. the type ontology of JSON is rich enough that I'm not likely to ever
  281. have to replace it. Of course this just opened up the next question -
  282. now that I can readily pass complex objects between gpsd and its
  283. client libraries, what do I actually do with this capability?
  284. The possibility that immediately suggested itself was "get rid of channels".
  285. In the old interface, subscribers could only listen to one device at
  286. a time - again, this was a consequence of the fact that 'O' and 'Y'
  287. reports were designed before multi-device support and didn't include a
  288. device field. JSON reports can easily include a device field and
  289. thus need not have this problem.
  290. Instead of a channel-oriented interface, then, how about one where the
  291. client chooses what classes of message to listen to, and then gets
  292. them from all devices?
  293. Note, however, that including the device field raises some problems of
  294. its own. I do most of my gpsd testing with a utility I wrote called
  295. gpsfake, which feeds one or more specified data logs through pty
  296. devices so gpsd sees them as serial devices. Because X also uses pty
  297. devices for virtual terminals, the device names that a gpsd instance
  298. running under gpsfake sees may depend on random factors like the
  299. number of terminal emulators I have open. This is a problem when
  300. regression-testing! I thought this issue was going to require me
  301. to write a configuration command that suppresses device display; I
  302. ended up writing a sed filter in my regression-test driver instead.
  303. Now we come back to our previous example:
  304. -------------------------------------------------------------------------
  305. ?WATCH={"raw":true,nmea:true}
  306. -------------------------------------------------------------------------
  307. This says: "Stream all reports from all devices at me, setting raw
  308. mode and dumping as pseudo-NMEA if it's a binary protocol." The way to
  309. add more controls to this is obvious, which is sort of the point --
  310. nothing like this could have fit in the fixed-length syntax of the old
  311. pre-JSON protocol.
  312. This is not mere theory. At the time of writing, the ?WATCH command is
  313. fully implemented in gpsd's Subversion repository, and I expect it to
  314. ship ready for use in our next release (2.90). Total time to build
  315. and test the JSON parsing infrastructure, the GPSD-NG parser, and the
  316. gpsd internals enhancements needed to support multi-device listening?
  317. About a working week.
  318. Just to round out this section, here is an example of what an
  319. actual AIS transponder report looks like in JSON.
  320. -------------------------------------------------------------------------
  321. {"class"="AIS","msgtype":5,"repeat":0,"mmsi":"351759000","imo":9134270,
  322. "ais_version":0,"callsign":"3FOF8","shipname":"EVER DIADEM",
  323. "shiptype":70,"to_bow":225,"to_stern":70,"to_port":1,"to_starboard":31,
  324. "epfd":1,"eta":05-15T14:00Z,"draught":122,"destination":"NEW YORK",
  325. "dte":0}
  326. -------------------------------------------------------------------------
  327. The above is an AIS type 5 message identifying a ship - giving, among
  328. other things, the ship's name and radio callsign and and destination
  329. and ETA. You might get this from an AIS transceiver, if you had one
  330. hooked up to your host machine; gpsd would recognize those data
  331. packets coming in and automatically make AIS reports available as
  332. an event stream.
  333. == The lessons of history ==
  334. In the introduction, I called out three trends apparent over time in
  335. protocol design. Let's now consider these in more detail.
  336. === Bandwidth economy versus extensibility and explicitness ===
  337. First, I noted *changing tradeoffs of bandwidth economy versus
  338. extensibility and explicitness*.
  339. One way you can compare protocols is by the amount of overhead they
  340. incur. In a binary format this is the percentage of the bit stream
  341. that goes to magic numbers, framing bits, padding, checksums, and
  342. the like. In a textual format the equivalent is the percentage
  343. of the bitstream devoted to field delimiters, sentence start and
  344. sentence-end sentinels, and (in protocols like NMEA 0183) textual
  345. checksum fields.
  346. Another way you can compare protocols is by implicitness versus
  347. explicitness. In the old GPSD protocol, you know the semantics of a
  348. request parameter within a request implicitly, by where it is in
  349. the order. In GPSD-NG, you know more explicitly because every parameter is a
  350. name-attribute pair and you can inspect the name.
  351. Extensibility is the degree to which the protocol can have new
  352. requests, responses, and parameters added without breaking old
  353. implementations.
  354. In general, *both extensibility and overhead rise with the degree
  355. of explicitness in the protocol*. The JSON-based TPV record has
  356. has much higher overhead than the O record it replaces, but what
  357. we gain from that is lots and *lots* of extensibility room. We
  358. win three different ways:
  359. * The command/response namespace in inexhaustibly huge.
  360. * Individual requests and responses can readily be extended by adding
  361. new attributes without breaking old implementations.
  362. * The type ontology of JSON is rich enough to make passing arbitrarily
  363. complex data structures through it very easy.
  364. With respect to the tradeoffs between explicitness/extensibility and
  365. overhead, we're at a very different place on the cost-benefit curves
  366. today from when the original GPSD protocol was designed.
  367. Communications costs for the pipes that GPSD uses have
  368. dropped by orders of magnitude in the decade-and-change since GPSD
  369. was designed. Thus, squeezing every last bit of overhead out of the
  370. protocol representation doesn't have the real economic payoff it used to.
  371. Under modern conditions, there is a strong case that implicit,
  372. tightly-packed protocols are false economy. If (as with the first GPSD
  373. protocol) they're so inextensible that natural growth in the
  374. software breaks them, that's a clear down-check. It's better to design
  375. for extensibility up front in order to avoid having to throw out
  376. a lot of work later on.
  377. The direction this points in for the future is clear, especially
  378. in combination with the increasing use of metaprotocols.
  379. === From lockstep to streaming ===
  380. Second, I noted *a shift from lockstep conversational interfaces to
  381. event streams*.
  382. The big change in the second protocol version was watcher mode. One
  383. of the possibilities this opens up is that you can put the report
  384. interpreter into an asychronous thread that magically updates a C
  385. struct for you every so often, without the rest of your program having
  386. to know or care how that is being done (except possibly by waiting a
  387. mutex to ensure it doesn't read a partially-updated state).
  388. Analogous developments have been visible in other Internet protocols
  389. over roughly the same period. Compare, for example, POP3 to IMAP. The
  390. former is a lockstep protocol, the latter designed for streaming - it's
  391. why IMAP responses have a transaction ID tying them back to the
  392. requesting command, so responses that are out of order due to
  393. processing delays can be handled sanely.
  394. Systems software has generally been moving in a similar direction,
  395. propelled there by distributed processing and networks with unavoidable
  396. variable delays. There is a distant, but perceptible, relationship
  397. between GPSD-NG's request-response objects and the way transactions
  398. are handled within (for example) the X window system.
  399. This trend, too, seems certain to continue, as the Internet becomes
  400. ever more like one giant distributed computing system.
  401. === Type ontology recapitulates trends in language design ===
  402. Third, *changes in the "sweet spot" of protocol designs
  403. due to increasing use of scripting languages.*
  404. The most exciting thing about JSON to me, speaking as an application
  405. protocol designer, is the rich type ontology - booleans, numbers,
  406. strings, lists, and dictionaries - and the ability to nest them to any
  407. level. In an important sense that is orthogonal to raw bandwidth,
  408. this makes the pipe wider - it means complex, structured data can more
  409. readily be passed through with a minimum of fragile and bug-prone
  410. serialization/deserialization code.
  411. The fact that I could build a JSON parser to unpack to fixed-extent C
  412. structures in 300-odd LOC demonstrates that this effect is a powerful
  413. code simplifier even when the host language's type ontology is limited
  414. to fixed-extent types and poorly matched to that of JSON (C lacks not
  415. only variable-extent lists but also dictionaries).
  416. JSON is built on dictionaries; in fact, every JSON object is a legal
  417. structure literal in the dictionary-centric Python language (with one
  418. qualified exception near the JSON null value). It seems like a simple
  419. idea in 2009, but the apparent simplicity relies on folk knowledge we
  420. didn't have before Perl introduced dictionaries as a first-class data
  421. type (c.1986) and Python built an object system around them (after
  422. 1991).
  423. Thus, GPSD-NG (and the JSON it's built on) reflects and recapitulates
  424. long-term trends in language design, especially those associated with
  425. the rise of scripting languages and of dictionaries as a first-class
  426. type within them.
  427. This produces several mutually reinforcing feedback loops. The
  428. rise of scripting languages makes it easier to use JSON to its full
  429. potential, if only because deserialization is so trivial. JSON will
  430. probably, in turn, promote the use of these languages.
  431. I think, in the future, application protocol designers will become
  432. progressively less reluctant to rely on being able to pass around
  433. complex data structures. JSON distils the standard type ontology of
  434. modern scripting languages (Perl, Python, Ruby, and progeny) into a
  435. common data language that is far more expressive than the structs of
  436. yesteryear.
  437. == Protocols on top of metaprotocols ==
  438. GPSD-NG is an application of JSON. Not a completely pure one; the
  439. request identifiers, are, for convenience reasons, outside the JSON
  440. objects. But close enough.
  441. In recent years, metaprotocols have become an important weapon in
  442. the application-protocol designer's toolkit. XML, and its
  443. progeny SOAP and XML-RPC, are the best known metaprotocols. YAML
  444. (of which JSON is essentially a subset) has a following as well.
  445. Designing on top of a metaprotocol has several advantages. The most
  446. obvious one is the presence of lots of open-source software to use for
  447. parsing the metaprotocol.
  448. But it is probably more important in the long run that it saves one
  449. from having to reinvent a lot of wheels and ad-hoc representations
  450. at the design level. This effect is muted in XML, which has a weak
  451. type ontology, but much more pronounced in YAML or JSON. As a
  452. relevant example, I didn't have to think three seconds about the right
  453. representation even for the relatively complex SKY object.
  454. == Paths not taken ==
  455. Following the first public release of this paper, the major questions
  456. to come up from early readers were "Why not XML?" and "Why not a
  457. super-efficient packed binary protocol?"
  458. I would have thought the case against packed binary application
  459. protocols was obvious from my preceding arguments, but I'll make it
  460. explicit here: generally, they are even more rigid and inextensible
  461. than a textual protocol relying on parameter ordering, and hence more
  462. likely to break as your application evolves. They have significant
  463. portability issues around things like byte order in numeric fields.
  464. They are opaque; they cannot be audited or analyzed without bug-prone
  465. special-purpose tools, adding a forbidding degree of complexity and
  466. friction to the life-cycle maintenance costs.
  467. When the type ontology of your application includes only objects like
  468. strings or numbers that (as opposed to large binary blobs like images)
  469. have textual representations differing little in size from packed
  470. binary, there is no case at all for incurring these large overheads.
  471. The case against XML is not as strong. An XML-based protocol at least
  472. need not be rigidly inextensible and opaque. XML's problem is that,
  473. while it's a good basis for document interchange, it doesn't naturally
  474. express the sorts of data structures cooperating applications want to
  475. pass around.
  476. While such things can be layered over XML with an appropriate schema,
  477. the apparatus required for schema-aware parsing is necessarily
  478. complicated and heavyweight - certainly orders of magnitude more so
  479. than the little JSON parser I wrote. And XML itself is pretty
  480. heavyweight, too - one's data tends to stagger under the bulk
  481. of the markup parts.
  482. == Envoi ==
  483. Finally, a note of thanks to the JSON developers...
  484. I think JSON does a better job of nailing the optimum in metaprotocols
  485. than anything I've seen before - its combination of simplicity and
  486. expressiveness certainly isn't matched by XML, for reasons already
  487. called out in my discussion of paths not taken.
  488. I have found JSON pleasant to work with, liberating, and
  489. thought-provoking; hence this paper. I will certainly reach for this
  490. Swiss-army knife first thing, next time I have to design an
  491. application protocol.