Consistent partitioning

@wvanbergen this is a fairly non-trivial PR. I would prefer to do the commits one-at-a-time but we're only back to sane default behaviour once all three land. It is still definitely worth reviewing one commit at a time, and I gave each one a substantial stand-alone commit message to help with that.

The goal here is related to better behaviour in cases documented like #210 and #213. Specifically:
- When using a consistent partitioner (aka the `HashPartitioner`), if some partitions go away (all replicas gone) then those messages should fail fast and messages to other partitions should keep flowing.
- When using an inconsistent partitioner (round-robin, random, etc), if some partitions go away then all messages should keep flowing to the available partitions and none should fail.

This PR gets us a lot closer, but leaves a handful of pieces still to do:
- with a consistent partitioner, we don't fail "fast" (i.e. performance probably degrades quite a bit when a partition goes down entirely)
- with an inconsistent partitioner, messages that were in-flight to a partition that goes away will be retried on that same partition, killing performance briefly (although unlike the consistent case it will recover once they've all been returned as errors)