注册中心就是微服务架构中的通讯录,它记录了服务和服务地址的映射关系,在分布式架构中,服务会注册到这里,服务通过注册中心调用其他服务。
注册中心主要进行服务管理和服务依赖关系管理
- 服务注册后及时发现
- 负载均衡
- 服务宕机后即使下线
- 水平拓展服务
- 服务发现后处理路由
- 服务异常后的降级处理
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z06hxC1n-1670555809009)(img/Eureka注册中心角色.jpeg)]](/uploadfile/202403/8c9c1fc81e85d27.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zDKFEadd-1670555809010)(img/Eureka注册中心运转流程.jpeg)]](/uploadfile/202403/bb6cee5cd6893.png)
0:实例化服务
1:将服务注册到注册中心
2:注册中心收录服务
3:从服务中心获取服务列表
4:基于负载均衡从地址列表中选择服务地址进行服务调用
5:定期发送心跳
6:检查没有定期发送心跳的服务,并在一定时间内剔除服务地址列表
4.0.0 com.byx scaffold-parent 0.0.1 ../parent scaffold-eureka ${packaging.type} org.springframework.cloud spring-cloud-starter-netflix-eureka-server org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine org.springframework.boot spring-boot-maven-plugin
application
package com.byx.scaffold.rcentre;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@EnableEurekaServer
@SpringBootApplication
public class ServiceRegistrationAndDiscoveryApplication {public static void main(String[] args) {SpringApplication.run(ServiceRegistrationAndDiscoveryApplication.class, args);}
}
spring:application:name: eureka # 应用名称
server:port: 8761 # 端口
eureka:instance:hostname: localhost # 主机名prefer-ip-address: trueclient:register-with-eureka: false # 是否将自己注册到注册中心,默认 true ,集群时要为 true,相互注册fetch-registry: false # 是否从注册中线获取注册信息,默认为 trueservice-url: # 注册中心对外暴露的注册地址defaultZone: http://toolKit:dily1314@localhost:8761/eureka/
启动类或配置中增加 RestTemplate Bean
//@EnableEurekaClient 如果配置了注册中心,则会默认开启,无需使用该注解
@SpringBootApplication
public class ConsumerApplication {@Beanpublic RestTemplate restTemplate(){return new RestTemplate();};public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}}
Service 中调用服务
@Service
public class OrderService {@Autowiredprivate RestTemplate restTemplate; // 启动类 或者 Config 中 要注入bean@Autowiredprivate DiscoveryClient discoveryClient;public Order getList() {return new Order(1, "202020202020", selectProductListWithBalancer("provider", "/product/list"));}/*** 跨服务调用* 无负载均衡** @param service 服务名* @param api 接口* @return 结果*/private List> selectProductList(String service, String api) {StringBuffer sb = new StringBuffer();// 获取服务方法List services = discoveryClient.getServices();if (CollectionUtils.isEmpty(services))return null;// 根据服务名获取服务List serviceInstances = discoveryClient.getInstances(service);if (CollectionUtils.isEmpty(serviceInstances))return null;ServiceInstance si = serviceInstances.get(0);sb.append("http://" + si.getHost() + ":" + si.getPort() + api);// 返回数据ResponseEntity> response = restTemplate.exchange(sb.toString(),HttpMethod.GET,null,new ParameterizedTypeReference>() {});return response.getBody();}
}
启动类或配置中增加 RestTemplate Bean
//@EnableEurekaClient 如果配置了注册中心,则会默认开启,无需使用该注解
@SpringBootApplication
public class ConsumerApplication {@Beanpublic RestTemplate restTemplate(){return new RestTemplate();};public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}
}
Service 中调用服务
@Service
public class OrderService {@Autowiredprivate RestTemplate restTemplate; // 启动类 或者 Config 中 要注入bean@Autowiredprivate LoadBalancerClient loadBalancerClient;public Order getList() {return new Order(1, "202020202020", selectProductListWithBalancer("provider", "/product/list"));}/*** 跨服务调用* 有负载均衡** @param service 服务名* @param api 接口* @return 结果*/private List> selectProductListWithBalancer(String service, String api) {StringBuffer sb = new StringBuffer();// 根据服务名 通过负载均衡 获取服务ServiceInstance si = loadBalancerClient.choose(service);if (si == null)return null;sb.append("http://" + si.getHost() + ":" + si.getPort() + api);// 返回数据ResponseEntity> response = restTemplate.exchange(sb.toString(),HttpMethod.GET,null,new ParameterizedTypeReference>() {});return response.getBody();}
}
启动类或配置中增加 RestTemplate Bean 加入注解 @LoadBalanced
//@EnableEurekaClient 如果配置了注册中心,则会默认开启,无需使用该注解
@SpringBootApplication
public class ConsumerApplication {@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();};public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}}
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TWYIZRcB-1670555809010)(img\Eureka原理图.jpeg)]](/uploadfile/202403/2ad1be6371e2ed7.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pj6Jhtj7-1670555809011)(img\CAP原则.jpeg)]](/uploadfile/202403/8c3ffc03bd6ef3b.png)
C: 一致性
A: 高可用
P: 分区容错
金融: CP(大型) 、AP(互联网)
CA: 如果不要求 P(不允许分区),则 C(强一致性)和 A(高可用性)是可以保证的.放弃P意味着放弃了系统的扩展性,也就是分布式节点受限,没办法部署子节点,这是违背分布式系统设计初衷的。
CP:如果不要求 A(不要求高可用),则 C 相当于每个请求都需要在服务之间保持强一致性,而 P (分区容错)会导致同步时间无线延长(也就是等待数据同步才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致后再让用户访问系统。设计成 CP 的系统其实不少,最典型的就是分布式数据库,如:redis,对于分布式数据库来说,数据的一致性时最基本的要求。
AP:要求高可用并允许分区容错,则放弃一致性,一旦分区发生,节点之间可能失去联系,为了高可用,每个节点都只能使用本地数据提供服务,而这会导致全局数据不一致,典型应用就是抢购商品的场景,可能浏览的页面还显示有商品,但是下单的时候发现商品已经售完,这其实就是在A(高可用)方面保证可以正常服务,在数据一直性方面做了牺牲。
一般情况,服务在 Eureka 注册后 每30s 发送一次心跳,Eureka 通过心跳包来判断服务是否健康,同时会定期删除超过 90s 没有发送心跳的服务。
在服务不可以时,不会立即释放服务信息,而是保存一段时间后再删除。
在运行期间,Eureka 会统计心跳失败比例,如果 15 分钟内的低于 85%,Eureka 会将这些实例保护起来,让这些实例不会过期,同时提醒一个警告
eureka:server:enable-self-preservation: false # true 开启自我保护, false 关闭自我保护eviction-interval-timer-in-ms: 60000 # 清除隔离(单位:毫秒) 默认 60 * 1000
配置优雅停服后,无需配置关闭自我保护,这里用actuator实现
org.springframework.boot spring-boot-starter-actuator
# 度量指标监控与健康检查
management:endpoints:web:exposure:include: shutdown # 开启 shutdown 端点访问endpoint:shutdown:enabled: true # 开启 shutdown 实现优雅停服
向需要停服的服务地址发送 shutdown 请求
服务地址:172.10.16:8080
停服请求:Post方式 172.10.16:8080/actuator/shutdown
org.springframework.boot spring-boot-starter-security
spring:# 安全认证security:user:name: root # 用户名password: 123456 # 密码
eureka:instance:hostname: localhostclient:service-url:# 前面加用户名:密码defaultZone: http://root:123456@localhost:8761/eureka/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {@Overrideprotected void configure(HttpSecurity http) throws Exception {supper.configure(http) // 访问eureka 的控制台和/actuator 是可以做安全控制http.csrf().ignoringAntMatchers("/eureka/**"); //忽略eureka/**的所有请求}
}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {@Overrideprotected void configure(HttpSecurity http) throws Exception {http // 直接disable 会把安全验证也禁用,.csrf().disable().authorizeRequests().anyRequest().authenticated().and().httpBasic();}
}