Spring Cloud 学习笔记

一、微服务概念

1. 微服务定义

广义上讲,微服务是一种架构风格,或者说是一种开发软件的架构和组织方法。狭义上讲,微服务是组成一个大型复杂软件系统的一个或多个小型独立的服务软件应用。

一个大型复杂软件应用由通过明确定义的 API 进行通信的一个或多个小型独立服务(微服务)组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。

2. 微服务架构

微服务架构是⼀种架构模式,它提倡将单⼀应⽤程序划分成⼀组⼩的服务,服务之间互相协调、互相配合,为⽤户提供最终价值。每个服务运⾏在其独⽴的进程中,服务与服务间采⽤轻量级的通信机制互相协作(通常是基于HTTP协议的RESTful API)。每个服务都围绕着具体业务进⾏构建,并且能够被独⽴的部署到⽣产环境、类⽣产环境等。另外,应当尽量避免统⼀的、集中式的服务管理机制,对具体的⼀个服务⽽⾔,应根据业务上下⽂,选择合适的语⾔、⼯具对其进⾏构建。

3. 微服务化

微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事,从技术角度看就是一种小而独立的处理过程,类似进程概念,能够自行单独启动或销毁,拥有自己独立的数据库。

4. 微服务的优缺点

优点
(1)每个服务足够内聚,足够小,代码容易理解这样能聚焦一个指定的业务功能或业务需求

(2)开发简单、开发效率提高,一个服务可能就是专一的只干一件事。
微服务能够被小团队单独开发,这个小团队是2到5人的开发人员组成。
微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的。
(3)微服务能使用不同的语言开发。
(4)易于和第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如Jenkins, Hudson, bamboo 。
(5)微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果。无需通过合作才能体现价值。
(6)微服务允许你利用融合最新技术。
(7)微服务只是业务逻辑的代码,不会和HTML,CSS 或其他界面组件混合。
(8)每个微服务都有自己的存储能力,可以有自己的数据库。也可以有统一数据库。

(9)由于多个微服务应用各自能独立运行,所以其中一些服务出现问题一般不会影响其他服务的正常运行和使用。

缺点
(1)开发人员要处理分布式系统的复杂性
(2)多服务运维难度,随着服务的增加,运维的压力也在增大
(3)系统部署依赖
(4)服务间通信成本
(5)数据一致性
(6)系统集成测试
(7)性能监控……

5. 微服务技术栈

服务开发 Springboot、Spring、SpringMVC
服务配置与管理 Netflix公司的Archaius、阿里的Diamond等
服务注册与发现 Eureka、Consul、Zookeeper等
服务调用 Rest、RPC、gRPC
服务熔断器 Hystrix、Envoy等
负载均衡 Ribbon、Nginx等
服务接口调用(客户端调用服务的简化工具) Feign等
消息队列 Kafka、RabbitMQ、ActiveMQ等
服务配置中心管理 SpringCloudConfig、Chef等
服务路由(API网关) Zuul等
服务监控 Zabbix、Nagios、Metrics、Spectator等
全链路追踪 Zipkin,Brave、Dapper等
服务部署 Docker、OpenStack、Kubernetes等
数据流操作开发包 SpringCloud Stream(封装与Redis,Rabbit、Kafka等发送接收消息)
事件消息总线 Spring Cloud Bus

6. 各微服务框架对比

1567359553065

7. 微服务中SpringCloud与Dubbo的区别

Dubbo SpringCloud
服务注册中心 Zookeeper Spring Cloud Netfilx Eureka
服务调用方式 RPC REST API
服务监控 Dubbo-monitor Spring Boot Admin
断路器 不完善 pring Cloud Netflix Hystrix
服务网关 Spring Cloud Netflix Zuul
分布式配置 Spring Cloud Config
服务跟踪 Spring Cloud Sleuth
消息总线 Spring Cloud Bus
数据流 Spring Cloud Stream
批量任务 Spring Cloud Task
…… …… ……

当然这里需要申明一点,Dubbo对于上表中总结为“无”的组件不代表不能实现,而只是Dubbo框架自身不提供,需要另外整合以实现对应的功能,比如:

  • 分布式配置:可以使用淘宝的diamond、百度的disconf来实现分布式配置管理。但是Spring Cloud中的Config组件除了提供配置管理之外,由于其存储可以使用git,因此它天然的实现了配置内容的版本管理,可以完美的与应用版本管理整合起来。
  • 服务跟踪:可以使用京东开源的Hydra
  • 批量任务:可以使用当当开源的Elastic-Job
  • ……

最大区别:
Spring Cloud抛弃了RPC通讯,采用基于HTTP的REST方式。Spring Cloud牺牲了服务调用的性能,但是同时也避免了原生RPC带来的问题。REST比RPC更为灵活,不存在代码级别的强依赖,在强调快速演化的微服务环境下,显然更合适。

Dubbo只是实现了服务治理,而Spring Cloud下面有17个子项目(可能还会新增)分别覆盖了微服务架构下的方方面面,服务治理只是其中的一个方面,一定程度来说,Dubbo只是Spring Cloud Netflix中的一个子集。

一句话:Dubbo像组装机,Spring Cloud像一体机
社区的支持与力度:Dubbo曾经停运了5年,虽然重启了,但是对于技术发展的新需求,还是需要开发者自行去拓展,对于中小型公司,显然显得比较费时费力,也不一定有强大的实力去修改源码

总结
解决的问题域不一样:Dubbo的定位是一款RPC框架,Spring Cloud的目标是微服务架构下的一站式解决方案 ;

二、SpringCloud

1. 概念

官网:http://projects.spring.io/spring-cloud/

参考文档:

本次开发API说明:https://springcloud.cc/spring-cloud-netflix.html http://cloud.spring.io/spring-cloud-static/Dalston.SR1/ https://springcloud.cc/spring-cloud-dalston.html

springcloud中国社区:http://springcloud.cn/

springcloud中文网:https://springcloud.cc/

SpringCloud是基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。

SpringCloud=分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶

1567361943293

SpringCloud利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等,它们都可以用SpringBoot的开发风格做到一键启动和部署。

SpringBoot并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过SpringBoot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包

2. SpringBoot和SpringCloud

SpringBoot专注于快速方便的开发单个个体微服务。

SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,
为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务

SpringBoot可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SpringBoot,属于依赖的关系.

SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。

3.项目搭建示例项目

3.1 需求

项目搭建为springmvc+mybatis+mysql,以Dept部门模块做一个微服务通用案例。Consumer消费者(Client)通过REST调用Provider提供者(Server)提供的服务。

创建maven聚合工程:

1
2
3
4
5
6
7
8
9
10
11
12
13
#父项目(microservicecloud)聚合很多子项目(microservicecloud-api,microservicecloud-provider-dept-8001,microservicecloud-consumer-dept-80)

---- microservicecloud #父项目(类似一个抽象父类)
|---- pom.xml (pom)
|
|-------- microservicecloud-api #封装的整体entity/接口/公共配置等
| |-------- pom.xml (jar)
|
|-------- microservicecloud-provider-dept-8001 #微服务落地的服务提供者
| |-------- pom.xml (jar)
|
|-------- microservicecloud-consumer-dept-80 #微服务调用的消费者
| |-------- pom.xml (jar)

3.2 第一步

(1)创建一个大包方式为pom的父项目microservicecloud

groupId: com.qcmoke

artifactId: microservicecloud

version: 0.0.1-SNAPSHOT

packaging: pom

(2)修改pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<?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.qcmoke</groupId>
<artifactId>microservicecloud</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>

<!-- 
1.dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显式的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。一般在我们项目顶层的pom文件中,我们会看到dependencyManagement元素
2.dependencies即使在子模块中不写该依赖项,那么子模块仍然会从父项目中继承该依赖项(全部继承)。-->
<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>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>
</project>

3.3 第二步

(1)创建子模块microservicecloud-api

(2)修改该模块的pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<!-- 当前Module我自己叫什么名字 -->
<artifactId>microservicecloud-api</artifactId>

<dependencies><!-- 当前Module需要用到的jar包,按自己需求添加,如果父类已经包含了,可以不用写版本号 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>


</project>

(3)创建完成后在父工程的pom文件的<project>标签下添加如下代码。(一般IDE工具会自动添加)

1
2
3
<modules>
<module>microservicecloud-api</module>
</modules>

(4)在src/main/java下新建com.qcmoke.entities.Dept

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.qcmoke.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

