基于Docker Compose进行服务编排时,一定碰到服务启动顺序的问题,例如:B服务启动之前,A服务要已经启动并且可以正常对外服务。

这个启动顺序的问题,Docker Compose本身它是无法解决的,即使定义了depends_on或者links,它只能保证该服务依赖这些服务,启动本服务时会将依赖的服务也启动,但是启动顺序无法得到保证。

目前本人实验比较好的方案有两种:

  • 基于wait-for-it.sh实现,前提条件是本镜像要支持bash
  • 对于自己构建的镜像时,让工程本身带一个监听类,用于监听依赖服务是否启动,这种方式有侵入性,同时对于第3方的镜像,不太好实现

1、wait-for-it.sh方案

wait-for-it.sh是GitHub中开源一个脚本,很轻量也很实用,以一个例子说明其的法:

本例子中定义了2个服务,一个mysql服务,一个cs2_serv服务,这个cs2_serv需要等mysql启动好并做好初始化后才能启动,要不然cs2_serv服务会由于没法连接到数据库而报错。

  1. version: "3"
  2. services:
  3. mysql:
  4. image: mysql:5.6
  5. ports:
  6. - "3306:3306"
  7. environment:
  8. - MYSQL_ROOT_PASSWORD=jgyw@123
  9. - MYSQL_USER=cs2
  10. - MYSQL_PASS=cs2123
  11. volumes:
  12. - ./db/mysql:/var/lib/mysql
  13. - ./db/init:/docker-entrypoint-initdb.d/
  14. cs2_serv:
  15. image: cs2_serv:v1
  16. ports:
  17. - "81:81"
  18. environment:
  19. - SERV_PORT=81
  20. - MYSQL_IP=mysql
  21. - MYSQL_PORT=3306
  22. - DB_USERNAME=root
  23. - DB_PASSWORD=jgyw@123
  24. links:
  25. - mysql
  26. volumes:
  27. - ./wait-for-it.sh:/wait-for-it.sh
  28. entrypoint: "/wait-for-it.sh -t 0 mysql:3306 -- "
  29. command:
  30. - /bin/sh
  31. - -c
  32. - |
  33. sleep 10
  34. java -Djava.security.egd=file:/dev/./urandom -jar /app.jar

此处最为核心的代码就是:

  1. entrypoint: "/wait-for-it.sh -t 0 mysql:3306 -- "
  2. command:
  3. - /bin/sh
  4. - -c
  5. - |
  6. sleep 10
  7. java -Djava.security.egd=file:/dev/./urandom -jar /app.jar

这2个配置的意思是,要等到mysql:3306服务可以用了,才去执行command对应的命令。

同时我在commad命令中再增加等待10s钟,主要为了完全确保mysql服务启动完成,还有就是初始化数据库也完成,最后才去启动cs2_serv服务。

2、自定义监听类

