SpringCloud-Eureka-1

尚硅谷周阳构建分布式微服务架构(Eureka篇)

B 站视频:https://www.bilibili.com/video/BV18E411x7eT?from=search&seid=13583080742381804215

原 Blog 笔记文档:https://blog.csdn.net/qq_41211642/article/details/104772140
本篇文章大部分图片用到了参考的 blog 笔记中的图片。

本人代码下载:下载
本人代码 Gitee 仓库地址:https://gitee.com/Sevattal/springcloud_project_eureka

技术 版本
cloud Hoxton.SR1
boot 2.2.2.RELEASE
cloud alibaba 2.1.0.RELEASE
java java8
Maven 3.5及以上
Mysql 5.7及以上

整体工程项目架构

springcloud2021 父项目
    cloud-api-commons 通用类项目
    cloud-provider-payment8001 模拟支付接收接口项目
    cloud-provider-payment8002 模拟支付接收接口项目(复制的cloud-provider-payment8001)
    cloud-consumer-order80 模拟客户端调用后端接口项目
    cloud-eureka-server7001 eureka注册中心项目
    cloud-eureka-server7002 eureka注册中心项目 (复制的cloud-eureka-server7001)

Eureka url命令的补充知识点

Operation HTTP action Description
Register new application instance POST /eureka/v2/apps/appID Input: JSON/XML payload HTTP Code: 204 on success
De-register application instance DELETE /eureka/v2/apps/appID/instanceID HTTP Code: 200 on success
Send application instance heartbeat PUT /eureka/v2/apps/appID/instanceID HTTP Code:* 200 on success* 404 if instanceID doesn’t exist
Query for all instances GET /eureka/v2/apps HTTP Code: 200 on success Output: JSON/XML
Query for all appID instances GET /eureka/v2/apps/appID HTTP Code: 200 on success Output: JSON/XML
Query for a specific appID/instanceID GET /eureka/v2/apps/appID/instanceID HTTP Code: 200 on success Output: JSON/XML
Query for a specific instanceID GET /eureka/v2/instances/instanceID HTTP Code: 200 on success Output: JSON/XML
Take instance out of service PUT /eureka/v2/apps/appID/instanceID/status?value=OUT_OF_SERVICE HTTP Code:* 200 on success* 500 on failure
Move instance back into service (remove override) DELETE /eureka/v2/apps/appID/instanceID/status?value=UP (The value=UP is optional, it is used as a suggestion for the fallback status due to removal of the override) HTTP Code:* 200 on success* 500 on failure
Update metadata PUT /eureka/v2/apps/appID/instanceID/metadata?key=value HTTP Code:* 200 on success* 500 on failure
Query for all instances under a particular vip address GET /eureka/v2/vips/vipAddress * HTTP Code: 200 on success Output: JSON/XML * 404 if the vipAddress does not exist.
Query for all instances under a particular secure vip address GET /eureka/v2/svips/svipAddress * HTTP Code: 200 on success Output: JSON/XML * 404 if the svipAddress does not exist.

1. 新建Maven父工程 springcloud2021

1.1 maven架构不需要选择

1.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
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
<?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.sevattal.springcloud</groupId>
<artifactId>springcloud2021</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!--子模块继承之后,提供作用:锁定版本+ 子modlue不用写groupId 和 version-->
<!--dependencyManagement 里只声明依赖,并不实现引入,因此子项目需要显示声明需要的依赖-->
<!--统一管理jar包版本-->
<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>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
<spring.boot.devtools.version>2.3.10.RELEASE</spring.boot.devtools.version>
</properties>
<!--子模块继承之后,提供作用:锁定版本+子module不用groupId和version-->
<dependencyManagement>
<dependencies>
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>${spring.boot.devtools.version}</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
</project>

2. 建立支付 module: cloud-provider-payment8001

2.1 在 springcloud2021 主项目下建立 cloud-provider-payment8001 模块(maven)

2.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
<?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>springcloud2021</artifactId>
<groupId>com.sevattal.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8001</artifactId>
<dependencies>
<!--引入自己定义的api通用包,-->
<dependency>
<groupId>com.sevattal.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

2.3 application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 8001
spring:
application:
name: cloud-provider-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver #mysql驱动包
url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding-utr-8&useSSL=false
username: sevattal
password: Lovemomo
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities #所有Entity别名类所在包

2.4 主启动类PaymentMain8001

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.sevattal.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
<!--
PaymentMain8001 在多个微服务的情况下建议在开发的过程中
将SpringBoot的启动类,名称要有对应的含义以及对应的端口号
-->
@SpringBootApplication
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args);
}
}

