前言
- 理解springCloud的概念知识,可以看之前的文章 文章链接
使用微服务架构的分布式系统,微服务之间通过网络通信。我们通过服务提供者与服务消费者来描述微服务间的调用关系。
- 服务提供者:服务的被调用方,提供调用接口的一方
- 服务消费者:服务的调用方,依赖于其他服务的一方
本文目录
准备工作
基础环境
软件 | 版本 |
---|---|
jdk | 1.8 |
mysql | 5 |
数据库表:
create table `easybuy_product` (
`ep_id` bigint (20),
`ep_name` varchar (300),
`sell_point` varchar (1500),
`ep_price` bigint (20),
`ep_stock` int (10),
`ep_file_name` varchar (1500),
`epc_id` bigint (10),
`status` tinyint (4),
`created` datetime ,
`updated` datetime ,
`ep_description` varchar (27000),
`epc_child_id` int (40)
);
搭建一个基础的项目
过程省略,直接上源码 gitee源码
项目结构
微服务理论基础
服务调用
在用户下单时需要调用商品微服务获取商品数据。那应该怎么做呢?
众人皆知商品微服务提供了供人调用的HTTP接口。
所以可以再下定单的时候使用http请求的相关
工具类完成,
如常见的HttpClient,OkHttp,当然也可以使用Spring提供的RestTemplate
RestTemplate介绍
Spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接,
我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。
在Spring应用程序中访问第三方REST服务与使用Spring RestTemplate类有关。RestTemplate类的设计原则与许多其他Spring 模板类(例如JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。
RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如Apache HttpComponents、Netty或OkHttp等其它HTTP library。 考虑到RestTemplate类是为调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连就不足为奇了,后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,RestTemplate类具有headForHeaders()、getForObject()、postForObject()、put()和delete()等方法
RestTemplate方法介绍
该模板类的主要切入点为以下几个方法(并对应着HTTP的六个主要方法)
RestTemplate调用实现
首先回顾下上述的实现
接下来实现order模块使用RestTemplate,通过HTTP协议获取product的服务!!!
order模块获取到product的远程服务!
硬编码存在的问题
至此已经可以通过RestTemplate调用商品微服务的RESTFul API接口。但是我们把提供者的网络地址ip,端口)等硬编码到了代码中,这种做法存在许多问题:
- 应用场景有局限
- 无法动态调整
服务注册Eureka基础
微服务的注册中心
注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就这里找到服务的地址,进行调用。
注册中心的主要作用
服务注册中心(下称注册中心)是微服务架构非常重要的一个组件,在微服务架构里主要起到了协调者的一个作用。注册中心一般包含如下几个功能:
- 服务发现:
- 服务注册/反注册:保存服务提供者和服务调用者的信息
- 服务订阅/取消订阅:服务调用者订阅服务提供者的信息,最好有实时推送的功能
- 服务路由(可选):具有筛选整合服务提供者的能力。
- 服务配置:
- 配置订阅:服务提供者和服务调用者订阅微服务相关的配置
- 配置下发:主动将配置推送给服务提供者和服务调用者
- 服务健康检测
- 检测服务提供者的健康情况
常见的注册中心
- zookeeper
zookeeper它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应
用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项
的管理等。简单来说zookeeper=文件系统+监听通知机制。
- Eureka(接下来要使用的)
Eureka是在Java语言上,基于Restful Api开发的服务注册与发现组件,Springcloud Netflix中的重要组件
- Consul(接下来要使用的)
Consul是由HashiCorp基于Go语言开发的支持多数据中心分布式高可用的服务发布和注册服务软件,
采用Raft算法保证服务的一致性,且支持健康检查
- Nacos
Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
简单来说 Nacos 就是 注册中心 + 配置中心的组合,提供简单易用的特性集,帮助我们解决微服务开发必会涉及到的服务注册 与发现,服务配置,服务管理等问题。Nacos 还是 Spring Cloud Alibaba 组件之一,负责服务注册与发现。
最后我们通过一张表格大致了解Eureka、Consul、Zookeeper的异同点。选择什么类型的服务注册与
发现组件可以根据自身项目要求决定。
组件名 | 语言 | CAP | 一致性算法 | 服务健康检查 | 对外暴露接口 |
---|---|---|---|---|---|
Eureka | JAVA | AP(可用性、分区容忍性) | 无 | 可配支持 | HTTP |
Consul | Go | CP(一致性、分区容忍性) | Raft | 支持 | HTTP/DNS |
Zookeeper | JAVA | CP(一致性、分区容忍性) | Paxos | 支持 | 客户端 |
Nacos | JAVA | AP(可用性、分区容忍性) | Raft | 支持 | HTTP |
Eureka的概述
Eureka的基础知识
Eureka是Netflix开发的服务发现框架,SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。
上图简要描述了Eureka的基本架构,由3个角色组成:
- Eureka Server
提供服务注册和发现 - Service Provider
服务提供方
将自身服务注册到Eureka,从而使服务消费方能够找到 - Service Consumer
服务消费方从Eureka获取注册服务列表,从而能够消费服务
Eureka的交互流程与原理
图是来自Eureka官方的架构图,大致描述了Eureka集群的工作过程。图中包含的组件非常多,可能比较难以理解,我们用通俗易懂的语言解释一下:
- Application Service 相当于本书中的服务提供者,Application Client相当于服务消费者;
- Make Remote Call,可以简单理解为调用RESTful API;
- us-east-1c、us-east-1d等都是zone,它们都属于us-east-1这个region;
由图可知,Eureka包含两个组件:Eureka Server 和 Eureka Client,它们的作用如下:
- Eureka Client是一个Java客户端,用于简化与Eureka Server的交互;
- Eureka Server提供服务发现的能力,各个微服务启动时,会通过Eureka Client向Eureka Server进行注册自己的信息(例如网络信息),Eureka Server会存储该服务的信息;
- 微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒)以续约自己的信息。如果Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服
务节点(默认90秒); - 每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的方式完成服务注册表的同步;
- Eureka Client会缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费
者依然可以使用缓存中的信息找到服务提供者。
综上,Eureka通过心跳检测、健康检查和客户端缓存等机制,提高了系统的灵活性、可伸缩性和可用 性。
开始搭建Eureka注册中心
搭建Eureka注册中心
这个模块就是上述结构图的ebuy-eureka模块
接下来讲一下具体搭建的步骤
- 1.引入jar包
<dependencies>
<!--引入eureka注册中心的jar-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
- 2.配置启动类和yaml文件
- yaml文件详解
- registerWithEureka: 是否将自己注册到Eureka服务中,本身就是所有无需注册
- fetchRegistry : 是否从Eureka中获取注册信息
- serviceUrlEureka: 客户端与Eureka服务端进行交互的地址
- 启动测试
因为eureka自带了可视化界面,直接访问这个端口,看下效果
商品服务注册
- 引入jar包
<!--基于eureka的注册方式-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 配置application.yaml文件
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9880/eureka/
instance:
prefer-ip-address: true #使用ip地址注册
lease-expiration-duration-in-seconds: 10 #eureka client 发送心跳给server端,续约到期时间(默认90秒)
lease-renewal-interval-in-seconds: 5 #发送心跳续约时间间隔
注:开发环境下,推荐将续约时间以及发送心跳间隔设置小一些,因为面临多次的代码调试,确保请求服务是最新的服务!!!
- 启动类加注解(这一步可以省略)
此版本可以省略该步骤(从Spring Cloud Edgware版本开始, @EnableDiscoveryClient 或 @EnableEurekaClient 可
省略。只需加上相关依赖,并进行相应配置,即可将微服务注册到服务发现组件上。
)
- 启动测试
订单服务注册
同上述步骤(完全相同)
结果展示
Eureka中的自我保护
微服务第一次注册成功之后,每30秒会发送一次心跳将服务的实例信息注册到注册中心。通知 Eureka Server
该实例仍然存在。如果超过90秒(三次心跳)没有发送更新,则服务器将从注册信息中将此服务移除。
Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况(在单机调试的时候很容易满足,实际在生产环境上通常是由于网络不稳定导致),Eureka Server会将当前的实例注册信息保护起来,同时提示这个警告。
保护模式主要用于一组客户端和Eureka Server
之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)
验证完自我保护机制开启后,并不会马上呈现到web上,而是默认需等待 5 分钟(可以通过eureka.server.wait-time-in-ms-when-sync-empty 配置),即 5 分钟后你会看到下面的提示信息:
- 如何关闭自我保护
通过设置
eureka.enableSelfPreservation=false
来关闭自我保护功能。
Eureka中的元数据
Eureka的元数据有两种:标准元数据和自定义元数据。
标准元数据:主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。
自定义元数据:可以使用eureka.instance.metadata-map配置,符合KEY/VALUE的存储格式。这些元数据可以在远程客户端中访问。 在程序中可以使用DiscoveryClient 获取指定微服务的所有元数据信息
- 查看元数据
改造我们的order的调用
由上述可以看到我们可以通过DiscoveryClient拿到我们指定服务名的注册信息,接下来通过这些注册信息改造之前的硬编码
- OrderController
@GetMapping("/dc/{id}")
public EasybuyProduct getOrderBydcId(@PathVariable long id){
List<ServiceInstance> serviceInstances= discoveryClient.getInstances("ebuy-product");
EasybuyProduct easybuyProduct=null;
for (ServiceInstance instance : serviceInstances) {
easybuyProduct=restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/product/"+id,EasybuyProduct.class);
}
return easybuyProduct;
}
通过discoveryClient对象取得服务的指定信息,再通过restTemplate请求指定的服务
注:discoveryClient.getInstances(服务名)
的服务名是product的spring.application.name: ebuy-product(yaml文件中配置)
因为此时该注册中心只有一个product服务,所以serviceInstances集合中只有一个,如果有product集群,那么如何请求服务做到负载均衡呢?
引入Ribbon
基于Ribbon实现订单调用商品服务
不论是基于Eureka的注册中心还是基于Consul的注册中心,SpringCloudRibbon统一进行了封装,所以对于服务调用,两者的方式是一样的。
改造product模块
- 添加一些提示信息
- 启动product集群(注意修改yaml中的server.port,避免冲突)
- 注册中心效果
改造order模块
- 启动类引入Ribbon负载均衡
这里回忆一下,之前我们的SOA面向服务架构的项目,使用dubbo进行服务注册与发现时,负载均衡是在提供者的@DubboService(loadbalance = "roundrobin")
配置的,而现在微服务则是在服务调用者这里进行负载均衡!!!
-
改造controller调用
-
重启order服务,测试
实现负载均衡,默认策略为轮询,那么如何修改负载均衡的策略呢?
- 修改order的yaml文件
- 重启order服务,然后测试
结果不再展示!
实现order的集群
不用修改任何配置,只需要再启动一个order服务就可(记得修改server.port端口)
实现Eureka集群
上述我们已经实现了product、order模块的集群,并且实现了负载均衡,接下来实现注册中心Eureka集群!!!
在上面,实现了单节点的Eureka Server的服务注册与服务发现功能。Eureka Client会定时连接 Eureka Server,获取注册表中的信息并缓存到本地。微服务在消费远程API时总是使用本地缓存中的数 据。因此一般来说,即使Eureka Server发生宕机,也不会影响到服务之间的调用。但如果Eureka Server宕机时,某些微服务也出现了不可用的情况,Eureka Server中的缓存若不被刷新,就可能会影 响到微服务的调用,甚至影响到整个应用系统的高可用。因此,在生成环境中,通常会部署一个高可用 的Eureka Server集群。 Eureka Server可以通过运行多个实例并相互注册的方式实现高可用部署,Eureka Server实例会彼此增 量地同步信息,从而确保所有节点数据一致。事实上,节点之间相互注册是Eureka Server的默认行为。
证实下:上述的Eureka即使宕机,也不会影响已经注册的服务使用
-
关闭erueka服务
-
再次请求
发现注册后的服务仍然是可以使用的!!!
搭建Eureka集群
- 关闭所有服务
记得切换application的spring.profiles.active=product
搭建Eureka集群只需要修改Eureka服务的yml文件即可,具体变动如下:
- 然后挨个启动Eureka服务
修改服务注册的微服务的yml文件
- product微服务和order微服务均修改这一行,然后启动
- 可视化界面显示全部注册成功!!!
Eureka中的常见问题
Eureka服务注册慢
默认情况下,服务注册到Eureka Server的过程较慢。SpringCloud官方文档中给出了详细的原因:
大致含义:服务的注册涉及到心跳,默认心跳间隔为30s。在实例、服务器、客户端都在本地缓存中具
有相同的元数据之前,服务不可用于客户端发现(所以可能需要3次心跳(未修改配置文件90s))。可以通过配置eureka.instance.leaseRenewalIntervalInSeconds
(心跳频率)加快客户端连接到其他服务的过
程。在生产中,最好坚持使用默认值,因为在服务器内部有一些计算,他们对续约做出假设。
服务节点剔除问题
默认情况下,由于Eureka Server剔除失效服务间隔时间为90s且存在自我保护的机制。所以不能有效而 迅速的剔除失效节点,这对开发或测试会造成困扰。解决方案如下:
Eureka Server:
- 配置关闭自我保护,设置剔除无效节点的时间间隔
server:
enable-self-preservation: false #关闭自我保护
eviction-interval-timer-in-ms: 4000 #剔除时间间隔,毫秒
Eureka Client:
配置开启健康检查,并设置续约时间
lease-expiration-duration-in-seconds: 10 #eureka client 发送心跳给server端,续约到期时间(默认90秒)
lease-renewal-interval-in-seconds: 5 #发送心跳续约时间间隔
注:我只配置了续约时间,没配置健康检查(上图第一个我注释掉了),Eureka的健康检查是可配支持,不内置
监控页面显示ip
install-id只是个名称
在Eureka
Server的管控台中,显示的服务实例名称默认情况下是微服务定义的名称和端口。为了更好的对所有服务进行定位,微服务注册到Eureka
Server的时候可以手动配置示例ID。配置方式如下:
-
修改之后,重启服务生效
注意:
prefer-ip-address: true和instance-id分不清吗?
prefer-ip-address是指实际能跳转的地址,instance-id是页面显示的名字 -
注释掉9015端口的prefer-ip-address
- 看左下角的地址是localhost,而不是ip地址
本文结束