使用docker部署微服务

一、Dockerfile构建微服务镜像

1、Dockerfile构建Docker镜像

Dockerfile是一个文本文件,其中包含了若干条指令,指令描述了构建镜像的细节先来编写一个最简单的Dockerfile,以前文下载的Nginx镜像为例,来编写一个Dockerfile修改该Nginx镜像的首页。

新建文件夹/app,在app目录下新建一个名为Dockerfile的文件,在里面增加如下内容:

FROM nginx
RUN echo '<h1>This is Tuling Nginx!!!</h1>' > /usr/share/nginx/html/index.html

FROMnginxRUNecho'<h1>ThisisTulingNginx!!!</h1>'>/usr/share/nginx/html/index.html

该Dockerfile非常简单,其中的 FORM、 RUN都是 Dockerfile的指令。 FROM指令用于指
定基础镜像, RUN指令用于执行命令。

在Dockerfile所在路径执行以下命令构建镜像:

# docker build -t nginx:tuling 

其中,-t指定镜像名字,命令最后的点(.)表示Dockerfile文件所在路径.

执行以下命令,即可使用该镜像启动一个 Docker容器:

docker run -d -p 92:80 nginx:tuling

访问 http://Docker宿主机IP:92/,可看到下图所示界面:

2、Dockerfile常用指令

命令 用途
FROM 基础镜像文件
RUN 构建镜像阶段执行命令
ADD <src> <dest> 添加文件,从src目录复制文件到容器的dest,其中 src可以是Dockerfile所在目录的相对路径,也可以是一个 URL,还可以是一个压缩包
COPY 拷贝文件,和ADD命令类似,但不支持URL和压缩包
CMD 容器启动后执行命令
EXPOSE 声明容器在运行时对外提供的服务端口
WORKDIR 指定容器工作路径
ENV 指定环境变量
ENTRYPINT 容器入口, ENTRYPOINT和 CMD指令的目的一样,都是指定
Docker容器启动时执行的命令,可多次设置,但只有最后一个有效。
USER 该指令用于设置启动镜像时的用户或者 UID,写在该指令后的 RUN、CMD以及 ENTRYPOINT指令都将使用该用户执行命令。VOLUME
VOLUME 指定挂载点,该指令使容器中的一个目录具有持久化存储的功能,该目录可被容器本身使用,也可共享给其他容器。当容器中的应用有持久化数据的需求时可以在 Dockerfile中使用该指令。格式为:VOLUME["/data"]。

注意:RUN命令在 image 文件的构建阶段执行,执行结果都会打包进入 image 文件;
CMD命令则是在容器启动后执行。另外,一个 Dockerfile 可以包含多个RUN命令,但是只
能有一个CMD命令。指定了CMD命令以后,docker container run命令就不能附加命令了(比如前面的/bin/bash),否则它会覆盖CMD命令。

3、使用Dockerfile构建微服务镜像

项目结构如下:

pom.xml: 

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.tuling.cloud</groupId>
  <artifactId>microservice-eureka-server</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <!-- 引入spring boot的依赖 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
  </dependencies>

  <!-- 引入spring cloud的依赖 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Edgware.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <!-- 添加spring-boot的maven插件 -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      
      <!-- docker的maven插件,官网:https://github.com/spotify/docker-maven-plugin -->
      <plugin>
          <groupId>com.spotify</groupId>
          <artifactId>docker-maven-plugin</artifactId>
          <version>0.4.12</version>
          <configuration>
              <!-- 注意imageName一定要是符合正则[a-z0-9-_.]的,否则构建不会成功 -->
              <!-- 详见:https://github.com/spotify/docker-maven-plugin    Invalid repository name ... only [a-z0-9-_.] are allowed-->
              <imageName>microservice-eureka-server</imageName>
              <baseImage>java</baseImage>
              <entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint>
              <resources>
                  <resource>
                      <targetPath>/</targetPath>
                      <directory>${project.build.directory}</directory>
                      <include>${project.build.finalName}.jar</include>
                  </resource>
              </resources>
          </configuration>
      </plugin>
      
    </plugins>
  </build>
