consul重启导致服务必须跟着重启解决方案

本文探讨了由于Consul与Eureka健康检查机制的差异,导致服务在Consul重启或网络不稳定时需要重启的困境。作者通过研究Consul的REST接口,实现了类似Eureka的心跳机制,使服务能自动重新注册。此外,文中更新提到Consul官方解决方案,通过在服务机器上部署Client节点,并调整健康检查参数,确保服务在Consul集群重启后仍能保持正常状态,避免服务重启。
摘要由CSDN通过智能技术生成

问题:consul作为注册中心和eureka的机制不同。

当微服务启动后首先向注册中心发注册请求,这点两者一致。之后consul在维护可用服务列表时,采用的是主动向微服务发健康检查的接口(也可以配置成微服务主动向consul发心跳,但是我看完官网和各类文章都没说清楚具体怎么搞)。如果微服务正常返回,那么就认为服务正常。eureka是等待微服务主动向eureka发心跳,eureka收到心跳后,就给这个服务续约,认为服务是正常的。

正是因为这点差异就产生了大问题!

场景一:consul和微服务间的网络不稳定,断开了几分钟。

在此情况下,consul向微服务发的健康检查请求发不通,认为微服务挂了,就从服务列表里剔除他。

当网络恢复后,注意,已经剔除的服务,consul是不会主动再发健康检查的。那么服务列表里没有他,

也就不正常了。网关的转发都是需要获取可用服务列表,才能做转发的!

这时候,你想让微服务注册上consul就只能重启微服务了。这在生产环境意味着什么就不用多说了吧。

是不是很坑?

 

场景二:consul因为某种情况需要重启。(可能是consul也需要投产,或者其他原因,总之就是需要重启consul)

这个场景和上面的类似。当consul重启后,之前已经注册上来的,状态正常的服务,consul忘记了。

因为他重启了,他不记得前世的服务列表。

所以这种情况下,也需要重启我们的微服务,才能使状态正常。

 

综上,因为consul的健康检查机制是consul主动发,跟eureka不同才导致这种问题。

问题很清楚了,要怎么解决呢?

我查了很多资料,除了consul可以用ttl的方式,让微服务主动发心跳来续约外,好像没有想成的方案。

但是这个ttl的方案,官网没仔细说,网上的文章也是一大抄,没人说的明白。

我经过研究后,发现consul提供了好多rest接口。可以通过接口注册,注销,查询等。

那么我们就可以仿照eureka的方式,主动查consul的服务列表,类似心跳。

如果没查到,或者发不通(consul挂了)那么久开始发注册的接口,把自己注册上去。

不停的发,直至成功。

 

我用2个服务来模拟,一个是springcloudgateway网关gate,一个是微服务one

在应用启动后起一个定时任务:

package com.example.one.init;


import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
//CommandLineRunner ApplicationRunner


@Component
public class InitRunner implements ApplicationRunner {
        private static final Logger logger = LoggerFactory.getLogger(InitRunner.class);

        
        @Value("${spring.cloud.consul.host}")
    	private String consulip;
        
        @Value("${spring.cloud.consul.port}")
    	private String consulport;
        
    	@Value("${spring.cloud.client.ip-address}")
    	private String ip;
    
    	@Value("${spring.application.name}")
    	private String servername;
    
    	@Value("${server.port}")
    	private String port;
    	
    	@Value("${my.consul.check.register:false}")
    	private String registerFlag;//检查服务在consul列表不存在,或者consul访问不通,就不停的注册自己。保证consul因为网络不稳定或者重启,服务不正常。(微服务只有在启动是会注册自己,这样做是为了不重启服务)
        
    	@Value("${my.consul.check.interval:10}")
    	private String interval;//检查服务在consul中是否正常的频率,每interval秒检查一次
    	
    	private long intervalNum=10;//interval转成long类型
    	
