2020.01更新 :
eureka宣布闭源维护。 其实大部分SpringCloud组件都进入了维护状态, 参考: https://blog.csdn.net/u012437781/article/details/85258505
其实停止更新不代表停止使用,还是要根据实际情况和长远考虑选择合适的服务组件,比如SpringCloud Alibaba 。
以下为原文:
微服务: 核心就是将传统的一站式应用,根据业务功能拆分成一个个的服务,彻底的去耦合。 每一个微服务提供单个业务功能和服务,一个服务做一件事,它关注的的某一个点,是具体解决某个问题/提供落地对应服务的一个应用。 从技术角度看就是一个小而独立的处理过程,类似进程的概念。能够自行单独启动或销毁,也可以拥有自己独立的数据库。
在折腾SpringCloud期间只了解到了单个服务注册中心的创建,不过之后又了解到单模式的部署方式在生产环境下中确实不提倡 :
因为有很多种原因可能导致服务注册中心宕机。
如果宕机就会有一些灾难性的问题出现 比如被杀掉祭天,所以保证服务注册中心处于持续运行状态显得尤为重要!!!
此时就需要搭建高可用集群Eureka服务注册中心。
环境: JDK - 1.8 IDEA - 2018.3.1 SpringBoot - 1.5.9 SpringCloud - Dalston.SR1 MYSQL - 5.5
一、项目结构
具体构建细节不赘述了,不在本文章讨论范围内。目录结构如下:
在microServiceCloud这个maven工程中,有8个由SpringBoot构成的独立服务。
API1.1:整个项目中用到的公共类如部门类,抽取成API以在其他子Maven中直接引入。
7001、7002、7003:实现集群的Eureka注册中心服务端口。
8001、8002、8003:实现负载均衡的生产者服务端口。
9002:消费者服务端口。
数据库方面,使用了三个独立库,分别是db1、db2、db3。专门给 8001、8002、8003 这三个端口服务。
贴一下父POM,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.lzm.springcloud</groupId>
<artifactId>microservicecloud1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>microservicecloud1</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<delimiters>
<delimit>$</delimit>
</delimiters>
</configuration>
</plugin>
</plugins>
</build>
<!-- 这是子模块 -->
<modules>
<module>microservicecloud1-api1.1</module>
<module>microservicecloud-eureka-7001</module>
<module>microservicecloud-eureka-7002</module>
<module>microservicecloud-eureka-7003</module>
<module>microservicecloud-provider-8001</module>
<module>microservicecloud-consumer-9002</module>
<module>microservicecloud-provider-8002</module>
<module>microservicecloud-provider-8003</module>
</modules>
</project>
二、最终实现效果
在注册中心可以观察到三个 Eureka的集群。
端口为7001-7003。
对相同请求实现负载均衡,还可以访问不同的数据库。
注意Spriongclouddb结尾的数字,代表的是1-3号库。
9002端口为消费者,通过RestTemplate,来访问位于8001-8003生产者端口的Rest服务。
三、实现细节
1、 对生产者provider负载均衡
重点1: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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>microservicecloud1</artifactId>
<groupId>com.lzm.springcloud</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>microservicecloud-provider-8001</artifactId>
<name>microservicecloud-provider-8001</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<!-- 引入自己定义的api通用包,可以使用公用Entity -->
<dependency>
<groupId>com.lzm.springcloud</groupId>
<artifactId>microservicecloud1-api1.1</artifactId>
<version>${project.version}</version>
</dependency>
<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 将生产微服务provider注册进eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
<build>
<finalName>microservicecloud-provider-8001</finalName>
</build>
</project>
持久层省略不贴了,controller层很简单:
@RestController
@RequestMapping("/dept")
public class DeptController {
@Resource(name="deptservice")
private DeptServiceImpl service;
@RequestMapping(value = "/add" , method = RequestMethod.POST)
public boolean add (Dept dept){
return service.add(dept);
}
@RequestMapping( value = "/get/{id}", method = RequestMethod.GET)
public Dept get( @PathVariable Long id){
return service.get(id);
}
@RequestMapping(value="/list", method=RequestMethod.GET)
public List<Dept> list() {
return service.list();
}
}
主启动类记得加Eureka客户端的注解:
@SpringBootApplication
@EnableEurekaClient /* 本服务启动后会自动注册进eureka服务中, 注意与@EnableEurekaServer区分 */
public class DeptProvider8001_App {
public static void main(String[] args) {
SpringApplication.run(DeptProvider8001_App.class, args);
}
}
重点2: application.yml配置文件
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.lzm.springcloud.entity # 所有Entity别名类所在包
mapper-locations: classpath:mybatis/mapper/*.xml # mapper映射文件
spring:
application:
name: cloud-dept
# 数据源
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/springclouddb1 # 数据库名称
username: # 用户名
password: # 密码
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
#test-while-idle: true
#test-on-borrow: false
#test-on-return: false
# 注意 多个Boot热部署工具同时运行会报错端口冲突, 需要在不同应用中设置不同端口. 默认端口为 35729
devtools:
livereload:
port: 35732
debug: true
# 把这个应用客户端注册进eureka服务列表内
eureka:
instance:
instance-id: dept-8001 # 自定义此服务的 名称信息
prefer-ip-address: true # 鼠标移到访问路径后,在左下角可以显示此应用IP地址和端口
client:
service-url:
# 单机:
#defaultZone: http://localhost:7001/eureka
# 集群:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
# 在eureka界面中点微服务后的info内容完整信息
info:
app.name: microservicecloud
company.name: lzyz.fun
build.artifactId: $project.artifactId$
build.version: $project.version$
8002和8003端口的服务和8001的java代码相同。直接复制即可。
注意不同的是yml中几处配置:
# 如果是8002,端口需要改
server:
port: 8001
#自定义服务名称信息:
instance-id: cloud-dept8002
#修改数据库ULR:
url: jdbc:mysql://localhost:3306/cloudDB2
# 多个Boot热部署工具同时运行会报错端口冲突, 需要在不同应用中设置不同端口. 默认端口为 35729
devtools:
livereload:
port: 35733
还要注意这三个服务的name值:spring.application.name: cloud-dept
一定要一致。
2、 对Eureka进行集群配置
关键1: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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>microservicecloud1</artifactId>
<groupId>com.lzm.springcloud</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>microservicecloud-eureka-7001</artifactId>
<name>microservicecloud-eureka-7001</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<!-- eureka-server服务端
你需要让这个boot具备服务注册与发现的功能,则需要导入相关的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</project>
启动类:
/**
* 注册与发现启动类
*/
@SpringBootApplication
@EnableEurekaServer /* EurekaServer服务器端启动类, 接受其它微服务注册进来 */
public class EurekaServer7001_App
{
public static void main( String[] args ) {
SpringApplication.run(EurekaServer7001_App.class, args);
}
}
关键2:yml配置文件。
注意我提前在本机的hosts文件做了映射:
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
server:
port: 7001
eureka:
instance:
#hostname: localhost # eureka服务器的实例名称( 单机环境下 )
hostname: eureka7001.com # eureka服务器的实例名称( 集群环境下 )
client:
register-with-eureka: false # false表示不向注册中心注册自己。
fetch-registry: false # false表示自己就是注册中心。我负责维护服务示例,并不需要去检索服务
service-url: # service-url的配置内容是map,map的内容是可以自己发挥的,只要是key: value格式就行(注意有个空格)。
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ # ( 集群情况 )
7002、7003的服务与7001的yml文件不同处在于:
修改端口
server:
port: 7002
修改实例名称
hostname: eureka7002.com # eureka服务器的实例名称( 集群环境下 )
关键点! 这里如果是7002. 则应该写7001和7003,手拉手做集群!7003同理。
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/ # ( 集群情况 )
3、 实现消费者
消费者是客户端。
而Ribbon负载均衡, 是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器
所以需要对消费者(客户端)进行负载均衡的配置:
下面的代码都比较关键
POM依赖:
<dependencies>
<!-- 引入自己定义的api通用包,可以使用Dept部门Entity -->
<dependency>
<groupId>com.lzm.springcloud</groupId>
<artifactId>microservicecloud1-api1.1</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Ribbon相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
配置类:
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced /* 客户端 - 获取Ribbon负载均衡支持 */
public RestTemplate getTemplate(){
return new RestTemplate();
}
}
controller层:
@RestController
public class ConsumerController {
//private final String REST_URL = "http://localhost:8001";
//这是错误写法。既然实现了负载均衡。就应该让其自主选择哪个服务节点。不需要我们手动来定义!
private static final String REST_URL = "http://cloud-dept"; //所以直接写服务名称。 之前在生产者yml配置文件中定义过,还强调了名字必须相同。
@Resource
private RestTemplate template;
@RequestMapping("/consumer/dept/list")
public List list(){
return template.getForObject(REST_URL+"/dept/list" , List.class);
}
@RequestMapping("/consumer/dept/get/{id}")
public Dept get( @PathVariable Long id){
return template.getForObject( REST_URL+"/dept/get/"+id , Dept.class );
}
@RequestMapping("/consumer/dept/add")
public boolean add( Dept dept ){
return template.postForObject( REST_URL+"/dept/add", dept , Boolean.class );
}
}
yml 配置:
#消费者端口
server:
port: 9002
# 注册eureka的地址。注意此时eureka已经集群。
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
spring:
devtools:
livereload:
port: 35733
四、稍微总结下
注意 eureka 的自我保护,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该Eureka Server节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话理解:好死不如赖活着 。
注意Boot热部署工具的端口冲突,默认是35729。 每增加一个服务最好将端口号+1。
同样 ,最好不要用8080或80这些容易出问题的端口。