Spring Cloud 微服务实战

                                                    SpringCloud微服务实战

服务治理

       主要用来实现各个微服务实例的自动化注册与发现。

       *服务注册:在服务治理框架中,通常都会构建一个注册中心,每个服务单元向注册中心登记自己提供的服务,将主机与端口号,版本号,通信协议等一些附加信息告知注册中心,注册中心按服务名分类组织服务清单。

另外,服务注册中心还需要以心跳的方式去检测清单中的服务是否可用,若不可用需要从服务清单中剔除,达到排除故障服务的效果。

         *服务发现:在服务治理框架下运作,服务间的调用不再通过指定具体的实例地址来实现,而是通过向服务名发起请求调用实现。所以,服务调用方在调用服务提供方接口的时候,并不知道具体的服务实例位置。因此,调用方需要向服务注册中心咨询服务,并获取所有服务的实例清单,以实现对具体服务实例的访问。比如,服务C希望调用服务A,服务C就需要向注册中心发起咨询服务请求,服务注册中心就会将服务A的位置清单返回给服务C。当服务C要发起调用的时候,便从该清单中以某种轮询策略(负载均衡)取出一个位置来进行服务调用。

 

Netflix Eureka

         Eureka服务端,也称服务注册中心,支持高可用配置。它依托于强一致性提供良好的服务实例可用性,可以应对多种不同的故障场景。如果Eureka以集群模式部署,当集群中有分片出现故障时,那么Eureka就转入自我保护模式。允许在分片故障期间继续提供服务的发现和注册,当故障分片恢复运行时,集群中的其他分片会把他们的状态再次同步回来。

         服务端配置:

                   Server.port:8080   //指定服务注册中心端口

                   eureka.client.register-with-eureka:false   //代表不向注册中心注册自己

                   eureka.client.fetch-registry:  //代表不需要去检索服务

         Eureka客户端,主要处理服务的注册与发现。客户端服务通过注解和参数配置的方式,嵌入在客户端应用程序的代码中,在应用程序运行时,Eureka客户端向注册中心注册自身提供的服务并周期性的发送心跳来更新它的服务租约。同时,它也能从服务端查询当前注册的服务信息并把它们缓存到本地并周期性的刷新服务状态。

         注册服务提供者:

                   Spring.application.name=hello-service    //为服务命名

                   eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka/   //指定服务注册中心地址

        

         高可用注册中心:

        

Eureka Server的高可用实际上就是讲自己作为服务向其他服务注册中心注册自己,这样就可以形成一组相互注册的服务注册中心,以实现服务清单的互相同步,达到高可用的效果。

构建一个双节点的服务注册中心集群:

 

 

         服务提供方也要做相应的改动:

         Eureka.client.serviceurl.defaultZone=http://peer:1111/eureka/,http://peer2:1112/eureka/

这样,服务就同时注册到了peer1和peer2上,若断开peer1,由于compute-service同时也向peer2注册,因此在peer2上的其他服务依然能访问heel-service,从而实现了服务注册中心的高可用。

         Eureka.instance.prefer-ip-address=true   //true使用IP地址来定义注册中心地址,false使用主机名

 

服务发现与消费:

         服务消费者主要完成两个目标:发现服务以及消费服务。其中发现服务的任务有Eureka的客户端完成,而消费服务的任务有Ribbon完成。Ribbon是一个基于TTP和TCP的客户端负载均衡器,它可以在通过客户端配置的ribbonServerList服务端列表去轮询访问已达到均衡负载的作用。在主类中通过@EnableDiscoveryClient注解让该应用注册为Eureka客户端应用,以获得服务发现的能力,同时在该主类中创建RestTemplate的Spring Bean实例,并通过@LoadBalanced注解开启客户端负载均衡。

 

Eureka详解

         Eureka服务治理体系中的三个核心角色:

服务注册中心:Eureka提供的服务端,提供服务注册与发现的功能

服务提供者:提供服务的应用,可以是Spring Boot应用,也可以是其他技术平台且遵循Eureka通信机制的应用,它将自己提供的服务注册到Eureka,以供其他应用发现。

服务消费者:消费者应用从服务注册中心获取服务列表,从而使消费者可以知道去何处调用其所需要的服务。

很多时候,客户端既是服务提供者也是服务消费者。

 

 

 

 

 

服务治理机制:

 

 

