java服务单例启动控制

在一台服务器上,某个服务的单例启动是通过在该服务器的某个路径下记录一个.pid文件实现的,当启动服务时,如果有该文件存在则不再启动,反之则启动.

实际开发中,团队成员协作开发某个工程,比如java定时器,可能会存在测试服务器开启着,其它开发人员本地也开启着的情况,多台机器同时运行一个服务,某些程序的重复执行导致数据库层面的并发可能造成数据错乱,本文目的就是通过zk和mysql对同一服务多处启动做一个控制.

思路:

1.通过redis进行控制

步骤1:启动时检测redis中luchkey的值,如果laucheky=0,则启动,并设值lauchkey=1,服务停止时,设值lauchkey=0

问题1:但是如果kill -9 杀掉服务进程,或由于某些原因中断服务进程,redis中lauchkey可能无法更改为0,其它服务无法启动,造成死锁

步骤2:对lauchkey设值过期时间如10s,在服务中单独启个进程,定时更新lauchkey的过期时间,为lauchkey续命

问题2:redis地址是配置在项目的配置文件中的,开发人员在启动时如果改为自己本地的redis地址,也可以启动,2个定时器依然会对mysql中的数据重复执行

步骤3:redis+mysql , 将redis地址存到mysql中,服务启动时读取redis地址,并去这个redis中读取laucheky的值进行判断

问题3:可行,但是如果将redis换为zookeeper,redis的过期时间的定时维护便会更换为与服务进程绑定的临时节点,节省开发成本且服务绑定关系更稳定

2.通过mysql进行控制

存在问题1,造成启动死锁

3.通过zk临时节点进行控制

服务进程挂掉后,临时节点自动删除,但是依然存在问题2

4.通过zk+mysql进行控制

将zk地址写在mysql中,服务启完成后立即(实现接口InitializingBean)进行检测,判断zk是否存在临时节点"/javatimer/singletonlaunch",不存在则正常启动,存在则终止服务,并报出错误信息

@Slf4j
public class JobManager implements InitializingBean {
    @Autowired
    private TaskJobService taskJobService;
    @Autowired
    private QuartzService quartzService;
    @Autowired
    private ZKClientUtil zkClientUtil;

    private String singletonLauchPath = "/javatimer";
    private String singletonLauchNode = "/javatimer/singletonlaunch";

    public void afterPropertiesSet() throws Exception {

        //检查定时器是否重复启动
        String active = SpringContextUtils.getByName("environment", Environment.class).getProperty("spring.profiles.active");
        if(!"prod".equals(active)){
            singletonLaunchCheck();
        }

        //开启定时任务
        List<TaskJob> list = taskJobService.getTaskList();
        quartzService.enableCronSchedule(list);

    }


    private void singletonLaunchCheck() {
        //初始化zk连接
        zkClientUtil.init();
        if (!zkClientUtil.exist(singletonLauchPath)) {
            zkClientUtil.createNode(singletonLauchPath, "javatimer", CreateMode.PERSISTENT);
        }

        //判断是否存在节点
        if (zkClientUtil.exist(singletonLauchNode)) {
            throw new RuntimeException("启动失败,ip=" + zkClientUtil.getData(singletonLauchNode) + "正在运行此服务");
        } else {
            try {
                InetAddress ia = InetAddress.getLocalHost();
                zkClientUtil.createNode(singletonLauchNode, ia.getHostAddress(), CreateMode.EPHEMERAL);
                log.info("启动成功,ip={}",ia.getHostAddress());
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
        }

    }
}
@Component("zKClientUtil")
public class ZKClientUtil {
    private String connectString;
    private int sessionTimeout = 3000;
    private ZooKeeper zkClient;

    @Autowired
    private SysContentService sysContentService;


    /**
     * 判断节点是否存在
     *
     * @param path
     * @return
     */
    public Boolean exist(String path) {
        Stat stat = null;
        try {
            stat = zkClient.exists(path, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return stat == null ? false : true;
    }

    /**
     * 获取客户端连接
     */
    public ZooKeeper init() {
        try {
            if (zkClient == null) {
                synchronized (this) {
                    if (zkClient == null) {
                       //从数据库中读取zk集群地址
                        SysContent jobZKLock = sysContentService.findBySkey("JobZKLock");
                        if (jobZKLock != null && StringUtils.isNotEmpty(jobZKLock.getSvalue())) {
                            this.connectString = jobZKLock.getSvalue();
                        }
                        this.zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
                            public void process(WatchedEvent event) {
                            }
                        });
                        int i = 0;
                        while (!zkClient.getState().isConnected() && i < 10){
                            Thread.sleep(1000);
                            i++;
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return zkClient;
    }

    /**
     * 获取节点数据
     *
     * @param nodePath
     * @return
     */
    public String getData(String nodePath) {
        byte[] data = new byte[0];
        try {
            data = zkClient.getData(nodePath, false, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new String(data);
    }

    /**
     * 创建节点
     *
     * @param nodePath
     * @param value
     */
    public void createNode(String nodePath, String value, CreateMode createMode) {
        try {
            zkClient.create(nodePath, value.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_31024823/article/details/88818388