前言
写这篇文章只是为了详细的记录下我的部署过程和要注意的地方。
因为本人公司需要,此文章对于每个server、provider、consumer都采用了单独的项目来做的,没有作为父子项目的形式创建。其中server采用了docker-compse和Dockerfile方式来部署,provider和consumer暂且使用Dockerfile
一、创建并发布Server
还是采用快速创建
选择springboot方式。
组件选择springcloud discovery 中的 eureka server,如图:
完成后,
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 就完成编译。
编译后,按照Dockerfile中定义的eurekaserver-0.0.1.jar 修改下生成的 jar的名字。
因为我用的是xshell,所以直接在 xshell 中登录 centos 环境后,使用 rz命令,将 jar包,Dockerfile以及 docker-compose.yml统统拷贝到centos环境中
4、部署到docker
完成后,直接运行
docker build -t eurekaserver . 生成 docker image。注意后面有个 .号
生成后,运行docker images查看
image生成后,运行 docker-compose up -d 来运行 docker-compse.yml生成container。
其中-d是 后台运行
完成后,运行docker ps 查看正在运行的docker container
结束后回到 windows环境,在浏览器中 访问:http://172.30.100.193:8001/ ,http://172.30.100.193:8002/ 可以看到 服务互相注册成功。
二、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 build -t eurekaprovider . 来创建docker image
创建完成后运行docker images查看如下图:
接着直接运行docker run来使用 image创建container
命令: docker run -d -p 8101:8101 --name eurekaprovider eurekaprovider
其中 -d是后台运行,
-p是端口映射 格式是 主机(宿主)端口:容器端口
–name 后面跟的是 container的名称,最后是image的名称
完成后运行docker ps来查看。
生成了docker container再windows中访问 http://172.30.100.193:8001/,http://172.30.100.193:8002/
可以看到:
直接访问 provider的 路径也是可以访问的:
访问:http://172.30.100.193:8101/internal-meeting-user-info/userinfo/1
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的项目
可以看到,这里除了选择了 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 和 轮询访问规则
完整的项目结果如下:
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 build -t eurekaconsumer .来生成 镜像,生成后运行docker images来查看:
完成后,运行 docker run -d -p 8201:8201 --name eurekaconsumer eurekaconsumer 来创建container
然后运行 docker ps来查看运行的container:
这样就部署完成了。可以通过 http://172.30.100.193:8201/userinfo/1 来访问 consumer看看结果
这样,整个集群就已经部署完毕,
下一次通过 父子项目 docker-compose的方式来再写一篇。