面向单体应用开发
将两个项目打成一个war包,包含了项目中所有需要的资源。
SOA,将两个项目分别打包,形成单独的服务
当线上的时候,本地依赖失效,当财务系统需要共同依赖中的资源时,此时就需要RPC(远程服务调用),调用共同依赖中的依赖。
财务服务调用商城服务:webservice(soap、wsdl、cxf),在解决了这个依赖问题,此时又有新的问题:财务向商城服务发起请求时,怎么知道商城的服务挂掉没,或是网络问题导致响应延时??等一系列问题。而这种就是一种强依赖问题。依赖包调来调去。
将这些依赖根据功能抽取为模块,只具备单一功能,比如订单服务,支付服务等,将整体一个大的模块拆分成更小的功能模块,这种服务就叫微服务。
由此,微服务可以分为三个方向拆分:
面向需求维度:应用维度;
面向技术栈;
看这样一个服务流程:
用户在前端页面发起请求,前端充当传话的角色,向后台请求,即真正干事的是后台服务。当一个服务忙不过来,就需要有多个的后台服务,前端可以向多个后台发起请求,轮着干活,这就叫做负载均衡,也称之为客户端的负载均衡。取决权在与客户端
那这三个服务想要做负载均衡(红色框中前端是支付,复制忘改了),客户端首先要获取服务列表才可以,每个服务都有其名字,但是获取到列表还不行,需要对应的ip、端口号——主机名+ip+端口号。而帮助我们注册这些信息的就是Eureka框架。eureka会从服务列表中拿到服务器名注册,然后通过Netflix Fegin(还有其他工具)远程调用服务器;但是前面提到过,服务器中总有些罢工的,所以此时就不能一个劲儿的发请求,硬刚。所以需要客户端的负载均衡Ribbon去判断,然后调用可用的服务。然后再将结果返回,View渲染。
实际微服务中,一个controller肯定是不可能的,所以也应该有多个controller,而且同样需要实现负载均衡,所以不应该直接将controller端口暴露出来,所以添加网关NetFlixZuul,可以实现url拦截,权限控制等。NetFlixZuul内部集成springOAuth2.0权限控制。
客户端负载均衡调用服务还涉及到两个问题:链路追踪、分布式事物,在判断服务之前,ribbon还会去做熔断、降级。Hystrix会记录每个微服务状态,当服务down了,客户端就不会再连接它,直到它上线(这称之为熔断),那这个状态又是怎么更新的呢?即每个服务节点都会跟一个Actuator,实时上报服务节点状态。降级是表示将服务节点数量减少。这些服务节点的状态都是同Actuator记录,一是提交给Hystrix,另一个是提交给springCloud Admin统一化管理。
同时还有消息管理组件spring Cloud Stream;Spring Integration和Apache Camel是一个级别,Spring Message次之。不过这个一般是非常大的项目才用的到。
SpringMessage比较简单:对单机消息中间件的包装,包装成统一的标准,屏蔽了框架之间的差异。其目的是为了集成Spring Integration
Spring Integration:消息集合、过滤等
Spring Cloud Stream:相当于对Spring Integration的加强,再一次包装,与SpringBoot做一次整合,为了实现Spring
至此,一个框架模型大致出来了。
来一张“一鸣大佬的神图”
搭建Eureka服务
步骤如下图
导入成功之后,配置eureka
application.properties
#是否将自己注册到Eureka Server,默认为true,由于当前就是server,故而设置成false,表明该服务不会向eureka注册自己的信息
eureka.client.register-with-eureka=false
#是否从eureka server获取注册信息,由于单节点,不需要同步其他节点数据,用false
eureka.client.fetch-registry=false
#设置服务注册中心的URL,用于client和server端交流
eureka.client.service-url.defaultZone=http://localhost:7900/eureka/
添加@EnableEurekaServer注解
package com.anzhi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaserverApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaserverApplication.class, args);
}
}
启动如图:
Eureka集群分为两种情况:
两个服务器互为客户端/服务端,然后对外提供服务
分别注册服务,两个服务没有依赖关系
两种方式各有什么优缺点:
第一种
第二种:
小结:
Eureka保证了AP,无法保证数据的一致性。其高可用体现在,及时Eureka服务节点都挂掉了,他仍然可以使用,和其他客户端进行通信,即通过本地缓存列表,进行交互。
添加eureka域名,将ip与eureka绑定,修改host文件,添加如下:
文件路径:win10: C:\Windows\System32\drivers\etc
# config eureka anzhi
127.0.0.1 eureka1.com
127.0.0.1 eureka2.com
win10不让修改,点击文件属性,安全,编辑,为当前用户赋予修改权限即可。
# 在dos下ping
ping eureka1.com
ping eureka2.com
添加eureka工程的配置文件
使用properties配置的参考这篇博客:https://blog.csdn.net/weixin_43155866/article/details/100937429
我使用第二种方法:
将application.properties修改为application.yml,配置如下:
spring:
application:
name: eureka
eureka:
client:
serviceUrl:
register-with-eureka: false
fetch-registry: false
#注册的服务地址,端口
defaultZone: https://eureka1.com:7900/eureka/,https://eureka2.com:7900/eureka/
server:
enable-self-preservation: false
renewal-percent-threshold: 0.85
eviction-interval-timer-in-ms: 1000
use-read-only-response-cache: false
---
spring:
profiles: 7900
server:
port: 7900
eureka:
instance:
# 主机名
hostname: eureka1.com
---
spring:
profiles: 7901
server:
port: 7901
eureka:
instance:
# 主机名
hostname: eureka2.com
配置相应的编译器
然后启动,结果如下
这是配置问题,页面爆红:
Eureka 访问页面出现红色字体提醒
系统在三种情况下会出现红色加粗的字体提示:
1.在配置上,自我保护机制关闭
RENEWALS ARE LESSER THAN THE THRESHOLD. THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
2.自我保护机制开启了
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE
NOT BEING EXPIRED JUST TO BE SAFE.
3.在配置上,自我保护机制关闭了,但是一分钟内的续约数没有达到85% , 可能发生了网络分区,会有如下提示
THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
更改配置如下
# 全局的服务器名,保证一致
spring:
application:
name: eureka
logging:
level:
root: info
---
spring:
# 在启动时指定的配置文件
profiles: 7900
server:
port: 7900
eureka:
instance:
# 与主机域名一致
hostname: eureka1.com
client:
service-url:
# 要注册到拿一台服务器
defaultZone: http://eureka2.com:7901/eureka/
---
spring:
profiles: 7901
server:
port: 7901
eureka:
instance:
hostname: eureka2.com
client:
service-url:
defaultZone: http://eureka1.com:7900/eureka/
idea不同,设置参数也不一样,我的是2018.3.6,所以指定profiles的时候,直接添我们配置的profiles名称
最终启动成功界面如下:
eureka来源于古希腊词汇,意为“发现了”
eureka分为两部分,Server端和Client端
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
服务注册
想要参与服务注册发现的实例首先需要向Eureka服务器注册信息
注册在第一次心跳发生时提交
续租,心跳
Eureka客户需要每30秒发送一次心跳来续租
10:00 00 第一次
10:00 30
10:01
10:01 30 最后
更新通知Eureka服务器实例仍然是活动的。如果服务器在90秒内没有看到更新,它将从其注册表中删除实例
Eureka客户端从服务器获取注册表信息并将其缓存在本地。
之后,客户端使用这些信息来查找其他服务。
通过获取上一个获取周期和当前获取周期之间的增量更新,可以定期(每30秒)更新此信息。
节点信息在服务器中保存的时间更长(大约3分钟),因此获取节点信息时可能会再次返回相同的实例。Eureka客户端自动处理重复的信息。
在获得增量之后,Eureka客户机通过比较服务器返回的实例计数来与服务器协调信息,如果由于某种原因信息不匹配,则再次获取整个注册表信息。
Eureka客户端在关闭时向Eureka服务器发送取消请求。这将从服务器的实例注册表中删除实例,从而有效地将实例从通信量中取出。
同步时间延迟
来自Eureka客户端的所有操作可能需要一段时间才能反映到Eureka服务器上,然后反映到其他Eureka客户端上。这是因为eureka服务器上的有效负载缓存,它会定期刷新以反映新信息。Eureka客户端还定期地获取增量。因此,更改传播到所有Eureka客户机可能需要2分钟。
通讯机制
Http协议下的Rest请求
默认情况下Eureka使用Jersey和Jackson以及JSON完成节点间的通讯
新建一个web项目,引入starterspring-cloud-starter-netflix-eureka-client
#续约发送间隔默认30秒,心跳间隔
eureka.instance.lease-renewal-interval-in-seconds=5
#表示eureka client间隔多久去拉取服务注册信息,默认为30秒,对于api-gateway,如果要迅速获取服务注册状态,可以缩小该值,比如5秒
eureka.client.registry-fetch-interval-seconds=5
# 续约到期时间(默认90秒)
eureka.instance.lease-expiration-duration-in-seconds=60
#关闭自我保护模式
eureka.server.enable-self-preservation=false
#失效服务间隔
eureka.server.eviction-interval-timer-in-ms=3000
我直接重新创建了一个工程,加入eureka client插件,spring web,
修改工程application.properties为application.yml
eureka:
client:
serviceUrl:
defaultZone: http://eureka1.com:7900/eureka/
server:
port: 80
spring:
application:
name: eurekaclient
建立controller层,controller层要放入application的同级目录,否则找不到,网页请求无响应
MainController
package com.anzhi;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MainController {
@GetMapping("/getHi")
public String getHi(){
return "get HI";
}
}
启动前面的7900服务,再启动客户服务,如下:
官方文档
https://github.com/Netflix/eureka/wiki/Eureka-REST-operations
Operation | HTTP action | Description |
---|---|---|
Register new application instance | POST /eureka/v2/apps/appID | Input: JSON/XMLpayload HTTPCode: 204 on success |
De-register application instance | DELETE /eureka/v2/apps/appID/instanceID | HTTP Code: 200 on success |
Send application instance heartbeat | PUT /eureka/v2/apps/appID/instanceID | HTTP Code: * 200 on success * 404 if instanceIDdoesn’t exist |
Query for all instances | GET /eureka/v2/apps | HTTP Code: 200 on success Output: JSON/XML |
Query for all appID instances | GET /eureka/v2/apps/appID | HTTP Code: 200 on success Output: JSON/XML |
Query for a specific appID/instanceID | GET /eureka/v2/apps/appID/instanceID | HTTP Code: 200 on success Output: JSON/XML |
Query for a specific instanceID | GET /eureka/v2/instances/instanceID | HTTP Code: 200 on success Output: JSON/XML |
Take instance out of service | PUT /eureka/v2/apps/appID/instanceID/status?value=OUT_OF_SERVICE | HTTP Code: * 200 on success * 500 on failure |
Move instance back into service (remove override) | DELETE /eureka/v2/apps/appID/instanceID/status?value=UP (The value=UP is optional, it is used as a suggestion for the fallback status due to removal of the override) | HTTP Code: * 200 on success * 500 on failure |
Update metadata | PUT /eureka/v2/apps/appID/instanceID/metadata?key=value | HTTP Code: * 200 on success * 500 on failure |
Query for all instances under a particular vip address | GET /eureka/v2/vips/vipAddress | * HTTP Code: 200 on success Output: JSON/XML * 404 if the vipAddressdoes not exist. |
Query for all instances under a particular secure vip address | GET /eureka/v2/svips/svipAddress | * HTTP Code: 200 on success Output: JSON/XML * 404 if the svipAddressdoes not exist. |
使用浏览器请求url会返回服务器状态信息
test
16
526mb
183mb (34%)
00:00
localhost
localhost
UNKNOWN
192.168.29.1
UP
UNKNOWN
8080
443
1
MyOwn
30
90
0
0
0
0
8080
7649
http://localhost:8080/
http://localhost:8080/actuator/info
http://localhost:8080/actuator/health
unknown
unknown
false
1586328420409
1586328420519
如果需要json格式 可以加个请求头Accept:application/json
{
"generalStats": {
"environment": "test",
"num-of-cpus": "16",
"total-avail-memory": "517mb",
"current-memory-usage": "45mb (8%)",
"server-uptime": "00:03"
},
"applicationStats": {
"registered-replicas": "",
"available-replicas": "",
"unavailable-replicas": ""
},
"instanceInfo": {
"instanceId": "localhost",
"hostName": "localhost",
"app": "UNKNOWN",
"ipAddr": "192.168.29.1",
"status": "UP",
"overriddenStatus": "UNKNOWN",
"port": {
"$": 8080,
"@enabled": "true"
},
"securePort": {
"$": 443,
"@enabled": "false"
},
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"leaseInfo": {
"renewalIntervalInSecs": 30,
"durationInSecs": 90,
"registrationTimestamp": 0,
"lastRenewalTimestamp": 0,
"evictionTimestamp": 0,
"serviceUpTimestamp": 0
},
"metadata": {
"management.port": "8080",
"jmx.port": "7649"
},
"homePageUrl": "http://localhost:8080/",
"statusPageUrl": "http://localhost:8080/actuator/info",
"healthCheckUrl": "http://localhost:8080/actuator/health",
"vipAddress": "unknown",
"secureVipAddress": "unknown",
"isCoordinatingDiscoveryServer": "false",
"lastUpdatedTimestamp": "1586328420409",
"lastDirtyTimestamp": "1586328420519"
}
get: {ip:port}/eureka/apps
get: {ip:port}/eureka/apps/{appname}/{id}
put:{ip:port}/eureka/apps/{appname}/{id}?lastDirtyTimestamp={}&status=up
put:{ip:port}/eureka/apps/{appname}/{id}/status?lastDirtyTimestamp={}&value={UP/DOWN} 对应eureka源码的:InstanceResource.statusUpdate
delete:{ip:port}/eureka/apps/{appname}/{id}/status?lastDirtyTimestamp={}&value={UP/DOWN}
delete: {ip:port}/eureka/apps/{appname}/{id}
Eureka的元数据有两种:标准元数据和自定义元数据。 标准元数据:主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。 自定义元数据:可以使用eureka.instance.metadata-map配置,这些元数据可以在远程客户端中访问,但是一般不改变客户端行为,除非客户端知道该元数据的含义。
可以在配置文件中对当前服务设置自定义元数据,可后期用户个性化使用
元数据可以配置在eureka服务器和eureka的客户端上
eureka.instance.metadata-map.dalao=mashibing
{
"applications": {
"versions__delta": "1",
"apps__hashcode": "UP_2_",
"application": [
{
"name": "EUREKA-CONSUMER",
"instance": [
{
"instanceId": "localhost:Eureka-Consumer:9001",
"hostName": "localhost",
"app": "EUREKA-CONSUMER",
"ipAddr": "192.168.29.1",
"status": "UP",
"overriddenStatus": "UNKNOWN",
"port": {
"$": 9001,
"@enabled": "true"
},
"securePort": {
"$": 443,
"@enabled": "false"
},
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"leaseInfo": {
"renewalIntervalInSecs": 30,
"durationInSecs": 90,
"registrationTimestamp": 1586331982283,
"lastRenewalTimestamp": 1586331982283,
"evictionTimestamp": 0,
"serviceUpTimestamp": 1586331982283
},
"metadata": {
"dalao": "mashibing666",
"management.port": "9001",
"jmx.port": "10158"
},
"homePageUrl": "http://localhost:9001/",
"statusPageUrl": "http://localhost:9001/actuator/info",
"healthCheckUrl": "http://localhost:9001/actuator/health",
"vipAddress": "Eureka-Consumer",
"secureVipAddress": "Eureka-Consumer",
"isCoordinatingDiscoveryServer": "false",
"lastUpdatedTimestamp": "1586331982283",
"lastDirtyTimestamp": "1586331982260",
"actionType": "ADDED"
},
{
"instanceId": "localhost:Eureka-Consumer:9000",
"hostName": "localhost",
"app": "EUREKA-CONSUMER",
"ipAddr": "192.168.29.1",
"status": "UP",
"overriddenStatus": "UNKNOWN",
"port": {
"$": 9000,
"@enabled": "true"
},
"securePort": {
"$": 443,
"@enabled": "false"
},
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"leaseInfo": {
"renewalIntervalInSecs": 30,
"durationInSecs": 90,
"registrationTimestamp": 1586331637223,
"lastRenewalTimestamp": 1586332057220,
"evictionTimestamp": 0,
"serviceUpTimestamp": 1586331637223
},
"metadata": {
"dalao": "mashibing",
"management.port": "9000",
"jmx.port": "10000"
},
"homePageUrl": "http://localhost:9000/",
"statusPageUrl": "http://localhost:9000/actuator/info",
"healthCheckUrl": "http://localhost:9000/actuator/health",
"vipAddress": "Eureka-Consumer",
"secureVipAddress": "Eureka-Consumer",
"isCoordinatingDiscoveryServer": "false",
"lastUpdatedTimestamp": "1586331637223",
"lastDirtyTimestamp": "1586331637182",
"actionType": "ADDED"
}
]
}
]
}
}
EurekaClient 可以在客户端获取eureka服务器上的注册者信息
org.springframework.cloud.client.discovery与com.netflix.discovery.DiscoveryClient
org.springframework.cloud.client.discovery是SpringCloud对注册中心client的抽象封装,提供公用功能
org.springframework.cloud.client.discovery定义用来服务发现的客户端接口,是客户端进行服务发现的核心接口,是spring cloud用来进行服务发现的顶级接口,在common中可以看到其地位。在Netflix Eureka和Consul中都有具体的实现类。
代表通用于服务发现的读操作,例如在 eureka或consul中有:
String description();//获取实现类的描述。
List getServices();//获取所有服务实例id。
List getInstances(String serviceId);//通过服务id查询服务实例信息列表。
com.netflix.discovery.DiscoveryClient为Eureka注册中心客户端的接口,功能更丰富
测试:点对点的consumer与provider,eureka是没有意义的,直接多搭几个
@RestController
public class MainController {
@Autowired
DiscoveryClient client; //spring Cloud自定义的抽象接口
@Autowired
EurekaClient client2;
@Autowired
LoadBalancerClient lb;
@GetMapping("/getHi")
public String getHi(){
return "get Hi";
}
@GetMapping("/client")
public String client(){
List<String> services = client.getServices();
for(String service:services){
System.out.println(service);
}
return "hi";
}
@GetMapping("/client2")
public Object client2(){
//获取服务提供方
return client.getInstances("provider");
}
@GetMapping("/client3")
public Object client3(){
//获取服务提供方
List<ServiceInstance> instances = client.getInstances("provider");
for (ServiceInstance instance : instances) {
System.out.println(ToStringBuilder.reflectionToString(instance));
}
return "anzhi";
}
@GetMapping("/client4")
public Object client4(){
//获取服务提供方
//List instances = client2.getInstancesById("localhost:provider:80");
// 使用服务名找多个
List<InstanceInfo> instances = client2.getInstancesByVipAddress("provider", false);
for (InstanceInfo instance : instances) {
System.out.println(ToStringBuilder.reflectionToString(instance));
}
if(instances.size()>0){
InstanceInfo instanceInfo = instances.get(0);
if(instanceInfo.getStatus() == InstanceInfo.InstanceStatus.UP){
String url = "http://"+instanceInfo.getHostName()+":"+
instanceInfo.getPort()+"/getHi";
System.out.println("url: "+url);
//发起请求
RestTemplate restTemplate = new RestTemplate();
String respStr = restTemplate.getForObject(url, String.class);
System.out.println("respStr: "+respStr);
}
}
return "anzhi";
}
@GetMapping("/client5")
public Object client5(){
//ribbon 完成客户端的负载均衡,过滤掉down了的节点
ServiceInstance instance = lb.choose("provider");
String url = "http://"+instance.getHost()+":"+instance.getPort()+"/getHi";
//发起请求
RestTemplate restTemplate = new RestTemplate();
String respStr = restTemplate.getForObject(url, String.class);
System.out.println("respStr: "+respStr);
return "anzhi";
}
}
Eureka在CAP理论当中是属于AP , 也就说当产生网络分区时,Eureka保证系统的可用性,但不保证系统里面数据的一致性
默认开启,服务器端容错的一种方式,即短时间心跳不到达仍不剔除服务列表里的节点
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
默认情况下,Eureka Server在一定时间内,没有接收到某个微服务心跳,会将某个微服务注销(90S)。但是当网络故障时,微服务与Server之间无法正常通信,上述行为就非常危险,因为微服务正常,不应该注销。
Eureka Server通过自我保护模式来解决整个问题,当Server在短时间内丢失过多客户端时,那么Server会进入自我保护模式,会保护注册表中的微服务不被注销掉。当网络故障恢复后,退出自我保护模式。
思想:宁可保留健康的和不健康的,也不盲目注销任何健康的服务。
客户端每分钟续约数量小于客户端总数的85%时会触发保护机制
自我保护机制的触发条件: (当每分钟心跳次数( renewsLastMin ) 小于 numberOfRenewsPerMinThreshold 时,并且开启自动保护模式开关( eureka.server.enable-self-preservation = true ) 时,触发自我保护机制,不再自动过期租约。) numberOfRenewsPerMinThreshold = expectedNumberOfRenewsPerMin * 续租百分比( eureka.server.renewalPercentThreshold, 默认0.85 ) expectedNumberOfRenewsPerMin = 当前注册的应用实例数 x 2 为什么乘以 2: 默认情况下,注册的应用实例每半分钟续租一次,那么一分钟心跳两次,因此 x 2 。
服务实例数:10个,期望每分钟续约数:10 * 2=20,期望阈值:20*0.85=17,自我保护少于17时 触发。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Spring Boot 2.0 的Actuator只暴露了health和info端点,提供的监控信息无法满足我们的需求
在1.x中有n多可供我们监控的节点,官方的回答是为了安全….
在application.yml中加入如下配置信息
*代表所有节点都加载,yml配置
#开启所有端点
management:
endpoints:
web:
exposure:
include: '*'
所有端点都开启后的api列表,http://localhost:7900/actuator 本地+端口号/actuator
{
"_links":{
"self":{
"href":"http://localhost:7900/actuator","templated":false},"beans":{
"href":"http://localhost:7900/actuator/beans","templated":false},"caches-cache":{
"href":"http://localhost:7900/actuator/caches/{cache}","templated":true},"caches":{
"href":"http://localhost:7900/actuator/caches","templated":false},"health":{
"href":"http://localhost:7900/actuator/health","templated":false},"health-path":{
"href":"http://localhost:7900/actuator/health/{*path}","templated":true},"info":{
"href":"http://localhost:7900/actuator/info","templated":false},"conditions":{
"href":"http://localhost:7900/actuator/conditions","templated":false},"configprops":{
"href":"http://localhost:7900/actuator/configprops","templated":false},"env-toMatch":{
"href":"http://localhost:7900/actuator/env/{toMatch}","templated":true},"env":{
"href":"http://localhost:7900/actuator/env","templated":false},"loggers":{
"href":"http://localhost:7900/actuator/loggers","templated":false},"loggers-name":{
"href":"http://localhost:7900/actuator/loggers/{name}","templated":true},"heapdump":{
"href":"http://localhost:7900/actuator/heapdump","templated":false},"threaddump":{
"href":"http://localhost:7900/actuator/threaddump","templated":false},"metrics-requiredMetricName":{
"href":"http://localhost:7900/actuator/metrics/{requiredMetricName}","templated":true},"metrics":{
"href":"http://localhost:7900/actuator/metrics","templated":false},"scheduledtasks":{
"href":"http://localhost:7900/actuator/scheduledtasks","templated":false},"mappings":{
"href":"http://localhost:7900/actuator/mappings","templated":false},"refresh":{
"href":"http://localhost:7900/actuator/refresh","templated":false},"features":{
"href":"http://localhost:7900/actuator/features","templated":false},"serviceregistry":{
"href":"http://localhost:7900/actuator/serviceregistry","templated":false}}}
shutdown节点显示:
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
shutdown:
enabled: true
剔除:
AbstractInstanceRegistry
public void evict(long additionalLeaseMs) {
logger.debug("Running the evict task");
if (!isLeaseExpirationEnabled()) {
logger.debug("DS: lease expiration is currently disabled.");
return;
}
此代码意思:if中判断为true,不走此逻辑,走下面的剔除。如果if为false。走此逻辑,不剔除。
PeerAwareInstanceRegistryImpl
@Override
public boolean isLeaseExpirationEnabled() {
if (!isSelfPreservationModeEnabled()) {
//如果打开自我保护,不进入此逻辑。
// The self preservation mode is disabled, hence allowing the instances to expire.
return true;
}
return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
}
eureka.server.enable-self-preservation=false
关闭后会提示
tupian
默认60秒
eureka.server.eviction-interval-timer-in-ms=3000
由于server和client通过心跳保持 服务状态,而只有状态为UP的服务才能被访问。看eureka界面中的status。
比如心跳一直正常,服务一直UP,但是此服务DB连不上了,无法正常提供服务。
此时,我们需要将 微服务的健康状态也同步到server。只需要启动eureka的健康检查就行。这样微服务就会将自己的健康状态同步到eureka。配置如下即可。
在client端配置:将自己真正的健康状态传播到server。
eureka:
client:
healthcheck:
enabled: true
org.springframework.boot
spring-boot-starter-actuator
@Service
public class HealthStatusService implements HealthIndicator{
private Boolean status = true;
public void setStatus(Boolean status) {
this.status = status;
}
@Override
public Health health() {
// TODO Auto-generated method stub
if(status)
return new Health.Builder().up().build();
return new Health.Builder().down().build();
}
public String getStatus() {
// TODO Auto-generated method stub
return this.status.toString();
}
@GetMapping("/health")
public String health(@RequestParam("status") Boolean status) {
healthStatusSrv.setStatus(status);
return healthStatusSrv.getStatus();
}
访问接口,发送fales和true观察服务状态
导入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
配置密码:
eureka:
client:
serviceUrl:
defaultZone: http://eureka1.com:7900/eureka/
register-with-eureka: false
fetch-registry: false
server:
port: 7900
spring:
application:
name: provider
security:
user:
name: anzhi
password: 123
management:
endpoints:
web:
exposure:
include: '*'
endpoint: # 远程关闭
shutdown:
enabled: true
如果服务注册报错
Root name 'timestamp' does not match expected ('instance') for type [simple
是默认开启了防止跨域攻击
手动关闭
在服务端增加配置类
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http.csrf().disable();
super.configure(http);
}
}
微服务中,很多服务系统都在独立的进程中运行,通过各个服务系统之间的协作来实现一个大项目的所有业务功能。服务系统间 使用多种跨进程的方式进行通信协作,而RESTful风格的网络请求是最为常见的交互方式之一。
spring cloud提供的方式:
RestTemplate自由,方便调用别的第三方的http服务。feign也可以,更面向对象一些,更优雅一些,就是需要配置。
负载均衡图解:
Ribbon&&RestTemplate图解
一个服务端:
yml
eureka:
client:
serviceUrl:
defaultZone: http://eureka1.com:7900/eureka/
# 自己注册到eureka server,但是本身就是server,所以设置为false,自己不注册自己,默认为true
register-with-eureka: false
fetch-registry: false
server:
port: 7900
spring:
application:
name: eurekaserver
management:
endpoints:
web:
exposure:
include: '*'
endpoint: # 远程关闭
shutdown:
enabled: true
MainController
import java.util.List;
@RestController
public class MainController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/client6")
public Object client6(){
//ribbon 完成客户端的负载均衡,过滤掉down了的节点
ServiceInstance instance = lb.choose("provider");
String url = "http://"+instance.getHost()+":"+instance.getPort()+"/getHi";
String respStr = restTemplate.getForObject(url, String.class);
System.out.println("respStr: "+respStr);
return "anzhi";
}
}
EurekaproviderApplication.java
@EnableEurekaServer
@SpringBootApplication
public class EurekaproviderApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaproviderApplication.class, args);
}
@Bean
RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
两个服务端
@RestController
public class MainController {
@Autowired
DiscoveryClient client; //spring Cloud自定义的抽象接口
@Autowired
EurekaClient client2;
@Autowired
LoadBalancerClient lb;
@Autowired
HealthStatusService healthStatusSrv;
@Autowired
RestTemplate restTemplate;
@Value("${server.port}")
String port;
@GetMapping("/getHi")
public String getHi(){
return "我的端口号是:"+port;
}
@GetMapping("/client6")
public Object client6(){
//ribbon 完成客户端的负载均衡,过滤掉down了的节点
ServiceInstance instance = lb.choose("provider");
String url = "http://"+instance.getHost()+":"+instance.getPort()+"/getHi";
String respStr = restTemplate.getForObject(url, String.class);
System.out.println("respStr: "+respStr);
return respStr;
}
}
yml
eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://eureka1.com:7900/eureka/
server:
port: 81 # 启动第二个时改变端口
spring:
application:
name: provider
再起一个客户端,并改变名称,还是同一个客户端,只是端口好,名称改变
eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://eureka1.com:7900/eureka/
server:
port: 90 # 启动第二个时改变端口
spring:
application:
name: consumer
然后访问http://localhost:90/client6,页面的端口号会发生变化81和80变化
在服务端配置
EurekaproviderApplication.java
@Bean
public IRule myRule(){
//return new RoundRobinRule();
return new RandomRule();
//return new RetryRule();
}
在90客户端实现
@GetMapping("/client7")
public Object client7(){
List<ServiceInstance> instances = discoveryClient.getInstances("provider");
// 自定义的轮询算法
// 随机
int nextInt = new Random().nextInt(instances.size());
ServiceInstance instance = instances.get(nextInt);
// 轮询
AtomicInteger atomicIntege = new AtomicInteger();
int i = atomicIntege.getAndIncrement();
instances.get(i%instances.size());
String url = "http://"+instance.getHost()+":"+instance.getPort()+"/getHi";
String respStr = restTemplate.getForObject(url, String.class);
return respStr;
}
注释掉服务器中EurekaclientApplication中定义的负载均衡策略,7900端口
针对服务定ribbon策略:客户端
provider:
ribbon:
NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule:
@GetMapping("/client8")
public Object client8(){
ServiceInstance instance = lb.choose("provider");
String url = "http://"+instance.getHost()+":"+instance.getPort()+"/getHi";
String respStr = restTemplate.getForObject(url, String.class);
return respStr;
}
给所有服务定ribbon策略:
ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
属性配置方式优先级高于Java代码。
ribbon.eureka.enabled=false
ribbon.listOfServers=localhost:80,localhost:81
为service-sms设置 请求的网络地址列表。
Ribbon可以和服务注册中心Eureka一起工作,从服务注册中心获取服务端的地址信息,也可以在配置文件中使用listOfServers字段来设置服务端地址。
客户端改叫服务消费者更恰当:EurekaclientApplication
@Bean
@LoadBalanced
RestTemplate getRestTemplate(){
return new RestTemplate();
}
MainController
@GetMapping("/client9")
public Object client9(){
//自动处理URL
String url = "http://provider/getHi";
String respStr = restTemplate.getForObject(url,String.class);
System.out.println(respStr);
return respStr;
}
摘自:https://blog.csdn.net/u014395955/article/details/106938527/