在软件开发中,一般的作法是将一些基础,简单的服务组合在一块儿而造成一个具备某一功能的特定服务。这种搭积木的结构,或者说自下而上的组合更有利于程序的资源隔离以及维护与拓展。高层的服务依赖底层服务提供业务计算,低层的服务提供诸如数据存储,网络传输等基础操做。java
这些低层的服务就如现实世界中的城市基础设施,没有道路车辆就无处行驶,没有发电厂提供的电力全部的电力设备就没法工做。因此,在程序世界构建的时候,这些基础服务必须预先完成加载(初始化)。当低层接口与高层接口变得比较多时,它们的关系错综复杂。因此,合理地管理它们变得很是有必要!下面咱们将讨论几种常见的模式:git
一种很是简单的方式是:将全部的服务按照依赖层级逐个地加载它们。以下图, C 分别依赖了 A 与 B 。在程序启动的时候能够按照 A -> B -> C 的方式依次加载它们。github
这种方式可以有效地解决服务依赖,在服务不是特别复杂,依赖的链不是很长的状况下它会是一个很不错的选择。可是这种方式会将整个服务启动的时候变得很长,由于它们的加载是逐个进行的。所以在大型的服务中这种方式是不被推荐的。网络
另外一种方式是按需加载,在服务被使用(调用)时,再去加载它所依赖的服务。以下图,有 2 个高层服务 C 与 E,它们分别依赖了基础服务 A,B 与 B,D。app
当访问 C 服务的时候,会按需加载 C <-> B <-> A。C 去尝试找到并加载 B,B 也会尝试找到并加载 A。这是一个有返回的过程,全部使用符号 <->
表示。同理,E 服务的加载过程也是按照 E <-> B <-> D。maven
这种模式最大的优势是能作到按需加载,当高层服务被使用的时候才会去加载依赖的服务。在容器技术中也称之为 依赖注入
。为避免重复加载的过程,实际上咱们会把这些服务放在一个服务容器里面,服务的加载会有容器处理,咱们要作的仅仅是从一个容器里面找到它们。例如上图中 C, E 都依赖了 B。ide
大多的应用服务都会选择按需加载的模式,它不只能避免有序加载的不能同时加载多个服务的问题,按需加载实际上由于不会加载那些无访问的服务,因此能有效地节省一部分资源。svg
但这就是终极方案吗?显然不是!下面咱们来了解第三种模式。性能
分组加载是将服务按照依赖的层级划分称不一样的 ServiceGroup
。ServiceGroup 之间是按照顺序加载的,但 ServiceGroup 内的服务是并行加载的。这种方式可以快速地将全部的服务一次性加载。与按需加载不一样,分组加载能够在应用启动成功之时就能够当即服务;同时对于按序加载能过作到更加快速地启动服务。以下图,ServiceGroup 1
中的 A
, B
, 'C' 是并行加载的,ServiceGroup 2
在 ServiceGroup 1
加载完成以后才开始加载。this
服务 A, B, D 之间没有依赖,因此它们的加载能够是无序的。当 ServiceGroup 1
加载完成以后,ServiceGroup 2
在加载以前就已经加载了必要的服务。这时候不会出现 ServiceNotFound
的问题。下面的代码使用了 DSL 来描述整个加载的过程:
GroupedServiceBus serviceBus = ... serviceBus.start(serviceA, serviceB, serviceD) .then(serviceC, serviceE) .awaitStarted();
销毁的顺序必须是加载顺序的反序。这样你才能保证不回出现必要服务的丢失。
使用 Gauva 的 Service 做为服务的基础接口,下面给出了一个分组加载的简单实现。
import com.google.common.util.concurrent.Service; public interface GroupedServiceBus { void awaitStarted(); void awaitStopped(); GroupedServiceBus start(Service... services); GroupedServiceBus then(Service... services); GroupedServiceBus awaitServiceGroupStarted(Service... services); }
使用 DSL 的方式设计 API 能过让它变得更加容易使用跟理解。
public interface KernelService { }
可使用 KernelService
来指定哪些服务是必须正确加载的,当 KernelService 加载失败应用并不能提供一个正确的服务,这时你也许能够将整个程序退出。
public class GroupedServiceBusImpl implements GroupedServiceBus { private final List<ServiceManager> serviceCluster = new ArrayList<>(); private final ServiceManager.Listener listener = new ServiceManager.Listener() { @Override public void failure(Service service) { if (service instanceof KernelService) { logger.error("KernelService [{}] start failure, system will be exit 1.", service.getClass()); System.exit(1); } else { logger.error("Service [{}] start failure.", service.getClass()); } } }; @Override public void awaitStarted() { if (!serviceCluster.isEmpty()) { for (final ServiceManager serviceManager : serviceCluster) { awaitStartedServiceManager(serviceManager); } } } @Override public void awaitStopped() { if (!serviceCluster.isEmpty()) { for (int i = serviceCluster.size() - 1; i > -1; i--) { final ServiceManager serviceManager = serviceCluster.get(i); serviceManager.stopAsync(); serviceManager.awaitStopped(); } serviceCluster.clear(); } } @Override public GroupedServiceBus start(final Service... services) { then(services); return this; } @Override public GroupedServiceBus then(Service... services) { final ServiceManager serviceManager = new ServiceManager(ImmutableList.copyOf(services)); serviceManager.addListener(listener); serviceCluster.add(serviceManager); return this; } private void awaitStartedServiceManager(final ServiceManager serviceManager) { if (!serviceManager.isHealthy()) { serviceManager.startAsync(); serviceManager.awaitHealthy(); } } }
上面的 serviceCluster
是一个 ServiceManager
的集合,在这里 ServiceManager
就是咱们上面讲到的 ServiceGroup
。在 ServiceGroup
的全部服务均可以并行加载。
这三种模式没有哪种是绝对正确跟优秀的。在程序设计中,除了要考虑性能,稳定性等同时还要避免陷入过分设计的陷阱。你的选择须要适应你的环境!