/**
* 部门实体类
*
* @author qcmoke
* @date 2019/09/02 03:14
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Dept {
private Long deptno;//主键
private String dname; //部门名称
private String db_source; // 来自那个数据库,因为微服务架构可以一个服务对应一个数据库,同一个信息被存储到不同数据库

public Dept(String dname) {
super();
this.dname = dname;
}

}

3.4 第三步

(1)创建子模块microservicecloud-provider-dept-8001

(2)编辑该模块的pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-provider-dept-8001</artifactId>

<dependencies>
<dependency><!-- 引入自己定义的其他模块(microservicecloud-api) -->
<groupId>com.qcmoke</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</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>


</project>

(3)创建完成后在父工程的pom文件的<project>标签下修改<modules>为如下代码。(一般IDE工具会自动添加)

1
2
3
4
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
</modules>

(4)在该模块下新建启动类com.qcmoke.DeptProvider8001App.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.qcmoke;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* microservicecloud-provider-dept-8001模块的启动类
*
* @author qcmoke
* @date 2019/09/02 03:34
*/
@SpringBootApplication
public class DeptProvider8001App {

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

(5)在该模块下创建src/main/resources/application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server:
port: 8001

mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.qcmoke.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件

spring:
application:
name: microservicecloud-dept #对外暴露的微服务的名称(重点)
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称
username: root
password: 123456
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间

(6)在该模块下创建src/main/resources/mybatis/mybatis.cfg.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<settings>
<setting name="cacheEnabled" value="true"/><!-- 二级缓存开启 -->
</settings>

</configuration>

(7)mysql执行以下sql创建数据库和添加相关表数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
DROP DATABASE IF EXISTS cloudDB01;
CREATE DATABASE cloudDB01 CHARACTER SET UTF8;
USE cloudDB01;
CREATE TABLE dept
(
deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(60),
db_source VARCHAR(60)
);


INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE()); -- DATABASE()函数可获取当前数据库的名称,在分库的情况下需要
INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('市场部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('运维部',DATABASE());

SELECT * FROM dept;

(9)在该模块下添加com.qcmoke.dao.DeptDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.qcmoke.dao;

import com.qcmoke.entities.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
* DeptDao
*
* @author qcmoke
* @date 2019/09/02 03:26
*/
@Repository
@Mapper
public interface DeptDao {
public boolean addDept(Dept dept);

public Dept findById(Long id);

public List<Dept> findAll();
}

(10)在该模块下创建src/main/resources/mybatis/mapper/DeptMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.qcmoke.dao.DeptDao">

<select id="findById" resultType="Dept" parameterType="Long">
select deptno,dname,db_source from dept where deptno=#{deptno};
</select>

<select id="findAll" resultType="Dept">
select deptno,dname,db_source from dept;
</select>

<insert id="addDept" parameterType="Dept">
INSERT INTO dept(dname,db_source) VALUES(#{dname},DATABASE());
</insert>

</mapper>

(11)在该模块下添加com.qcmoke.service.DeptService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.qcmoke.service;

import com.qcmoke.entities.Dept;

import java.util.List;

/**
* DeptService
*
* @author qcmoke
* @date 2019/09/02 03:30
*/
public interface DeptService {
public boolean add(Dept dept);

public Dept get(Long id);

public List<Dept> list();
}

(12)在该模块下添加com.qcmoke.service.DeptServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.qcmoke.service;

import com.qcmoke.dao.DeptDao;
import com.qcmoke.entities.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* DeptServiceImpl
*
* @author qcmoke
* @date 2019/09/02 03:30
*/
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
DeptDao deptDao;

@Override
public boolean add(Dept dept) {
return deptDao.addDept(dept);
}

@Override
public Dept get(Long id) {
return deptDao.findById(id);
}

@Override
public List<Dept> list() {
return deptDao.findAll();
}
}

(13)在该模块下添加com.qcmoke.controller.DeptController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.qcmoke.controller;

import com.qcmoke.entities.Dept;
import com.qcmoke.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
* DeptController
*
* @author qcmoke
* @date 2019/09/02 03:32
*/
@RestController
public class DeptController {
@Autowired
private DeptService service;

@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean add(@RequestBody Dept dept) {
return service.add(dept);
}

@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") Long id) {
return service.get(id);
}

@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List<Dept> list() {
return service.list();
}

}

(14)启动com.qcmoke.DeptProvider8001App.java测试

http://localhost:8001/dept/get/2

http://localhost:8001/dept/list

3.5 第四步

(1)创建子模块microservicecloud-consumer-dept-80

(2)修改该模块的pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-consumer-dept-80</artifactId>


<description>部门微服务消费者</description>
<dependencies>
<dependency><!-- 自己定义的api -->
<groupId>com.qcmoke</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>

(3)创建完成后在父工程的pom文件的<project>标签下修改<modules>为如下代码。(一般IDE工具会自动添加)

1
2
3
4
5
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
</modules>

(4)在该模块下创建src/main/resources/application.yml

1
2
server:
port: 80

(5)在该模块下创建com.qcmoke.config.ConfigBean.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.qcmoke.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
* ConfigBean
*
* @author qcmoke
* @date 2019/09/02 04:32
*/
@Configuration
public class ConfigBean {
/**
* RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
*
* @author qcmoke
* @date 2019/9/2 4:41
*/
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}

RestTemplate官网地址

https://docs.spring.io/spring-framework/docs/4.3.7.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html

使用
使用restTemplate访问restful接口非常的简单粗暴无脑。(url, requestMap, ResponseBean.class)这三个参数分别代表REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。

(6)在该模块下创建com.qcmoke.controller.DeptConsumerController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.qcmoke.controller;

import com.qcmoke.entities.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
* DeptConsumerController
*
* @author qcmoke
* @date 2019/09/02 04:34
*/
@RestController
public class DeptConsumerController {
private static final String REST_URL_PREFIX = "http://localhost:8001";

@Autowired
private RestTemplate restTemplate;

@RequestMapping(value = "/consumer/dept/add")
public boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}

@RequestMapping(value = "/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
}

@SuppressWarnings("unchecked")
@RequestMapping(value = "/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}

}

(7)在该模块下创建启动类com.qcmoke.MicroServiceCloudConsumerDept80.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.qcmoke;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* MicroservicecloudConsumerDept80
*
* @author qcmoke
* @date 2019/09/02 04:37
*/
@SpringBootApplication
public class MicroServiceCloudConsumerDept80 {
public static void main(String[] args) {
SpringApplication.run(MicroServiceCloudConsumerDept80.class, args);
}

}

(8)测试

首先启动microservicecloud-provider-dept-8001模块的com.qcmoke.DeptProvider8001App.java

然后启动microservicecloud-consumer-dept-80模块的com.qcmoke.MicroServiceCloudConsumerDept80.java

http://localhost/consumer/dept/get/2

http://localhost/consumer/dept/list

http://localhost/consumer/dept/add?dname=AI

4. Eureka服务注册与发现

4.1 概念

Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务架构来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了。功能类似于dubbo的注册中心,比如Zookeeper。

Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现。

Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。

而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。SpringCloud 的一些其他模块(比如Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。

Eureka包含两个组件:Eureka Server和Eureka Client
Eureka Server提供服务注册服务
各个节点启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到

EurekaClient是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)

Eureka构架设计:

1567421642929

对比Dubbo架构的设计:

1567421772107

4.2 需求

三大角色

Eureka Server 提供服务注册和发现

Service Provider服务提供方将自身服务注册到Eureka,从而使服务消费方能够找到

Service Consumer服务消费方从Eureka获取注册服务列表,从而能够消费服务

4.3 第一步

(1)创建 eureka服务注册中心子模块microservicecloud-eureka-7001

(2)修改该模块pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-eureka-7001</artifactId>

<dependencies>
<!--eureka-server服务端 -->
<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>

创建完成后在父工程的pom文件的<project>标签下添加如下代码。(一般IDE工具会自动添加)

1
2
3
4
5
6
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
</modules>

(3)在该模块下创建src/main/resources/application.yml

1
2
3
4
5
6
7
8
9
10
11
server:
port: 7001

eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己就是注册中心,职责就是维护服务实例,并不需要自我注册,自我检查。
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。

(4)创建启动类com.qcmoke.EurekaServer7001App.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.qcmoke;

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

/**
* EurekaServer7001App
*
* @author qcmoke
* @date 2019/09/02 18:39
*/
@SpringBootApplication
@EnableEurekaServer//EurekaServer服务器端启动类,接受其它微服务注册进来
public class EurekaServer7001App {
public static void main(String[] args) {
SpringApplication.run(EurekaServer7001App.class, args);
}

}

(5)运行com.qcmoke.EurekaServer7001App.java 测试

访问http://localhost:7001/

4.4 第二步

将已有的部门微服务microservicecloud-provider-dept-8001注册进eureka服务中心microservicecloud-eureka-7001

(1)修改microservicecloud-provider-dept-8001的pom.xml

添加如下依赖

1
2
3
4
5
6
7
8
9
<!-- 将微服务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>

修改好的pom.xml如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-provider-dept-8001</artifactId>

<dependencies>
<dependency><!-- 引入自己定义的其他模块(microservicecloud-api) -->
<groupId>com.qcmoke</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</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>


</project>

(2)修改该项目的application.yml

添加如下配置

1
2
3
4
eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://localhost:7001/eureka

修改好的application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
server:
port: 8001

mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.qcmoke.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件

spring:
application:
name: microservicecloud-dept #对外暴露的微服务的名称(重点)
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称
username: root
password: 123456
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间

eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://localhost:7001/eureka

(3)修改该项目的启动类com.qcmoke.DeptProvider8001App.java

