概念:
-
由Netflix开源,并被Pivatal集成到SpringCloud体系中,它是基于 RestfulAPI⻛格开发的服务注册与发现组件;
-
对于任何⼀个微服务,原则上都应存在或者⽀持多个提供者(⽐如简历微服务部署多个实例),这是由微服务的分布式属性决定的。更进⼀步,为了⽀持弹性扩缩容特性,⼀个微服务的提供者的数量和分布往往是动态变化的,也是⽆法预先确定的。因此,原本在单体应⽤阶段常⽤的静态LB机制就不再适⽤了,需要引⼊额外的组件来管理微服务提供者的注册与发现,⽽这个组件就是服务注册中⼼(服务注册中⼼本质上是为了解耦服务提供者和服务消费者);
-
分布式微服务架构中,服务注册中⼼⽤于存储服务提供者地址信息、服务发布相关的属性信息,消费者通过主动查询和被动通知的⽅式获取服务提供者的地址信息,⽽不再需要通过硬编码⽅式得到提供者的地址信息。消费者只需要知道当前系统发布了那些服务,⽽不需要知道服务具体存在于什么位置,这就是透明化路由;
-
pull模式:服务消费者可以主动拉取可用的服务消费者;
-
push模式:服务消费者订阅服务(当服务提供者有变化时,注册中心也会主动推送更新后的服务清单给消费者);
与其它主流服务中心对比
- Zookeeper:它是⼀个分布式服务框架,是Apache Hadoop 的⼀个⼦项⽬,它主要是⽤来解决分布式应 ⽤中经常遇到的⼀些数据管理问题,如:统⼀命名服务、状态同步服务、集群管理、分布式应⽤配置项的管理等。简单来说zookeeper本质=存储+监听通知;
- Consul:由HashiCorp基于Go语⾔开发的⽀持多数据中⼼分布式⾼可⽤的服务发布和注册服务软件, 采⽤Raft算法保证服务的⼀致性,且⽀持健康检查;
- Nacos:⼀个更易于构建云原⽣应⽤的动态服务发现、配置管理和服务管理平台。简单来说 Nacos 就是 注册中⼼ + 配置中⼼的组合,帮助我们解决微服务开发必会涉及到的服务注册 与发现,服务配置,服务管理等问题。Nacos 是Spring Cloud Alibaba 核⼼组件之⼀,负责服务注册与发现,还有配置;
组件名 | 语言 | CAP | 对外暴露接口 |
---|---|---|---|
Eureka | Java | AP | HTTP |
Consul | Go | CP | HTTP/DNS |
Zookeeper | Java | CP | 客户端 |
Nacos | Java | CP与AP切换 | HTTP |
- P:分区容错性(⼀定的要满⾜的);
- C:数据⼀致性;
- A:⾼可⽤;
CAP不可能同时满⾜三个,要么是AP,要么是CP;
搭建项目
服务注册中心
#application.yml配置文件
server:
port: 8762
spring:
application:
name: eureka-server # 应⽤名称,会在Eureka中作为服务的id标识(serviceId)
security:
user:
name: admin
password: 123
eureka:
client:
register-with-eureka: true #是否需要⾃⼰注册⾃⼰(默认为true)
fetch-registry: true #是否需要从Eureka Server获取服务信(默认为true)
service-url:
defaultZone: http://admin:123@localhost:8761/eureka,http://admin:123@localhost:8762/eureka #eureka注册中心地址多个用‘,’号分割
server:
enable-self-preservation: false #关闭自我保护模式-eureka会尝试保护其服务的注册表中的信息,不再删除服务注册表中的数据
eviction-interval-timer-in-ms: 5000 #设置清理间隔时间-默认6000ms
对于搭建单体的服务注册中心register-with-eureka、fetch-registry需设置为false,因为对于单个注册中心而言,它自己本身就是个服务,不用向自己注册自己,也没有其他注册中心服务可以提供信息拉取,service-url也不需要设置;
有多个注册中心,则需把register-with-eureka、fetch-registry设置为true,同时在service-url.defaultZone上也配置相对应的地址,如果注册中心程序有使用SpringSecurity认证,则需值在服务地址中加入用户名与密码;
//启动类
@SpringBootApplication
//开启Eureka服务
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
//服务上下线监控,每个节点都会触发时间,需要控制发送通知的行为
@Component
public class EurekaStateChangeListener {
/**
* 功能描述: 服务下线通知
* @author LinPeiChou
* @date: 2022/7/23 9:51
* @param:
* @return:
*/
@EventListener
public void listen(EurekaInstanceCanceledEvent eurekaInstanceCanceledEvent){
System.out.println(eurekaInstanceCanceledEvent.getAppName()+":服务下线");
}
/**
* 功能描述: 服务注册通知
* @author LinPeiChou
* @date: 2022/7/23 9:53
* @param:
* @return:
*/
@EventListener
public void listen(EurekaInstanceRegisteredEvent eurekaInstanceRegisteredEvent){
InstanceInfo instanceInfo = eurekaInstanceRegisteredEvent.getInstanceInfo();
System.out.println(instanceInfo.getAppName()+":服务注册");
}
/**
* 功能描述: 服务续约通知
* @author LinPeiChou
* @date: 2022/7/23 9:54
* @param:
* @return:
*/
@EventListener
public void listen(EurekaInstanceRenewedEvent eurekaInstanceRenewedEvent){
System.out.println(eurekaInstanceRenewedEvent.getAppName()+":服务续约");
}
/**
* 功能描述: 服务启动通知
* @author LinPeiChou
* @date: 2022/7/23 9:55
* @param:
* @return:
*/
@EventListener
public void listen(EurekaServerStartedEvent eurekaServerStartedEvent){
System.out.println("Eureka Server服务启动");
}
}
服务提供者
#application.yml配置文件
server:
port: 8081
spring:
application:
name: eureka-client-user-service
eureka:
instance:
prefer-ip-address: true #采用IP注册
# prefer-ip-address: false #采用IP注册
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} #定义实例ID格式
status-page-url: https://baidu.com #自定义跳转地址,点击实例时,跳转的地址
lease-renewal-interval-in-seconds: 5 #发送心跳检测频率(默认30S),在开发环境中用于快速快速移除失效的服务信息,生产环境不推荐使用
lease-expiration-duration-in-seconds: 5 #表示自上一次收到client心跳后,等待下一次的超时时间(默认90S),在这个时间内若没有收到下一次心跳,则移除该Instance,在开发环境中用于快速快速移除失效的服务信息,生产环境不推荐使用
client:
service-url:
defaultZone: http://admin:123@localhost:8761/eureka,http://admin:123@localhost:8762/eureka #eureka注册中心地址多个用‘,’号分割
register-with-eureka: true #向服务注册中心注册
fetch-registry: true #向服务注册中心获取服务信息
healthcheck:
enabled: true #开启健康检查
//启动类
@SpringBootApplication
@EnableDiscoveryClient//开启发现客户端
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
服务提供者启动类需要添加@EnableDiscoveryClient注解,表示当前项目为一个Eureka客户端;
//服务提供者对外提供接口
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
}
服务提供者
#application.yml配置文件
server:
port: 8082
spring:
application:
name: eureka-client-srticle-service
eureka:
instance:
hostname: LagouCloudEurekaServerA
# prefer-ip-address: true #采用IP注册
prefer-ip-address: false #采用IP注册
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} #定义实例ID格式
client:
service-url:
defaultZone: http://admin:123@localhost:8761/eureka,http://admin:123@localhost:8762/eureka #eureka注册中心地址多个用‘,’号分割
register-with-eureka: true #向服务注册中心注册
fetch-registry: true #向服务注册中心获取服务信息
//启动类
@SpringBootApplication
@EnableDiscoveryClient//开启发现客户端
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
@Bean
@LoadBalanced //Ribbon客户端负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
//服务消费者消费服务提供者对外提供接口
@RestController
@RequestMapping("/article")
public class CallController {
@Autowired
private RestTemplate restTemplate;
/*第一版*/
// @GetMapping("/callhello")
// public String hello(){
// return restTemplate.getForObject("http://localhost:8081/user/hello",String.class);
// }
/*第二版*/
@GetMapping("/callhello")
public String hello(){
//不再使用固定地址,而是使用服务提供者的spring.applicatio.name
return restTemplate.getForObject("http://eureka-client-user-service/user/hello",String.class);
}
}
获取项目服务的数据
@Autowired
private EurekaClient eurekaClient;
public Object setviceUrl(){
return eurekaClient.getInstanceByVipAddress("eureka-client-srticle-service");
}
<!-- 使用上述方法返回为Json格式的信息,以下结果为使用postman请求接口直接获取为XML格式 -->
<application>
<name>EUREKA-CLIENT-USER-SERVICE</name>
<instance>
<instanceId>eureka-client-user-service:192.168.62.108:8081</instanceId>
<hostName>192.168.62.108</hostName>
<app>EUREKA-CLIENT-USER-SERVICE</app>
<ipAddr>192.168.62.108</ipAddr>
<status>UP</status>
<overriddenstatus>UNKNOWN</overriddenstatus>
<port enabled="true">8081</port>
<securePort enabled="false">443</securePort>
<countryId>1</countryId>
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
<name>MyOwn</name>
</dataCenterInfo>
<leaseInfo>
<renewalIntervalInSecs>5</renewalIntervalInSecs>
<durationInSecs>5</durationInSecs>
<registrationTimestamp>1658633941565</registrationTimestamp>
<lastRenewalTimestamp>1658634947570</lastRenewalTimestamp>
<evictionTimestamp>0</evictionTimestamp>
<serviceUpTimestamp>1658633941565</serviceUpTimestamp>
</leaseInfo>
<metadata>
<management.port>8081</management.port>
</metadata>
<homePageUrl>http://192.168.62.108:8081/</homePageUrl>
<statusPageUrl>https://baidu.com</statusPageUrl>
<healthCheckUrl>http://192.168.62.108:8081/actuator/health</healthCheckUrl>
<vipAddress>eureka-client-user-service</vipAddress>
<secureVipAddress>eureka-client-user-service</secureVipAddress>
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
<lastUpdatedTimestamp>1658633941565</lastUpdatedTimestamp>
<lastDirtyTimestamp>1658633940984</lastDirtyTimestamp>
<actionType>ADDED</actionType>
</instance>
</application>