微服务架构设计模式对于单块设计模式而言有很多优点。核心思想就是将单个巨大的应用划分成互联的不同应用。与单块应用类似,每个微服务都有其自己的层级架构。
使用下列的模式,微服务可以轻易取得如下优点:
可扩展性一款典型的应用会使用3个方向的扩展。X轴扩展是指横向扩展应用,Y轴扩展是指划分不同的应用功能,Z轴扩展是指对于数据的分区(partioning)和分片(sharding)。当Y轴扩展应用到单块应用时,该应用就会被打散,按照符合微服务特性的业务功能,形成许多更小的单元。
模式:每个微服务有其隔离的实例或容器。服务级别的负载均衡可以通过将相同服务托管于多个实例实现。
可用性如同相同的微服务会托管于多个实例中一样,微服务会部署于不同的实例中,这使整个系统高度可用。
模式:服务级别的负载均衡可被用以实现高可用性,断路器模式可被用以实现容错,服务配置和服务发现可使发现新服务以通讯成为可能。
持续部署每个微服务均是独立的。这就导致任何服务均可独立于其它服务完成部署,从而达到更加快速且可持续性的部署目标。
松耦合微服务提供不同的方式实现松耦合。每个微服务都应在服务级别有其自身的层级架构,并且在使用数据库作为持久层时,它运行于自己的独立环境中。
技术多样性将微服务视为隔离的特性,多种技术的混合可以用于实现服务于整体应用的多种微服务。
高效成本服务实例可以基于应用使用率进行优化。低价实例可以用以最低优先的服务,而高价实例则可以用到关键业务服务。
性能考虑到微服务技术多样化的优点,这会对性能有直接影响。例如,高阻塞率的服务调用在进线程技术栈中实现,CPU密集型服务在多线程技术栈中实现。
和单块架构相比,微服务也有一些缺点。
诸如,和单块应用相比,开发和管理分布式应用很困难。微服务需要进程间通讯(IPC)机制以使不同微服务之间得以通讯,当然这可能会对性能有点影响(取决于网络带宽)。
以下是为了实现高效微服务需要实现的功能:
自行实现上述的所有特性是很复杂的,会占用大量时间,并且会使开发者消耗大量时间来开发和测试基础框架配置。然而,如果你已经具备了上述的一切,那么你就只需要聚焦于业务逻辑了。
Spring Cloud 框架提供了相关工具在分布式服务中快速构建一些基础模式,例如配置管理,服务发现,断路保护,分布式session,服务网关,rest客户端和服务级负载均衡器等。Spring Cloud项目通过在项目中添加一些Maven依赖来完成初始化。这些也可以由使用 spring initialize 的项目创建。
Spring Cloud的许多组件来自Netflix开源软件中心(Netflix OSS),它们对于微服务部署而言是至关重要的。
Spring Cloud基于Spring Boot构建,其中Spring Boot包含了使用最小配置的内嵌tomcat服务器。
如下是在AWS云上使用微服务的分布式应用的架构图。
上述图例中,我们有3个不同的微服务,其中主微服务使用REST客户端与微服务A和微服务B通信。微服务A运行于2个容器中,微服务B运行于同一实例的2个容器中。主微服务暴露给客户端,会通过服务客户端与任何微服务A或微服务B的实例互动,这个客户端是一款负载均衡器。这将会使得负载均衡不仅存在于实例级别,更存在于同一实例的服务级别。这就是所谓的服务级负载均衡。当向一个服务发送请求时,客户端服务会通过查询Eureka服务注册和服务发现取得服务实例的地址。
Spring Cloud已经支持使用Eureka作为服务注册,并且Eureka服务注册可以通过添加 @EnableEurekaServer
标注开启。
@SpringBootApplication @EnableEurekaServer public class EurekaServer { public static void main(String[] args) { System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(EurekaServer.class, args); } }
用于服务注册的application.yml:
eureka: instance: hostname: localhost client: # Not a client, don't register with yourself registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ server: port: 1111 # HTTP (Tomcat) port
任何微服务都可以通过添加 @EnableDiscoverClient
标注,将Eureka服务注册的地址以及端口放置于application.yml或application.properties文件,以将自己注册到Eureka服务器。
eureka: client: serviceUrl: defaultZone: http://localhost:1111/eureka/ fetchRegistry: true
Spring Cloud提供多种进程间通讯方式,其中包括外部客户端和Rest模板。外部客户端是十分灵巧、干净以及易于实现的。可以通过添加 @EnableFeignClient
标注来开启外部客户端。
客户端应用必须创建一个接口,该接口使用 @FeignClient
标注,需配合服务ID,内部的方法使用 @RequestMapping
标注。
@FeignClient(value = "serviceA") public interface ServiceClientA { @RequestMapping(value = "/user/{userId}", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE }) public UserProfile getUserProfile( @PathVariable("userId") Integer userId); }
我们可以通过创建一个新的类来调用这些接口方法,ServiceClientBean.java。
@Component public class ServiceClientBean { @Autowired private ServiceClientA serviceClientA; @HystrixCommand(fallbackMethod = "defaultMethod") public UserProfile getUserProfile(Integer userId) { UserProfile user=serviceClientA.getUSerProfile(userId); return user; } public UserProfile defaultMethod() { return new UserProfile(); } }
通过在 @EnableFeignClients
中添加 basePackageClasses
属性值,所有 @FeignClient
类都能以Spring bean的形式注册。
@EnableFeignClients(basePackageClasses = ServiceClientA.class)
开发者可以在客户端和服务器之间共享相同的接口定义,但这会增加客户端和服务器之间的耦合度。
外部客户端自动支持使用Ribbon的负载均衡。Ribbon是客户端的负载均衡器,其对HTTP和TCP请求提供了大量控制支持。我们可以使用外部属性 client.ribbon.*
来配置Ribbon客户端。
在ServiceClientBean.java中,我们添加了一个 @HystrixCommand
标注来处理部分失败。此命令会告诉Spring,该方法容易出错。Spring Cloud库包装了这些方法以通过断路器来实施容错和延迟容忍。典型的Hystrix命令后面跟着回滚方法。至于故障,Hystrix会自动启用回滚方法提醒,并且将流量引导至回滚方法。
如果微服务A无法在限定时间内发送响应或者是干脆直接宕掉,那么Hystrix将会调用回滚方法来获取默认的响应。
我们可以通过添加Hystrix仪表盘应用来查看Hystrix统计和监控。
这里还有一个称为Zuul代理的组件,能够充当服务网关。上文的架构图例中并没有提及。服务网关内部调用多个微服务,并将来自这些微服务的结果聚合,并返回给客户端服务。
Zuul代理内部使用Eureka服务器作为服务发现,使用Ribbon作为服务实例间的负载均衡。
分布式应用需要某种中心化日志框架,这可以通过ELK工具栈来简单实现。