</project>

 application.yml:

server:
  port: 8761                    # 指定该Eureka实例的端口
eureka:
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

EurekaApplication.java

package com.tuling.cloud.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * 使用Eureka做服务发现.
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
  public static void main(String[] args) {
    SpringApplication.run(EurekaApplication.class, args);
  }
}

以项目05-ms-eureka-server为例,将该微服务的可运行jar包构建成docker镜像。

将jar包上传linux服务器/app/eureka目录,在jar包所在目录创建名为Dockerfile的文件。

在Dockerfile中添加以下内容:

# 基于哪个镜像
From java:8
# 将本地文件夹挂载到当前容器
VOLUME /tmp
# 复制文件到容器
ADD microservice-eureka-server-0.0.1-SNAPSHOT.jar /app.jar
# 声明需要暴露的端口
EXPOSE 8761
# 配置容器启动后执行的命令
ENTRYPOINT ["java","-jar","/app.jar"]

使用docker build命令构建镜像:

# docker build -t microservice-eureka-server:0.0.1 .
# 格式: docker build -t 镜像名称:标签 Dockerfile的相对位置

在这里,使用-t选项指定了镜像的标签,执行该命令后,终端将会输出如下的内容。

启动镜像,加-d可在后台启动:

# docker run -p 8761:8761 microservice-eureka-server:0.0.1

访问http://Docker宿主机IP:8761/,可正常显示Eureka Server首页。

二、Docker Compose编排微服务 

1、Docker Compose介绍    

使用微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例。如果每个微服务都要手动启停,那么效率之低、维护量之大可想而知。本节课将讨论如何使用 Docker Compose来轻松、高效地管理容器。为了简单起见将 Docker Compose简称为 Compose。

Compose 是一个用于定义和运行多容器的Docker应用的工具。使用Compose,你可以在一个配置文件(yaml格式)中配置你应用的服务,然后使用一个命令,即可创建并启动配置中引用的所有服务。下面我们进入Compose的实战吧

2、Docker Compose的安装

Compose的安装有多种方式,例如通过shell安装、通过pip安装、以及将compose作为容器安装等等,本文讲解通过pip安装的方式。

详情可查看Docker的官方文档:Install Docker Compose | Docker Documentation

安装python-pip:

# yum -y install epel-release
# yum -y install python-pip

安装docker-compose:

# pip install docker-compose

待安装完成后,执行查询版本的命令:

# docker-compose version

3、Docker Compose使用

Compose的使用非常简单,只需要编写一个docker-compose.yml,然后使用docker-compose 命令操作即可。docker-compose.yml描述了容器的配置,而docker-compose 命令描述了对容器的操作。

我们首先通过一个示例快速入门:

前面使用了Dockerfile为项目microservice-eureka-server构建Docker镜像,我们还以此项目为例镜像测试。我们在microservice-eureka-server-0.0.1-SNAPSHOT.jar所在目录的上一级目录,创建docker-compose.yml 文件。

目录树结构如下:

├── docker-compose.yml
└── eureka
    ├── Dockerfile
    └── microservice-eureka-server-0.0.1-SNAPSHOT.jar

然后再docker-compose.yml 中添加内容如下:

eureka:  	    #指定服务名
  build: ./eureka   #指定Dockfile所在路劲
  ports:
    - "8761:8761"   #指定端口映射
  expose:
    - 8761	    #容器提供服务端口

在docker-compose.yml 所在路径执行:

# docker-compose up

Compose就会自动构建镜像并使用镜像启动容器。也可使用 docker-compose up -d后台启动并运行这些容器。

访问:http://宿主机IP:8761/ ,发现可以正常启动。

4、Docker Compose管理容器的结构

Docker Compose将所管理的容器分为三层,分别是工程( project),服务(service)以及容器( container)。 Docker Compose运行目录下的所有文件( docker-compose.yml、 extends文件或环境变量文件等)组成一个工程(默认为 docker-compose.yml所在目录的目录名称)。一个工程可包含多个服务,每个服务中定义了容器运行的镜像、参数和依赖,一个服务可包括多个容器实例。

