1、Eureka 注册中心三种角色。
答:a、Eureka Server,注册中心,通过 Register、Get、Renew 等接口提供服务的注册和发现。
b、Application Service (Service Provider),服务提供方,把自身的服务实例注册到 Eureka Server中。
c、Application Client (Service Consumer),服务调用方,通过 Eureka Server获取服务列表,消费服务。
2、Erueka的架构图,如下所示。来源:https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance
解释如下所示:
Eureka Server是Eureka的注册中心。Application Service是Service Provider。Application Client是Service Consumer。Application Service和Application Client都是Eureka Client(即Eureka客户端)。
a、Register(服务注册):把自己的 IP 和端口注册给 Eureka。
b、Renew(服务续约):发送心跳包,每 30 秒发送一次。告诉 Eureka 自己还活着。
c、Cancel(服务下线):当 provider 关闭时会向 Eureka 发送消息,把自己从服务列表中删除。防止 consumer 调用到不存在的服务。
d、Get Registry(获取服务注册列表):获取其他服务列表。
e、Replicate(集群中数据同步):eureka 集群中的数据复制与同步。
f、Make Remote Call(远程调用):完成服务的远程调用。
3、什么是CAP原则?
答:CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(强一致性)、Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。CAP 由 Eric Brewer 在 2000 年 PODC 会议上提出。该猜想在提出两年后被证明成立,成为我们熟知的 CAP 定理。
CAP定律:任何分布式系统只可同时满足二点,没法三者兼顾。
3.1、ACID和CAP的对比如下所示:
1)、传统得ACID分别是什么,A(Atomicity)原子性、C(Consistency)一致性、I(Isolation)独立性、D(Durability)持久性。
2)、CAP原则,Consistency(强一致性)、Availability(可用性)、Partition tolerance(分区容错性)。CAP的三进二,任何分布式系统,没要办法同时满足强一致性,可用性,分区容错性,三者不可兼得,只能三个选择其中两个。
3)、典型的CAP,CA(强一致性、可用性)的有RABMS传统的关系型数据库,包含Mysql、Oracle、Sql server。CP(强一致性、分区容错性)的有MongoDB、HBase、Redis。AP(可用性、分区容错性)的有CouchDB、Cassandra、DynamoDB、Riak。
3.2、CAP理论的核心是,一个分布式系统不可能同时很好的满足一致性、可用性和分区容错性这三个需求,因此,根据cap原理将NoSQL数据库分成了满足CA原则、满足CP原则,和满足AP原则三大类。分布式集群,分区容错性必须要实现,所以只能在一致性和可用性之间进行选择。
1)、CA原则,单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
2)、CP原则,分布式集群,满足一致性,分区容错性的系统,通常性能不是特别高。
3)、AP原则,分布式集群,满足可用性,分区容错性的系统,通常可能对一致性要求低一些。
4、Zookeeper与Eureka的区别。
4.1)、Zookeeper保证CP原则,当向注册中心查询服务列表的时候,我们可以忍受注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系的时候,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120秒,且选举期间整个zk集群都是不可用的,这样就导致选举期间服务瘫痪。在云部署的环境下,因为网络问题使得zk集群失去master节点是较大概率会发生的事情,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
4.2)、Eureka设计的时候优先保证可用性。Eureka各个节点都是平等的,几个节点挂点不会影响节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka得客户端在向某个Eureka注册得时候如果发现了连接失败,则会自动切换至其他节点,只要有一台Eureka还存活,就能保证注册服务得可用性(即保证可用性),只不过查询的信息部署最新的(不能保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没要正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况。
第一种情况,Eureka不再从注册列表中移除因为长时间没有收到心跳而应该过期的服务。
第二种情况,Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证了当前节点依然可用)。
第三种情况,当网络稳定的的时候,当前实例新的注册信息会被同步到其他节点中。
因此,Eureka可以很好的应对因为网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。
5、Eureka的优雅停服。在什么条件下,Eureka会启动自我保护?什么是自我保护模式?
1)、自我保护的条件,一般情况下,微服务在 Eureka上注册后,会每30秒发送心跳包,Eureka通过心跳来判断服务时候健康。同时会定期删除超过 90秒没有发送心跳服务。
2)、但是有两种情况会导致 Eureka Server 收不到微服务的心跳。第一种情况是微服务自身的原因。第二种情况是是微服务与 Eureka 之间的网络故障。如果未设置优雅停服的情况下,此两种情况是不会将服务进行删除的。
通常(微服务的自身的故障关闭,也被称为单节点故障)只会导致个别服务出现故障,一般不会出现大面积故障。而(网络故障)通常会导致 Eureka Server 在短时间内无法收到大批心跳。考虑到这个区别,Eureka 设置了一个阀值,当判断挂掉的服务的数量超过阀值时, Eureka Server 认为很大程度上出现了网络故障,将不再删除心跳过期的服务。
3)、那么这个阀值是多少呢?
如果15 分钟之内是否低于 85%,Eureka Server 在运行期间,会统计心跳失败的比例在 15 分钟内是否低于 85% 这种算法叫做 Eureka Server 的自我保护模式。
4)、案例如下:启动之前搭建的Eureka Server集群版,启动Provider客户端,启动Consumer客户端,可以在页面查看到都已经注册到Eureka Server注册中心了。
现在将Provider客户端、Consumer客户端关闭(在eclipse停止启动的Provider服务、Consumer服务即可)。
大概翻译如下所示:
如果是大于阈值(85%),Eureka注册中心判定为网络故障,如果小于阈值(85%),Eureka注册中心判定为单节点故障。因为我这里是启动4个服务,就是向Eureka注册中心注册了4个服务,挂了2个,就是50%,远远低于85%。所以Eureka注册中心判定此种情况为单节点故障。Eureka注册中心认为节点在以后可以修复的,所以对出现故障的节点进行了保留,这就是Eureka注册中心的自我保护。
6 、Eureka为什么要启动自我保护。
答:因为同时保留"好数据"与"坏数据"总比丢掉任何数据要更好,当网络故障恢复后,这个 Eureka 节点会退出"自我保护模式"。当你的节点修复好以后,Eureka注册中心会退出自我保护模式。
为什么Eureka要进行自我保护,因为Eureka 还有客户端缓存功能(也就是微服务的缓存功能),Eureka注册中心缓存了之前注册的信息。即便 Eureka 集群中所有节点都宕机失效,微服务的 Provider 和 Consumer都能正常通信。微服务的负载均衡策略会自动剔除死亡的微服务节点。
7、如何关闭Eureka的自我保护。可以修改Eureka Server配置文件,在Eureka Server的注册中心中进行操作,如下所示:
在你的application.properties配置文件中新增如下配置即可以,关闭自我保护,并在注册中心清理你的无效服务。如果是高可用的,在两个或者多个配置文件application-eureka1.properties、application-eureka2.properties都新增如下配置即可。重启,打包,部署,运行可以查看效果。
1 # 关闭自我保护,值设置为true为开启自我保护。值设置为false为关闭自我保护。 2 eureka.server.enable-self-preservation=false 3 4 # 清理间隔,单位毫秒,默认是60*1000 5 eureka.server.eviction-interval-timer-in-ms=60000
8、如何进行Eureka的优雅停服,不需要在Eureka Server中配置关闭自我保护,需要再服务中添加actuator.jar包。在服务端进行配置,进行Eureka的优雅停服,上面的在Eureka Server注册中心的关闭自我保护配置就可以注释掉,或者删除掉了。
我这里修改的是服务端provider的pom.xml配置文件,如下所示。将spring-cloud-starter-netflix-eureka-client修改成了spring-cloud-starter-netflix-eureka-client,
因为springcloud eureka 服务端包含了actuator.jar包。
1 "1.0" encoding="UTF-8"?> 2"http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 5 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 6 4.0.0 78 14org.springframework.boot 9spring-boot-starter-parent 102.2.0.RELEASE 1112 13 com.bie 15springcloud-eureka-provider 160.0.1-SNAPSHOT 17springcloud-eureka-provider 18Demo project for Spring Boot 19 2021 27 28UTF-8 22UTF-8 231.8 24Hoxton.RC1 253.1.1 2629 55 5630 33 34 35org.springframework.boot 31spring-boot-starter-web 3236 39org.springframework.boot 37spring-boot-starter-actuator 3840 43org.springframework.cloud 41spring-cloud-starter-netflix-eureka-client 4244 54org.springframework.boot 45spring-boot-starter-test 46test 4748 5349 52org.junit.vintage 50junit-vintage-engine 5157 67 6858 6659 65org.springframework.cloud 60spring-cloud-dependencies 61${spring-cloud.version} 62pom 63import 6469 76 7770 7571 74org.springframework.boot 72spring-boot-maven-plugin 7378 84 8579 83spring-milestones 80Spring Milestones 81https://repo.spring.io/milestone 82
新增配置文件,启动shutdown、禁用密码验证,如下所示:
由于springboot1.x版本和springboot2.x版本的差异性,这些配置都进行改变。
可以参考官网:https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-endpoints
1 # 启用 shutdown 2 management.endpoint.shutdown.enabled=true 3 4 # 暴露shutdown。include后面可以添加你想用到的端点 5 management.endpoints.web.exposure.include=shutdown
如果是springboot2.x版本配置,springboot1.x版本的配置会,出现如下所示报错:
1 http-outgoing-0 << "{"timestamp":"2019-11-16T05:07:19.423+0000","status":404,"error":"Not Found","message":"No message available","path":"/shutdown"}[\r][\n]" 2 2019-11-16 13:07:19.450 [main] DEBUG org.apache.http.headers - 3 http-outgoing-0 << HTTP/1.1 404
如何进行优雅停服的,这个时候不能停止服务。需要发送一个http请求,但是不可以使用浏览器发送请求,因为浏览器发送的都是get请求,该请求必须使用post发送请求的,所以可以使用postman工具或者httpClient工具类,进行优雅停服。
httpclient工具类,代码如下所示:
我这里使用的是"http://localhost:8080/actuator/shutdown,如果只是"http://localhost:8080/shutdown可能会发现并不能将服务进行停止的。
1 package com.bie.utils; 2 3 import java.io.IOException; 4 import java.net.URI; 5 import java.util.ArrayList; 6 import java.util.List; 7 import java.util.Map; 8 9 import org.apache.http.NameValuePair; 10 import org.apache.http.client.entity.UrlEncodedFormEntity; 11 import org.apache.http.client.methods.CloseableHttpResponse; 12 import org.apache.http.client.methods.HttpGet; 13 import org.apache.http.client.methods.HttpPost; 14 import org.apache.http.client.utils.URIBuilder; 15 import org.apache.http.entity.ContentType; 16 import org.apache.http.entity.StringEntity; 17 import org.apache.http.impl.client.CloseableHttpClient; 18 import org.apache.http.impl.client.HttpClients; 19 import org.apache.http.message.BasicNameValuePair; 20 import org.apache.http.util.EntityUtils; 21 22 public class HttpClientUtil { 23 24 public static String doGet(String url, Mapparam) { 25 26 // 创建Httpclient对象 27 CloseableHttpClient httpclient = HttpClients.createDefault(); 28 29 String resultString = ""; 30 CloseableHttpResponse response = null; 31 try { 32 // 创建uri 33 URIBuilder builder = new URIBuilder(url); 34 if (param != null) { 35 for (String key : param.keySet()) { 36 builder.addParameter(key, param.get(key)); 37 } 38 } 39 URI uri = builder.build(); 40 41 // 创建http GET请求 42 HttpGet httpGet = new HttpGet(uri); 43 44 // 执行请求 45 response = httpclient.execute(httpGet); 46 // 判断返回状态是否为200 47 if (response.getStatusLine().getStatusCode() == 200) { 48 resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); 49 } 50 } catch (Exception e) { 51 e.printStackTrace(); 52 } finally { 53 try { 54 if (response != null) { 55 response.close(); 56 } 57 httpclient.close(); 58 } catch (IOException e) { 59 e.printStackTrace(); 60 } 61 } 62 return resultString; 63 } 64 65 public static String doGet(String url) { 66 return doGet(url, null); 67 } 68 69 public static String doPost(String url, Map param) { 70 // 创建Httpclient对象 71 CloseableHttpClient httpClient = HttpClients.createDefault(); 72 CloseableHttpResponse response = null; 73 String resultString = ""; 74 try { 75 // 创建Http Post请求 76 HttpPost httpPost = new HttpPost(url); 77 // 创建参数列表 78 if (param != null) { 79 List paramList = new ArrayList<>(); 80 for (String key : param.keySet()) { 81 paramList.add(new BasicNameValuePair(key, param.get(key))); 82 } 83 // 模拟表单 84 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, "utf-8"); 85 httpPost.setEntity(entity); 86 } 87 // 执行http请求 88 response = httpClient.execute(httpPost); 89 resultString = EntityUtils.toString(response.getEntity(), "utf-8"); 90 } catch (Exception e) { 91 e.printStackTrace(); 92 } finally { 93 try { 94 response.close(); 95 } catch (IOException e) { 96 // TODO Auto-generated catch block 97 e.printStackTrace(); 98 } 99 } 100 101 return resultString; 102 } 103 104 public static String doPost(String url) { 105 return doPost(url, null); 106 } 107 108 public static String doPostJson(String url, String json) { 109 // 创建Httpclient对象 110 CloseableHttpClient httpClient = HttpClients.createDefault(); 111 CloseableHttpResponse response = null; 112 String resultString = ""; 113 try { 114 // 创建Http Post请求 115 HttpPost httpPost = new HttpPost(url); 116 // 创建请求内容 117 StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); 118 httpPost.setEntity(entity); 119 // 执行http请求 120 response = httpClient.execute(httpPost); 121 resultString = EntityUtils.toString(response.getEntity(), "utf-8"); 122 } catch (Exception e) { 123 e.printStackTrace(); 124 } finally { 125 try { 126 response.close(); 127 } catch (IOException e) { 128 // TODO Auto-generated catch block 129 e.printStackTrace(); 130 } 131 } 132 133 return resultString; 134 } 135 136 public static void main(String[] args) { 137 // 请求地址是你的客户端服务的地址。 138 String url = "http://localhost:8080/actuator/shutdown"; 139 // 该url必须要使用dopost方式来发送 140 HttpClientUtil.doPost(url); 141 } 142 143 }
使用postman停止的效果如下所示:
9、如何加强Eureka注册中心的安全认证。如果不进行安全认证的话,无论谁根据url打开Eureka管理界面,这样很不安全,如下操作进行安全认证操作。在Eureka Server中添加security 包,新增jar包,配置如下所示:
1 23 org.springframework.boot 4spring-boot-starter-security 5
由于我使用的Eureka是高可用的,所以集群节点之间的相互访问,需要将账号密码写入到请求地址里面。新增的配置如下所示:
修改配置文件,maven install,上传到服务器,运行,启动以后,登录界面如下所示:
登录成功以后和之前的界面完全一致。
注意,因为现在开启了安全认证,如果服务端,Provider客户端或者Consumer客户端,在配置文件里面都需要加上你配置的账号和密码。如果不添加会导致启动失败的哦。这块尤为的坑,自己注意吧,感觉Springboot的升级太变态了。
1 # 客户端需要向服务端进行注册,必须配置服务端的地址。知道服务端在那里。 2 #设置服务注册中心地址,可以将集群中的所有节点列处理。 3 eureka.client.serviceUrl.defaultZone=http://eureka:123456@eureka1:8761/eureka/,http://eureka:123456@eureka2:8761/eureka/
作者:别先生
博客园:https://www.cnblogs.com/biehongli/
如果您想及时得到个人撰写文章以及著作的消息推送,可以扫描上方二维码,关注个人公众号哦。