服务提供者

         服务注册

         “服务提供者”在启动的时候会通过发送REST请求的方式将自己注册到Eureka Server上,同时带上自身服务的一些元数据信息。Eureka Server接收到这个REST请求之后,将元数据信息存储在一个双层结构Map中,其中第一次的key是服务名,第二次的key是具体服务的实例名。

         服务同步:

         如上图架构图所示,两个服务提供者分别注册到了两个不同的服务注册中心上,它们的信息分别被两个服务注册中心所维护。由于服务注册中心之间相互注册为服务,当服务提供者发送注册请求到一个服务注册中心时,它会将该请求转发给集群中相连的其他注册中心,从而实现注册中心之间的服务同步。通过服务同步,两个服务提供者的服务信息就可以通过这两台服务注册中心的任意一台获取到。

         服务续约:

         在注册完服务之后,服务提供者会维护一个心跳用来持续告诉Eureka Server:“我还活着”,以防止Eureka Server的“剔除任务”将该服务实例从服务列表中排除出去,称该操作为服务续约(Renew)。

         关于服务续约有两个重要属性,我们可以关注并根据需要来进行调整:

         eureka.instance.lease-renewal-interval-in-seconds = 30      //定义服务续约任务的调用间隔时间,默认30s

         eureka.instance.lease-expiration-duration-in-seconds = 90  //定义服务失效的时间,默认为90s

 

         服务消费者

         获取服务

         当启动服务消费者的时候,它会发送一个REST请求给服务注册中心,来获取上面注册的服务清单。为了性能考虑,Eureka Server会维护一份只读的服务清单来返回给客户端,同时该缓存清单会每隔30s更新一次。

         eureka.client.fetch-registry = true // 设置能否获取服务

         eureka.client.registry-fetch-interval-seconds = 30  //缓存清单更新时间设置

         服务调用

         服务消费者在获取服务清单后,通过服务名可以获得具体提供服务的实例名和该实例的元数据信息。因为有这些服务实例的详细信息,所以客户端可以根据自己的需要决定具体调用哪些实例,在Ribbon中会默认采用轮询的方式进行调用,从而实现客户端的负载均衡。

         对应访问实例的选择,Eureka中有Region和Zone的概念,一个Region中可以包含多个Zone,每个服务客户端需要被注册到一个Zone中,所以每个客户端对应一个Region和一个Zone。在进行服务调用的时候,优先访问同处一个Zone中的服务提供方,若访问不到,就访问其他的Zone。

         服务下线

         在客户端程序中,当服务实例进行正常的关闭操作(关闭或者重启服务)时,它会出发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我要下线了”。服务端在接收到请求之后,将该服务妆台置为下线(DOWN),并把该下线事件传播出去。

 

         服务注册中心

         失效剔除

         有时候,服务实例并不一定会正常下线,可能由于内存溢出,网络故障等原因使得服务不能正常工作,而服务注册中心并未收到“服务下线”的请求。为了从服务列表中将无法提供服务的实例剔除,Eureka Server 在启动时会创建一个定时任务,默认每隔一段时间(默认60s)将当前清单中超时(默认90s)没有续约的服务剔除出去。

         自我保护

         服务注册到Eureka Server之后,会维护一个心跳连接,告诉Eureka Server自己还活着。Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况(单机调试很容易满足,实际在生产环境上通常是由于网络不稳定导致),Eureka Server会将当前的实例注册信息保护起来,让这些实例不会过期,尽可能保护这些注册信息。但是,在这段保护期间内实例若出现问题,客户端很容易拿到实际已经不存在的服务实例,会出现调用失败的情况,所以客户端必须要有容错机制,比如可以使用请求重试,断路器等机制。

         在本地开发的时候,可以使用eureka.server.enable-self-preservation=false来关闭保护机制,以确保注册中心可以将不可用的实例正确剔除。

        

         配置详解

         在Eureka的服务治理体系中,主要分为服务端和客户端,服务端为服务注册中心,客户端为各个提供接口的微服务应用。当构建了高可用的注册中心后,该集群中所有的微服务应用和后续将要介绍的基础类应用(入配置中心,API网关等)都可以视作该体系下的一个微服务(Eureka客户端)。服务注册中心也是一样,只是高可用环境下的服务注册中心除了作为客户端之外,还为集群中的其他客户端提供服务注册的特殊功能。

         Eureka客户端的配置主要分为以下两个方面:

  1. 服务注册相关的配置信息,包括服务注册中心的地址,服务获取的间隔时间,可用区域等。
  2. 服务实例相关的配置信息,包括读物实例的名称,IP地址,端口号,健康检查路径等。

 

 

