一、Eureureka(目前已停用)服务注册与发现
1、Eureureka基础知识
(1)什么是服务治理?
Spring Cloud封装了Netfetflix公司开发的Eureka模块来实现服务治理
在传统的rpc远程调用框架中,管理服务和服务之间的依赖关系比较复杂,管理起来非常麻烦,所以需要使用服务治理,实现服务调用、负载均衡、容错等,实现服务发现与注册。
(2)什么是服务注册与发现?
----服务注册
:在服务治理框架中,通常都会构建一个注册中心,每个服务单元向注册中心登记自己提供的服务,将主机与端口号、版本号、通信协议等一些附加信息告知注册中心,注册中心按照服务名分类组织服务清单,服务注册中心还需要以心跳的方式去监控清单中的服务是否可用,若不可用需要从服务清单中剔除,达到排除故障服务的效果。
----服务发现
:由于在服务治理框架下运行,服务间的调用不再通过指定具体的实例地址来实现,而是通过向服务名发起请求调用实现。
(3)Eureka的两个组件
----EurekaServer
提供服务注册服务
各个服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务列表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
----EurekaClient
通过注册中心访问服务
是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳(默认周期为30s),如果EurekaServer在多个心跳周期内没有收到某个节点的心跳,EurekaServer将会从服务列表中把这个服务节点移除(默认90s)。
2、Eureka单机使用
(1)创建一个新的Module,引入eureka-server的pom
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
(2)编写application.yml配置文件
#服务端口号
server:
port: 7001
spring:
application:
name: cloud-eureka-server7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
#表示不向注册中心注册自己
register-with-eureka: false
#表示自己是服务注册中心职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
(3)编写主配置类,便可以启动服务注册中心,通过http://localhost:7001可以访问服务注册中心
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //表示该模块为Eureka的服务注册中心
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}
(4)将其他服务Module添加到服务Eureka注册中心
如何将其他服务注册到服务中心呢?
A、修改pom添加eureka-client依赖
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
B、在application.yml添加如下配置
eureka:
client:
#表示将自己注册到EurekaServer
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
C、在Module主启动类上添加@EnableEurekaClient
注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
D、测试:分别启动cloud-eureka-server7001模块和cloud-provider-payment8001模块
通过http:localhost:7001访问Eureka,可以看到cloud-provider-payment8001模块服务已经注册到EurekaServer中:
Eureka单机整体框图
3、Eureka集群原理
(1)Eureka集群
如果我们只配置一个Eureka服务端,那么如果这个服务端崩盘,那么所有服务都无法获取,这肯定不是我们不期望的。所以为了保证高可用性,我们需要搭建Eureka集群。
前面讲原理时已经提到Eureka Server在启动时默认会注册自己,成为一个服务,所以Eureka Server也是一个客户端。也就是说们我们可以配置多个Eureka Server,让他们之间相互注册,当服务提供者向其中一个Eureka注册服务时,这个服务就会被共享到其他Eureka上
,这样所有的Eureka都会有相同的服务。
(2)Eureka集群搭建
A、创建一个新的Eureka模块cloud-eureka-server7002
,先按照cloud-eureka-server7001
对其进行pom,application.yml和启动类的配置
B、修改映射配置
找到C:\Windows\System32\drivers\etc
目录下的hosts文件:
在hosts配置文件末尾添加,然后保存关闭:
C、修改cloud-eureka-server7001
和cloud-eureka-server7002
的application.yml实现两者互相注册,相互守望
cloud-eureka-server7001:
cloud-eureka-server7002:
D、依次启动7001和7002
可以通过单机版的http://localhost:7001
和http://localhost:7002
访问7001和7002的EurekaServer中心;
也可以通过http://eureka7001.com:7001
和http://eureka7001.com:7002
来访问。
通过访问可以同时看到访问7001可以指向7002,访问7002可以指向7001则证明集群搭建成功!
E、将服务注册进Eureka集群:修改application.yml
单机版将服务注册到EurekaServer配置:
集群版讲服务注册到集群EurekaServer:
启动80模块和8001模块,访问EurekaServer,可以看到两个服务已经注册到Eureka集群中:
4、actuator微服务信息完善
Spring Boot Actuator可以帮助你监控和管理Spring Boot应用,比如健康检查、审计、统计和HTTP追踪等。
前提需要在项目pom文件中引入actuator依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
(1)主机名称(服务名称)的修改
修改application.yml文件
修改后等项目重启后进入EurekaServer查看,可以看到显示的名称即为在yml配置文件中指定的eureka.instance.instance-id
的值:
(2)访问信息有IP显示
默认不开启的情况下当我们鼠标指向服务名时左下角并不会显示IP,只会显示:http://主机名:port/*
修改application.yml文件,指定eureka.instance.prefer-ip-address
为true
重启服务后刷新EurekaServer,当鼠标指向服务名称左小角就显示出http://IP:port/*
:
5、服务发现Discovery
(1)介绍
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
(2)如何使用?
在controller类中依赖注入DiscoveryClient
的Bean,然后通过其对象调用其方法实现服务发现。
@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
@Resource
private DiscoveryClient discoveryClient; //服务发现Discovery
@GetMapping("/discovery")
public Object discovery(){
//获取EurekaServer中心注册的服务名
List<String> services = discoveryClient.getServices();
for (String service:services){
log.info("-------------"+service);
}
//根据服务名获取该服务名下对应的实例信息
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for(ServiceInstance instance:instances){
log.info("**************"+instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return this.discoveryClient;
}
}
启动模块调用discovery方法,看控制台打印结果:
5、Eureka自我保护
(1)概述
保护模式主要用于一组客户端和EurekaServer之间存在网络分区场景下的保护。一旦进入保护模式,EurekaServer将会尝试保护其服务注册表中的数据,也就是不会注销任何微服务
。
(2)故障现象
如果 在EurekaServer的页面看到下面这段提示,则说明Eureka进入了保护模式:
(3)导致原因
某时刻一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务 的信息进行保存。属于CAP里面的AP分支。
(4)为神魔Eureka会产生自我保护机制
为了防止EurekaClient可以正常运行,但是与EurekaServer网络不通情况下EurekaServer不会立刻
将EurekaClient服务剔除。
(5)什么是自我保护模式
默认情况下,如果EurekaServer在一定的时间内没有收到某个微服务的心跳,EurekaServer将会注销该实例(默认为90s)。当时当网络分区故障发生( 延时、卡顿、拥挤)时,微服务和EurekaServer之间不能够正常通信,以上行为可能变得非常危险,此时本不应该注销这个微服务
。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
在自我保护模式中,EurekaServer会保护服务表中的信息,不在注销任何服务实例。一句话讲解:好死不如赖活着
.
(6)如何禁止自我保护模式
A、出厂默认,自我保护机制是开启的:eureka.server.enable-selfpreservation:true
B、关闭自我保护机制
a.Server端
关闭自我保护机制,保证不可用服务被及时剔除:eureka.server.enable-selfpreservation: flase
,
设置接收心跳间隔时间为2s(默认为90s):eureka.server.eviction-interval-timer-in-ms: 2000
访问http://eureka7001.com:7001/
红色英文提醒显示自我保护机制已关闭。而且后台每隔2s进行一次心跳接收:
b.Client端
#Eureka客户端向服务端发送心跳的时间间隔,单位为s(默认是30s)
eureka.instance.lease-renewal-interval-in-seconds=30
#Eureka服务端在收到最后一次心跳后等待的时间上限,单位为s(默认为90s),超时将剔除服务
eureka.instance.lease-expiration-duration-in-seconds=90
二、SpringCloud整合Zookeeper代替Eureka
1、Zookeeper介绍
zookeeper是一个分布式协调工具,可以实现注册中心的功能
2、进入linux系统使用docker安装zookeeper
docker pull zookeeper
3、运行zookeepe
查看zookeeper的docker镜像的id
docker images
官方给出的docker启动命令为:$ docker run --name some-zookeeper --restart always -d zookeeper
此镜像暴露端口:2181(client port:客户端交互) 、2888(follower port:集群)、 3888(election port:选举)
docker run --name zk01 -p 2181:2181 --restart always -d e1763fd3a0e3
此处我们指定name为zko1,-p选择暴露linux的端口2181映射到docker容器的2181端口,-d指定需要后台运行的zookeeper的id
3、查看启动运行状况
docker ps
注:有些特殊情况不关闭防火墙可能会导致zookeeper无法访问到
(1)Linux下查看防火墙运行状态:service iptables status
或systemctl status firewalld
(2)临时关闭防火墙:service iptables stop
或systemctl stop firewalld
(3)永久关闭防火墙:chkconfig iptables off
或systemctl disable firewald
·
(4)重启防火墙:service iptables restart
或systemctl enable firewalld
(5)永久关闭后重启:chkconfig iptables on
4、SpringBoot整合Zookeeper
(1)引入依赖
<!-- SpringBoot整合zookeeper客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
(2)编写yml配置文件
#服务端口号
server:
port: 8004
spring:
application:
name: cloud-provider-payment8004
cloud:
zookeeper:
connect-string: 安装zookeeper的主机ip:2181
(3)编写主启动类(注意添加@EnableDiscoveryClient
注解)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class,args);
}
}
(4)编写业务实现类
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/payment/zk")
public String paymentzk(){
return "SpringCloud with zookeeper :"+serverPort+"\t"+ UUID.randomUUID().toString();
}
}
启动主程序将8004服务注册进zookeeper,进入Linux查看docker容器zookeeper容器中是否有payment8004注册服务:
A、然后进入镜像容器中zookeeper的 zkCli.sh,可以查看注册的服务
B、查看注册服务的详细信息
查看到的服务信息为:
{
"name": "cloud-provider-payment8004",
"id": "09a096c8-d7e8-46c6-8a5b-12264e743e3a",
"address": "DESKTOP-2TROCP1",
"port": 8004,
"sslPort": null,
"payload": {
"@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance",
"id": "application-1",
"name": "cloud-provider-payment8004",
"metadata": {}
},
"registrationTimeUTC": 1609904528772,
"serviceType": "DYNAMIC",
"uriSpec": {
"parts": [
{
"value": "scheme",
"variable": true
},
{
"value": "://",
"variable": false
},
{
"value": "address",
"variable": true
},
{
"value": ":",
"variable": false
},
{
"value": "port",
"variable": true
}
]
}
}
(5)注册进zookeeper的节点是临时还是持久的呢?
当关闭payment8004服务,查询ls /services/cloud-provider-payment8004
,可以发现短时间内是有流水号存在的,但是当超过一次心跳时服务节点被删掉,所以zookeeper的节点是临时的。
三、Consul
1、介绍
(1)Consul是一套开源的分布式服务发现和配置管理系统。提供了微服务中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网站,总之Consul提供了一种完整的服务网格解决方案。
(2)优点:基于raft协议,比较简洁;支持将康检查;同时支持HTTP和DNS协议;支持跨数据中心的WAN集群;提供Web图形页面;跨平台,支持Linux、Mac、Windows。
Consul中文文档
2、Linux系统安装Consul
A、在/home目录下创建consul目录,进入consul,下载安装包:get https://releases.hashicorp.com/consul/1.3.0/consul_1.3.0_linux_amd64.zip
B、解压安装包:unzip consul_1.3.0_linux_amd64.zip
删除安装包:rm- rf consul_1.3.0_linux_amd64.zip
C、安全组配置8300和8500 tcp,防火墙开放8300 8500
D、启动consul:./consul agent -dev -ui -node=consul-dev -client=172.30.71.145
172.30.71.145是服务器的私有ip
E、启动端口8500上的服务器模式的代理,其中ui可以在http:// 机器ip:8500
上找到
3、服务提供者注册进Consul
创建子模块:cloud-providerconsul-payment8006
(1)引入依赖
<!-- Springcloud consul-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
(2)编写yml
server:
port: 8006
spring:
application:
name: consul-provider-payment8006
#consul注册中心地址
cloud:
consul:
host: localhost或服务器IP
port: 8500
discovery:
service-name: ${spring.application.name}
#开启心跳协议
heartbeat:
enabled: true
(3)编写主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8006.class,args);
}
}
(4)编写一个简单的业务测试方法
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/payment/consul")
public String paymentConsul(){
return "SpringCloud with consul:"+serverPort+"\t"+ UUID.randomUUID().toString();
}
}
(5)运行payment8006子模块,前往consol服务中心查看:
可以看到服务已经注册进服务中心。
4、服务消费者注册进Consul
创建子模块:cloud-consumerconsul-order80
(1)引入依赖
<!-- Springcloud consul-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
(2)编写yml
server:
port: 80
spring:
application:
name: cloud-consumerconsul-order80
#consul注册中心地址
cloud:
consul:
host: localhost或服务器IP
port: 8500
discovery:
service-name: ${spring.application.name}
#开启心跳协议
heartbeat:
enabled: true
(3)编写主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul或着zookeeper作为注册中心注册服务
public class OrderConsulMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderConsulMain80.class,args);
}
}
(4)编写一个简单的业务测试方法
package com.atguigu.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderConsulController {
public static final String INVOKE_URL = "http://consul-provider-payment8006";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/consul")
public String paymentInfo(){
String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class);
return result;
}
}
(5)运行cloud-consumerconsul-order80子模块,测试consul中的服务:
order80通过consul服务中心调用payment8006中的方法:
根据返回结果表示能够调用成功。
四、Eureureka、Zookeeper和Consul三种注册服务中心的异同点
(1)
组件名 | 语言 | CAP | 服务健康检查 | 对外暴露接口 | SpringCloud集成 |
---|---|---|---|---|---|
Eureka | Java | AP | 可配支持 | HTTP | 已集成 |
Consul | Go | CP | 支持 | HTTP/DNS | 已集成 |
Zookeeper | Java | CP | 支持 | 客户端 | 已集成 |
注
CAP:CAP理论关注粒度是数据而不是整体系统设计
C:Consistency(强一致性)
A:Availability(可用性)
P:Partition Tolerance(分区容错性)
CAP理论的核心是:一个分布式系统不可能同时满足一致性、可用性和分区容错性这三个需求,最多只能同时较好的满足两个
因此,根据CAP原理将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
CA-单点集群,满足一致、可用性的系统,通常在扩展性上不太强大。
CP-满足一致性、分区容错性的系统,通常性能不是特别高。
AP-满足可用性、分区容错性的系统,通常可能对一致性要求低一点。