spring微服务初识
什么是微服务
微服务是松藕合的分布式软件服务,这些服务执行少量的定义明确的任务。
在微服务的概念逐步形成前,绝大部分基于web的应用都是使用单体架构的风格来进行构建的。在单体架构中,应用程序作为单个可部署的软件制品交付,所有的UI(用户接口)、业务、数据库访问逻辑都被打包在一个应用程序制品中并且部署在一个应用程序服务器上。图1-1展示了这个应用程序的基本架构。
图1-1 单体应用程序强迫开发团队人工同步他们的交付,因为他们的代码需要被作为一个整体进行构建、测试和部署
微服务是一个小的,松耦合的分布式服务,通过将大型代码分解为小型的精确定义的部分,帮助解决大型代码库中传统的复杂问题,思考微服务时,一个重要概念就是分解和分离应用程序的功能,使他们彼此完全独立。图1-2为分解图1-1的应用程序。
图1-2 使用微服务架构,CRM应用将会被分解成一系列完全彼此独立的微服务,让每个开发团队都能够按照自己的步伐前进
微服务架构具有以下特征:
- 应用程序逻辑分解为具有明确定义了职责范围的细粒度组件,这些组件相互协调提供解决方案。
- 每个组件都有一个小的职责领域,并且完全独立部署。微服务应该对业务领域的单个部分负责。此外,一个微服务应该可以跨多个应用程序复用。
- 微服务通信基于一些基本的原则,并采用Http和json这样的轻量级通信协议,在服务消费者和服务提供者之间进行数据交换
- 服务的底层采用什么技术实现并没有什么影响,因为应用程序始终使用技术中立的协议(JSON最常见)进行通信。
- 微服务利用其小的、独立和分布式的性质,使组织拥有明确责任领域的小型开发团队。
使用SpringBoot来构建微服务
如何使用Spring编写一个简单的“Hello World”REST服务。
图1-3展示了这个服务将会做什么,以及Spring Boot微服务将会如何处理用户请求的一般流程
下面是一个例子,它用以访问路径为/hello的REST端点暴露出来
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.PathVariable; @SpringBootApplication @RestController @RequestMapping(value="hello") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @RequestMapping(value="/{firstName}/{lastName}",method = RequestMethod.GET) public String hello( @PathVariable("firstName") String firstName, @PathVariable("lastName") String lastName) { return String.format("{\"message\":\"Hello %s %s\"}", firstName, lastName); } }
@SpringBootApplication告诉SpringBoot框架,这是SpringBoot服务的入口点
@RestController告诉SpringBoot,这个类将作为Spring RestController暴露
@RequestMapping(value="hello")表示这个应用程序的所有的URL将以前缀/hello进行暴露
@RequestMapping(value="/{firstName}/{lastName}",method = RequestMethod.GET)表示SpringBoot将端点暴露为基于GET的REST端点,它将接受两个参数:firstName和lastName。
@PathVariable("firstName") String firstName表示将URL传入的firstName和lastName参数映射到hello方法的两个变量
这只是一个简单的例子,真实开发中可不会只有这么点代码。
如果我们将单体应用程序拆分成微服务,我们能够使系统具有以下特性:灵活的、有弹性的、可扩展的,为此,我们要牢记以下内容:小的,简单的,解耦的服务 = 可扩展的,有弹性的,灵活的应用
云究竟是什么
云已经是一个被过度使用的术语。每个软件供应商通过大肆宣传,宣称提供云,每个平台都是基于云计算的。云计算中存在三种基本模型,它们是:
- Infrastructure as a Service(IaaS)-----基础设施即服务
- Platform as a service(PaaS)-----平台即服务
- Software as a Service(Saas)-----软件即服务
为了理解,我们把每天做饭的任务映射到不同的云计算模型,当你想吃一顿饭,你有四个选择:
- 你可以在家做饭
- 你可以去便利店买预先做好的饭,自己加热
- 你可以通过外卖定一份饭送到家里
- 你可以乘车到餐馆吃饭
不同的云计算模型归结为谁负责什么:云供应商或你。在家吃饭需要你做所有的工作,不使用云模型,便利店买就像使用基础设施即服务(Iaas)计算模型,便利店做好了饭,但你仍然负责加热饭菜,在PaaS模式中,你还是需要提供桌子之类的,就像你定外卖,,在软件即服务(SaaS)模式中,就好比你去一家餐厅,他们为你准备所有东西。使用这些模型的关键因素是以下内容的决策:谁负责维护基础设施?以及构建应用程序用什么技术?在IaaS模型,云供应商提供的基础设施,但你负责选择技术和提供最终解决方案。另一方面,使用SaaS模型,你是供应商提供的服务的被动使用者,对技术选择或维护应用程序的基础设施没有任何责任。
除了上面提到的三种类型,还有功能即服务(Faas)和容器即服务(CaaS)。
作为一个微服务开发者,你不得不决定是否将你的服务部署到以下的环境中:
- 物理服务器:虽然你可以构建和部署你的微服务到物理机,但很少有机构这么做是因为物理服务器的局限性。
- 虚拟机镜像:微服务另一个关键的好处是它们具有快速启动和关闭服务实例以响应应用伸缩和服务故障事件处理的能力。虚拟机是主要的云计算提供商提供的核心服务。一个微服务可以被打包在一个虚拟机镜像。
- 虚拟容器:虚拟容器是在虚拟机镜像里部署微服务的自然延申。与其将一个服务部署到一台完整的虚拟机,许多开发者将他们的服务部署到云上的Docker容器
微服务传统的部署哲学有以下几点:1.简化基础设施管理,2.大规模的横向扩展能力,3.通过地理分布实现高冗余。
微服务不仅仅是编写代码
构建个别微服务的概念很容易理解,运行和支持一个强大的微服务应用(特别是运行在云中)涉及服务更多的编写代码。写一个强大的服务需要考虑几个因素:
- 位置透明:如何管理物理位置,以便在不影响客户端服务的情况下添加和删除服务实例?
- 合适的大小:如何确保服务集中在一个责任领域?
- 可扩展的:如何确保应用程序能够在服务之间的最小依赖关系下快速扩展?
- 可复用的:你如何确保每次启动新的服务实例时,它始终具有与现有实例相同的代码和配置?
- 有弹性的:当服务出现问题时,如何确保客户端服务“fail fast”?
以下六种微服务模式回答了这些问题:
核心开发模式
路由模式
客户端弹性模式
安全模式
日志记录和跟踪模式
构建和部署模式
使用Spring Cloud构建微服务
从零开始实现上述所有模式是一份惊人的工作量。幸运的是,Spring团队集成了大量经过充分测试的开源项目为Spring子项目,统称为Spring Clound。
下面的代码示例与SpringBoot的不同,不能运行此示例,因为需要设置和配置一些支持服务。不过它演示了如何将服务发现、断路器、舱壁和远程服务的客户端负载均衡集成到我们的“Hello World”示例中。
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; @SpringBootApplication @RestController @RequestMapping(value="hello") @EnableCircuitBreaker //使服务能够使用 Hystrix 和 Ribbon 库 @EnableEurekaClient //告诉服务,它应该注册一个 Eureka 服务发现代理, 服务调用是使用服务发现来“查找”远程服务的位置 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @HystrixCommand(threadPoolKey = "helloThreadPool") //使用一个Hystrix断路器包装对helloRemoteServiceCall方法的调用 public String helloRemoteServiceCall(String firstName,String lastName){ ResponseEntity<String> restExchange = restTemplate.exchange( "http://logical-service-id/name/ //使用装饰类RestTemplate以一个“逻辑”的服务ID在Eureka范围里查找服务的物理位置。 [ca]{firstName}/{lastName}", HttpMethod.GET, null, String.class, firstName, lastName); return restExchange.getBody(); } @RequestMapping(value="/{firstName}/{lastName}", method = RequestMethod.GET) public String hello( @PathVariable("firstName") String firstName, @PathVariable("lastName") String lastName) { return helloRemoteServiceCall(firstName, lastName) } }
@EnableCircuitBreaker注解告诉你Spring微服务将在你的应用中使用Netflix的hystrix库。@EnableEurekaClient注解告诉你的微服务使用Eureka服务发现代理注册其本身,你要使用服务发现查找在你的代码里的远程REST服务端点。@HystrixCommand注解将做两件事:首先,任何时候helloremoteservicecall方法被调用时,它不会直接调用。相反,该方法将被委派被Hystrix管理的一个线程池,如果调用时间太长(默认是1秒),将进入Hystrix并中断调用。这就是断路由器模式的实现。其次,该注释的作用是创建一个称为helloThreadPool的线程池,它由Hystrix管理。所有到helloRemoteServiceCall方法的调用只会发生在这个线程池,并由任何其他远程服务发起的调用将被隔离。
那helloRemoteServiceCall方法内发生了什么。@EnableEurekaClient的存在告诉Spring Boot,每当你做出一个Rest服务调用,你要使用一个修改的RestTemplate类。RestTemplate类将允许你为你试图调用的服务引入一个逻辑服务ID:ResponseEntity<String> restExchange = restTemplate.exchange(http://logical-service-id/name/{firstName}/{lastName} ,RestTemplate类将与Eureka服务联系,并查找一个或多个“名称服务”实例的物理位置。作为服务消费者,你的代码永远不必知道该服务位于何处。
学习一个新东西,对他就要有一个整体的概念,有了整体的框架和意思,再添砖加瓦就知道该怎么做了。