What you are speaking about is the most important Ggr feature — it is completely stateless. All information about hub hostname is stored in session ID. For example standard Selenium hub returns session ID like: f00e4ca7–8c44–4a92–8400–0e5e79820181. Ggr #1 in its turn knows this hub hostname and extends session ID by adding MD5 sum of hostname (e.g. 8190d941d538327ccfa316216594b376). This is why it returns to user new longer session ID (just by concatenating two strings) = 8190d941d538327ccfa316216594b376f00e4ca7–8c44–4a92–8400–0e5e79820181. When next request arrives to Ggr #2 it extracts MD5 sum from extended session ID and finds hub hostname in MD5 to hostname map — this is very fast operation.
So the answer is — you don’t need any particular balancing logic — simple round-robin is enough. The only important thing you should always check is quota consistency. This is because in order to function properly MD5 to hostname maps should be consistent and they are generated from quota XML files during Ggr startup. And because of this we preferred manual quota reload (via SIGHUP) to automatic quota reload (triggered by file change) — you need to do this operation synchronously so maps stay the same.