背景
1.问题描述
当远程调用的时候,我们的URL是写死的,当更换机器/新增机器时,这个URL就需要跟着变更,就需要去通知所有的相关服务去修改,随之而来的就是各个项目的配置文件反复更新,各个项目的频繁部署,这种没有具体意义,但不得不做的工作,会让人非常痛苦
2.解决思路
在生活中,避免不了和各个机构打交道,就需要保存各个机构的电话号码 如果机构换了电话号码,就需要通知各个使用方,但是这些机构的使用方群体是巨大的,没办法做到一一通知,怎么处理呢?
机构电话如果发生变化,通知114,用户需要联系机构时,先打114查询电话,然后再联系各个机构
114查号台的作用主要有两个:1.号码注册 服务方把电话上报给114 2.号码查询:使用方通过114可以查到对应的号码
同样,微服务开发时,也可以采用类似的方案
服务启动/变更时,向注册中心报道,注册中心记录应用和IP的关系
调用方调用时,先去注册中心获取服务方的IP,再去服务方进行调用
3.注册中心
注册中心主要有三种角色:
1.服务提供者(server):一次业务中,被其他微服务调用的服务,也就是提供接口给其他微服务
2.服务注册者(client):一次业务中,调用其他微服务的服务,也就是其他微服务提供的接口
3.服务注册中心(registry):用于保存server的注册信息,当server节点发生变更的时候,registry会不同变更,服务与注册中心使用一定机制通信,如果注册中心与某服务长时间无法通信,就会注销该实例
他们之间的关系已经工作内容,可以通过两个概念来描述:
服务注册:服务提供者在启动的时候,向registry注册自身服务,并向registry定期发送心跳汇报存活状态
服务发现:服务消费者从注册中心查询服务提供者的地址,并通过该地址调用服务提供者的接口.服务发现的一个重要作用就是提供给服务消费者一个可用的服务列表
CAP理论
CAP理论是分布式系统中最基础而是最关键的理论
C:一致性,此处的一致性,指的是强一致性
强一致性:主库和从库,不论何时,对外提供的服务都是一致的
弱一致性:随着时间的推移最终达到了一致性
A:可用性 对所有的请求都有响应(这个响应可能是错误的数据)
P:分区容错性 在网络分区的情况下,系统仍然可以对外提供服务
CAP理论告诉我们:一个分布式系统不可能同时满足数据一致性,服务可用性和分区容错性这三个基本需求,最多只能满足其中的两个
在分布式系统中,系统间的网络并不能100%保证健康,服务又必须对外保证服务,因此Partition Tolerance不可避免,那就只能在A/C中选择一个.也就是CP/AP架构
正常情况下:
网络异常情况下:
CP架构:为了保证分布式系统对外数据一致性,于是选择不返回任何数据
AP架构:为了保证分布式系统的可用性,节点2返回v0版本的数据(即使这个数据不正确)
常见的注册中心
1.Zookeeper
Zookeeper的官方并没有说他是一个注册中心,但是国内的Java体系,大部分的集群环境都是依赖Zookeeper来完成注册中心的功能
2.Eureka
Eureka是Netflix开发的基于REST的服务发现架构,主要用于服务注册,管理,负载均衡和服务故障转移
3.Nacos
Nacos是spring cloud alibaba架构中的重要的组件,除了服务注册,服务发现功能之外,nacos还支持配置管理,流量管理,DNS,动态DNS等多种特性
| Zookeeper | Eureka | Nacos | |
| CAP理论 | CP | AP | CP/AP 默认为AP |
在分布式环境中,即使拿到一个错误的数据,也胜过无法提供实例信息而造成请求失败好(淘宝双11,京东618都是遵守AP原则)
Eureka介绍
Eureka是Netflix OSS套件中关于服务注册和发现的解决方案.SpringCloud 对Eureka进行了集成,并作为优先推荐方案进行宣传,虽然目前Eureka2.0已经停止维护,新的微服务架构设计中也不再建议使用,但是目前依然有很多公司的微服务系统使用Eureka作为注册中心
Eureka 主要分为两个部分:
Eureka Server:作为注册中心的Server端,向微服务应用程序提供服务注册,发现,健康检查等能力
Eureka Client:服务提供者,服务启动的时候,会向Eureka Server注册自己的信息(IP,端口,服务信息等) Eureka Server会存储这些信息
关于Eureka的学习,主要包含以下三个部分:
1.搭建Eureka Server
2.将service(order-service product-service)都注册到Eureka
3.order-service远程调用的时候,从Eureka中获取到product-service的服务列表,然后进行交互
搭建Eureka Server
Eureka server是一个独立的微服务
1.搭建Eureka-server子模块
2.pom中引入Eureka-server的依赖
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies>3.项目构建插件
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>4.完善启动类
给该项目编写一个启动类,并在启动类上添加@EnableEurekaServer注解,开启Eureka注册中心服务
@EnableEurekaServer @SpringBootApplication public class EurekaServiceApplication { public static void main(String[] args) { SpringApplication.run(EurekaServiceApplication.class,args); } }5.编写配置文件
server: port: 10010 spring: application: name: eureka-server eureka: instance: hostname: localhost client: fetch-registry: false # 表示是否从Eureka Server获取注册信息,默认为true.因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,这里设置为false register-with-eureka: false # 表示是否将自己注册到Eureka Server,默认为true.由于当前应用就是Eureka Server,故而设置为false. service-url: # 设置Eureka Server的地址,查询服务和注册服务都需要依赖这个地址 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #logging: # pattern: # console: '%d{MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n'6.启动服务
访问注册中心:http://127.0.0.1:10010/
我们就可以看到eureka-server已经启动成功了
服务注册
接下来我们把product-service注册到eureka-server中
1.引入eureka-client依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> 2.完善配置文件
eureka: client: service-url: defaultZone: http://127.0.0.1:10010/eureka/3.启动服务
public OrderInfo selectOrderById(Integer orderId){ OrderInfo orderInfo = orderMapper.selectOrderById(orderId); // String url="http://127.0.0.1:9090/product/"+orderInfo.getProductId(); //从Eureka中获取服务列表 List<ServiceInstance> instances = discoveryClient.getInstances("product-service"); String uri = instances.get(0).getUri().toString(); String url=uri+"/product/"+orderInfo.getProductId(); log.info("远程调用URL的信息+ "+url); ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class); orderInfo.setProductInfo(productInfo); return orderInfo; }就可以看到product-service已经注册到eureka上了
服务发现
接下来我们修改order-service ,在远程调用的时候,从eureka-server拉取product-service的服务信息,实现服务发现
1.引入依赖(因为服务注册和服务发现都封装在eureka-client依赖中,所以服务发现时,也是引入eureka-client依赖)
2.完善配置文件
服务发现也需要知道eureka的地址,因此配置内容依然和服务注册一直,都是配置eureka信息
3.远程调用
远程调用的时候,我们需要从eureka-server中获取到prouduct-service的列表(可能存在多个服务),并选择其中一个进行调用
public OrderInfo selectOrderById(Integer orderId){ OrderInfo orderInfo = orderMapper.selectOrderById(orderId); // String url="http://127.0.0.1:9090/product/"+orderInfo.getProductId(); //从Eureka中获取服务列表 List<ServiceInstance> instances = discoveryClient.getInstances("product-service"); String uri = instances.get(0).getUri().toString(); String url=uri+"/product/"+orderInfo.getProductId(); log.info("远程调用URL的信息+ "+url); ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class); orderInfo.setProductInfo(productInfo); return orderInfo; }4.启动服务
刷新注册中心,就可以看到order-service已经注册到eureka上了
这里我们对服务注册和服务发现进行总结
服务注册:
1.加入eureka的依赖 2.修改配置信息 3.启动,测试
服务发现:
1.加入eureka的依赖, 2.修改配置信息 3.修改远程调用的代码
Eureka和Zookeeper的区别
Eureka和Zookeeper都是用于服务注册和发现的工具,区别如下:
1.Eureka是Netflix开源的项目,而Zookeeper是Apache的开源的项目
2.Eureka基于AP原则,保证高可用 而Zookeeper基于CP原则,保证数据一致性
3.Eureka每个节点都是均等的,Zookeeper的节点区分Leader和Follower或Observer 也正是因为这个原因,如果Zookeeper的Leader发生故障时,需要重新选举,选举过程集群会有短暂时间的不可用