Jenkins + NACOS + GATEWAY 实现微服务不停机部署

Nacos 版本  


    com.alibaba.cloud
    spring-cloud-starter-alibaba-nacos-discovery

Gateway 版本

org.springframework.cloud
spring-cloud-starter-gateway
3.1.1
  1. gateway 服务配置

 在Nacos 管理端下线服务后,Nacos服务端有定时延迟机制导致不能及时更新状态,导致服务重启后gateway网关依然会把请求分配到已经下线且停机的服务,导致接口提示未找到服务

此时需要更改gateway服务配置,定制自己的监听事件

增加MySubscribeConfig配置文件

/resources/META-INF 下增加spring.factories配置文件,内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.daishu.gateway.config.MySubscribeConfig

  
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.daishu.common.core.utils.SpringContextHolder;
import com.daishu.gateway.config.loadbalancer.MyNacosEventListener;
import com.daishu.gateway.config.loadbalancer.MyNacosSubscribe;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.cloud.loadbalancer.cache.LoadBalancerCacheManager;
import org.springframework.cloud.loadbalancer.config.LoadBalancerCacheAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;


/**
 * 首先订阅当前网关关注的服务
 * nacos服务更新通知,但是gateway有一套自己的服务缓存列表。每次接到通知更新不及时导致转发到已经下线的服务
 * gateway获取缓存参考:org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier
 * nacos订阅参考:com.alibaba.cloud.nacos.discovery.NacosWatch#start()
 *
 */
@Configuration
@Import(SpringContextHolder.class)
@AutoConfigureAfter(LoadBalancerCacheAutoConfiguration.class)
public class MySubscribeConfig {

    @Bean
    public MyNacosSubscribe getMyNacosSubscribe(NacosServiceManager nacosServiceManager, NacosDiscoveryProperties properties){
        LoadBalancerCacheManager cacheManager = SpringContextHolder.getBean(LoadBalancerCacheManager.class);
        return new MyNacosSubscribe(nacosServiceManager,properties,new MyNacosEventListener(cacheManager));
    }
}


import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.naming.NamingService;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;

import java.util.HashSet;
import java.util.Set;

/**
 * 订阅nacos推送更新事件
 * 启动和加载路由时重新订阅
 */
public class MyNacosSubscribe implements ApplicationRunner {
    private NacosServiceManager nacosServiceManager;
    private NacosDiscoveryProperties properties;
    private MyNacosEventListener myEventListener;

    private Set getRouteServices(){
        //  这里返回自己要订阅的服务名称
        Set serviceName=new HashSet<>();
        serviceName.add("server-name");
        return serviceName;
    }
    // 这里监听时间是为了在路由信息修改时候,重新订阅服务的。如果说路由重新加载不会有订阅变动的话,可以去掉
    @org.springframework.context.event.EventListener({RefreshRoutesEvent.class})
    public void subscribe() {
        NamingService namingService = nacosServiceManager
                .getNamingService(properties.getNacosProperties());
        try {
            Set services = getRouteServices();
            if(CollectionUtil.isNotEmpty(services)){
                for (String service : services) {
                    namingService.subscribe(service, properties.getGroup(),
                            null, myEventListener);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        subscribe();
    }

    public MyNacosSubscribe(NacosServiceManager nacosServiceManager, NacosDiscoveryProperties properties, MyNacosEventListener myEventListener) {
        this.nacosServiceManager = nacosServiceManager;
        this.properties = properties;
        this.myEventListener = myEventListener;
    }


import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.springframework.cache.Cache;
import org.springframework.cloud.loadbalancer.cache.LoadBalancerCacheManager;
import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier;

import java.util.List;

/**
 * 处理nacos推送更新事件
 */
public class MyNacosEventListener implements EventListener {

    private LoadBalancerCacheManager loadBalancerCacheManager;
    @Override
    public void onEvent(Event event) {
        try {
            if (event instanceof NamingEvent) {
                Cache cache = loadBalancerCacheManager.getCache(CachingServiceInstanceListSupplier.SERVICE_INSTANCE_CACHE_NAME);
                if(cache!=null){
                    NamingEvent namingEvent = ((NamingEvent) event);
                    String serviceName = namingEvent.getServiceName();
                    List instances = namingEvent.getInstances();
                    cache.put(serviceName,  NacosServiceDiscovery.hostToServiceInstanceList(instances,serviceName));
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public MyNacosEventListener(LoadBalancerCacheManager loadBalancerCacheManager) {
        this.loadBalancerCacheManager = loadBalancerCacheManager;
    }
}

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;

/**
 * spring工具类
 */
@Slf4j
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

    private static ApplicationContext applicationContext = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        SpringContextHolder.applicationContext = applicationContext;
    }

    public static  T getBean(Class requiredType) {
        return applicationContext.getBean(requiredType);
    }

    public static void clearHolder() {
        if (log.isDebugEnabled()) {
            log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
        }
        applicationContext = null;
    }


    @Override
    public void destroy() {
        SpringContextHolder.clearHolder();
    }


}

配置好重启网关服务,naocs管理端修改服务下线状态,发现断点已经进入监听类即完成配置

你可能感兴趣的:(JAVA,微服务,jenkins,gateway)