springcloud 配置服务间启动顺序
springcloud 微服务是由多个可独立运行的springboot服务组成,服务间可互相调用。但是如果在服务启动的时候,A服务依赖B服务的一些接口,此时B服务未启动完成,则A服务需等待B服务启动完成后才能启动。本文通过EUREKA服务注册与发现功能实现自定义服务启动顺序。
eureka服务注册与发现的机制原理此处不再叙述,本文主要通过EurekaDiscoveryClient获取注册中心注册的服务列表,轮询检查各服务的状态,根据状态(UP)以及调用服务的接口测试判断某个服务是否可以启动。
1.在EUREKA启动类上加注解@EnableDiscoveryClient
eureka状态改变监听器
import com.netflix.appinfo.InstanceInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.eureka.server.event.*;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* eureka状态改变监听器
*
*/
@Component
@Slf4j
public class EurekaStateChangeListener {
/**
* 服务下线事件
* @param eurekaInstanceCanceledEvent
*/
@EventListener
public void listen(EurekaInstanceCanceledEvent eurekaInstanceCanceledEvent) {
// 服务断线事件
String appName = eurekaInstanceCanceledEvent.getAppName();
String serverId = eurekaInstanceCanceledEvent.getServerId();
Objects.requireNonNull(appName, "服务名不能为空!");
log.info(">>>>>>> 失效服务:{},已被剔除!", serverId);
}
/**
* 服务注册事件
* @param event
*/
@EventListener
public void listen(EurekaInstanceRegisteredEvent event) {
// 服务注册
InstanceInfo instanceInfo = event.getInstanceInfo();
String appName = instanceInfo.getAppName();
Objects.requireNonNull(appName, "服务名不能为空!");
log.info(">>>>>>> 服务名:{},端口号:{}", appName, instanceInfo.getPort());
}
/**
* 服务续约事件(即心跳事件)
* @param event
*/
/*@EventListener(condition = "#event.replication==false")
public void listen(EurekaInstanceRenewedEvent event) {
//服务续约事件
event.getAppName();
event.getServerId();
log.info(">>>>>>>>>>>>>>>Server续约:" + event.getServerId());
}*/
/**
* 注册中心启动
* @param event
*/
@EventListener
public void listen(EurekaRegistryAvailableEvent event) {
log.info(">>>>>>>>>>>>>>>Server注册中心:" + event);
}
/**
* Server启动
* @param event
*/
@EventListener
public void listen(EurekaServerStartedEvent event) {
log.info(">>>>>>>>>>>>>>>Server启动:" + event);
//Server启动
}
}
eureka配置需禁用readOnlyCacheMap
eureka.client.refresh.enable = true
# 默认开启
eureka.client.fetch-registry = true
## 禁用readOnlyCacheMap
eureka.server.useReadOnlyResponseCache=false
eureka.client.registry-fetch-interval-seconds = 30
# 默认开启增量更新注册表缓存,即不禁用增量
#eureka.client.disable-delta = false
2.在A服务启动之前判断其他服务的状态以及接口是否可以调用成功
@PostConstruct:@PostConstruct注解好多人以为是Spring提供的。其实是Java自己的注解。
Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:
Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
/**
* A服务启动前判断其他服务是否注册成功且状态为UP
* @throws InterruptedException
*/
@PostConstruct
void started() throws InterruptedException {
while(!isProcessRun()){
// 服务未完全启动成功,等待1秒
Thread.sleep(1000);
}
log.info("B服务启动成功,开始启动A服务!!!!!!!!");
}
/**
* 判断服务是否注册成功且状态为UP,以及调用接口是否成功
* @return
*/
private boolean isProcessRun(){
boolean flag = true;
//获取注册中心注册的服务列表。对应的就是Application
List<String> applicationList = discoveryClient.getServices();
System.out.println("applicationList:"+applicationList);
if(!applicationList.contains("B服务名")){
log.error("B服务未注册!!!!!!!!");
flag = false;
return flag;
}
for(String applicationName : applicationList) {
//获取每个服务的提供者。对应的就是Application的status
List<ServiceInstance> instanceList = discoveryClient.getInstances(applicationName);
// System.out.println(instanceList.toString());
// 判断服务状态是否为UP
for (ServiceInstance serviceInstance : instanceList) {
String instanceInfoStr = serviceInstance.toString();
if (StringUtil.isNotBlank(instanceInfoStr)) {
// 实例状态(通过正则获取status的属性值)
String status = RegExUtils.matchGroup0("(\\bstatus = [A-Z]+\\w)", instanceInfoStr.trim());
// 判断B服务状态是否为UP
if (StringUtil.isNotBlank(instanceInfoStr)) {
status = status.substring(status.indexOf("= ") + 1).trim();
// System.out.println(status);
if (!"UP".equals(status)) {
flag = false;
log.error(serviceInstance.getServiceId()+"服务未启动成功,等待中!!!!!!!!");
break;
}
}
// 测试接口调用
//第一种方式 通过IP地址访问B服务接口测试。
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(serviceInstance.getUri()+"/connection",String.class);
System.out.println("服务URI:" + serviceInstance.getUri()+"/connection" + ";服务接口调用状态:"+response);
if(StringUtil.isBlank(response) || !"success".equals(response)){
flag = false;
log.error(serviceInstance.getServiceId()+"服务调用接口失败,等待中!!!!!!!!");
break;
}
}
}
}
return flag;
}
3.在B服务控制层添加测试接口调用的接口
/**
* 功能简述:测试接口是否可以调用成功
*
* @author aa
* @date 2021/10/22
* @since 1.0.0
*/
@RestController
public class ServiceController {
@GetMapping("/connection")
public String connection(){
return "success";
}
}
版权声明:本文为weixin_42123075原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。