在企业级系统中,针对业务进行模块化拆分是不可避免的,而系统模块化拆分之后,必然存在A模块(服务使用方)依赖B模块(服务提供方)的场景,通常拆分之后的模块都是相对独立的,所以这样的场景下一般由服务提供方提供远程调用接口给服务使用方使用。而服务提供方为了保证在高并发的情况下仍然能为服务使用方提供稳定的服务,通常会对服务提供方部署多个实例,并对来自服务使用方的请求就行负载均衡,使这些请求尽量均匀的分布在所部署的各个服务提供方实例上。

而要实现负载均衡,通常可以使用LVS、HAProxy、协议代理等技术解决。不过LVS的代价相对较大,而剩余的技术基本都是以加入代理的方式来实现负载均衡。如服务提供方与服务使用方之间的通信协议是基于HTTP的,那么简单有效的方法是在服务提供方、使用方之间加一层HTTP反向代理(nginx、apache),利用反向代理完实现负载均衡。但是使用代理的方式会增加一次额外的请求转发,无论代理的性能做得多好,这次额外的请求转发总还是需要消耗时间的。而且代理的引入也会导致服务提供、使用方之间的调用风险增加。比如之前在我司的业务系统中就遇到过一次nginx代理重复转发了一个请求,而服务提供方又没法判断重复请求,最后因为这个重复的请求导致系统出现问题。再者由于代理的存在,每次增加新的服务提供方部署实例时,可能都需要修改代理的配置,这样就不利于自动化新增服务提供方实例来对整个系统进行扩容了。

更加靠谱的实现负载均衡的方法应该是,不用引入代理,直接在服务使用方做负载均衡。要在服务使用方做负载均衡,那么服务使用方就必须知道所有的服务提供方可用实例列表,只有满足了这个前置条件之后才有做负载均衡的基础。要使服务使用方知道所有的服务提供方可用实例列表最简单的方法就是所有的服务提供方可用实例列表直接配置在服务使用方中,但是这仅是简单的去除了中间代理,还是难以达到自动化新增服务提供方实例的目的。为了解决这个问题我们应该引入一个注册中心。

服务自动注册与发现

如上图所示,服务的提供方上线时将访问信息(如IP,端口)注册到注册中心,服务提供方下线时应该从注册中心取消注册;服务的使用方则在上线的时候从注册中心获取可用的服务信息(服务提供方可用实例列表);当可用的服务信息发生变化时,注册中心应该立刻通知到服务使用方。这样服务使用方就始终有一份最新的可用服务信息(服务提供方可用实例列表),基于该信息就可以根据实际业务需要选择合适的负载均衡策略来实现对服务提供方调用的负载均衡。

就目前来说使用ZooKeeper在当做注册中心就是一个非常不错的选择。ZooKeeper有一种 EPHEMERAL 类型的目录节点,这样的目录节点在ZooKeeper会话断开后将会被自动删除,这样就非常适合服务提供方上线时创建一个这样的目录节点,并将访问信息存在这样的目录节点上(或者其子目录节点)。而服务使用方只需要从同样的目录节点上读取信息就可以知道所有可用服务信息(服务提供方可用实例列表),如果需要实时知道最新的可用服务信息,只需要监听对应目录节点的变化即可,因为当所监听的目录节点有变化时,ZooKeeper会主动通知。因此利用ZooKeeper就可以很好的完成服务的自动注册与发现,而且服务的提供方也可以很方便的就行扩容(直接上线、下线服务提供方实例即可,服务使用方可以立刻感知到新上线、下线的服务提供方实例)。