给启动类添加@EnableEurekaClient注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.qcmoke;

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

/**
* microservicecloud-provider-dept-8001模块的启动类
*
* @author qcmoke
* @date 2019/09/02 03:34
*/
@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
public class DeptProvider8001App {

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

(4)测试

先启动microservicecloud-eureka-7001的com.qcmoke.EurekaServer7001App.java

然后再启动microservicecloud-provider-dept-8001的com.qcmoke.DeptProvider8001App.java

访问http://localhost:7001/

1567432116207

如图标注的名称就是microservicecloud-provider-dept-8001 yml配置文件中指定的name:

1
2
3
spring:
application:
name: microservicecloud-dept #对外暴露的微服务的名称(重点)

在microservicecloud-eureka-7001中能检测microservicecloud-provider-dept-8001,说明服务注册成功。

(5)个性化配置(可选)

(5.1)指定如下图标注的服务名称

1567432324651

修改microservicecloud-provider-dept-8001的application.yml,在eureka下添加如下配置:

1
2
instance:
instance-id: microservicecloud-dept8001

修改好后的全配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
server:
port: 8001

mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.qcmoke.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件

spring:
application:
name: microservicecloud-dept #对外暴露的微服务的名称(重点)
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称
username: root
password: 123456
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间

eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://localhost:7001/eureka
instance:
instance-id: microservicecloud-dept8001

修改好后,先启动microservicecloud-eureka-7001的com.qcmoke.EurekaServer7001App.java,然后再启动microservicecloud-provider-dept-8001的com.qcmoke.DeptProvider8001App.java,最后访问http://localhost:7001/

1567432653207

(5.2)修改服务访问信息ip的提示,如下图:

1567432874373

修改microservicecloud-provider-dept-8001的application.yml,在eureka下添加如下配置:

1
2
3
4
5
6
7
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
instance-id: microservicecloud-dept8001
prefer-ip-address: true #添加的配置

修改好后的全配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
server:
port: 8001

mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.qcmoke.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件

spring:
application:
name: microservicecloud-dept #对外暴露的微服务的名称(重点)
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称
username: root
password: 123456
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间

eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://localhost:7001/eureka
instance:
instance-id: microservicecloud-dept8001
prefer-ip-address: true #访问路径可以显示IP地址

修改好后,先启动microservicecloud-eureka-7001的com.qcmoke.EurekaServer7001App.java,然后再启动microservicecloud-provider-dept-8001的com.qcmoke.DeptProvider8001App.java,最后访问http://localhost:7001/

1567433179633

(5.3)配置微服务http://ip/info的详细信息页面内容(点击服务超链接报告ErrorPage)

修改microservicecloud-provider-dept-8001的pom.xml

添加如下依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

修改好后的全部配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-provider-dept-8001</artifactId>

<dependencies>
<dependency><!-- 引入自己定义的其他模块(microservicecloud-api) -->
<groupId>com.qcmoke</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</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>

<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</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>


</project>

总的父工程microservicecloud修改pom.xml添加构建build配置

添加如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<finalName>microservicecloud</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>

修改好后的全部配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<?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.qcmoke</groupId>
<artifactId>microservicecloud</artifactId>
<version>0.0.1-SNAPSHOT</version>

<!--模块(子项目),会被构建成项目的一部分 -->
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
</modules>
<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>

<!-- 
1.dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显式的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。一般在我们项目顶层的pom文件中,我们会看到dependencyManagement元素
2.dependencies即使在子模块中不写该依赖项,那么子模块仍然会从父项目中继承该依赖项(全部继承)。-->
<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>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>microservicecloud</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>
</project>

修改microservicecloud-provider-dept-8001的application.yml

添加如下配置:

1
2
3
4
5
info:
app.name: microservicecloud
company.name: www.qcmoke.site
build.artifactId: ${project.artifactId}
build.version: ${project.version}

修改好后的全部配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
server:
port: 8001

mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.qcmoke.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件

spring:
application:
name: microservicecloud-dept #对外暴露的微服务的名称(重点)
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称
username: root
password: 123456
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间

eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://localhost:7001/eureka
instance:
instance-id: microservicecloud-dept8001
prefer-ip-address: true #访问路径可以显示IP地址


info:
app.name: microservicecloud
company.name: www.qcmoke.site
build.artifactId: ${project.artifactId}
build.version: ${project.version}

修改好后,先启动microservicecloud-eureka-7001的com.qcmoke.EurekaServer7001App.java,然后再启动microservicecloud-provider-dept-8001的com.qcmoke.DeptProvider8001App.java,最后访问http://localhost:7001/ 并点击microservicecloud-dept8001

1567480194285

(5.3)eureka自我保护的状况

故障现象

1567434204116

导致原因:

什么是自我保护模式?

默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该Eureka Server节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

在Spring Cloud中,可以在microservicecloud-eureka-7001application.yml中使用eureka.server.enable-self-preservation = false禁用自我保护模式,但不建议。

4.5 第三步

服务发现Discovery,对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息

(1)修改microservicecloud-provider-dept-8001工程的DeptController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.qcmoke.controller;


import com.qcmoke.entities.Dept;
import com.qcmoke.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;

import java.util.List;


/**
* DeptController
*
* @author qcmoke
* @date 2019/09/02 03:32
*/
@RestController
public class DeptController {
@Autowired
private DeptService service;

@Autowired
private DiscoveryClient client;

@RequestMapping(value = "/dept/discovery", method = RequestMethod.GET)
public Object discovery() {
List<String> list = client.getServices();
System.out.println("**********" + list);

List<ServiceInstance> srvList = client.getInstances("MICROSERVICECLOUD-DEPT");
for (ServiceInstance element : srvList) {
System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
+ element.getUri());
}
return this.client;
}


@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean add(@RequestBody Dept dept) {
return service.add(dept);
}

@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") Long id) {
return service.get(id);
}

@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List<Dept> list() {
return service.list();
}

}

(2)修改microservicecloud-provider-dept-8001的DeptProvider8001App.java,添加@EnableDiscoveryClient注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.qcmoke;

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

/**
* microservicecloud-provider-dept-8001模块的启动类
*
* @author qcmoke
* @date 2019/09/02 03:34
*/
@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
@EnableDiscoveryClient //服务发现
public class DeptProvider8001App {

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

(3)修改microservicecloud-consumer-dept-80工程的DeptConsumerController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.qcmoke.controller;

import com.qcmoke.entities.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
* DeptConsumerController
*
* @author qcmoke
* @date 2019/09/02 04:34
*/
@RestController
public class DeptConsumerController {
private static final String REST_URL_PREFIX = "http://localhost:8001";

//测试@EnableDiscoveryClient,消费端可以调用服务发现
@RequestMapping(value = "/consumer/dept/discovery")
public Object discovery() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/discovery", Object.class);
}

@Autowired
private RestTemplate restTemplate;

@RequestMapping(value = "/consumer/dept/add")
public boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}

@RequestMapping(value = "/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
}

@SuppressWarnings("unchecked")
@RequestMapping(value = "/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}

}

(4)测试

先启动microservicecloud-eureka-7001的com.qcmoke.EurekaServer7001App.java

然后启动microservicecloud-provider-dept-8001的com.qcmoke.DeptProvider8001App.java

最后再启动microservicecloud-consumer-dept-80的com.qcmoke.MicroServiceCloudConsumerDept80.java

访问http://localhost/consumer/dept/discovery

1567436164552

4.6 Eureka集群配置

需求:集群三个Eureka服务器、并且注册一台微服务。

(1)新建microservicecloud-eureka-7002和microservicecloud-eureka-7003

(2)按照microservicecloud-eureka-7001为模板粘贴pom.xml到microservicecloud-eureka-7002/microservicecloud-eureka-7003

创建完成后在父工程的pom文件的<project>标签下添加如下代码。(一般IDE工具会自动添加)

1
2
3
4
5
6
7
8
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
<module>microservicecloud-eureka-7002</module>
<module>microservicecloud-eureka-7003</module>
</modules>

(3)修改主启动类

microservicecloud-eureka-7002:

com.qcmoke.EurekaServer7002App.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.qcmoke;

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

/**
* EurekaServer7002App
*
* @author qcmoke
* @date 2019/09/02 18:39
*/
@SpringBootApplication
@EnableEurekaServer//EurekaServer服务器端启动类,接受其它微服务注册进来
public class EurekaServer7002App {
public static void main(String[] args) {
SpringApplication.run(EurekaServer7002App.class, args);
}

}

microservicecloud-eureka-7003:

com.qcmoke.EurekaServer7003App.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.qcmoke;

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

/**
* EurekaServer7002App
*
* @author qcmoke
* @date 2019/09/02 18:39
*/
@SpringBootApplication
@EnableEurekaServer//EurekaServer服务器端启动类,接受其它微服务注册进来
public class EurekaServer7003App {
public static void main(String[] args) {
SpringApplication.run(EurekaServer7003App.class, args);
}

}

(4)配置ip域名映射