服务注册类配置

         指定注册中心:通过eureka.client.serviceUrl参数实现,该参数配置值存储在HahMap类型中,并且设置了一组默认值,默认值key为defaultZone,value为http://localhost:8761/eureka/。(配置多个服务注册中心用逗号分隔)

         其他配置:

 

 

服务实例类配置:

         元数据:元数据是Eureka客户端在向服务注册中心发送注册请求时,用来描述自身服务信息的对象,其中包含了一些标准化的元数据,比如服务名称,实例名称,实例IP,实例端口等用于服务治理的重要信息;以及一些用于负载均衡策略或是其他特殊用途的自定义元数据信息。

 

客户端负载均衡:Spring Cloud Ribbon

         Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,基于Netflix Ribbon实现。微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续的Feign。

         客户端负载均衡

         负载均衡是对系统高可用,网络压力的缓解和处理能力扩容的重要手段之一。通常所说的负载均衡都指的是服务端负载均衡,其中发呢为硬件负载均衡和软件负载均衡。硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备,比如F5等;而软件负载均衡则是通过在服务器上安装一些具有负载均衡功能或模块的软件来完成请求分发工作,比如Nginx。架构如下:

         硬件和软件负载均衡都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务器节点以保证清单中都是可以正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按某种算法(比如线性轮询,按权重负载,按流量负载等)从维护的可用服务端清单中取出一台服务端的地址,然后进行转发。

         客户端负载均衡和服务端负载均衡最大的不通电在于服务清单所存储的位置。在客户端负载均衡中,所有客户端节点都维护者自己要访问的服务端清单,这些服务端的清单来自于服务注册中心。同负载均衡的架构类似,在客户端负载均衡中也需要心跳去维护服务端清单的健康性。

 

RestTemplate详解

RestTemplate会使用Ribbon的自动化配置,同时通过配置@LoadBalanced还能够开启客户端负载均衡。

下面详细介绍RestTemplate针对几种不同请求类型和参数类型的服务调用实现:

GET请求

在RestTemplate中,对GET请求可以通过下面两种方法进行调用实现:

第一种:getForEntity函数。该方法返回的是ResponseEntity,该对象是Spring对HTTP请求响应的封装,其中主要存储了HTTP的几个重要元素,比如HTTP请求状态码的枚举对象HttpStatus(也就是常说的404,500这些状态码),在它的父类HttpEntity中还存储者HTTP请求的头信息对象HttpHeader以及泛型类型的请求体对象。

比如下面的例子,就是访问USER-SERVER服务的/user请求,返回的ResponseEntity对象中的body内容会根据第二个参数转换为String类型。

 

若希望返回的body是一个User对象类型,可以这项实现:

 

 

         getForEntity函数实际上提供了以下三种不同的重载实现:

         1. getForEntity(String url,Class responseType,Object …urlVariables)该方法提供了三个参数,url为请求的地址,responseType为请求响应体body的包装类型,UrlVariables为url中的参数绑定。例如:ResponseEntity<String> tity =restTemplate.getForentity(http://USER-SERVICE/user?name={1},String class,”didi”);

         2. getForEntity(String url,Class responseType,Map urlVariables),例如:

 

         3. getForEntity(URI url,Class responseType):该方法使用URI对象来替代之前的url和urlVarivables参数来指定访问地址和参数绑定。URI是JDK java.net包下的一个类,表示一个统一资源标识符引用。例如:

 

 

         第二种:getForObject函数。该方法可以理解为对getForEntity的进一步封装,它通过HttpMessageConverterExtractor对HTTP请求响应体body内容进行对象转换,实现请求直接返回包装好的对象内容。比如:

当body是一个User对象时,可以直接这样实现:

同样也提供了三种不同的重载实现:

  1. getForObject(String url,Class responseType,Object … urlVariables);
  2. getForObject(String url,Class responseType,Map urlVariables);(Map类型中的key需要与url中占位符名称一一对应)
  3. getForObject(URI url,Class responseType);

POST请求

         在RestForEntity中,对POST请求时可以通过如下三个方法进行调用实现。

         第一种:postForEntity函数。

 

         三种不同的重载方法。

         1. postForEntity(String url,object request,Class responseType,Object …uriVariables);

         2. postForEntity(String url,object request,Class responseType,Map uriVariables);

         3. postForEntity(URI url,object request,Class responseType);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值