例如工程名称是 docker-compose.yml所在的目录名,该工程包含了1个服务,服务名称是 eureka,执行 docker-compose up时,启动了eureka服务的1个容器实例。

5、docker-compose.yml常用指令

① image

指定镜像名称或者镜像id,如果该镜像在本地不存在,Compose会尝试pull下来。

示例:

image: java

② build

指定Dockerfile文件的路径。可以是一个路径,例如:

build: ./dir

也可以是一个对象,用以指定Dockerfile和参数,例如:

build:  context: ./dir  dockerfile: Dockerfile-alternate  args:    buildno: 1

③ command

覆盖容器启动后默认执行的命令。

示例:

command: bundle exec thin -p 3000

也可以是一个list,类似于Dockerfile总的CMD指令,格式如下:

command: [bundle, exec, thin, -p, 3000]

④ links

链接到其他服务中的容器。可以指定服务名称和链接的别名使用SERVICE:ALIAS 的形式,或者只指定服务名称,示例:

web:  links:   - db   - db:database   - redis

⑤ external_links

表示链接到docker-compose.yml外部的容器,甚至并非Compose管理的容器,特别是对于那些提供共享容器或共同服务。格式跟links类似,示例:

external_links: - redis_1 - project_db_1:mysql - project_db_1:postgresql

⑥ ports

暴露端口信息。使用宿主端口:容器端口的格式,或者仅仅指定容器的端口(此时宿主机将会随机指定端口),类似于docker run -p ,示例:

ports: - "3000" - "3000-3005" - "8000:8000" - "9090-9091:8080-8081" - "49100:22" - "127.0.0.1:8001:8001" - "127.0.0.1:5000-5010:5000-5010"

⑦ expose

暴露端口,只将端口暴露给连接的服务,而不暴露给宿主机,示例:

expose: - "3000" - "8000"

⑧ volumes

卷挂载路径设置。可以设置宿主机路径 (HOST:CONTAINER) 或加上访问模式 (HOST:CONTAINER:ro)。示例:

volumes:  # Just specify a path and let the Engine create a volume  - /var/lib/mysql  # Specify an absolute path mapping  - /opt/data:/var/lib/mysql  # Path on the host, relative to the Compose file  - ./cache:/tmp/cache  # User-relative path  - ~/configs:/etc/configs/:ro  # Named volume  - datavolume:/var/lib/mysql

⑨ volumes_from

从另一个服务或者容器挂载卷。可以指定只读或者可读写,如果访问模式没有指定,则默认是可读写。示例:

volumes_from: - service_name - service_name:ro - container:container_name - container:container_name:rw

⑩ environment

设置环境变量。可以使用数组或者字典两种方式。只有一个key的环境变量可以在运行Compose的机器上找到对应的值,这有助于加密的或者特殊主机的值。示例:

environment:  RACK_ENV: development  SHOW: 'true'  SESSION_SECRET: environment:  - RACK_ENV=development  - SHOW=true  - SESSION_SECRET

⑪ env_file

从文件中获取环境变量,可以为单独的文件路径或列表。如果通过 docker-compose -f FILE 指定了模板文件,则 env_file 中路径会基于模板文件路径。如果有变量名称与 environment 指令冲突,则以envirment 为准。示例:

env_file: .env env_file:  - ./common.env  - ./apps/web.env  - /opt/secrets.env

⑫ extends

继承另一个服务,基于已有的服务进行扩展。

⑬ net

设置网络模式。示例:

net: "bridge" net: "host" net: "none" net: "container:[service name or container name/id]"

⑭​​​​​​​ dns

配置dns服务器。可以是一个值,也可以是一个列表。示例:

dns: 8.8.8.8 dns:  - 8.8.8.8  - 9.9.9.9

⑮​​​​​​​ dns_search

配置DNS的搜索域,可以是一个值,也可以是一个列表,示例:

dns_search: example.com dns_search:  - dc1.example.com  - dc2.example.com