由于是在本地测试,可以通过配置windows的C:\Windows\System32\drivers\etc\hosts文件来实现ip域名映射。添加如下配置到hosts文件中即可。

1
2
3
127.0.0.1  eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com

(5)3台eureka服务器的yml配置

microservicecloud-eureka-7001

1
2
3
4
5
6
7
8
9
10
11
server:
port: 7001

eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。

microservicecloud-eureka-7002

1
2
3
4
5
6
7
8
9
10
11
server:
port: 7002

eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。

microservicecloud-eureka-7003

1
2
3
4
5
6
7
8
9
10
11
server:
port: 7003

eureka:
instance:
hostname: eureka7003.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。

(6)配置microservicecloud-provider-dept-8001的yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
server:
port: 8001

mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.qcmoke.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件

spring:
application:
name: microservicecloud-dept #对外暴露的微服务的名称(重点)
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称
username: root
password: 123456
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间

eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: microservicecloud-dept8001
prefer-ip-address: true #访问路径可以显示IP地址


info:
app.name: microservicecloud
company.name: www.qcmoke.site
build.artifactId: ${project.artifactId}
build.version: ${project.version}

(7)依次启动microservicecloud-eureka-7001、microservicecloud-eureka-7002、microservicecloud-eureka-7003、microservicecloud-provider-dept-8001。然后访问http://eureka7001.com:7001/、http://eureka7002.com:7002/、http://eureka7003.com:7003/ 都能看到如下页面,说明集群成功。

1567481690839

4.7 Eureka对比Zookeepe

作为服务注册中心,Eureka比Zookeeper好在哪里?
著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)A(可用性)P(分区容错性)。由于分区容错性P在是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。在权衡选择中:

Zookeeper保证的是CP,
Eureka则是AP。

4.1 Zookeeper保证CP
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是Zookeeper会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间整个Zookeeper集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得Zookeeper集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。

4.2 Eureka保证AP
Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:

  1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
  2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
  3. 当网络稳定时,当前实例新的注册信息会被同步到其它节点中

因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。

5. Ribbon负载均衡

5.1 概念

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。

简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。

5.2 负载均衡

LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA。
常见的负载均衡有软件Nginx,LVS,硬件 F5等。
相应的在中间件,例如:dubbo和SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义。

负载均衡有应用情景:

(1)集中式LB

即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;

(2)进程内LB

将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。

Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

5.3 工作原理

Ribbon在工作时分成两步
第一步先选择 EurekaServer ,它优先选择在同一个区域内负载较少的server.
第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。
其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。

1567531615870

5.4 Ribbon配置

在做负载均衡的案例时,首先先来看看怎么配置Ribbon。

需求:Ribbon和Eureka整合后Consumer可以通过使用Ribbon调用微服务的名称的方式直接调用微服务而不用再关心地址和端口号。

(1)修改microservicecloud-consumer-dept-80工程的pom.xml

添加如下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 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>

全:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-consumer-dept-80</artifactId>


<description>部门微服务消费者</description>
<dependencies>
<dependency><!-- 自己定义的api -->
<groupId>com.qcmoke</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</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>

</dependencies>


</project>

(2)修改application.yml 追加eureka的服务注册地址

1
2
3
4
5
6
7
8
server:
port: 80

eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

(3)对com.qcmoke.config.ConfigBean.java添加注解@LoadBalanced 获得Rest时加入Ribbon的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.qcmoke.config;

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;

/**
* ConfigBean
*
* @author qcmoke
* @date 2019/09/02 04:32
*/
@Configuration
public class ConfigBean {
/**
* RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
*
* @author qcmoke
* @date 2019/9/2 4:41
*/
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}

(4)主启动类com.qcmoke.MicroServiceCloudConsumerDept80.java添加@EnableEurekaClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.qcmoke;

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

/**
* MicroservicecloudConsumerDept80
*
* @author qcmoke
* @date 2019/09/02 04:37
*/
@SpringBootApplication
@EnableEurekaClient
public class MicroServiceCloudConsumerDept80 {
public static void main(String[] args) {
SpringApplication.run(MicroServiceCloudConsumerDept80.class, args);
}

}

(5)修改com.qcmoke.controller.DeptConsumerController.java客户端访问类访问微服务的地址为微服务的名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.qcmoke.controller;

import com.qcmoke.entities.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
* DeptConsumerController
*
* @author qcmoke
* @date 2019/09/02 04:34
*/
@RestController
public class DeptConsumerController {
//private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT";

//测试@EnableDiscoveryClient,消费端可以调用服务发现
@RequestMapping(value = "/consumer/dept/discovery")
public Object discovery() {


return restTemplate.getForObject(REST_URL_PREFIX + "/dept/discovery", Object.class);
}

@Autowired
private RestTemplate restTemplate;

@RequestMapping(value = "/consumer/dept/add")
public boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}

@RequestMapping(value = "/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
}

@SuppressWarnings("unchecked")
@RequestMapping(value = "/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}

}

(6)先启动3个eureka集群后,再启动microservicecloud-provider-dept-8001,最后启动启动microservicecloud-consumer-dept-80。

(7)测试

访问:

http://localhost/consumer/dept/get/1

http://localhost/consumer/dept/list

http://localhost/consumer/dept/add?dname=大数据部

5.5 Ribbon负载均衡

需求:通过使用Ribbon负载均衡实现:浏览consumer时,consumer在自身应用里使用Ribbon实现轮询访问的负载均衡方式访问3个不同的provider。

(1)参考microservicecloud-provider-dept-8001,新建两份,分别命名为microservicecloud-provider-dept-8002,microservicecloud-provider-dept-8003,并且pom.xml都加入以下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<dependencies>
<dependency><!-- 引入自己定义的其他模块(microservicecloud-api) -->
<groupId>com.qcmoke</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</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>

<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</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>

(2)将8001的src/main的所有内容覆盖掉8002/8003的src/main,然后修改8002的启动类为DeptProvider8002App.java;将8003的启动类修改为DeptProvider8003App.java

(3)创建完成后在父工程的pom文件的<project>标签下添加如下代码。(一般IDE工具会自动添加)

1
2
3
4
5
6
7
8
9
10
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
<module>microservicecloud-eureka-7002</module>
<module>microservicecloud-eureka-7003</module>
<module>microservicecloud-provider-dept-8002</module>
<module>microservicecloud-provider-dept-8003</module>
</modules>

(4)新建8002/8003数据库,各自微服务分别连各自的数据库

8002的sql:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 
DROP DATABASE IF EXISTS cloudDB02;

CREATE DATABASE cloudDB02 CHARACTER SET UTF8;

USE cloudDB02;

CREATE TABLE dept
(
deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(60),
db_source VARCHAR(60)
);

INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('市场部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('运维部',DATABASE());

SELECT * FROM dept;

8003的sql:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
DROP DATABASE IF EXISTS cloudDB03;

CREATE DATABASE cloudDB03 CHARACTER SET UTF8;

USE cloudDB03;


CREATE TABLE dept
(
deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(60),
db_source VARCHAR(60)
);

INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('市场部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('运维部',DATABASE());

SELECT * FROM dept;

(5)修改8002/8003各自application.yml

修改服务端口和instance-id已经数据库名称即可

8002:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
server:
port: 8002

mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.qcmoke.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件

spring:
application:
name: microservicecloud-dept #对外暴露的微服务的名称(重点)
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB02 # 数据库名称
username: root
password: 123456
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间

eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: microservicecloud-dept8002
prefer-ip-address: true #访问路径可以显示IP地址


info:
app.name: microservicecloud
company.name: www.qcmoke.site
build.artifactId: ${project.artifactId}
build.version: ${project.version}

8003:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
server:
port: 8003

mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.qcmoke.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件

spring:
application:
name: microservicecloud-dept #对外暴露的微服务的名称(重点)
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB03 # 数据库名称
username: root
password: 123456
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间

eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: microservicecloud-dept8003
prefer-ip-address: true #访问路径可以显示IP地址


info:
app.name: microservicecloud
company.name: www.qcmoke.site
build.artifactId: ${project.artifactId}
build.version: ${project.version}

(6)测试

先启动3个eureka集群配置区,然后启动3个provider,最后启动consumer。

访问http://localhost/consumer/dept/list

浏览consumer页面时,consumer是通过轮询的方式访问3个provider的(可通过db_source字段来判断轮询情况),说明负载均衡实现。

ribbon

5.6 Ribbon负载均衡调度策略

Ribbon负载均衡调度策略关系到Ribbon的一个核心组件IRule。

该组件规定的调度策略规则有以下

1567565174256

(1)RoundRobinRule 轮询(默认)

(2)RandomRule 随机

(3)AvailabilityFilteringRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问

(4)WeightedResponseTimeRule 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高。刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够,
会切换到WeightedResponseTimeRule。

(5)RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务

(6)BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

(7)ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择服务器

在知道了以上调度策略规则后,那怎么来自定义地使用这些规则呢?

需求:基于前面轮询访问的规则改为RandomRule随机访问的规则。

只需要修改和配置microservicecloud-consumer-dept-80模块即可。

(1)在该模块下新建自定义org.qcmoke.rules.MyRuleConfig.java规则配置类

官方文档明确给出了警告:这个自定义规则配置类不能放在@ComponentScan(也就是启动类)所扫描的当前包下以及子包下,否则我们自定义的这个规则配置类就会被所有的Ribbon客户端所共享,也就是说我们达不到特殊化定制的目的了。

为了避免自定义的规则配置类会被所有的Ribbon客户端所共享,只要不在com.qcmoke这个包以及子包下即可。故我们可以定义在org.qcmoke.rules下即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.qcmoke.rules;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* MyRuleConfig
*
* @author qcmoke
* @date 2019/09/04 01:48
*/
@Configuration
public class MyRuleConfig {
@Bean
public IRule myRule() {
return new RandomRule();//Ribbon默认是轮询,我自定义为随机
}
}

(4)修改该模块下的主启动类

在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.qcmoke;

import org.qcmoke.rules.MyRuleConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

/**
* MicroservicecloudConsumerDept80
*
* @author qcmoke
* @date 2019/09/02 04:37
*/
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "MICROSERVICECLOUD-DEPT", configuration = MyRuleConfig.class)
public class MicroServiceCloudConsumerDept80 {
public static void main(String[] args) {
SpringApplication.run(MicroServiceCloudConsumerDept80.class, args);
}
}

(5)测试

先启动3个eureka集群配置区,然后启动3个provider,最后启动consumer。

访问 http://localhost/consumer/dept/list

ribbon2

发现访问的规则已经变成随机的了。

5.7 自定义调度策略规则

只需要继承AbstractLoadBalancerRule抽象类并实现choose(ILoadBalancer lb, Object key)方法就能实现自定义的调度策略。

需求:依旧轮询策略,但是加上新需求,每个服务器要求被调用5次。也即以前是每个服务调用一次后调用下一个,现在是每个服务调用5次后调用下一个。

(1)创建规则类org.qcmoke.rules.MyRule.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package org.qcmoke.rules;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;

/**
* 自定义调度策略
* 需求:
* 依旧轮询策略,但是加上新需求,每个服务器要求被调用5次。也即以前是每个服务调用一次后调用下一个,现在是每个服务调用5次后调用下一个
*
* @author qcmoke
* @date 2019/09/04 10:43
*/
public class MyRule extends AbstractLoadBalancerRule {
/**
* 总共被调用的次数,目前要求每个服务被调用5次
*/
private int total = 0;
/**
* /当前提供服务的序号
*/
private int currentIndex = 0;


/**
* 自定义调度策略
*
* @param lb 当前Ribbon到Eureka获取到的服务负载列表
* @param key
* @return 调度到的服务
*/
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;

while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();

int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}


// int index = rand.nextInt(serverCount);
// server = upList.get(index);
if (total < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex >= upList.size()) {
currentIndex = 0;
}

}


if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}

if (server.isAlive()) {
return (server);
}

// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}

return server;

}

@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}

@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {

}
}

(2)修改org.qcmoke.rules.MyRuleConfig.java的调度配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package org.qcmoke.rules;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;

/**
* 自定义调度策略
* 需求:
* 依旧轮询策略,但是加上新需求,每个服务器要求被调用5次。也即以前是每个服务调用一次后调用下一个,现在是每个服务调用5次后调用下一个
*
* @author qcmoke
* @date 2019/09/04 10:43
*/
public class MyRule extends AbstractLoadBalancerRule {
/**
* 总共被调用的次数,目前要求每个服务被调用5次
*/
private int total = 0;
/**
* /当前提供服务的序号
*/
private int currentIndex = 0;


/**
* 自定义调度策略
*
* @param lb 当前Ribbon到Eureka获取到的服务负载列表
* @param key
* @return 调度到的服务
*/
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;

while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();

int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}


// int index = rand.nextInt(serverCount);
// server = upList.get(index);
if (total < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex >= upList.size()) {
currentIndex = 0;
}

}


if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}

if (server.isAlive()) {
return (server);
}

// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}

return server;

}

@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}

@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {

}
}

(3)测试

先启动3个eureka集群配置区,然后启动3个provider,最后启动consumer。

访问 http://localhost/consumer/dept/list

ribbon3

6. Feign负载均衡

6.1 概念

官网解释:
http://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign

Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

Feign是一个声明式的Web服务客户端,使得编写Web服务客户端变得非常容易,只需要创建一个接口,然后在上面添加注解即可。

参考官网:https://github.com/OpenFeign/feign

Feign能干什么
Feign旨在使编写Java Http客户端变得更容易。
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。

Feign集成了Ribbon
利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用

简单的说:Feign是Ribbon的再封装。

6.2 使用步骤

(1)新建microservicecloud-consumer-dept-feign模块

(2)复制microservicecloud-consumer-dept-80模块的src内容到microservicecloud-consumer-dept-feign的src中

(3)该模块的pom.xml配置

复制microservicecloud-consumer-dept-80模块的依赖并额外添加Feign依赖

1
2
3
4
5
<!--Feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

注释掉Ribbon(因为Feign依赖中已经包含了Ribbon)

1
2
3
4
5
6
7
<!-- Ribbon相关 -->
<!--
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
-->

全:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-consumer-dept-feign</artifactId>

<description>部门微服务消费者</description>
<dependencies>
<dependency><!-- 自己定义的api -->
<groupId>com.qcmoke</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>

<!--Eureka Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- Ribbon相关 -->
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>-->
<!--Feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>




<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>

(4)父项目添加该子模块

1
2
3
4
5
6
7
8
9
10
11
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
<module>microservicecloud-eureka-7002</module>
<module>microservicecloud-eureka-7003</module>
<module>microservicecloud-provider-dept-8002</module>
<module>microservicecloud-provider-dept-8003</module>
<module>microservicecloud-consumer-dept-feign</module>
</modules>

(5)为了Feign能在多个客户端中都能通用,所以把Feign服务定义到microservicecloud-api模块中。

给microservicecloud-api工程的pom.xml添加如下依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

全:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<!-- 当前Module我自己叫什么名字 -->
<artifactId>microservicecloud-api</artifactId>

<dependencies><!-- 当前Module需要用到的jar包,按自己需求添加,如果父类已经包含了,可以不用写版本号 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>

</project>

(6)在microservicecloud-api模块中新建com.qcmoke.service.DeptClientService.java接口并新增注解@FeignClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.qcmoke.service;

import com.qcmoke.entities.Dept;
import org.springframework.cloud.netflix.feign.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;

/**
* DeptClientService
* 针对于MICROSERVICECLOUD-DEPT服务的Feign
*
* @author qcmoke
* @date 2019/09/04 13:49
*/
@FeignClient(value = "MICROSERVICECLOUD-DEPT")
public interface DeptClientService {
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") long id);

@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List<Dept> list();

@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean add(Dept dept);
}

(7)修改microservicecloud-consumer-dept-feign模块的com.qcmoke.controller.DeptConsumerController.java,调用上一步新建的DeptClientService接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.qcmoke.controller;

import com.qcmoke.entities.Dept;
import com.qcmoke.service.DeptClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* DeptConsumerController
*
* @author qcmoke
* @date 2019/09/02 04:34
*/
@RestController
public class DeptConsumerController {
@Autowired
private DeptClientService service;

@RequestMapping(value = "/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return this.service.get(id);
}

@RequestMapping(value = "/consumer/dept/list")
public List<Dept> list() {
return this.service.list();
}

@RequestMapping(value = "/consumer/dept/add")
public Object add(Dept dept) {
return this.service.add(dept);
}

}

(8)给microservicecloud-consumer-dept-feign模块的主启动类添加@EnableFeignClients

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.qcmoke;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

/**
* MicroservicecloudConsumerDept80
*
* @author qcmoke
* @date 2019/09/02 04:37
*/
@SpringBootApplication
@EnableEurekaClient
//@RibbonClient(name = "MICROSERVICECLOUD-DEPT", configuration = MyRuleConfig.class)
@EnableFeignClients(basePackages = {"com.qcmoke"})
public class MicroServiceCloudConsumerDept80 {
public static void main(String[] args) {
SpringApplication.run(MicroServiceCloudConsumerDept80.class, args);
}
}

删除com.qcmoke.config.ConfigBean.java和org.qcmoke.rules包

(9)测试

先启动3个eureka集群,然后启动3个部门微服务8001/8002/8003,最后启动Feign。

访问http://localhost/consumer/dept/list

发现实现了Ribbon+RestTemplate的效果。只是Feign省去了RestTemplate调用的复杂过程,让com.qcmoke.controller.DeptConsumerController(Ribbon)也实现了面向接口编程。

7.Hystrix断路器

7.1 分布式系统面临的问题

复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。

1567593776002

服务雪崩

