服务注册与发现 {Eureka、Zookeeper、Nacos}
写在前面,本文主要是对Nocas的总结和巩固
Eureka的思想
Netflix又称网飞公司,是国外一家最开始卖碟片的公司到后来发展了类似与爱奇艺的一家在线视频服务公司,Spring Cloud最开始的几大组件均来自于这家公司,只是后来由于该公司很多组件都停更不停用,进入维护阶段,导致广大用户的不满,纷纷找好了后路,以至于(Eureka,Feign,Hystrix,Zuul,Config,Bus)基本全死在了版本的更迭之上,但是这篇博客我想只说说注册中心。后面可能还会更新几篇文章,将这段恩怨情仇叙一叙,下面我就将以前的学习笔记拿来在这里作为一个了解内容了,主要是这个思想值得借鉴和学习
Eureka:中文意思"我发现了","我找到了",你们知道Zookeeper吗,就是Dubbo建议使用的注册中心那个Zookeeper,二者就是差不多的,但是Dubbo和Eureka侧重点不同,Eureka是牺牲了一致性,保证了可用性,而Zookeeper牺牲了可用性,保留了一致性,所谓的CAP的"三二原则",
Eureka的作用这里也简单带过:
-
【服务的注册、发现】:负责管理,纪录服务提供者的信息,服务调用者无需自己寻找服务的,而是把自己的需求告诉Eureka,Eureka就会吧符合你需求的服务告诉你,好比租房中介。
-
【服务的监控】:服务的提供者和Eureka之间还通过"心跳",保持着联系,当某个服务提供方出现了问题,Eureka自会在指定的时间范围内将其剔除,就好比租房子,房东已经把房子租出去了,中介就会把这个房子排除在自己掌握的房源名单里
这张图理解为一个房东,一个中介,一个打工仔:
-
房东有房,把房子托给中介公司帮忙出租,房东和中介保持着联系(心跳),如果这个房子房东自己z住不想出租了或者房子漏水暂时不租了,中介第一时间就会知道,然后停止该房子的出租,而打工仔一个人孤苦伶仃的来到一个陌生的城市拼搏,他找到了中介,中介给了他一种表单,里面罗列了这个中介的所有的房源,看他需求什么,自己有的话就可以帮他联系上房东,通过这种方式,打工仔身在异乡但仍然感受到了家的温暖。
Eureka:中介
pom.xml——>
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud-demo</artifactId> <groupId>com.ccl.demo</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>00-eureke-server</artifactId> <dependencyManagement> <dependencies> <!-- springCloud --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> <version>2.0.1.RELEASE</version> </dependency> </dependencies> </project>
application.yml——>
server: port: 8080 spring: application: name: Eureka-Server #会在Eureka服务列表显示的应用名 eureka: instance: hostname: localhost client: register-with-eureka: false #是否注册值的信息到Eureka,默认True fetch-registry: false #是否拉取Eureka上的服务列表,当前是Server,不需要 service-url: # EurekaServer的地址,如果是集群,需要加上其它Server的地址。 defaultZone: Http://${eureka.instance.hostname}:${server.port}/eureka
启动类——>
@SpringBootApplication @EnableEurekaServer public class EurekServerRun { public static void main(String[] args) { SpringApplication.run(EurekServerRun.class, args); } }
服务的提供者:房东向中介注册房子
我们上一个服务提供者,稍加改造:
pom.xml—添加Eureka客户端依赖—>
<!-- Eureka客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
aapplication.yml——>
增加了application.name和Eureka相关的配置
server: port: 8081 spring: application: name: 02-provider eureka: client: service-url: #Eureka的地址 defaultZone: Http://localhost:8080/eureka instance: prefer-ip-address: true #当调用getHostname获取实例的hostname时,返回ip而不是host名称 ip-address: 127.0.0.1 #指定自己的ip,不指定的话会自己寻找
这里注意一下:
-
不用指定register-with-eureka和fetch-registry,因为默认是true
-
fetch-registry: true #eureka注册中心配置 表明该项目是服务端,不用拉取服务 register-with-eureka: true #不用在eureka中注册自己
启动类:
@SpringBootApplication @EnableDiscoveryClient //激活Eureka客户端,@EurekaClient注解之匹配Eureka public class ProviderRun { public static void main(String[] args) { SpringApplication.run(ProviderRun.class, args); } }
至于中间业务层和持久层,和上一个Demo一样,不做说明
这个时候访问localhost:8080,应该就会发现相关的服务应该被注册上了,但我此时不做演示,一轮测试
服务的消费者:打工仔向中介打听房子
改造之前的服务消费者,这次我们要想Eureka索取在线服务列表,调用我们想调用的服务
添加依赖:
<!-- Eureka客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
配置application.yml:
server: port: 8082 spring: application: name: 02-consumer eureka: client: service-url: #Eureka的地址 defaultZone: http://localhost:8080/eureka instance: prefer-ip-address: true #当其它服务获取地址时提供ip而不是hostname ip-address: 127.0.0.1 #指定自己的ip信息,不指定的话会自己寻找
启动类:
@SpringBootApplication @EnableDiscoveryClient //激活Eureka客户端,@EurekaClient注解之匹配Eureka public class ConsumerRun { public static void main(String[] args) { SpringApplication.run(ConsumerRun.class, args); } @Bean public RestTemplate restTemplate(){ //RestTemplate支持三种三种http客户端类型 //HttpClient 、 OkHttp 、JDK原生的URLConnection(这个是默认的 ) //默认就是不给参数,现在我们使用的是OkHttp return new RestTemplate(new OkHttp3ClientHttpRequestFactory()); } }
Controller:
@RestController @RequestMapping("/consumer/employee") public class ConsumerController { @Autowired private RestTemplate template; @Autowired private DiscoveryClient client; @GetMapping("/getAll") public List<Employee> allListEmployee(){ //根据服务的名称获取服务的实列,一个服务可能有多个提供者,所以是List //你可以把这个步骤想象成在向Spring容器根据接口索要实列对象 List<ServiceInstance> instances = client.getInstances("02-provider"); for (ServiceInstance si : instances){ String host = si.getHost(); int port = si.getPort(); System.out.println(host + ":" + port); //获取的ip和端口,动态的组装成url String url = "http://" + host + ":" + port + "/provider/employee/list"; //发起请求,获得数据并返回 List employees = this.template.getForObject(url, List.class); return employees; } return null; }
Postman测试
我们通过Eureka获取到了指定应用名的服务的IP和Port,动态的组成了我们的请求Rest连接,成功的调用了服务的提供者的一个接口
然后我们去看看我们的Eureka:
暂时就这么点东西吧,当然Eureka还支持集群和负载均衡:
还记得之前的故事吗?中介给了打工仔一个表单,在实际中也是这样的,Eureka将所有的服务列表全部给调用方,调用方自己匹配,负载均衡是调用方自己的事,而不是Eureka的事,他只是一个房子的搬运工,负载均衡也很简单,下面来看看: 在返回RestTemplate的方法上给一个注解 : @LoadBalanced,默认使用的轮询的方式,也可以指定为特定的负载均衡算法,当然这里不做更深入的了解,负载均衡算法亦可自己实现,但是一般都采用系统提供的固定几个算法
Eureka集群:多个中介
之前我们都是跑的单机,但是实际生产中,服务的高可用是必须保证的,所以Eureka基本上都是集群部署的,避免单点故障,提高吞吐量,我们就弄三个Eureka服务吧,因为都是本地为了区分,这里又得动hosts文件了:
先说一下思路:三个Eureka
-
第一步分别修改 eureka.instance.hostname: eureka8080 / eureka8081 / eureka8082
-
第二步修改本地的映射文件,eureka8080 / eureka8081 / eureka8082 都指向192.0.0.1
-
第三步删除register-with-eureka=false和fetch-registry=false两个配置。这样就会把自己注册到注册中心了。
-
第四步将参与集群的机器全部罗列在service-url : defaultZon中,整体如下:
-
然后就是修改我们本地的映射文件,为了区分,不为其他的用途,如下
-
然后这几个机器算是形成集群了,我们先启动访问测试一下:
接下来就修改一下我们的服务注册者和服务消费者的注册和拉取请求连接即可:
修改服务提供者的配置文件:
不慌,我的端口和前面的Eureka的端口碰撞了,这里重新修改了端口,在这里还得注意一下,不知道是不是使用负载均衡的原因,还是因为使用了集群的原因,我们的RestTemplate在发起请求的时候不能再拼接真实的地址了,会服务器异常,抛出异常:No instances available for 127.0.0.1
只能用在Eureka中注册的服务名进行调用,如下:
Postman测试工具启动:完美获得数据;
结束语 = 补充干货
但我以前的学习中的有些内容并没有讲解,所以我做点补充吧!
上面的很多属性经过这个Demo + 后面的注释,我相信大家都已经比较熟悉了,但是下面的部分属性,算作补充内容吧
-
从服务的注册说起
服务的提供者在服务启动时,就会检查属性中的是否将自己也想Eureka注册这个配置,默认为True,如果我们没有手动设置,那么就会向Eureka服务发起一个Rest请求,并带上自己的数据,Eureka收到这些数据时,就会将其保存到一个双层Map结构中,第一层Map的key就是服务名称,第二层Map的key就是服务实列id
-
然后再说服务的续约,牵扯到上图的属性
在我们的服务注册完成之后,通过心跳维持联系,证明自己还或者(这是服务的提供者定时向Eureka发),这个我们成为服务的续约,续约方式:"发起心跳"
然后就有两个属性,可以被我们安排上:在开发上可以如我那样配置参数
lease-renewal-interval-in-seconds: 30 :每隔30秒一个心跳
lease-expiration-duration-in-seconds: 90 :90秒没有动静,认为服务提供者宕机了
-
再然后就是 "实列id"
看看这张图:
UP(1) : 表示只有一个实列
DESK...:port :实列的名称(instance-id)
默认格式就是 "hostname" + "spring.application.name" + "server.port"
可以改成为我笔记中的那样,简单明了且大方!
这里得说明一下这个属性是区分统一服务不同实现的唯一标准,是不能重复的
-
服务的提供者说完,我们来说服务的消费者
当我们的服务消费者启动后,会检测eureka.client.fetch-registry=true,如果为true,就会去备份Eureka的服务列表到本地,并且可以通过registry-fetch-interval-seconds: 5 来设置多久更新一下备份数据,默认30 ,生产环境下不做修改,开发环境可以调小
-
服务的提供者也说完了,最后我们就来说说Eureka吧
-
失效剔除
有时候,我没得服务提供方并不一定是宕机了,可能是一起其他的原因(网络延迟啥的)造成的服务一时没有正常工作,也就是没心跳且超贵最长时限,Eureka会将这些服务剔除出自己的服务列表
属性:eureka.server.eviction-interval-timer-in-ms,默认60S 单位毫秒,
-
自我保护
这个横幅就是触发了Eureka的自我保护机制了,当一个服务为按时心跳续约时,Eureka会统计最近15分钟所有的服务的爽约比列,超过85%(生产环境下因为网络原因及其有可能造成这么局面),Eureka此时并不会将服务给剔除,而是将其保护起来,生产环境下作用还是很明显,起码不会因为网络原因导致大部分服务爽约,Eureka将全部服务剔除的局面出现
但在开发中,我们一般将其关闭 :enable-self-preservation: false
-
Zookeeper
思想都是和Eureka差不多的,这里就不做过多赘述,想了解相关知识的,可以看看以前的学习笔记,这里我干脆贴自己以前的学习笔记好了,懒得学习还在copy
上面三个我们重点说一下CAP原则,这个是重点,分布式的重点,建议大家认真看看,有什么错误的认识,望指正,就是因为Zookeeper采用的的CP原则,牺牲了可用性,copy一句以前的学习笔记
当Leader宕机以后,集群机器马上会进去到新的Leader选举中,但是选举时长在30s — 120s之间,这个选取Leader期间,是不提供服务的,不满足可用性,所以牺牲了可用性
这个和eureka不同的是,需要独立安装第三方服务Zokkeeper,且没有UI界面,就是在命令行里看参数,这里我就不祥述安装和使用了,很多Dubbo + Zookeeper 在搭配一起使用,但是到了2020年,我觉得Spring Cloud Alibaba才是未来的主流,我们将更多的篇幅留给Nacos
Nacos(总结目标)
Nocas是阿里开源的一个项目,除了做注册发现中心,他还可以替换Spring Cloud的config和bus,相当于说一个Nocas集成了Eureka、Config、Bus三个组件,下面我们简单来总结一下,目前Spring Cloud Alibaba基本上可以替代之前的Spring Cloud,后面我会一一总结,先从Nocas开始吧
Nacos的生产环境搭建
机器是这样安排的,一共有三台机器,
-
192.168.0.150 //mysql nginx 后崩溃,服务迁移到70
-
192.168.0.170 //nacos mysql nginx
-
192.168.0.180 //nacos
-
192.168.0.190 //nacos
Nacos的单机版,我们就不搭建了,这里直接上生产环境标准搭建集群,持久化数据库为mysql,使用Nginx做统一的负载均衡和最外层入口(参考官网而来)
此次的安装均使用Linux环境,基础环境为Maven + JDK + Nginx + Nacos + Mysql
Maven 3.2+ + JDK 1.8+ Mysql + Nginx环境搭建不做过多赘述,备好即可
Nacos下载:
-
wget https://github.com/alibaba/nacos/releases/download/1.3.0/nacos-server-1.3.0.tar.gz
-
解压 tar -zxvf nacos-server-1.3.0.tar.gz
Mysql初始化
修改持久化源为mysql(生产建议一主一从)(目前只支持mysql)
-
进入解压目录, vim conf/application.properties
然后就是进入mysql,创建Nacao给我们准备的sql语句,该sql在Nacos的conf目录下,copy
这个sql是不会创建数据库的,在执行命令之前先把数据库nacos数据库创建选中了先
create database nacos;
use nacos;
将sql复制进入,执行即可,我们可以看到创建了哪些表
mysql> show tables;
后来这套机器崩溃了,将mysql和ngxin移动到了170机器
Nacos集群初始化
下面就是我们的集群设置了,配置文件也在conf目录下。三台机器做同一配置
cp cluster.conf.example cluster.conf
vim cluster.conf
这里我们在一台机器上搭建集群,ip必须为机器ip,我机器ip就是192.168.0.*
192.168.0.170:8848 192.168.0.180:8848 192.168.0.190:8848
在这里说句抱歉了,集群环境搭不起来了,我笔记本内存太小了,服务启动后都爆出了内存不足的信息
Nginx初始化
Nginx的配置很简单,配置一个负载均很upstream用proxy_pass指向即可完成负载,然后我们访问统一对外端口80,即可访问nacosUI页面则视为集群搭建成功