前言

写这篇文章只是为了详细的记录下我的部署过程和要注意的地方。
因为本人公司需要,此文章对于每个server、provider、consumer都采用了单独的项目来做的,没有作为父子项目的形式创建。其中server采用了docker-compse和Dockerfile方式来部署,provider和consumer暂且使用Dockerfile

一、创建并发布Server

还是采用快速创建

docker节点意思 docker集群部署多节点eureka_maven


选择springboot方式。

组件选择springcloud discovery 中的 eureka server,如图:

docker节点意思 docker集群部署多节点eureka_spring_02


docker节点意思 docker集群部署多节点eureka_docker节点意思_03


docker节点意思 docker集群部署多节点eureka_docker节点意思_04


完成后,

1、在application上加上注解 :@EnableEurekaServer

如果采用maven直接install的方式 安装到docker中,需要在pom中加入插件,但是本文采用半手动的方式
所以可以不需要。加入的插件如下:

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>1.2.0</version>
                <executions>
                    <!--设置在执行maven 的install时构建镜像-->
                    <execution>
                        <id>build-image</id>
                        <phase>install</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
                <!--<configuration>-->
                    <!--<imageName>reg-service</imageName>-->
                    <!--<dockerDirectory>src/main/docker</dockerDirectory>-->
                    <!--<dockerHost>http://XXXXX</dockerHost>-->
                    <!--<resources>-->
                        <!--<resource>-->
                            <!--<targetPath>/</targetPath>-->
                            <!--<directory>${project.build.directory}</directory>-->
                            <!--<include>${project.build.finalName}.jar</include>-->
                        <!--</resource>-->
                    <!--</resources>-->
                <!--</configuration>-->

            </plugin>
        </plugins>
    </build>

其中docker-maven-plugin 就是上面提到的 插件。

2、编写application.yml

application.yml的有效位置,要么在classes目录的根目录下,或者 classes目录下的config目录下,要么在resources的根目录下,或者resources目录下的config目录下。

spring:
  application:
    name: eureka-server

---
spring:
  profiles: eureka-server1
server:
  port: 8001
eureka:
  instance:
    hostname: 172.30.100.193
    prefer-ip-address: true
    instance-id: 172.30.100.193:8001
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://172.30.100.193:8002/eureka/

---
spring:
  profiles: eureka-server2
server:
  port: 8002
eureka:
  instance:
    hostname: 172.30.100.193
    prefer-ip-address: true
    instance-id: 172.30.100.193:8002
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://172.30.100.193:8001/eureka/

这里为了做高可用,做了集群。需要注意的是,两个server是作为两个docker container运行的,所以这里需要用ip来作为hostname,所以

eureka:
      instance:
        hostname: 172.30.100.193
        prefer-ip-address: true

3、编写Dockerfile 和dockercompose.yml

在项目的 src/main 目录下创建docker目录,在docker目录下创建Dockerfile以及docker-compose.yml文件。
Dockerfile 如下:

# 基于哪个镜像
FROM java:8
# 将本地文件夹挂载到当前容器
VOLUME /tmp
# 拷贝文件到容器,handcuffs-reg-0.0.1-SNAPSHOT.jar这里是maven打包后的名字
ADD eurekaserver-0.0.1.jar app.jar
RUN bash -c 'touch /app.jar'
# 配置容器启动后执行的命令
ENTRYPOINT  ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

没有任何特殊地方。
docker-compose.yml如下:

version: '2'
services:
  eureka-server1:
    image: eurekaserver
    container_name: eureka-server1
    hostname: eureka-server1
    networks:
      - eureka-net
    ports:
      - 8001:8001
    environment:
      - spring.profiles.active=eureka-server1

  eureka-server2:
    image: eurekaserver
    container_name: eureka-server2
    hostname: eureka-server2
    networks:
      - eureka-net
    ports:
      - 8002:8002
    environment:
      - spring.profiles.active=eureka-server2
networks:
  eureka-net:
    driver: bridge

这里是用来编排docker container 服务的

完成后,直接 在 idea中找到 maven package 就完成编译。