在微服务的架构中,服务间通常会形成相互依赖的关系,比如现在有三个微服务节点:A,B和C,B为A的消费者,C为B的消费者,就是所谓的“扇出”。假如由于网络波动或者A服务自身故障,导致B调用A服务的线程被挂起进入长时间的等待。在高并发的情况下可能导致B的资源被耗竭随之崩溃,从而导致C服务也不可用,进而引起系统崩溃,所谓的“雪崩效应”。这种连环式的雪崩效应在微服务中较为常见,为了解决这个问题,服务熔断技术应运而出。熔断一词来自电路学,指的是电路在出现短路状况时,“断路器”能够及时地切断故障电路,避免电路过载发热引发火灾。

类似的,微服务架构中的断路器能够及时地发现故障服务,并向服务调用方返回错误响应,而不是长时间的等待。Spring Cloud Hystrix在Hystrix(又是一款由Netflix开发的开源软件,Github地址https://github.com/Netflix/Hystrix)的基础上进行了封装,提供了服务熔断,服务降级,线程隔离,限流模式等功能,通过这些功能可以提供服务的容错率。

备注:一般情况对于服务依赖的保护主要有4种解决方案:

(1)熔断模式: 这种模式主要是参考电路熔断,如果一条线路电压过高,保险丝会熔断,防止火灾。放到我们的系统中,如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。

(2)隔离模式: 这种模式就像对系统请求按类型划分成一个个小岛的一样,当某个小岛被火少光了,不会影响到其他的小岛。例如可以对不同类型的请求使用线程池来资源隔离,每种类型的请求互不影响,如果一种类型的请求线程资源耗尽,则对后续的该类型请求直接返回,不再调用后续资源。这种模式使用场景非常多,例如将一个服务拆开,对于重要的服务使用单独服务器来部署,再或者公司最近推广的多中心。

(3)限流模式: 上述的熔断模式和隔离模式都属于出错后的容错处理机制,而限流模式则可以称为预防模式。限流模式主要是提前对各个类型的请求设置最高的QPS阈值,若高于设置的阈值则对该请求直接返回,不再调用后续资源。这种模式不能解决服务依赖的问题,只能解决系统整体资源分配问题,因为没有被限流的请求依然有可能造成雪崩效应。

(4)服务降级: 其实就是线程池中单个线程障处理,防止单个线程请求时间太长,导致资源长期被占有而得不到释放,从而导致线程池被快速占用完,导致服务崩溃。
Hystrix能解决如下问题:

1.请求超时降级,线程资源不足降级,降级之后可以返回自定义数据
2.线程池隔离降级,分布式服务可以针对不同的服务使用不同的线程池,从而互不影响
3.自动触发降级与恢复
4.实现请求缓存和请求合并

7.2 服务熔断

熔断机制是应对雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回"错误"的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。

(1)参考microservicecloud-provider-dept-8001,新建microservicecloud-provider-dept-hystrix-8001模块

创建完成后在父工程的pom文件的<project>标签下添加如下代码。(一般IDE工具会自动添加)

1
2
3
4
5
6
7
8
9
10
11
12
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
<module>microservicecloud-eureka-7002</module>
<module>microservicecloud-eureka-7003</module>
<module>microservicecloud-provider-dept-8002</module>
<module>microservicecloud-provider-dept-8003</module>
<module>microservicecloud-consumer-dept-feign</module>
<module>microservicecloud-provider-dept-hystrix-8001</module>
</modules>

(2)复制microservicecloud-provider-dept-8001 src里的内容到microservicecloud-provider-dept-hystrix-8001的src里。

(3)microservicecloud-provider-dept-hystrix-8001的pom.xml

新增依赖

1
2
3
4
5
 <!--  hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

全:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-provider-dept-hystrix-8001</artifactId>

<dependencies>

<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>


<dependency><!-- 引入自己定义的其他模块(microservicecloud-api) -->
<groupId>com.qcmoke</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</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>

<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</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>


</project>

(4)修改application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
server:
port: 8001

mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.qcmoke.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件

spring:
application:
name: microservicecloud-dept #对外暴露的微服务的名称(重点)
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称
username: root
password: 123456
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间

eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: microservicecloud-dept8001-hystrix #自定义服务名称信息
prefer-ip-address: true #访问路径可以显示IP地址


info:
app.name: microservicecloud
company.name: www.qcmoke.site
build.artifactId: ${project.artifactId}
build.version: ${project.version}

(5)修改主启动类为DeptProviderHystrix8001App.java,并添加@EnableCircuitBreaker注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.qcmoke;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
* microservicecloud-provider-dept-8001模块的启动类
*
* @author qcmoke
* @date 2019/09/02 03:34
*/
@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker//对hystrixR熔断机制的支持
public class DeptProviderHystrix8001App {

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

(6)修改DeptController.java

一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.qcmoke.controller;


import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.qcmoke.entities.Dept;
import com.qcmoke.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;


/**
* DeptController
*
* @author qcmoke
* @date 2019/09/02 03:32
*/
@RestController
@Slf4j
public class DeptController {
@Autowired
private DeptService service;

@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "fallbackForGetDept")
public Dept get(@PathVariable("id") Long id) {
Dept dept = service.get(id);
if (null == dept) {
throw new RuntimeException("该ID:" + id + "没有没有对应的信息");
}
return dept;
}


/**
* 服务熔断处理
* @param id id
* @return Dept
*/
public Dept fallbackForGetDept(@PathVariable("id") Long id) {
Dept dept = new Dept().setDeptno(-1L).setDname("null").setDb_source("null");
log.error("fallbackForGetDept = {}", dept);
return dept;
}

}

(7)测试
先启动3个eureka先启动,然后启动microservicecloud-provider-dept-hystrix-8001,最后启动microservicecloud-consumer-dept-80。

访问http://localhost/consumer/dept/get/112 ,由于数据库不存在112这个部门,所以会触发服务熔断处理。

7.3 服务降级

整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来。服务降级处理是在客户端实现完成的,与服务端没有关系。

(1)修改microservicecloud-api工程,根据已经有的DeptClientService接口新建一个实现了FallbackFactory接口的类com.qcmoke.service.DeptClientServiceFallbackFactory。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.qcmoke.service;

import com.qcmoke.entities.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* DeptClientServiceFallbackFactory
*千万不要忘记在类上面新增@Component注解
* @author qcmoke
* @date 2019/09/08 01:19
*/
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept get(long id) {
return new Dept().setDeptno(id)
.setDname("该ID:" + id + "没有没有对应的信息,Consumer客户端提供的降级信息,此刻服务Provider已经关闭")
.setDb_source("no this database in MySQL");
}

@Override
public List<Dept> list() {
return null;
}

@Override
public boolean add(Dept dept) {
return false;
}
};
}

}

(2)修改microservicecloud-api工程,DeptClientService接口在注解@FeignClient()中添加fallbackFactory属性值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.qcmoke.service;

import com.qcmoke.entities.Dept;
import org.springframework.cloud.netflix.feign.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;

/**
* DeptClientService
* 针对于MICROSERVICECLOUD-DEPT服务的Feign
*
* @author qcmoke
* @date 2019/09/04 13:49
*/
@FeignClient(value = "MICROSERVICECLOUD-DEPT", fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") long id);

@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List<Dept> list();

@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean add(Dept dept);
}

(3)修改microservicecloud-consumer-dept-feign工程的yml,添加如下配置

1
2
3
feign: 
hystrix:
enabled: true

全:

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 80

feign:
hystrix:
enabled: true

eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

(4)测试
先启动3个eureka先启动,
然后启动microservicecloud-provider-dept-8001,
再启动microservicecloud-consumer-dept-feign。
先尝试访问http://localhost/consumer/dept/get/1
然后故意关闭微服务microservicecloud-provider-dept-8001,
然后再次访问http://localhost/consumer/dept/get/1
此时服务端provider已经down了,但是我们做了服务降级处理,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器。

7.4 服务监控hystrixDashboard

除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。

(1)新建工程microservicecloud-consumer-hystrix-dashboard

创建完成后在父工程的pom文件的<project>标签下添加如下代码。(一般IDE工具会自动添加)

1
2
3
4
5
6
7
8
9
10
11
12
13
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
<module>microservicecloud-eureka-7002</module>
<module>microservicecloud-eureka-7003</module>
<module>microservicecloud-provider-dept-8002</module>
<module>microservicecloud-provider-dept-8003</module>
<module>microservicecloud-consumer-dept-feign</module>
<module>microservicecloud-provider-dept-hystrix-8001</module>
<module>microservicecloud-consumer-hystrix-dashboard</module>
</modules>

(2)POM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-consumer-hystrix-dashboard</artifactId>

<dependencies>
<!-- hystrix和 hystrix-dashboard相关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</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>

(3)YML

1
2
server:
port: 9001

(4)主启动类改名为com.qcmoke.MicroServiceCloudDashboard.java+新注解@EnableHystrixDashboard

(5)所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置,添加这个依赖后所有的HystrixCommand 就都会被监控了。

1
2
3
4
5
<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

(6)启动microservicecloud-consumer-hystrix-dashboard该微服务监控消费端

(7)监控测试

(7.1)先启动3个eureka集群,然后启动microservicecloud-provider-dept-hystrix-8001,最后启动microservicecloud-consumer-hystrix-dashboard。