6、用Docker Compose编排Spring Cloud微服务

如果微服务较多,则可以用docker compose来统一编排,我们打算用docker compose来统一编排三个微服务:eureka服务(项目05-ms-eureka-server),user服务(项目05-ms-provider-user),order服务(项目05-ms-consumer-order-ribbon)。

1)项目结构

① 05-ms-consumer-order-ribbon 

pom.xml:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.tuling.cloud</groupId>
  <artifactId>microservice-consumer-order-ribbon</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <!-- 引入spring boot的依赖 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <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-starter-actuator</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
  </dependencies>

  <!-- 引入spring cloud的依赖 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Edgware.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <!-- 添加spring-boot的maven插件 -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

application.yml:

server:
  port: 8010
spring:
  application:
    name: microservice-consumer-order
eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/
  instance:
    prefer-ip-address: true

User.java:

package com.tuling.cloud.study.user.entity;

import java.math.BigDecimal;

public class User {
  private Long id;
  private String username;
  private String name;
  private Integer age;
  private BigDecimal balance;

  public Long getId() {
    return this.id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getUsername() {
    return this.username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Integer getAge() {
    return this.age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  public BigDecimal getBalance() {
    return this.balance;
  }

  public void setBalance(BigDecimal balance) {
    this.balance = balance;
  }
}

OrderController.java:

package com.tuling.cloud.study.user.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.tuling.cloud.study.user.entity.User;

@RestController
public class OrderController {
  private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);
  @Autowired
  private RestTemplate restTemplate;
  @Autowired
  private LoadBalancerClient loadBalancerClient;

  @GetMapping("/user/{id}")
  public User findById(@PathVariable Long id) {
    return this.restTemplate.getForObject("http://microservice-provider-user/" + id, User.class);
  }
  
  @GetMapping("/user/getIpAndPort")
  public String getIpAndPort() {
	  return this.restTemplate.getForObject("http://microservice-provider-user/getIpAndPort", String.class);
  }

  @GetMapping("/log-user-instance")
  public void logUserInstance() {
    ServiceInstance serviceInstance = this.loadBalancerClient.choose("microservice-provider-user");
    // 打印当前选择的是哪个节点
    OrderController.LOGGER.info("{}:{}:{}", serviceInstance.getServiceId(), serviceInstance.getHost(), serviceInstance.getPort());
  }
}

ConsumerOrderApplication.java

package com.tuling.cloud.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerOrderApplication {
  @Bean
  @LoadBalanced //ribbon的负载均衡注解
  public RestTemplate restTemplate() {
    return new RestTemplate();
  }

  public static void main(String[] args) {
    SpringApplication.run(ConsumerOrderApplication.class, args);
  }
}

② 05-ms-provider-user

pom.xml:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.tuling.cloud</groupId>
  <artifactId>microservice-provider-user</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <!-- 引入spring boot的依赖 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <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-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
    <dependency>
	  <groupId>com.tuling.cloud</groupId>
	  <artifactId>microservice-provider-user-api</artifactId>
	  <version>0.0.1-SNAPSHOT</version>
	</dependency>
  </dependencies>

  <!-- 引入spring cloud的依赖 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Edgware.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <!-- 添加spring-boot的maven插件 -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

application.yml:

server:
  port: 8000
spring:
  application:
    name: microservice-provider-user
  jpa:
    generate-ddl: false
    show-sql: true
    hibernate:
      ddl-auto: none
  datasource:                           # 指定数据源
    platform: h2                        # 指定数据源类型
    schema: classpath:schema.sql        # 指定h2数据库的建表脚本
    data: classpath:data.sql            # 指定h2数据库的数据脚本
logging:                                # 配置日志级别,让hibernate打印出执行的SQL
  level:
    root: INFO
    org.hibernate: INFO
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
    org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/
  instance:
    prefer-ip-address: true

data.sql

insert into user (id, username, name, age, balance) values (1, 'account1', '张三', 20, 100.00);
insert into user (id, username, name, age, balance) values (2, 'account2', '李四', 28, 180.00);
insert into user (id, username, name, age, balance) values (3, 'account3', '王五', 32, 280.00);

schema.sql:

drop table user if exists;
create table user (id bigint generated by default as identity, username varchar(40), name varchar(20), age int(3), balance decimal(10,2), primary key (id));

ProviderUserApplication.java:

package com.tuling.cloud.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class ProviderUserApplication {
  public static void main(String[] args) {
    SpringApplication.run(ProviderUserApplication.class, args);
  }
}

User.java:

package com.tuling.cloud.study.entity;

import java.math.BigDecimal;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  @Column
  private String username;
  @Column
  private String name;
  @Column
  private Integer age;
  @Column
  private BigDecimal balance;

  public Long getId() {
    return this.id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getUsername() {
    return this.username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Integer getAge() {
    return this.age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  public BigDecimal getBalance() {
    return this.balance;
  }

  public void setBalance(BigDecimal balance) {
    this.balance = balance;
  }

}

UserRepository.java:

package com.tuling.cloud.study.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.tuling.cloud.study.entity.User;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

UserController.java:

package com.tuling.cloud.study.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.tuling.cloud.study.entity.User;
import com.tuling.cloud.study.repository.UserRepository;

@RestController
public class UserController {
  @Autowired
  private UserRepository userRepository;
  @Autowired
  private Registration registration;

  @GetMapping("/{id}")
  public User findById(@PathVariable Long id) {
    User findOne = userRepository.findOne(id);
    return findOne;
  }
  
  @GetMapping("/getIpAndPort")
  public String findById() {
	  return registration.getHost() + ":" + registration.getPort();
  }
}

RefactorUserController.java:

package com.tuling.cloud.study.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.tuling.cloud.study.entity.User;
import com.tuling.cloud.study.repository.UserRepository;
import com.tuling.cloud.study.service.UserService;

@RestController
public class RefactorUserController implements UserService {
	
	@Autowired
	private UserRepository userRepository;
	
	@Override
    public User getUser(@PathVariable Long id) {
		User findOne = userRepository.findOne(id);
	    return findOne;
    }

}

③ 05-ms-provider-user-api

pom.xml:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.tuling.cloud</groupId>
  <artifactId>microservice-provider-user-api</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <!-- 引入spring boot的依赖 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

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

</project>

User.java:

package com.tuling.cloud.study.entity;

import java.math.BigDecimal;

public class User {
  private Long id;
  private String username;
  private String name;
  private Integer age;
  private BigDecimal balance;

  public Long getId() {
    return this.id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getUsername() {
    return this.username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Integer getAge() {
    return this.age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  public BigDecimal getBalance() {
    return this.balance;
  }

  public void setBalance(BigDecimal balance) {
    this.balance = balance;
  }

}

UserService.java:

package com.tuling.cloud.study.service;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import com.tuling.cloud.study.entity.User;

public interface UserService {

  @GetMapping("/user/{id}")
  public User getUser(@PathVariable(value = "id") Long id);
  
}

④ 08-ms-eureka-server-ha

pom.xml:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.tuling.cloud</groupId>
  <artifactId>microservice-eureka-server-ha</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <!-- 引入spring boot的依赖 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
  </dependencies>

  <!-- 引入spring cloud的依赖 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Edgware.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <!-- 添加spring-boot的maven插件 -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

application.yml:

spring:
  application:
    name: microservice-eureka-server-ha
---
spring:
  profiles: peer1                                 # 指定profile=peer1
server:
  port: 8761
eureka:
  instance:
    hostname: peer1                               # 指定当profile=peer1时,主机名是peer1
  client:
    serviceUrl:
      defaultZone: http://peer2:8762/eureka/      # 将自己注册到peer2这个Eureka上面去

---
spring:
  profiles: peer2
server:
  port: 8762
eureka:
  instance:
    hostname: peer2
  client:
    serviceUrl:
      defaultZone: http://peer1:8761/eureka/

application-easy.yml:

spring:
  application:
    name: microservice-discovery-eureka-ha
eureka:
  client:
    serviceUrl:
      defaultZone: http://peer2:8762/eureka/,http://peer1:8761/eureka/
---
spring:
  profiles: peer1
server:
  port: 8761
eureka:
  instance:
    hostname: peer1
---
spring:
  profiles: peer2
server:
  port: 8762
eureka:
  instance:
    hostname: peer2

2)编排微服务

1. 在根目录创建文件夹/app。

2. 在app目录下新建docker-compose.yml文件和三个文件夹eureka,user,order。

3. 在eureka,user,order三个文件夹下分别构建eureka服务镜像,user服务镜像,order服务镜像,以构建eureka服务镜像为例,在eureka文件夹下新建dockerfile文件并且将eureka服务的可运行jar包上传到该目录(注意:需要将配置eureka.client.serviceUrl.defaultZone的值改为http://eureka:8761/eureka/,默认情况下Compose以服务名称作为hostname被其他容器访问),dockerfile文件内容如下:

# 基于哪个镜像
From java:8
# 将本地文件夹挂载到当前容器
VOLUME /tmp
# 复制文件到容器
ADD microservice-eureka-server-0.0.1-SNAPSHOT.jar /app.jar
# 声明需要暴露的端口
EXPOSE 8761
# 配置容器启动后执行的命令
ENTRYPOINT ["java","-jar","/app.jar"]

docker-compose.yml 内容如下:

version: '2'              #docker的文件格式版本
services:
  eureka:                 #docker服务名
    image: eureka         #docker镜像
    ports: 
      - "8761:8761"
  user: 
    image: user
    ports:  
      - "8000:8000" 
  order:    
    image: order
    ports: 
      - "8010:8010"

启动所有微服务,在命令后面加-d可以后台启动:

# docker-compose up

访问三个微服务是否正常。

3)编排高可用微服务

1. 在根目录创建文件夹/app-ha。

2. 在app-ha目录下新建docker-compose.yml文件和三个文件夹eureka-ha,user-ha,order-ha。

3. 在eureka-ha,user-ha,order-ha三个文件夹下分别构建eureka-ha服务镜像,user-ha服务镜像,order-ha服务镜像,eureka-ha服务参考项目08-ms-eureka-server-ha,(注意:需要修改user服务和order服务配置文件eureka.client.serviceUrl.defaultZone的值为http://peer1:8761/eureka/,http://peer2:8762/eureka/)

4. docker-compose.yml内容如下:

version: '2'          #docker的文件格式版本
services:
  peer1:             #docker微服务名称
    image: eureka-ha     #docker镜像
    ports:
      - "8761:8761"
    environment:
      - spring.profiles.active=peer1
  peer2:    
    image: eureka-ha
    ports:  
      - "8762:8762" 
    environment:
      - spring.profiles.active=peer2
  user:       
    image: user-ha
    ports:
      - "8000:8000" 
  order:
    image: order-ha
    ports:
      - "8010:8010"

构建镜像:

启动所有微服务,在命令后面加-d可以后台启动:

docker-compose up

访问三个微服务是否正常:

4)动态扩容微服务

有时我们需要扩容微服务,比如我们想把用户和订单微服务各部署两个微服务,则docker-compose.yml文件应该如下配置:

version: '2'          #docker的文件格式版本
services:
  peer1:             #docker微服务名称
    image: eureka-ha     #docker镜像
    ports:
      - "8761:8761"
    environment:
      - spring.profiles.active=peer1
  peer2:    
    image: eureka-ha
    ports:  
      - "8762:8762" 
    environment:
      - spring.profiles.active=peer2
  user:       
    image: user-ha
  order:
    image: order-ha

执行如下扩容命令:

# docker-compose up    #必须先正常编排微服务,然后才能动态扩容
# docker-compose scale user=2 order=2

注意:如果是在同一台物理机上做动态扩容,则需要在docker-compose.yml里去掉除了eureka其它微服务ports端口映射。

运行完查看eureka注册中心如下图所示:

订单两个微服务,对应容器也是两个。

猜你喜欢

转载自blog.csdn.net/qq_35029061/article/details/126673271
今日推荐