这种方式有一定侵入性,但是配置起来会比较方便,在此以Spring Boot为例,写了一个简单的监听类,即:

  1. package com.swnote.cs2.common.listener;
  2. import java.io.IOException;
  3. import java.net.InetSocketAddress;
  4. import java.net.Socket;
  5. import java.util.Map;
  6. import org.apache.log4j.Logger;
  7. import org.springframework.boot.context.event.ApplicationStartingEvent;
  8. import org.springframework.context.ApplicationListener;
  9. /**
  10. * 依赖服务检查
  11. */
  12. public class DependsOnServiceCheckListener implements ApplicationListener<ApplicationStartingEvent> {
  13. private Logger logger = Logger.getLogger(DependsOnServiceCheckListener.class);
  14. @Override
  15. public void onApplicationEvent(ApplicationStartingEvent event) {
  16. // 获取环境变量
  17. Map<String, String> envs = System.getenv();
  18. // 环境变量中DEPENDS_ON值,即是依赖的服务,值的内容格式为:host1:port1,host2:port2
  19. if (envs.containsKey("DEPENDS_ON")) {
  20. // 依赖服务是否启动的标志
  21. boolean flag = false;
  22. String val = envs.get("DEPENDS_ON");
  23. String[] servs = val.split(",");
  24. while (!flag) {
  25. try {
  26. Thread.sleep(5000L);
  27. } catch (InterruptedException e) {
  28. logger.warn("Wait depends on Service started...");
  29. }
  30. for (String serv : servs) {
  31. flag = checkServ(serv);
  32. if (!flag) {
  33. break;
  34. }
  35. }
  36. }
  37. logger.info("Depends on Service started...");
  38. }
  39. }
  40. /**
  41. * 检查服务是否启动
  42. *
  43. * @param serv
  44. * @return
  45. */
  46. private boolean checkServ(String serv) {
  47. String[] servs = serv.split(":");
  48. String host = servs[0].trim();
  49. int port = Integer.parseInt(servs[1].trim());
  50. Socket socket = null;
  51. try {
  52. socket = new Socket();
  53. socket.connect(new InetSocketAddress(host, port));
  54. logger.info(serv + ": Service started...");
  55. return true;
  56. } catch (Exception e) {
  57. logger.warn(serv + ": Service not started...");
  58. return false;
  59. } finally {
  60. if (socket != null) {
  61. try {
  62. socket.close();
  63. } catch (IOException e) {
  64. e.printStackTrace();
  65. }
  66. }
  67. }
  68. }
  69. }

这个监听类,是将依赖的服务信息放到环境变量DEPENDS_ON中,即是依赖的服务,值的内容格式为:host1:port1,host2:port2,然后每隔5s去测试依赖的服务是否是通的,如果所有依赖的服务都是通的,那么本服务就可以启动,否则本服务一直处于等待状态。

以一个实例说明使用方式,即:

  1. cs2_web:
  2. image: cs2_web:v1
  3. ports:
  4. - "82:82"
  5. environment:
  6. - WEB_PORT=82
  7. - SERV_DOMAIN=cs2_serv
  8. - DEPENDS_ON=cs2_serv:81
  9. links:
  10. - cs2_serv

这里定义了一个cs2_web服务,该服务是依赖上面例子中的cs2_serv,但是它配置依赖关系是通过环境变量DEPENDS_ON来配置的。

3、参考资料

https://github.com/vishnubob/wait-for-it

关注我

以你最方便的方式关注我:

微信公众号:

微服务架构 - 解决Docker-Compose服务编排启动顺序问题的更多相关文章

  1. asp.net core容器&mysql容器network互联 & docker compose方式编排启动多个容器

    文章简介 asp.net core webapi容器与Mysql容器互联(network方式) docker compose方式编排启动多个容器 asp.net core webapi容器与Mysql ...

  2. 庐山真面目之十二微服务架构基于Docker搭建Consul集群、Ocelot网关集群和IdentityServer版本实现

    庐山真面目之十二微服务架构基于Docker搭建Consul集群.Ocelot网关集群和IdentityServer版本实现 一.简介      在第七篇文章<庐山真面目之七微服务架构Consul ...

  3. 庐山真面目之十四微服务架构的Docker虚拟技术深入探究

    庐山真面目之十四微服务架构的Docker虚拟技术深入探究 一.我的开场白 曾几何时,分布式的发展也影响了后来的微服务架构的实现方式.到了现在,只要涉及到互联网技术领域,就会设计一个概念,那就是微服务. ...

  4. Spring Cloud构建微服务架构(五)服务网关

    通过之前几篇Spring Cloud中几个核心组件的介绍,我们已经可以构建一个简略的(不够完善)微服务架构了.比如下图所示: 我们使用Spring Cloud Netflix中的Eureka实现了服务 ...

  5. Spring Cloud构建微服务架构(二)服务消费者

    Netflix Ribbon is an Inter Process Communication (IPC) cloud library. Ribbon primarily provides clie ...

  6. 整合SPRING CLOUD云服务架构 - 企业分布式微服务云架构构建

    整合SPRING CLOUD云服务架构 - 企业分布式微服务云架构构建 1.   介绍 Commonservice-system是一个大型分布式.微服务.面向企业的JavaEE体系快速研发平台,基于模 ...

  7. AspNetCore容器化(Docker)部署(三) —— Docker Compose容器编排

    一.前言 上一篇部署了一个最基础的helloworld应用,创建了两个容器和一个network,还算应付得过来. 如果该应用继续引入mysql.redis.job等若干服务,到时候发布一次得工作量之大 ...

  8. Docker | 第七章:Docker Compose服务编排介绍及使用

    前言 前面章节,我们学习了如何构建自己的镜像文件,如何保存自己的镜像文件.大多都是一个镜像启动.当一个系统需要多个子系统进行配合时,若每个子系统也就是镜像需要一个个手动启动和停止的话,那估计实施人员也 ...

  9. 微服务架构 - 搭建docker本地镜像仓库并提供权限校验及UI界面

    搭建docker本地镜像仓库并提供权限校验及UI界面 docker本地镜像仓库的作用跟maven私服差不多,特别是公司级或者是小组级开发好的docker仓库可以上传到本地镜像仓库中,需要用时,直接从本 ...