(7.2)先访问监控窗口http://localhost:9001/hystrix,然后填写监控地址

1567946756312

1:Delay:该参数用来控制服务器上轮询监控信息的延迟时间,默认为2000毫秒,可以通过配置该属性来降低客户端的网络和CPU消耗。

2:Title:该参数对应了头部标题Hystrix Stream之后的内容,默认会使用具体监控实例的URL,可以通过配置该信息来展示更合适的标题

(7.3)再打开另一个浏览器便签访问并多次刷新http://localhost:8001/dept/get/1

(7.4)然后观察监控窗口

如何看

7色1圆1曲

  • 7色

20181029140925793

从左到右:请求的成功,熔断数,错误的请求,超时的请求,线程池拒绝数,失败的请求和最近10秒内错误的比率

通过上面颜色的含义来表示下图的数字图标:

20181029140728892

  • 1圆

实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。
该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实例中快速的发现故障实例和高压力实例。

  • 1曲

曲线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。

1567946420308

1567946442001

8.zuul路由网关

8.1 概念

其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础.Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。

Zuul提供: 代理+路由+过滤三大功能

注意:Zuul服务最终还是会注册进Eureka

8.2 路由基本配置

(1)新建Module模块microservicecloud-zuul-gateway-9527

创建完成后在父工程的pom文件的<project>标签下添加如下代码。(一般IDE工具会自动添加)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
<module>microservicecloud-eureka-7002</module>
<module>microservicecloud-eureka-7003</module>
<module>microservicecloud-provider-dept-8002</module>
<module>microservicecloud-provider-dept-8003</module>
<module>microservicecloud-consumer-dept-feign</module>
<module>microservicecloud-provider-dept-hystrix-8001</module>
<module>microservicecloud-consumer-hystrix-dashboard</module>
<module>microservicecloud-zuul-gateway-9527</module>
</modules>

(2)pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-zuul-gateway-9527</artifactId>
<dependencies>
<!-- zuul路由网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- actuator监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- hystrix容错-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- 日常标配 -->
<dependency>
<groupId>com.qcmoke</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</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>

</project>

(3)主启动类com.qcmoke.ZuulApplicationApp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.qcmoke;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

/**
* ZuulApplicationApp
*
* @author qcmoke
* @date 2019/09/08 21:27
*/
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplicationApp {
public static void main(String[] args) {
SpringApplication.run(ZuulApplicationApp.class, args);
}
}

(4)修改C:\Windows\System32\drivers\etc\hosts,添加以下映射

1
127.0.0.1  myzuul.com

(5)application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server:
port: 9527

spring:
application:
name: microservicecloud-zuul-gateway

eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: gateway-9527.com
prefer-ip-address: true


info:
app.name: microservicecloud
company.name: www.qcmoke.site
build.artifactId: ${project.artifactId}
build.version: ${project.version}

(6)先启动三个eureka集群,然后启动microservicecloud-provider-dept-8001,最后启动microservicecloud-zuul-gateway-9527。

通过路由网关访问的服务接口地址为:http://路由网关ip:路由网关port/访问的服务名/具体的controller接口

访问http://myzuul.com:9527/microservicecloud-dept/dept/get/2

8.3 路由访问映射规则

需求:忽略原真实服务名,修改为其他名称以不向外透露服务详细情况。

(1)修改yml,添加如下路由网关配置

1
2
3
4
5
6
7
zuul:
#prefix: /qcmoke #设置统一公共前缀,那么访问地址为:http://myzuul.com:9527/qcmoke/mydept/dept/get/1
#ignored-services: microservicecloud-dept #原真实服务名忽略
ignored-services: "*" #忽略所有的服务名(如果有多个)
routes:
mydept.serviceId: microservicecloud-dept
mydept.path: /mydept/**

全:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
server:
port: 9527

spring:
application:
name: microservicecloud-zuul-gateway

eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: gateway-9527.com
prefer-ip-address: true


zuul:
#prefix: /qcmoke #设置统一公共前缀,那么访问地址为:http://myzuul.com:9527/qcmoke/mydept/dept/get/1
#ignored-services: microservicecloud-dept #原真实服务名忽略
ignored-services: "*" #忽略所有的服务名(如果有多个)
routes:
mydept.serviceId: microservicecloud-dept
mydept.path: /mydept/**

info:
app.name: microservicecloud
company.name: www.qcmoke.site
build.artifactId: ${project.artifactId}
build.version: ${project.version}

(2)先启动三个eureka集群,然后启动microservicecloud-provider-dept-8001,最后启动microservicecloud-zuul-gateway-9527。

访问http://myzuul.com:9527/microservicecloud-dept/dept/get/2(并网关屏蔽拦截)

访问http://myzuul.com:9527/mydept/dept/get/1(访问成功)

9.SpringCloud Config分布式配置中心

9.1 SpringCloud Config服务端配置

(1)在git远程仓库上新建一个名为microservicecloud-config
的新Repository。

(2)再本地创建一个仓库,名称为microservicecloud-config,然后进入到仓库里进行初始化。

1
$ git init

(3)再仓库里创建一个文件application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
profiles:
active:
- dev
---
spring:
profiles: dev #开发环境
application:
name: microservicecloud-config-qcmoke-dev
---
spring:
profiles: test #测试环境
application:
name: microservicecloud-config-qcmoke-test
# 请保存为UTF-8格式

保存格式必须为UTF-8

(4)提交application.yml到git远程仓库

1
2
3
4
$ git add .
$ git commit -m "init"
$ git remote add origin https://github.com/qcmoke/springcloud-config.git
$ git push -u origin master

(5)新建microservicecloud-config-3344模块
它即为Cloud的配置中心模块

创建完成后在父工程的pom文件的<project>标签下添加如下代码。(一般IDE工具会自动添加)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
<module>microservicecloud-eureka-7002</module>
<module>microservicecloud-eureka-7003</module>
<module>microservicecloud-provider-dept-8002</module>
<module>microservicecloud-provider-dept-8003</module>
<module>microservicecloud-consumer-dept-feign</module>
<module>microservicecloud-provider-dept-hystrix-8001</module>
<module>microservicecloud-consumer-hystrix-dashboard</module>
<module>microservicecloud-zuul-gateway-9527</module>
<module>microservicecloud-config-3344</module>
</modules>

(6)pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-config-3344</artifactId>

<dependencies>
<!-- springCloud Config -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- 避免Config的Git插件报错:org/eclipse/jgit/api/TransportConfigCallback -->
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>4.10.0.201712302008-r</version>
</dependency>

<!-- 热部署插件 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>

</project>

(7)bootstrap.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 3344

spring:
application:
name: microservicecloud-config
cloud:
config:
server:
git:
uri: https://github.com/qcmoke/springcloud-config.git #git远程仓库上面的git仓库名字
# search-paths: pathName
# username: qcmoke #账号密码写真实的快一些我觉得,不使用也能访问有点慢
# password: ******

(8)主启动类com.qcmoke.ConfigApplication3344.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.qcmoke;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

/**
* ConfigApplication3344
*
* @author qcmoke
* @date 2019/09/08 22:25
*/
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication3344.class, args);
}
}

(9)修改C:\Windows\System32\drivers\etc\hosts,添加以下映射

1
127.0.0.1  config-3344.com

(10)测试通过Config微服务是否可以从git远程仓库上获取配置内容

启动微服务microservicecloud-config-3344

访问 http://config-3344.com:3344/application-dev.yml

1567955954426

访问 http://config-3344.com:3344/application-test.yml

1567955988306

访问 http://config-3344.com:3344/application-xxx.yml (不存在的配置)

1567956026833

9.2 配置读取规则

/{application}-{profile}.yml

http://config-3344.com:3344/application-dev.yml
http://config-3344.com:3344/application-test.yml
http://config-3344.com:3344/application-xxx.yml(不存在的配置)

/{application}/{profile}[/{label}]

http://config-3344.com:3344/application/dev/master
http://config-3344.com:3344/application/test/master
http://config-3344.com:3344/application/xxx/master

/{label}/{application}-{profile}.yml

http://config-3344.com:3344/master/application-dev.yml
http://config-3344.com:3344/master/application-test.yml

9.3 SpringCloud Config客户端配置与测试

(1)再本地仓库新建microservicecloud-config-client.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
spring:
profiles:
active:
- dev
---
server:
port: 8201
spring:
profiles: dev
application:
name: microservicecloud-config-client
eureka:
client:
service-url:
defaultZone: http://eureka-dev.com:7001/eureka/
---
server:
port: 8202
spring:
profiles: test
application:
name: microservicecloud-config-client
eureka:
client:
service-url:
defaultZone: http://eureka-test.com:7001/eureka/

(2)提交到远程库

1
2
3
$ git add .
$ git commit -m "modify"
$ git push origin master

(3)新建microservicecloud-config-client-3355