        public long getIntervalNum() {
        	if(interval!=null) {
        		try {
        			intervalNum = Long.valueOf(interval.trim());
				} catch (NumberFormatException e) {
					intervalNum=10;
					e.printStackTrace();
				}
        	}
			return intervalNum;
		}
		public void setIntervalNum(long intervalNum) {
			this.intervalNum = intervalNum;
		}
		
		public String getRegisterFlag() {
        	//保证只有2个值
        	if(registerFlag!=null && registerFlag.trim().equals("true")) {
        		return "true";
        	}
			return "false";
		}


		public void setRegisterFlag(String registerFlag) {
			this.registerFlag = registerFlag;
		}


		@Autowired
    	private RestTemplate restTemplate;
//        @Autowired
//        private DbRouteDefinitionRepository dbRouteDefinitionRepository;

        public RestTemplate getRestTemplate() {
			return restTemplate;
		}


		public void setRestTemplate(RestTemplate restTemplate) {
			this.restTemplate = restTemplate;
		}


        @Override
        public void run(ApplicationArguments args) throws Exception {
        	DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        	String dateStr=LocalDateTime.now().format(df);
        	String serverInfo="["+servername+":"+ip+":"+port+"]";
        	
        	System.out.println(serverInfo+",InitRunner初始化逻辑。。。");
        	
        	/
        	
//          LocalDateTime startTime = LocalDateTime.now();
    		String message=serverInfo;
    		
    		//gate-192-168-124-17-8888
    		String defalutConsulId=servername+"-"+ip.replace(".", "-")+"-"+port;
    		
//    		long interVal=10;
        	ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        	scheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
        	    @Override
        	    public void run() {
        	    	if(getRegisterFlag().equals("false")) {
        	    		logger.info("InitRunner consul检查纠正服务开关未开启,不执行检查纠错服务。。。。。");
        	    		return;
        	    	}
        	    	try {
        	    		logger.info("InitRunner 执行consul检查纠正服务"+serverInfo+"。。。delay "+getIntervalNum()+" seconds...");
            	    	
            	    	//查询consul的已经注册的服务列表
            	    	String consulQueryUrl="http://"+consulip+":"+consulport+"/v1/agent/services";
            	    	String consulRegisterUrl="http://"+consulip+":"+consulport+"/v1/agent/service/register";
            	    	
//            	    	getRestTemplate().getForObject(url, responseType, uriVariables)
            	        HttpHeaders headers = new HttpHeaders();
            	        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
            	        HttpEntity<String> entity = new HttpEntity<String>(headers);
//            	        String strbody=restTemplate.exchange(consulUrl, HttpMethod.GET, entity,String.class).getBody();
//            	        Map responseData=restTemplate.exchange(consulUrl, HttpMethod.GET, entity,Map.class).getBody();
            	        
            	        ResponseEntity<Map> responseEntity=restTemplate.exchange(consulQueryUrl, HttpMethod.GET, entity,Map.class);
            	        Map responseData = responseEntity.getBody();
            	        
            	        boolean isExistSelf=false;//是否能从consul查到自己的信息。查不到说明自己没注册上或者被注销了,需要主动注册。
            	        if(responseData!=null) {
            	        	logger.info("InitRunner 执行consul检查纠正服务。。。返回 :\r\n"+responseData.toString());
            	        	
            	        	Iterator it= responseData.keySet().iterator();
            	        	while(it.hasNext()) {
            	        		String key= (String) it.next();
            	        		Map instance = (Map) responseData.get(key);
            	        		if(instance!=null) {
                	        		String Service =(String) instance.get("Service");
                	        		String Address =(String) instance.get("Address");
                	        		String Port =null;
                	        		if(instance.get("Port")!=null) {
                	        			Port =""+ instance.get("Port");
                	        		}
                	        		String ID =(String) instance.get("ID");
                	        		
                	        		if(Service.equalsIgnoreCase(servername) && Address.equals(ip) && Port.equals(port)) {
                	        			logger.info("InitRunner 执行consul检查纠正服务,找到本实例的ID==="+
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值