2.5 数据库

1
2
3
4
5
CREATE TABLE `payment`(
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`serial` varchar(200) DEFAULT '',
PRIMARY KEY (`id`)
) ENGING=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

2.6 业务类

1
2
3
4
5
6
7
8
9
10
11
12
13
// 主实体类 Payment
package com.sevattal.springcloud.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
private Long id;
private String serial;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Json封装体CommentResult,传给前端,判断编码是否成功,成功才显示
package com.sevattal.springcloud.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> { //泛型:如果装的payment 返回payment,装的order 返回order
private Integer code;
private String message;
private T data;
public CommonResult(Integer code,String message){
this(code,message,null);
}
}
1
2
3
4
5
6
7
8
9
10
//PaymentDao
package com.atguigu.springcloud.dao;
import com.atguigu.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface PaymentDao {
int create(Payment payment);
Payment getPaymentById(@Param("id") Long id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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.sevattal.springcloud.dao.PaymentDao">
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(#{serial})
</insert>
<resultMap id="BaseResultMap" type="com.sevattal.springcloud.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id};
</select>
</mapper>
1
2
3
4
5
6
7
8
//PaymentService
package com.sevattal.springcloud.service;
import com.sevattal.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Param;
public interface PaymentService {
public int create(Payment payment);
public Payment getPaymentById(@Param("id") Long id);
}
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
//PaymentServiceImpl
package com.sevattal.springcloud.service;
import com.sevattal.springcloud.dao.PaymentDao;
import com.sevattal.springcloud.entities.Payment;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PaymentServiceImpl implements PaymentService{
/*
* @resource是基于j2ee的注解,默认是按名字进行注解,若不指定装配bean的名字,
* 当注解写在字段上时,默认取字段名,按照名称查找通过set方法进行装配,
* 倘若有多个子类,则会报错。若想不报错,只需加(required=false)
*
* @autowired是基于spring的注解org.springframework.beans.factory.annotation.Autowired,
* 它默认是按类型进行的装配的,如果想要它按名字进行装配只需在@autowired
* 下面加@qualifier("name")`注解
*
* */
@Resource
private PaymentDao paymentDao;
public int create(Payment payment){
return paymentDao.create(payment);
}
public Payment getPaymentById(Long id){
return paymentDao.getPaymentById(id);
}
}
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
//PaymentController
package com.sevattal.springcloud.controller;
import com.sevattal.springcloud.entities.CommonResult;
import com.sevattal.springcloud.entities.Payment;
import com.sevattal.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
//只传给前端CommonResult,不需要前端了解其他的组件
@PostMapping(value = "/payment/create")
public CommonResult create(Payment payment){
int result = paymentService.create(payment);
log.info("*****插入结果:"+result);
if(result > 0){
return new CommonResult(200,"插入数据成功",result);
}else{
return new CommonResult(444,"插入数据失败",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
log.info("*****插入结果:"+payment);
if(payment != null){
return new CommonResult(200,"查询成功",payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID:"+id,null);
}
}
}

2.7 测试

Chrome 浏览器可能不支持 Post 请求,可以使用 PostMan 工具测试

总结: 1. 建 module 2. 改 pom 3. 写 yml 4. 主启动 5. 业务类

3. 建立消费者订单 module:

3.1 在 springcloud2021 主项目下建立 cloud-consumer-order80 模块(maven)

3.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
<?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>springcloud2021</artifactId>
<groupId>com.sevattal.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-order80</artifactId>
<dependencies>
<!--引入自己定义的api通用包,-->
<dependency>
<groupId>com.sevattal.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

3.3 application.yml

1
2
server:
port: 80

3.4 主启动类

1
2
3
4
5
6
7
8
9
package com.sevattal.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}

3.5 业务类

订单也需要Payment、CommonResult实体类,但是不需要操作数据库,没有Service、Dao,只需添加Controller即可。

1
2
3
4
5
6
7
8
9
10
11
12
//Payment
package com.sevattal.springcloud.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment {
private Long id;
private String serial;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//CommonResult
package com.sevattal.springcloud.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> { //泛型:如果装的payment 返回payment,装的order 返回order
private Integer code;
private String message;
private T data;
public CommonResult(Integer code,String message){
this(code,message,null);
}
}

首说 RestTemplate: RestTemplate 提供了多种便捷访问远程 Http 服务的方法,是一种简单便捷的访问 restful 服务模板类,是 Spring 提供的用于访问 Rest 服务的客户端模板工具集,实现 80 到 8001 的远程调用。

官网地址:
https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html

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

将 RestTemplate 对象注册到容器中

1
2
3
4
5
6
7
8
9
10
11
package com.sevattal.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
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.sevattal.springcloud.controller;
import com.sevattal.springcloud.entities.CommonResult;
import com.sevattal.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderController {
private static final String PAYMENT_URL="http://localhost:8001";
@Resource
private RestTemplate restTemplate;

@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment){
return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}

3.6 启动 80、8001 服务,测试

80 服务调用 8001 服务,实现效果如下:

查询

添加

浏览器并没有返回错误,但是我们来看数据库:

可以看到数据库只插入主键,并没有插入内容,要在8001的PaymentController加@RequestBody注解。

然后就可以插入了

4. 工程重构

项目中存在相同的代码(entities 包下的 Payment.class 和 CommonResult.class),造成代码冗余,可以进行重构。

通过 Maven 聚合父工程,把相同重复的代码移到公开公用的工程里面,还可以放第三方接口、工具类,统一调配使用。

4.1 建立公共module在springcloud2021 主项目下建立 cloud-api-commons 模块(maven)

4.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
<?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>springcloud2021</artifactId>
<groupId>com.sevattal.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-api-commons</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- huto 工具包, 时间日期格式-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies>
</project>

4.3 将 entities 包复制到 cloud-api-commons

4.4 使用 Maven 打包发布上传到公用本地库里

打开 Maven 窗口,执行 clean 测试一下,无误后出现 BUILD SUCCESS,然后执行 install

注:若 Maven 运行操作失败,请查看 Maven 配置的环境是否正确

4.5 删除重复 entities,引入 maven install 的 jar 包坐标即可使用。

1
2
3
4
5
6
<!--引入自己定义的api通用包,可以使用Payment支付Entity-->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>

5. EurekaServer服务端安装

5.1 在 springcloud2021 主项目下建 cloud-eureka-server7001 模块(Maven)

5.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
<?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>springcloud2021</artifactId>
<groupId>com.sevattal.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7001</artifactId>
<dependencies>
<!--eureka server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--引入自己定义的api通用包,可以使用payment支付Entity-->
<dependency>
<groupId>com.sevattal.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--boot web actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>

5.3 application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
server:
port: 7001
eureka:
instance:
hostname: localhost # eureka服务端的实例名称
client:
#false表示不向注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZ4 one: http://${eureka.instance.hostname}:${server.port}/eureka/

5.4 主启动类

1
2
3
4
5
6
7
8
9
10
11
package com.sevattal.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}

这是个服务注册中心,主要干的活就是服务注册,不需要写业务类。
但是注意:Eureka有两个组件,一定要标清楚哪个是Server,哪个是Client。@EnableEurekaServer代表服务注册中心

5.5 测试

出现上面图标,表示 Eureka 服务端安装成功。No instances available 表示当前没有服务注册进来

6. 单机 Eureka 构建:支付微服务 8001 入驻进 eurekaServer

6.1 将 Eureka-client 依赖引入,便于使用注解@EnableEurekaClient标注这是个 Eureka Client 端

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

6.2 在 application.yml 添加 Eureka 相关配置

1
2
3
4
5
6
7
8
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka

6.3 主启动类添加注解 @EnableEurekaClient

6.4 测试

这样就注册进来了,入住进 Eureka 服务器的名称就是 8001 yml 中配置的 spring.application.name。红色警告是 Eureka 的自我保护机制,后面会详细说。

7. 单机 Eureka 构建:订单微服务入驻进 eurekaServer (操作与支付微服务 8001 一样)

7.1 在 pom 添加 Eureka-client 依赖

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

7.2 在 application.yml 添加相关配置

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: cloud-order-server
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eu

7.3 主启动类添加注解 @EnableEurekaClient

7.4 测试

PS: 先启动 EurekaServer,7001 服务,再启动服务提供者 provider,8001 服务

cloud-order-server 服务以入住,查询功能也可以正常执行

8. EurekaServer 集群环境构建

8.1 创建 module cloud-eureka-server7002 (与创建 cloud-eureka-server7001 相似,除了端口改为 7002 外)

8.3 写 yml 之前修改 hosts 文件

找到 C:\Windows\System32\drivers\etc 路径下的 hosts 文件
添加如下主机名

8.4 修改 7001 和 7002 的 application.yml

8.5 测试

同时看到 Eureka 图标,且 7001 指着 7002,7002 指着 7001,说明 Eureka 集群搭建成功。

9. 将两个微服务发布到 Eureka 集群配置中

9.1 只需修改 application.yml

9.1 测试

PS: 先启动 EurekaServer,7001/7002 服务;再启动服务提供者 provider,8001;再启动消费者,80

现在,就已经把支付服务 8001、订单服务 80 注册进 Eureka 集群环境,调用也 OK。

10. 支付提供者 8001 集群环境搭建

10.1 创建 module cloud-provider-payment8002 (与创建 cloud-provider-payment8002 一样不做过多介绍)

10.2 修改 8001 和 8002 的 controller,默认的负载均衡方式是轮询,看执行查询具体调用那台 provider

10.3 测试

PS: 启动顺序:7001、7002、8001、8002、80

8001 和 8002 也都访问正常,那如果我们用 80 访问呢?发现怎么刷新都是 8001,这是因为我们的源程序地址是写死的:

单机版写死是没有问题的,但是现在有 8001、8002 了,所有不应该再关注具体的 IP 和端口,而是只认服务名称。代码修改为一下在试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@Slf4j
public class OrderController {
//private static final String PAYMENT_URL="http://localhost:8001";
//通过在eureka上注册过的微服务名称调用
private static final String PAYMENT_URL = "http://CLOUD-PROVIDER-SERVICE";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment){
return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}

发现报错了,现在对外暴露的不再是地址和端口,只认微服务名称了,可是微服务并不知道下面有几个,找不到这个主机名称,需要使用 # LoadBalanced 注解开启 RestTemplate 负载均衡功能。
提前说一下:这个就是后面要介绍的 Ribbon 负载均衡功能。

然后测试,多次刷新,就会发现 8001、8002 端口交替出现。

这样 Ribbon 和 Eureka 整合后 Consumer 可以直接调用服务而不用再关心地址和端口号,且该服务还有负载均衡功能了。O(∩_∩)O

11. Eureka 服务发现

11.1 SpringBoot 启动主类配置 @EnableDiscoveryClient 注解启动服务发现

11.2 Controller 中配置服务发现测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
* 服务发现
* */
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/payment/discovery")
public Object discovery() {
/*
* 获得微服务的名称
*
* */
List<String> services = discoveryClient.getServices();
for (String element: services){
log.info("*******element: " + element);
}
/*
* 获得某一微服务下的所有实例实例名
* */
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PROVIDER-SERVICE");
for (ServiceInstance instance: instances){
log.info(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" +instance.getUri());
}
return this.discoveryClient;
}

11.3 测试

从上述 json 以及后台日志的返回信息中可以看出,已经将 Eureka 中注册的微服务的对应信息都查询出来了。

12. Eureka 的自我保护机制

默认情况下,如果 Eureka Server 在一定时间内没有接收到某个微服务实例的心跳,Eureka Server 将会注销该实例(默认90秒)。
但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与 Eureka Server 之间无法正常通信,以上行为可能变得非常危险了。
因为微服务本身其实是健康的,此时不应该注销该微服务。Eureka 通过”自我保护模式”来解决这个问题。
当 Eureka Server 节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。

在自我保护模式中,Eureak Server 会保护服务注册表中的信息,不再注销任何服务实例。
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。– 好死不如赖活着

以下示例为解决 Eureka 的自我保护机制

12.1 配置 Eureka application.yml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7002.com:7002/eureka/
# 关闭eurka的自我保护机制,当发现微服务没有在指定时间内发送心跳直接剔除该微服务
server:
# 关闭禁用自我保护机制
enable-self-preservation: false
# 配置心跳时间为2秒钟
eviction-interval-timer-in-ms: 2000

12.2 测试查看

注:从上述信息可以看到,Erueka 已经关闭了自我保护机制

12.3 配置微服务 8001 application.yml 文件(以下只展示 eureka 部分,配置心跳间隔时间等)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
# eureka中显示的主机名
instance:
instance-id: payment8001
prefer-ip-address: true # 访问路径可以显示IP地址
# Eureka 客户端向服务端发送心跳的时间间隔,单位为秒(默认为30秒)
lease-renewal-interval-in-seconds: 1
# Eureka 服务端在接收到最后一次心跳后等候时间上限,单位为秒(默认90秒),超时将剔除服务
lease-expiration-duration-in-seconds: 2

12.4 当关闭了微服务 8001 后,查看 Eureka Web 界面

注:可以很快的看到 8001 服务,已经在 Eureka 中剔除了。

Contents
  1. 1. 尚硅谷周阳构建分布式微服务架构(Eureka篇)
    1. 1.1. 整体工程项目架构
    2. 1.2. Eureka url命令的补充知识点
    3. 1.3. 1. 新建Maven父工程 springcloud2021
      1. 1.3.1. 1.1 maven架构不需要选择
      2. 1.3.2. 1.2 pom.xml代码如下
    4. 1.4. 2. 建立支付 module: cloud-provider-payment8001
      1. 1.4.1. 2.1 在 springcloud2021 主项目下建立 cloud-provider-payment8001 模块(maven)
      2. 1.4.2. 2.2 pom.xml如下:
      3. 1.4.3. 2.3 application.yml
      4. 1.4.4. 2.4 主启动类PaymentMain8001
      5. 1.4.5. 2.5 数据库
      6. 1.4.6. 2.6 业务类
      7. 1.4.7. 2.7 测试
    5. 1.5. 3. 建立消费者订单 module:
      1. 1.5.1. 3.1 在 springcloud2021 主项目下建立 cloud-consumer-order80 模块(maven)
      2. 1.5.2. 3.2 pom.xml如下:
      3. 1.5.3. 3.3 application.yml
      4. 1.5.4. 3.4 主启动类
      5. 1.5.5. 3.5 业务类
      6. 1.5.6. 3.6 启动 80、8001 服务,测试
    6. 1.6. 4. 工程重构
      1. 1.6.1. 4.1 建立公共module在springcloud2021 主项目下建立 cloud-api-commons 模块(maven)
      2. 1.6.2. 4.2 pom.xml 如下:
      3. 1.6.3. 4.3 将 entities 包复制到 cloud-api-commons
      4. 1.6.4. 4.4 使用 Maven 打包发布上传到公用本地库里
      5. 1.6.5. 4.5 删除重复 entities,引入 maven install 的 jar 包坐标即可使用。
    7. 1.7. 5. EurekaServer服务端安装
      1. 1.7.1. 5.1 在 springcloud2021 主项目下建 cloud-eureka-server7001 模块(Maven)
      2. 1.7.2. 5.2 pom.xml
      3. 1.7.3. 5.3 application.yml
      4. 1.7.4. 5.4 主启动类
      5. 1.7.5. 5.5 测试
    8. 1.8. 6. 单机 Eureka 构建:支付微服务 8001 入驻进 eurekaServer
      1. 1.8.1. 6.1 将 Eureka-client 依赖引入,便于使用注解@EnableEurekaClient标注这是个 Eureka Client 端
      2. 1.8.2. 6.2 在 application.yml 添加 Eureka 相关配置
      3. 1.8.3. 6.3 主启动类添加注解 @EnableEurekaClient
      4. 1.8.4. 6.4 测试
    9. 1.9. 7. 单机 Eureka 构建:订单微服务入驻进 eurekaServer (操作与支付微服务 8001 一样)
      1. 1.9.1. 7.1 在 pom 添加 Eureka-client 依赖
      2. 1.9.2. 7.2 在 application.yml 添加相关配置
      3. 1.9.3. 7.3 主启动类添加注解 @EnableEurekaClient
      4. 1.9.4. 7.4 测试
    10. 1.10. 8. EurekaServer 集群环境构建
      1. 1.10.1. 8.1 创建 module cloud-eureka-server7002 (与创建 cloud-eureka-server7001 相似,除了端口改为 7002 外)
      2. 1.10.2. 8.3 写 yml 之前修改 hosts 文件
      3. 1.10.3. 8.4 修改 7001 和 7002 的 application.yml
      4. 1.10.4. 8.5 测试
    11. 1.11. 9. 将两个微服务发布到 Eureka 集群配置中
      1. 1.11.1. 9.1 只需修改 application.yml
      2. 1.11.2. 9.1 测试
    12. 1.12. 10. 支付提供者 8001 集群环境搭建
      1. 1.12.1. 10.1 创建 module cloud-provider-payment8002 (与创建 cloud-provider-payment8002 一样不做过多介绍)
      2. 1.12.2. 10.2 修改 8001 和 8002 的 controller,默认的负载均衡方式是轮询,看执行查询具体调用那台 provider
      3. 1.12.3. 10.3 测试
    13. 1.13. 11. Eureka 服务发现
      1. 1.13.1. 11.1 SpringBoot 启动主类配置 @EnableDiscoveryClient 注解启动服务发现
      2. 1.13.2. 11.2 Controller 中配置服务发现测试代码
      3. 1.13.3. 11.3 测试
    14. 1.14. 12. Eureka 的自我保护机制
      1. 1.14.1. 12.1 配置 Eureka application.yml 文件
      2. 1.14.2. 12.2 测试查看
      3. 1.14.3. 12.3 配置微服务 8001 application.yml 文件(以下只展示 eureka 部分,配置心跳间隔时间等)
      4. 1.14.4. 12.4 当关闭了微服务 8001 后,查看 Eureka Web 界面
|