创建完成后在父工程的pom文件的<project>标签下添加如下代码。(一般IDE工具会自动添加)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
<module>microservicecloud-eureka-7002</module>
<module>microservicecloud-eureka-7003</module>
<module>microservicecloud-provider-dept-8002</module>
<module>microservicecloud-provider-dept-8003</module>
<module>microservicecloud-consumer-dept-feign</module>
<module>microservicecloud-provider-dept-hystrix-8001</module>
<module>microservicecloud-consumer-hystrix-dashboard</module>
<module>microservicecloud-zuul-gateway-9527</module>
<module>microservicecloud-config-3344</module>
<module>microservicecloud-config-client-3355</module>
</modules>

(4)POM

在需要请求config服务的模块里加如下依赖即可

1
2
3
4
5
<!-- SpringCloud Config客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

全:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-config-client-3355</artifactId>


<dependencies>
<!-- SpringCloud Config客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</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-hystrix</artifactId>
</dependency>
<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>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>
</project>

(5)再模块里创建bootstrap.yml

1
2
3
4
5
6
7
spring:
cloud:
config:
name: microservicecloud-config-client #需要从github上读取的资源名称,注意没有yml后缀名
profile: dev #本次访问的配置项
label: master
uri: http://config-3344.com:3344 #本微服务启动后先去找3344号服务,通过SpringCloudConfig获取GitHub的服务地址

applicaiton.yml是用户级的资源配置项
bootstrap.yml是系统级的,优先级更加高

Spring Cloud会创建一个Bootstrap Context,作为Spring应用的Application Context的父上下文。初始化的时候,Bootstrap Context负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的EnvironmentBootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖。 Bootstrap contextApplication Context有着不同的约定,
所以新增了一个bootstrap.yml文件,保证Bootstrap ContextApplication Context配置的分离。

(6)windows下修改hosts文件,增加映射

1
127.0.0.1  client-config.com

(9)创建启动类com.qcmoke.ConfigClientApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.qcmoke;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* ConfigClientApplication
*
* @author qcmoke
* @date 2019/09/09 02:26
*/

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

}

(8)新建测试com.qcmoke.controller.ConfigClientRestController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.qcmoke;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* ConfigClientRestController
*
* @author qcmoke
* @date 2019/09/09 02:22
*/
@RestController
public class ConfigClientRestController {
@Value("${spring.application.name}")
private String applicationName;

@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServers;

@Value("${server.port}")
private String port;

@RequestMapping("/config")
public String getConfig() {
String str = "applicationName: " + applicationName + "\t eurekaServers:" + eurekaServers + "\t port: " + port;
System.out.println("******str: " + str);
return "applicationName: " + applicationName + "\t eurekaServers:" + eurekaServers + "\t port: " + port;
}

}

(9)测试

先启动Config配置中心3344 config服务,然后再启动3355作为Client准备访问

决定从github上读取什,决定于bootstrap.yml里面的profile值是什么。

假如目前是 profile: dev,dev默认在github上对应的端口就是8201,那就访问http://client-config.com:8201/config

假如目前是 profile: test,test默认在github上对应的端口就是8202,那就访问http://client-config.com:8202/config

9.4 SpringCloud Config配置实战

(1)本地仓库新建文件microservicecloud-config-eureka-client.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
spring: 
profiles:
active:
- dev
---
server:
port: 7001 #注册中心占用7001端口,冒号后面必须要有空格

spring:
profiles: dev
application:
name: microservicecloud-config-eureka-client

eureka:
instance:
hostname: eureka7001.com #冒号后面必须要有空格
client:
register-with-eureka: false #当前的eureka-server自己不注册进服务列表中
fetch-registry: false #不通过eureka获取注册信息
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
---
server:
port: 7001 #注册中心占用7001端口,冒号后面必须要有空格

spring:
profiles: test
application:
name: microservicecloud-config-eureka-client

eureka:
instance:
hostname: eureka7001.com #冒号后面必须要有空格
client:
register-with-eureka: false #当前的eureka-server自己不注册进服务列表中
fetch-registry: false #不通过eureka获取注册信息
service-url:
defaultZone: http://eureka7001.com:7001/eureka/

(2)再本地仓库新建文件microservicecloud-config-dept-client.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
spring: 
profiles:
active:
- dev
---
server:
port: 8001
spring:
profiles: dev
application:
name: microservicecloud-config-dept-client
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/cloudDB01
username: root
password: 123456
dbcp2:
min-idle: 5
initial-size: 5
max-total: 5
max-wait-millis: 200
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.qcmoke.entities
mapper-locations:
- classpath:mybatis/mapper/**/*.xml

eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://eureka7001.com:7001/eureka
instance:
instance-id: dept-8001.com
prefer-ip-address: true

info:
app.name: microservicecloud
company.name: www.qcmoke.site
build.artifactId: ${project.artifactId}
build.version: ${project.version}
---
server:
port: 8001
spring:
profiles: test
application:
name: microservicecloud-config-dept-client
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/cloudDB02
username: root
password: 123456
dbcp2:
min-idle: 5
initial-size: 5
max-total: 5
max-wait-millis: 200


mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.qcmoke.entities
mapper-locations:
- classpath:mybatis/mapper/**/*.xml

eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://eureka7001.com:7001/eureka
instance:
instance-id: dept-8001.com
prefer-ip-address: true

info:
app.name: microservicecloud
company.name: www.qcmoke.site
build.artifactId: ${project.artifactId}
build.version: ${project.version}

(3)提交到远程库

1
2
3
$ git add .
$ git commit -m "modify"
$ git push origin master

(4)新建工程microservicecloud-config-eureka-client-7001,配置Config版的eureka服务端

创建完成后在父工程的pom文件的<project>标签下添加如下代码。(一般IDE工具会自动添加)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<modules>
<module>microservicecloud-api</module>
<module>microservicecloud-provider-dept-8001</module>
<module>microservicecloud-consumer-dept-80</module>
<module>microservicecloud-eureka-7001</module>
<module>microservicecloud-eureka-7002</module>
<module>microservicecloud-eureka-7003</module>
<module>microservicecloud-provider-dept-8002</module>
<module>microservicecloud-provider-dept-8003</module>
<module>microservicecloud-consumer-dept-feign</module>
<module>microservicecloud-provider-dept-hystrix-8001</module>
<module>microservicecloud-consumer-hystrix-dashboard</module>
<module>microservicecloud-zuul-gateway-9527</module>
<module>microservicecloud-config-3344</module>
<module>microservicecloud-config-client-3355</module>
<module>microservicecloud-config-eureka-client-7001</module>
</modules>

microservicecloud-config-eureka-client-7001的pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-config-eureka-client-7001</artifactId>

<dependencies>
<!-- SpringCloudConfig配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<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>

bootstrap.yml

1
2
3
4
5
6
7
spring:
cloud:
config:
name: microservicecloud-config-eureka-client #需要从github上读取的资源名称,注意没有yml后缀名
profile: dev
label: master
uri: http://config-3344.com:3344 #SpringCloudConfig获取的服务地址

application.yml

1
2
3
spring:
application:
name: microservicecloud-config-eureka-client

启动类com.qcmoke.ConfigGitEurekaServerApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.qcmoke;

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

/**
* ConfigGitEurekaServerApplication
*
* @author qcmoke
* @date 2019/09/10 02:50
*/
@SpringBootApplication
@EnableEurekaServer
public class ConfigGitEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigGitEurekaServerApplication.class, args);
}
}

(5)参考之前的8001拷贝后新建工程microservicecloud-config-dept-client-8001

把microservicecloud-provider-dept-8001的src下的文件和目录copy到microservicecloud-config-dept-client-8001的src下

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?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>microservicecloud</artifactId>
<groupId>com.qcmoke</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>microservicecloud-config-dept-client-8001</artifactId>


<dependencies>
<!-- SpringCloudConfig配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</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-eureka</artifactId>
</dependency>
<dependency>
<groupId>com.qcmoke</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</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>
</project>

bootstrap.yml

1
2
3
4
5
6
7
8
9
spring:
cloud:
config:
name: microservicecloud-config-dept-client #需要从github上读取的资源名称,注意没有yml后缀名
#profile配置是什么就取什么配置dev or test
#profile: dev
profile: test
label: master
uri: http://config-3344.com:3344 #SpringCloudConfig获取的服务地址

application.yml

1
2
3
#spring:
# application:
# name: microservicecloud-config-dept-client

主启动类com.qcmoke.ConfigDeptProvider8001App.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.qcmoke;

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

/**
* ConfigDeptProvider8001App
*
* @author qcmoke
* @date 2019/09/10 02:59
*/
@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
@EnableDiscoveryClient //服务发现
public class ConfigDeptProvider8001App {
public static void main(String[] args) {
SpringApplication.run(ConfigDeptProvider8001App.class, args);
}
}

测试:

先启动microservicecloud-config-3344微服务,保证Config总配置是OK的,再启动microservicecloud-config-eureka-client-7001微服务,最后启动microservicecloud-config-dept-client-8001

先访问http://eureka7001.com:7001/

test配置默认访问,http://localhost:8001/dept/list

本地换配置成dev,http://localhost:8001/dept/list



----------- 本文结束 -----------




如果你觉得我的文章对你有帮助,你可以打赏我哦~
0%