随机推荐

  1. java--接口的定义与实现

    利用接口方法计算矩形面积 代码如下: //接口的定义与实现[public] interface A{ //定义一个接口[public] [static][final] void conter(doub ...

  2. 2017阿里Java编程题第2题

    题意是给一组数字+符号(自增1:^,相乘*,相加+)和一个长度为16的stack.栈空取数返回-1,栈满推数返回-2. 输入样例是1 1 + 2 ^ 3 * 这样子,做的时候紧张忽略了空格,用char ...

  3. python爬虫人门(10)Scrapy框架之Downloader Middlewares

    设置下载中间件(Downloader Middlewares) 下载中间件是处于引擎(crawler.engine)和下载器(crawler.engine.download())之间的一层组件,可以有 ...

  4. IDEA修改编辑背景图片

    1.打开File -> Setting -> Plugs -> 搜索BackgroundImage. 然后安装.如图 2.按快捷键ctrl+shift+A,搜索set backgro ...

  5. 适合Python 新手的5大练手项目,你练了么?

    接下来就给大家介绍几种适合新手的练手项目. 0.算法系列-排序与查找 Python写swap很方便,就一句话(a, b = b, a),于是写基于比较的排序能短小精悍.刚上手一门新语言练算法最合适不过 ...

  6. 【淘宝客】根据淘客联盟精选清单(淘宝天猫内部优惠券)随机显示淘宝天猫优惠券dome

    也许大家在生活中经常淘宝看到[淘宝天猫内部优惠券]的网站,或者在微博中经常有博主发券,让大家生活中购物便宜许多,作为一个站长,我们也希望自己的网站也能有这样的一个功能,现在就分享给大家,还是免后台哦. ...

  7. css3 resize属性

    http://www.w3school.com.cn/cssref/pr_resize.asp 实例 规定可以由用户调整 div 元素的大小: div { resize:both; overflow: ...

  8. ehcache与redis的比较与应用场景分析(转)

    ehcache直接在jvm虚拟机中缓存,速度快,效率高:但是缓存共享麻烦,集群分布式应用不方便.redis是通过socket访问到缓存服务,效率比ecache低,比数据库要快很多,处理集群和分布式缓存 ...

  9. windows远程桌面到linux

    centos:  http://lovesoo.org/xmanager-to-connect-to-the-remote-desktop-centos.html redhat:  http://ww ...

  10. Linux kernel的中断子系统之(三):IRQ number和中断描述符

    返回目录:<ARM-Linux中断系统>. 总结: 二描述了中断处理示意图,以及关中断.开中断,和IRQ number重要概念. 三介绍了三个重要的结构体,irq_desc.irq_dat ...