微服务架构是目前非常火的一个架构,许多的大型互联网项目也都会采用这样的架构,而Spring Cloud便是基于这样的一种架构所提供的一种一站式解决方案,这样的一种方案也是在进行微服务架构学习过程中必须学习的一部分
我一直认为,在对一个东西进行学习或者操作之前,一定要先对这个东西进行了解,那么,微服务架构为什么这么火呢?
在进行许多的资料查看和分析之后,我总结了一些别人的我比较认同的看法以及我自己的看法,总结出来的原因大概如下:
在目前这个“互联网+”的时代,我们会发现身边的各行各业各种公司都在结合互联网进行模式的转型或者是进行核心竞争优势的打造或者扩大,软件市场整体需求应该还是在一个上扬的阶段;而互联网时代的产品通常有几类特点:创新成本低、需求变化快,用户群体庞大,它和几年前我们熟悉的单块架构应用有着本质的不同,从系统架构的角度出发,构建灵活、易扩展的系统,快速应对需求的变化;同时,随着用户量的增加,如何保证系统的可伸缩性、高可用性,成为系统架构面临的挑战,而微服务便可以很好去解决这样的一个问题。
纵观IT行业过去的十年,敏捷、精益、持续交付等价值观、方法论的提出以及实践,让很多组织意识到应变市场变化、提高响应力的重要性。精益创业(Lean Startup)帮助组织分析并建立最小可实行产品(Minimum Viable Product),通过迭代持续改进;敏捷方法帮助组织消除浪费,通过反馈不断找到正确的方向;持续交付帮助组织构建更快、更可靠、可频繁发布的交付机制。经过这些方法论以及实践的推行和尝试后,从宏观上而言,大部分组织已经基本上形成了一套可遵循、可参考、可实施的交付体系。这时候,逐渐完善并改进各个细节的需求就会更加强烈。所谓细节,就是类似如何找到灵活性高、扩展性好的架构方式、如何用更有效的技术、工具解决业务问题等。
虚拟化技术和基础设施自动化(Infrastructure As Code)的快速发展极大的简化了基础设施的创建、配置以及系统的安装和部署。譬如云平台的成熟以及像Chef、Puppet、Ansible等工具的使用,让更多的基础设施能够通过自动化的方式动态创建。同时,容器化技术的发展以及Docker的出现,更是将虚拟化技术推向了一个史无前例的高潮。另外,DevOps文化的推行打破了传统开发与运维之间的壁垒,帮助组织形成更高效的、开发与运维高度协作的交付团队。这些技术与文化的快速发展,极大程度上解决了传统环境创建难、配置难以及‘最后一公里’的部署难、交付难等问题,成为推动微服务诞生、发展的重要因素之一。
几年前我们熟悉的传统IT系统,也可以称之为单块架构系统,是以技术分层,譬如逻辑层、数据层等。但随着用户需求个性化、产品生命周期变短、市场需求不稳定等因素的出现,单块架构系统面临着越来越多的挑战,而在时间的不断累积过程中,软件工程的规模的迅速扩大,对响应的速度的要求的越来越高;同样的,软件的开发过程中为了提高开发的效率和质量,以及对成本的压缩,对软件的模块化,以及希望像硬件模块一样,能即
插即用,成为了迫切的需求。因此,便需要一种更有效的、更灵活、更适应当前互联网时代需求的系统架构方式。
上面总结起来就是在现在市场发展趋势和背景下,以前的单块架构无法满足当下未来市场的一些需求,而微服务能解决这样的问题,满足这样的需求,同时,能带来更高的利润和收益。那么,为什么说单块架构可能无法满足未来市场需求呢?
1、代码难以理解,新功能难以实现,维护成本增加。
2、阻碍持续发布。测试、发布速度太慢,持续交付周期长。
3、新人培养周期长。随着项目的扩大,新成员的加入也需要花费更多时间去熟悉项目以及熟悉和搭建相关环境
4、可靠性低;任何一个模块的BUG都可能导致整个应用崩溃。
5、技术选型成本高。如果团队希望尝试引入新的框架、技术,或者对现有技术栈升级,通常会面临不小的风险。
6、可伸缩性差
那么我们再来看一下微服务的一些优势和缺点;微服务的优势大概会有一下4点:
1.解决复杂性问题。分解了一个怪物成为一组服务,功能不会受影响,但应用已经被分解成可管理的服务。每个服务都以RPC或者消息驱动的API来定义明确的边界。微服务也强制使用模块化,通常在单体应用代码中比较难以实现。因此,单个服务开发要快的多,易于理解和维护。
2.使每个服务由专注于该服务的团队独立开发。然后一系列的独立运行的微服务共同构建起整个系统,而开发则可以随意选择技术和框架,只要符合API;创建新服务就可以使用新的技术框架了。
3.每个微服务都能独立部署。由于每个服务相对独立并且比较小,修改测试部署都是非常方便的,同时便于持续集成,使持续部署成为可能。
4.微服务架构使每个服务都可以独立调整。你可以为每个服务部署满足它的需求的实例。此外,你可以使用最匹配服务需求的硬件。比如,你可以在EC2上选择CPU密集型处理图像,内存优化型上部署内存数据库服务。
那么微服务是否就是很完美的呢?不,至少当下不是,微服务目前也存在这样的缺点:
1、过于强调服务规模。实际上,一些开发者主张构建极细粒度的10-100LOC服务。虽然小服务是最好的,但是这只是手段而不是微服务的目标。目标是充分分解应用,以便敏捷应用程序的开发和部署。
2、分布式的复杂性。开发人员需要选择和实现基于消息或者RPC的内部交流机制。更有甚者,需要些代码去处理请求目标慢或者不可用的错误。虽然这些都不难,但是它比通过语言级别的方法/过程调用要复杂的多。分区数据库架构。更新多个业务实体的业务交易是相当普遍的。因为有单体数据库,所以在单体应用程序中微不足道。但是,在基于微服务的应用中,你需要更新不同服务的多个数据库。用分布式事务通常不是一个选择,不仅是因为CAP定理。这根本不被现在很多NOSQL数据库和邮件代理支持。你最后不得不用基于一致性(注:放弃一致性,保证可用性和分区容错性。由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。可用性是数据库的容灾,NOSQL有支持,如MongoDB的Replica Sets,需要自行配置)的方法,这对开发者来说更具有挑战性。
3、测试微服务应用更加复杂。例如使用一个现代框架就像Spring Boot,写一个启动代替应用的REST API测试是简单的。相反,一个类似的测试类需要启动这个服务依赖的其他服务(或者配置一个虚拟的)。
4、跨多个服务的修改更复杂。例如,让我们想象你在实现一个需要改变服务A、B、C的故事卡片,A依赖B,B依赖C。在单体应用你可以简单修改对应模块,并与其他集成。相反,在微服务架构,你需要很小心计划和协调每个服务的更改。比如你可能需要更改C,C被B使用,最后服务与A。幸运的是,很多修改通常只影响一个服务,需要协调多个服务的比较少。
5、部署更复杂。单体应用在相同的服务和负载均衡器上部署是很简单的。每个应用实例都配置有基础架构(数据库和消息代理)的访问入口(主机和端口)。相反,微服务通常包括很多服务。比如,Hailo有160个不同的服务,Adrian Cockcroft说Netflix有超过600个。每个服务端都有很多运行时实例。很多可移动组件需要被配置,部署,扩展和监控。另外,你需要实现一个服务发现机制(稍后讨论)使服务可以发现任何其需要通信的其他服务的位置(主机和端口)。传统的基于故障单子和手动操作的方式无法扩展到这种复杂程度。所以,成功部署一个微服务应用程序需要更高水平人员实现的的部署方法和自动化。
那么在对于整体有一定的认识之后,我们回到主题,Spring Cloud的学习,那么,Spring Cloud特点是什么呢?
Spring Cloud的特点是什么呢?
实际上,Spring Cloud是微服务架构的集大成者,将一系列优秀的组件进行了整合。SpringCloud的组件相当繁杂,拥有诸多子项目。大概如下:
图片上可以看出,Spring Cloud拥有诸多组件,而我接下来会介绍的就是Spring Cloud的几大核心组件
这张图片是Eureka的原理图,Spring Cloud Eureka是Spring Cloud Netflix项目下的服务治理模块,而这张原理图也大概阐述了微服务注册发现的过程,具体意思就是服务提供者将服务注册到注册中心;而服务消费者通过注册中心查找服务然后后进行调用,在整个过程中服务的消费者与服务注册中心保持心跳连接,一旦服务提供者的地址发生变更时,注册中心会通知服务消费者。
Eureka包含两个组件:Eureka Server和Eureka Client。
Eureka Server对应的就是上面的注册中心,支持集群部署;Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
而Eureka Client是一个java客户端,用来处理服务注册与发现。同时会将服务端的服务信息缓存到本地。客户端会和服务端周期性的进行心跳交互,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
1、新建一个单独应用(这样才能做到分布式部署,提高稳定性。如果不单独的话,高可用不好实现),都需要在pom.xml进行依赖的导入,也就是需要导入Spring Cloud的管理依赖以及为Eureka服务端(eureka-server)添加安全认证依赖:。
2、编写程序启动类:
记得加@SpringBootApplication注解和@EnableEurekaServer 注解,@EnableEurekaServer的作用是申明这是一个Eureka服务(org.springframework.cloud.netflix.eureka.server.EnableEurekaServer);
3、编写application.yml配置文件
大概内容是这样的:
###服务端口号
server:
port: 8100
###服务名称
spring:
application:
name: app-eureka-center
security:
basic:
enable: true #开启基于HTTP basic的认证
user: #配置用户的账号信息
name: user
password: 123456
eureka:
instance:
#注册中心地址
hostname: 127.0.0.1
###客户端调用地址
client:
serviceUrl:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:8100/eureka/
###是否将自己注册到Eureka服务中,因为该应用本身就是注册中心,不需要再注册自己(集群的时候为true)
register-with-eureka: false
###是否从Eureka中获取注册信息,因为自己为注册中心,不会在该应用中的检索服务信息
fetch-registry: true
4、在eurka服务端添加一个安全认证类
5、将其他微服务注册到Eureka服务中。
第一步:修改pom文件,引入Spring Cloud的管理依赖以及eureka服务依赖。
第二步:修改application.yml配置文件:
###服务端口号(本身是一个web项目)
server:
port: 8081
###起个名字作为服务名称(该服务注册到eureka注册中心的名称,比如商品服务)
spring:
application:
name: app-item
###服务注册到eureka注册中心的地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8100/eureka
###因为该应用为服务提供者,是eureka的一个客户端,需要注册到注册中心
register-with-eureka: true
###是否需要从eureka上检索服务
fetch-registry: true
# 将Instance ID设置成IP:端口的形式
instance:
prefer-ip-address: true
instance-id: 127.0.0.1:${server.port}
第三步:修改启动类,增加@EnableEurekaClient注解(org.springframework.cloud.netflix.eureka.EnableEurekaClient)
而在RestTemplate(不一定在启动类中)的方法上加 @LoadBalanced注解
客户端配置文件中的
instance:
prefer-ip-address: true
instance-id: 127.0.0.1:${server.port}
一定要记得配置,否则将报错,大概会说客户端失效,然后可能还会报一下java.lang.IllegalArgumentException。
在短时间内丢失了服务实例的心跳,不会剔除该服务,这是eurekaserver的自我保护机制的宗旨。主要是为了防止由于短暂的网络故障误删除可用的服务。
所以,一般进入自我保护模式,无需处理。如果,需要禁用自我保护模式,只需要在配置文件中添加配置即可:
(测试环境、开发环境可以关闭自我保护机制,保证服务不可用时及时剔除)
配置方式:在server端 配置文件中添加server配置
eureka:
instance:
#注册中心地址
hostname: 127.0.0.1
###客户端调用地址
client:
serviceUrl:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:8100/eureka/
register-with-eureka: false
fetch-registry: true
server:
enable-self-preservation: false #禁用自我保护模式
Eureka服务是一个单点服务,在生产环境就会出现单点故障,为了确保Eureka服务的高可用,我需要搭建Eureka服务的集群。
搭建Eureka集群非常简单,只要启动多个Eureka Server服务并且让这些Server端之间彼此进行注册即可实现。
第一步,修改eureka server端的application.yml文件:
端口为8100的机器注册到端口为9100的注册中心
###服务端口号
server:
port: 8100
###服务名称
spring:
application:
name: app-eureka-center
security:
basic:
enable: true #开启基于HTTP basic的认证
user: #配置用户的账号信息
name: user
password: 123456
eureka:
instance:
#注册中心地址
hostname: 127.0.0.1
###客户端调用地址
client:
serviceUrl:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:9100/eureka/
###是否将自己注册到Eureka服务中,集群的时候为true
register-with-eureka: true
fetch-registry: true
server:
enable-self-preservation: false
第二步,修改配置文件,再建一个Eureka server工程,启动两个工程(两个工程的name属性一样)测试:
端口为9100的机器注册到端口为8100的注册中心
###服务端口号
server:
port: 9100
###服务名称
spring:
application:
name: app-eureka-center
security:
basic:
enable: true #开启基于HTTP basic的认证
user: #配置用户的账号信息
name: user
password: 123456
eureka:
instance:
#注册中心地址
hostname: 127.0.0.1
###客户端调用地址
client:
serviceUrl:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:8100/eureka/
###是否将自己注册到Eureka服务中,集群的时候为true
register-with-eureka: true
fetch-registry: true
###测试环境、开发环境可以关闭自我保护机制,保证服务不可用时及时剔除
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
把其他的微服务注册到Eureka集群时,可以指定多个,也可以指定一个Eureka服务(因为Eureka服务集群间彼此互联)。若是想指定多个,那么直接在application.yml配置文件的defaultZone 写上想要指定的注册中心的地址即可,在Eureka集群情况下,即使停掉了部分Eureka服务端,其他的微服务模块只要访问其他的Eureka服务端,项目不受影响。
通俗点讲就是某一个服务提供者在Eureka中注册了多个服务(将同个服务部署在多个机器上),那么请求哪个呢?Ribbon的作用是就是会帮你解决这个问题,在每次请求时选择一台机器,根据负载均衡算法均匀的把请求分发到各个机器上。
而Ribbon的实现同样不难,步骤相似,也是引入依赖(可不引,因为spring-cloud-starter-netflix-eureka-client中已经包含所需依赖);接着为某些服务设置注解,重写某些具体实现,就可以了;当然,你也可以通过设置配置文件来设置负载均衡算法。
在微服务架构中,为了保证高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。而整个项目存在很多服务单元,若某个单元出现故障,就很容易因为依赖关系产生故障蔓延,最终导致整个系统瘫痪,这就是服务故障的“雪崩”效应;而这样相比较于传统架构更加不稳定。
通俗来讲就是当系统处于高并发的场景下,此时如果大量请求涌过来的时候,某个服务出现了故障,基本所有的线程都会卡在请这个服务这里,导致其他服务没有一个线程可以处理请求;从而导致其他的用户在请求其他服务的时候,发现其他的服务也挂了,不响应任何请求了,最终整个系统雪崩的情况。
其实,Hystrix 是隔离、熔断以及降级的一个框架。
在实际环境中,Hystrix会搞很多个小小的线程池,比如某个请求库存服务是一个线程池,请求仓储服务是一个线程池,请求订单服务也是一个线程池。每个线程池里的线程就仅仅用于请求那个服务。
如果现在某个服务挂了,就会导致那些用来调用这个挂掉的服务的线程都卡死不能工作。
但由于其他服务的线程池都是正常工作的,所以这些服务不会受到任何影响。只不过调用挂掉的服务的时候,每次都会报错。这个时候我们可以对这个挂掉的服务直接熔断,比如在 5 分钟内请求这个服务直接就返回了,不要去走网络请求卡住几秒钟,这个过程,就是所谓的熔断!实际上,我们还可以来个降级:每次调用这个挂掉的服务,就在数据库里记录一条消息,记录本来要干嘛,因为挂掉了,所以导致没能成功干活。等服务恢复了,你可以根据这些记录手工干活。这个过程,就是所谓的降级。
基本实现过程其他组件一样,大同小异,可以参考:http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html
Feign介绍
Feign搭建:
1、pom.xml导入依赖
2.创建一个FeignClient接口
3、业务具体实现类去进行接口调用
4、在启动类中添加 @EnableFeignClients 注解
实际上,Feign Client会在底层根据你的注解,跟你指定的服务建立连接、构造请求、发起请求、获取响应、解析响应等等。这一系列脏活累活,人家 Feign 全给你干了。
那么问题来了,Feign 是如何做到这么神奇的呢?很简单,Feign的一个关键机制就是使用了动态代理。大概流程如下
首先,如果你对某个接口定义了 @FeignClient 注解,Feign 就会针对这个接口创建一个动态代理。
接着你要是调用那个接口,本质就是会调用 Feign 创建的动态代理,这是核心中的核心。
Feign的动态代理会根据你在接口上的 @RequestMapping 等注解,来动态构造出你要请求的服务的地址,然后基于Ribbon的负载均衡去调用REST服务。
最后,针对这个地址,发起请求、解析响应。
服务方参数列表使用注解@RequestParam、@PathVariable修饰,然后指定多个参数;或者通过Map指定参数
在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。
像 Android、iOS、PC 前端、微信小程序、H5 等等,不用去关心后端有多少个服务,就知道有一个网关,所有请求都往网关走,网关会根据请求中的一些特征,将请求转发给后端的各个服务;而且有一个网关之后,还有很多好处,比如可以做统一的降级、限流、认证授权、安全等等。
在对Zuul使用过程中,过程也是导入依赖,然后配置使用,可以结合Eureka,做到比较高可用
过滤器是Zuul的重要组件,是一个抽象类,其实现类需要实现4个方法:
shouldFilter:返回一个Boolean值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
run:过滤器的具体业务逻辑。
filterType:返回字符串代表过滤器的类型
a)pre:请求在被路由之前执行
b)routing:在路由请求时调用
c)post:在routing和errror过滤器之后调用
d)error:处理请求时发生错误调用
filterOrder:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。
执行流程:
Config Server是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认使用Git存储配置文件内容,也可以使用SVN存储,或者是本地文件存储,因此它轻松支持标签版本的配置环境,以及可以访问用于管理内容的各种工具。
Config Client是Config Server的客户端,用于操作存储在Config Server中的配置内容。微服务在启动时会请求Config Server获取配置文件的内容,请求到后再启动容器。
使用Spring Cloud Config的架构:
整体的简单实现:
服务端整体的搭建和上面的相似,都是进行工程建立、导入依赖、入口类编写、编写配置文件这样类似的步骤;进行客户端搭建的时候,只需要在各个微服务端进行搭建就可以了,需要注意的是:不能直接在application.yml上去配置,而是需要重新建立一个bootstrap.yml专门去配置,然后记得要使用actuator 监控中心以及借助与git的webhook(web钩子)实现自动更新,这样就可以自动实现项目在上线之后,发现有更新了就自动请求监控中心,进行配置文件的更新。
码云、github等git服务器提供了web hook功能,意思是在仓库中的资源发生更新时会通知给谁,这里的谁是一个url地址。配合进行使用的话就可以做到用户通过工具修改配置中心的某些配置文件,然后这个过程中会通知某个微服务端,让这个微服务端自动去请求服务中心,进行配置文件的更新。
大概流程如下:
将Config Server作为一个微服务,并且将其注册的Eureka中,可以解决硬编码的问题
不使用的话,整体流程是这样的:
各个微服务端通过在配置文件采用硬编码的方式配置Config
Server也就是配置中心的地址,然后从配置中心获取到某些配置文件。
借助于actuator 监控中心以及git的webhook(web钩子)然后 一旦用户通过工具提交修改了配置中心的配置文件,可以让微服务端自动请求监控中心,进行配置稳健的更新;但是采用硬编码的形式,一旦服务中心本身的端口改变,那么就需要手动修改全部的使用了config组件的所有微服务的bootstrap.yml。如果把Config Server作为一个微服务,并且将其注册的Eureka中,那么每次获取服务中心的端口号,都是通过Eureka服务端去动态获取的,就可以解决硬编码所带来的问题。(这样的话需要在其他微服务端进行配置,主要是修改bootstrap.yml配置,而对bootstrap.yml的修改核心就是配置Eureka的信息,而实际上我们对其他的微服务端早就在application.yml配置了Eureka的信息,为什么在bootstrap.yml还需要配置?这是因为在Spring Boot中bootstrap.yml在application.yml之前加载,所以即使在application.yml中以及配置Eureka的信息,是使用不了的,所以需要在bootstrap.yml中配置Eureka的信息。)
那么使用了Spring Cloud Bus 后的架构大概如下:
目前Spring Cloud Bus消息总线只是实现了对RabbitMQ以及Kafka的支持。
具体配置可以直接百度。配置过程和其他组件相似。
上面这个架构其实可以优化,因为上面的架构实际上违反了微服务架构中的职责单一的原则。可以通过让Config Server不仅仅提供配置查询的服务,而且还要负责更新消息的发送的服务这样的方式进行优化,架构如下:
具体实现过程可以百度,和Eureka实现过程基本一样,只是注意在 添加依赖和编写配置文件的时候要注意zk、Eureka、consul不能共存,只能使用一种。
关于这一份资料,这也是我在学习过程中,进行总结的,很多都是前辈经验,网上 也可以查到很多相似的甚至更好的
在学习过程中,一定要养成的习惯是不断去总结,形成自己的东西,自己写过,中间肯定会理清自己的一些思路和思绪,这样比你只是对着看或者对着做效果更好
形态意识也很重要,在进行一定了解之后再进行学习,再总结,再补充学习,再总结,中间会不断实践,而这样的一个过程,是一个不断去补充和提高你对某一事物的形态意识的认知的过程。
关于Spring Cloud ,我也是新手,有错的地方请指正,大家一起学习。