docker节点意思 docker集群部署多节点eureka_docker_05


编译后,按照Dockerfile中定义的eurekaserver-0.0.1.jar 修改下生成的 jar的名字。

因为我用的是xshell,所以直接在 xshell 中登录 centos 环境后,使用 rz命令,将 jar包,Dockerfile以及 docker-compose.yml统统拷贝到centos环境中

docker节点意思 docker集群部署多节点eureka_docker节点意思_06

4、部署到docker

完成后,直接运行

docker build -t eurekaserver . 生成 docker image。注意后面有个 .号

生成后,运行docker images查看

docker节点意思 docker集群部署多节点eureka_spring_07


image生成后,运行 docker-compose up -d 来运行 docker-compse.yml生成container。

其中-d是 后台运行

完成后,运行docker ps 查看正在运行的docker container

docker节点意思 docker集群部署多节点eureka_maven_08


结束后回到 windows环境,在浏览器中 访问:http://172.30.100.193:8001/ ,http://172.30.100.193:8002/ 可以看到 服务互相注册成功。

docker节点意思 docker集群部署多节点eureka_docker_09

二、Eureka-provider 的创建和部署,

eureka-provider 顾名思义,服务提供者,那么一般都是 restapi,我们使用前面文章中创建的 springboot 集成mybatis 的那个项目

springboot集成mybatis-plus连接sqlserver 直接 修改下application.yml, pom.xml 添加Dockerfile 部署到docker中。步骤如下:

1、修改pom.xml

在pom中添加对于 springcloud、eureka-client的依赖。完整的 pom如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xyzh.mybatisPlus</groupId>
    <artifactId>demo</artifactId>
    <packaging>jar</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>sqljdbc4</artifactId>
            <version>4.2</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.2</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
            <version>1.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

在application上面 添加注解:@EnableDiscoveryClient

2、修改application.yml

在原有的spring中添加配置 spring.application.name: microservice-provider
新加完整的 eureka的配置,完整如下:

mybatis-plus:
  mapper-locations: classpath:/mapper/*.xml
  global-config:
    db-config:
      id-type: auto
      table-underline: true
      logic-not-delete-value: 0
      logic-delete-value: 1
spring:
  datasource:
    driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:sqlserver://xxxx:1433;databaseName=xxxx
    username: sa
    password:xxxx
  application:
    name: microservice-provider
server:
  port: 8101
eureka:
  client:
    service-url:
      defaultZone: http://172.30.100.193:8001/eureka/,http://172.30.100.193:8002/eureka/
  instance:
    instance-id: microservice-provider8101
    prefer-ip-address: true
info:
  app.name: microservice-provider
  company.name: www.xyzh.com
  build.artifactId: ${project.artifactId}
  build.version: ${project.version}

需要注意的是 这里一样要添加

instance:
        instance-id: microservice-provider8101
        prefer-ip-address: true

来保证可以以ip的形式 注册到eurekaserver上,如果步添加 prefer-ip-address : true,那么因为默认采用的是域名注册,所以后面 consumer 会找不到provider 报出 unknownhostexception的错误。

3、添加Dockerfile

同样在src/main下创建docker,在docker目录中新建文件Dockerfile

#基于哪个镜像
FROM java:8
# 将本地文件夹挂载到当前容器
VOLUME /tmp
# 拷贝文件到容器,demo-0.0.1.jar这里是maven打包后的名字
ADD demo-0.0.1.jar app.jar
# 配置容器启动后执行的命令
ENTRYPOINT  ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

完成后,编译,打包项目,修改jar包名称等于 demo-0.0.1.jar

4、上传centos、部署docker

将 jar包 和 Dockerfile上传到centos中,

docker节点意思 docker集群部署多节点eureka_spring_10


上传完成后,运行docker build -t eurekaprovider . 来创建docker image

创建完成后运行docker images查看如下图:

docker节点意思 docker集群部署多节点eureka_docker节点意思_11


接着直接运行docker run来使用 image创建container

命令: docker run -d -p 8101:8101 --name eurekaprovider eurekaprovider

其中 -d是后台运行,

-p是端口映射 格式是 主机(宿主)端口:容器端口

–name 后面跟的是 container的名称,最后是image的名称

完成后运行docker ps来查看。

docker节点意思 docker集群部署多节点eureka_docker节点意思_12


生成了docker container再windows中访问 http://172.30.100.193:8001/,http://172.30.100.193:8002/

可以看到:

docker节点意思 docker集群部署多节点eureka_docker_13


直接访问 provider的 路径也是可以访问的:

访问:http://172.30.100.193:8101/internal-meeting-user-info/userinfo/1

docker节点意思 docker集群部署多节点eureka_docker_14

5、遵循上例继续创建 eureka-provid

因为是集群,所以这个服务可以使用多个docker一起来提供,供consumer 按照某个规则来选择调用。
所以我们还需要创建多个eureka-provider,当然可以通过 在application.yml中添加 profiles的形式来创建,然后再添加docker-compose.yml来编排服务,这个后面 再通过文章介绍吧,这里就直接复制 我们的demo工程,修改下application.yml来创建多个eureka-provider,修改的时候请注意, 需要修改的内容 是 server.port 、eureka.instance.instance-id 其他的都不改。尤其是 spring.application.name 这个是不变的.然后再遵循上面的步骤 创建 部署。

三、Eureka-consumer的创建和部署

1、创建springboot的项目

docker节点意思 docker集群部署多节点eureka_docker节点意思_15


docker节点意思 docker集群部署多节点eureka_spring_16


docker节点意思 docker集群部署多节点eureka_docker_17


docker节点意思 docker集群部署多节点eureka_maven_18


docker节点意思 docker集群部署多节点eureka_docker节点意思_19


可以看到,这里除了选择了 eureka-client-discovery 之外,还选择了 spring cloud routing 中的ribbon和 openFeign。其实这里就是通过 openFeign来访问 eureka-provider的。

2、编写 Feign。

再application类上添加注解:
@EnableDiscoveryClient,这里表示既是发现服务,也要注册到eurekaserver上。
@EnableFeignClients(“com.xyzh.eurekaconsumer.service”),这里表示启用Feign,其实Feign 就是一个AOP封装Ribbon。

在 项目中添加 service 接口 ConsumerService,完整代码如下:

package com.xyzh.eurekaconsumer.service;

import com.xyzh.eurekaconsumer.models.InternalMeetingUserInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.List;

@FeignClient(name = "microservice-provider")
public interface ConsumerService {
    @RequestMapping(value="/internal-meeting-user-info/userinfo/{userStatus}",method= RequestMethod.GET)
    public List<InternalMeetingUserInfo> get(@PathVariable("userStatus") Integer userStatus);
}

这里需要注意的地方,就是FeignClient中用name 表示了 需要包装访问的 服务提供者,这里客户端可以通过默认规则来选择服务提供者访问

接下来在项目中添加控制器 contoller userInfoController
控制器就是通过刚才定义的 FeignClient 类 来访问 服务提供者了。完整代码如下:

import com.xyzh.eurekaconsumer.models.InternalMeetingUserInfo;
import com.xyzh.eurekaconsumer.service.ConsumerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
public class userInfoController {
    @Autowired
    ConsumerService consumerService;

    @RequestMapping(value = "/userinfo/{userStatus}",method = RequestMethod.GET )
    @ResponseBody
    public List<InternalMeetingUserInfo> get(@PathVariable(value = "userStatus") Integer userStatus){
        try {
            InternalMeetingUserInfo userinfo=new InternalMeetingUserInfo();

            return consumerService.get(userStatus);
        }
        catch (Exception ex){
            ex.printStackTrace();
            throw ex;
        }
    }
}

需要注意的是,上面的 InternalMeetingUserInfo 是一个实体,所以需要实体 类是可序列化的,
需要实现 Serializable 接口,且有getter setter方法以及一个 无参的构造函数。否则会报错
实体类完整代码如下:

package com.xyzh.eurekaconsumer.models;

    import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.Date;
@Slf4j
@Getter
@Setter
@NoArgsConstructor
public class InternalMeetingUserInfo implements Serializable {

    private static final long serialVersionUID = 1L;
    private Integer id;


    private Integer meetingId;

    private Integer userId;

    private String userName;

    private String eName;

    private Integer areaName;

    private String telephone;

    private String email;

    private Integer service;

    private String secretName;

    private String secretEmail;

    private String secretPhone;

    private String weixinNo;

    private String whatsApp;

    private Integer tshirt;

    private String language;

    private String otherLanguage;

    private Integer meal;

    private String otherMeal;

    private Integer allergy;

    private String otherAllergy;

    private String firstName;

    private String lastName;

    private String passPortNum;

    private Integer gender;

    private Date passportDate;

    private Date passportBirthday;

    private Integer invitation;

    private Integer withFamily;

    private String otherService;

    private String enfirstName;

    private String enLastName;

    private String chineseName;

    private String countryCode;

    private String passPortIssuing;

    private Integer internalStatus;

    private Integer isDeleted;

    private Date createDateTime;

    private String createUser;

    private Integer userStatus;

    private Date lastUpdateTime;


}

另外,还有
本例中,因为涉及到时间转换,所以需要用到一个 时间转换类,在config目录下新增 时间转换类: DateFormatConfig

完整代码如下:

package com.xyzh.eurekaconsumer.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.jackson.JsonComponent;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@JsonComponent
public class DateFormatConfig {
    private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static class DateJsonSerializer extends JsonSerializer<Date>{
        @Override
        public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(dateFormat.format(date));
        }
    }

    public static class DateJsonDeserializer extends JsonDeserializer<Date>{


        @Override
        public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            try{
                return dateFormat.parse(jsonParser.getText());
            }catch (ParseException e){
                throw new RuntimeException(e);
            }
        }
    }
}

还有:使用 Feign 固然有默认的访问规则 和RestTemplate,但是我们也可以自定以替换如果自定义替换,定义一个配置对象即可
在config中创建 ConfigBean,如下:

package com.xyzh.eurekaconsumer.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ConfigBean {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
    @Bean
    public IRule myRule(){
        return new RoundRobinRule();
    }
}

其中定义了 loadbalance template 和 轮询访问规则
完整的项目结果如下:

docker节点意思 docker集群部署多节点eureka_docker_20

3、编写 application.yml 和pom.xml

pom.xml就不用再改写了,按照生成来的就好,
applicaiton.yml如下

server:
  port: 8201
spring:
  application:
    name: microservice-consumer
eureka:
  instance:
    instance-id: microserver-consumer8201
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://172.30.100.193:8001/eureka/,http://172.30.100.193:8002/eureka/

依然注意 prefer-ip-address: true

4、创建Dockerfile

再src/main中创建Dockerfile,内容如下:

#基于哪个镜像
FROM java:8
# 将本地文件夹挂载到当前容器
VOLUME /tmp
# 拷贝文件到容器,eurekaconsumer-0.0.1.jar这里是maven打包后的名字
ADD eurekaconsumer-0.0.1.jar app.jar
# 配置容器启动后执行的命令
ENTRYPOINT  ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

5、部署到docker

编译后 按照 eurekaconsumer-0.0.1.jar 修改生成的jar包名称,然后拷贝jar包 以及 Dockerfile 到 centos中,

docker节点意思 docker集群部署多节点eureka_spring_21


然后 运行 docker build -t eurekaconsumer .来生成 镜像,生成后运行docker images来查看:

docker节点意思 docker集群部署多节点eureka_docker节点意思_22


完成后,运行 docker run -d -p 8201:8201 --name eurekaconsumer eurekaconsumer 来创建container

然后运行 docker ps来查看运行的container:

docker节点意思 docker集群部署多节点eureka_docker_23


这样就部署完成了。可以通过 http://172.30.100.193:8201/userinfo/1 来访问 consumer看看结果

docker节点意思 docker集群部署多节点eureka_spring_24

这样,整个集群就已经部署完毕,
下一次通过 父子项目 docker-compose的方式来再写一篇。