尚硅谷springcloud学习笔记
参考资料:
尚硅谷springcloud教程
springcloud文档:https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/
springboot文档:https://docs.spring.io/spring-boot/docs/2.2.2.RELEASE/reference/htmlsingle/
1.springcloud简介与学习版本
springcloud=分布式微服务架构的一站式解决方案,是多重微服务架构落地技术的集合体,俗称微服务全家桶
springcloud所包含的技术
下面就是我们需要掌握的技术
下面是学习课程是的版本选型
springboot:2.x版本,springcloud H版
下面这张图是springcloud和springboot版本号的对应
最终学习选择的版本:
注意:springcloud版本决定springboot版本,看可以在springcloud官网查看对应版本所推荐的springboot版本,我使用的idea是2022版本的
查看springcloud推荐的springboot版本的方式:
下面是本次课程中所要学的一些技术(主要是打勾的)
2.微服务架构编码构建(初体验)
2.1搭建一个微服务整体聚合父工程Project
a)创建一个父maven工程
修改好字符编码
b)父工程的pom.xml文件
DependencyManagement和Dependencies的区别:前者一般用于父工程,使用pom.xml 中的dependencyManagement 元素能让所有在子项目中引用一个依赖而不用显式的列出版本号。
Maven 会沿着父子层次向上走,直到找到一个拥有dependencyManagement 元素的项目,然后它就会使用这个
注意:DependencyManagement只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖(报错的话把他去掉刷新一下maven引入依赖后再加上即可)
只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom
如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
<?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.javalearn.springcloud</groupId>
<artifactId>springcloudLearn</artifactId>
<version>1.0-SNAPSHOT</version>
<!--这是一个总的父工程-->
<packaging>pom</packaging>
<!-- 统一管理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>8.0.27</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
</properties>
<!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写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>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<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.2支付模块cloud-provider-payment8001
1、建模块
在我们原来的父工程下新建一个模块
查看父工程的pom,我们发现多出了一个模块
2、改pom
引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.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>
<!--jdbc-->
<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>
3、改yml
在resources文件夹中创建一个application.yml文件
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password:
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.javalearn.springcloud.entities # 所有Entity别名类所在包
4、主启动
即springboot的主启动类,我们在springboot中已经学习过了
5、业务类
vue->controller->service->dao->mysql
a)建表sql
b)创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Payment implements Serializable {
private long id;
private String serial;
}
下面是返回给前端的实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult <T>{
private Integer code;//状态码
private String message;
private T data;
public CommonResult(Integer code,String message){
this(code,message,null);
}
}
c)dao(Mapper)
@Mapper
public interface PaymentDao {
public int create(Payment payment);
public Payment getPaymentById(@Param("id")Long id);
}
同时创建他的映射文件
<?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.javalearn.springcloud.dao.PaymentDao">
<!--public int create(Payment payment);-->
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment (serial) values (#{serial})
</insert>
<resultMap id="BaseResultMap" type="Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<id column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<!--public Payment getPaymentById(@Param("id")Long id);-->
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id}
</select>
</mapper>
d)service
public interface PaymentService {
public int create(Payment payment);
public Payment getPaymentById(@Param("id")Long id);
}
以及他的实现类
@Service
public class PaymentServiceImpl implements PaymentService {
@Resource
PaymentDao paymentDao;
@Override
public int create(Payment payment) {
return paymentDao.create(payment);
}
@Override
public Payment getPaymentById(Long id) {
return paymentDao.getPaymentById(id);
}
}
e)controller
@Slf4j
@RestController
public class PaymentController {
@Resource
private PaymentService paymentService;
@PostMapping("/payment/create")
public CommonResult createPayment(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 ("/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
if (payment!=null){
return new CommonResult(200,"查询成功",payment);
}else{
return new CommonResult(444,"查询失败,查询id:"+id,null);
}
}
}
六、测试
经过测试,由于我的idea版本过高,所以用周阳老师的lombok版本会报错,修改了一下版本
<lombok.version>1.18.20</lombok.version>
测试查询的时候,由于需要post请求,所以这里使用postman,结果插入成功
总结:业务模块的步骤
1.建module
2.改pom
3.写yml
4.写run
5.写业务
2.3消费者模块cloud-consumer-order-80(操作支付模块)
1、建模块
2、改pom
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.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>
3、写yml
使用8080端口
server:
port: 80
4、主启动
5、写业务
我们使用消费者模块来操作我们刚刚写的支付模块,所以只需要有controller即可
消费者模块也需要用到Payment的实体类,所以我们先复制一份实体类
2.3.1RestTemplate
接下来写我们的controller类,去调用我们之前写的支付模块,这里就要使用RestTemplate了:是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
使用
使用restTemplate访问restful接口非常的简单粗暴无脑。
(url, requestMap, ResponseBean.class)这三个参数分别代表
REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
a)配置类,注入RestTemplate
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
b)controller
@Slf4j
@RestController
public class OrderController {
public static final String PAYMENT_URL = "http://localhost:8001";//这个是我们需要转发到的端口
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment){
//调用restTemplate的这个方法
return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}
接下来分别启动80和8001的主启动类,进行测试,启动后idea会弹出一个选项,勾选使用
这样我们就可以方便的看每一个服务了
经过测试,我们可以使用consumer模块调用payment模块的查询方法
但是当我们要调用增加方法时,由于我们的consumer传递给payment模块的是一个json数据,所以我们需要增加一个@RequestBody注解用于接受json字符串,所以要修改8001的代码
2.4提取两个模块的公共实体类entity
在我们的8001和80两个模块中,他们有重复部分,也就是entities实体类,这就会造成代码的冗余,那么接下来我们就要抽取公共部分了。
还是通过maven抽取,通过新建一个cloud-api-commons模块来抽取
1、建模块(不演示了)
2、改pom
<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>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies>
3、复制前两个工程的实体类
4、使用maven命令:clean->install
5、删除原来模块中的entities内容,并引入这个自己定义的jar包
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.javalearn.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
到这里,我们的基础构件入门就完成了
3.Eureka服务注册与发现(已停更,了解一下)
3.1基础知识
虽然Eureka目前已经停止更新,但是许多老项目还是在使用的,我们需要先来了解下面的一些概念
1、服务治理
在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
2、服务注册与发现
Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行下图是工作原理。
其包括两个组件:
Eureka Server提供服务注册服务: 各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
EurekaClient通过注册中心进行访问: 是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
3.2单机Eureka:cloud-eureka-server7001构建步骤
1、建立一个module:cloud-eureka-server7001
2、改pom
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.javalearn.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</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>
3、写yml
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4、创建springboot主启动类
5、测试
运行主启动类,在浏览器中输入对应网址,进入成功
3.3支付微服务8001与消费者80入驻进EurekaServer
修改8001模块
1、改pom
注意这里引入的是client,而7001注册中心是sever
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、写yml
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#入驻哪个主机的哪个端口
defaultZone: http://localhost:7001/eureka
3、主启动
4、测试
a)注意要先启动7001注册中心
b)如果启动报错
修改一下client依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!-- 排除Jersey,用SpringMVC Rest方式-->
<exclusions>
<exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-apache-client4</artifactId>
</exclusion>
</exclusions>
</dependency>
c)刷新Eureka界面,发现服务已经注册进来了
d)发现微服务注册到名称与我们yml文件中的一致
5、消费者模块80也是一样的流程,直接到测试环节
a)先启动7001,和8001,然后启动80
b)发现80也被注册成功了
c)到现在为止的工作原理
3.4搭建Eureka集群
1、集群原理
如果说注册中心只有一台,那么如果他除了故障,整个服务环境就不可用了,所以我们需要搭建集群,下图是到目前为止的工作原理,来保证高可用。
集群原理:相互注册,相互守望,并且对外暴露出一个整体,下面是搭建的步骤
2、建module,参考7001创建cloud-eureka-server7002
3、改pom
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.javalearn.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>
<!--加上这个防止启动报错-->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.15</version>
</dependency>
</dependencies>
4、修改映射配置
找到C:\Windows\System32\drivers\etc路径下的hosts文件
修改映射配置添加进hosts文件,添加如下内容,方便区分两台sever
5、写yml
先改7001
再写7002
6、创建7002的主启动类,与7001一样,别忘了加注解
7、测试,分别运行7001和7002的主启动类,启动服务
1、经过测试,曾经的localhost还可以访问到Eureka主页
2、访问域名,我们发现1指着2,2指着1,自此集群搭建成功
3.5将订单与支付模块注册进Eureka集群
1、注册8001,修改yml
2、注册80,修改yml
3、测试
1、启动7001和7002集群,然后启动服务提供者8001,最后启动80
3.6支付微服务提供者8001的集群配置
我们工程目前的架构图是80是消费者,8001是服务的提供者,为了保持高可用,我们微服务的提供者也需要进行集群配置
1、参考8001新建8002
2、改pom
直接复制8001的即可
3、写yml
复制粘贴8001的yml,修改端口为8002即可,此时8001和8002对外暴露的服务名其实是一样的
4、主启动
5、业务类
也是直接从8001粘贴
6、修改8001和8002的ontroller(重点)
以8001为例,8002也是一样的修改,获取端口号,来获取是哪台服务器执行的请求
7、测试
启动顺序:7001-7002-8001-8002-80
观察Eureka界面,我们发现支付服务有两台服务器了
8、修改80的controller
在之前的项目中,我们的80是使用了RestTemplate,所以请求只会转发给8001,达不到集群的效果,所以我们需要修改。
a)在原先的配置类中,使用 @LoadBalanced注解赋予RestTemplate负载均衡能力
b)修改地址,80controller中的地址,不再写死8001
这样我们就完成了微服务集群的配置,注意,访问集群的方式默认是轮询,即:你一次我一次
3.7微服务信息完善
目前存在的问题
a)主机名修改
(1)需要的依赖
<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>
(2)修改部分
b)显示ip
修改部分:在8001和8002的yml中加入
这样就可以显示ip了
3.8服务发现Discovery
功能:对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
1、修改8001的controller,8002也是一样的,就不演示了
首先注入DiscoveryClient
@Resource
private DiscoveryClient discoveryClient;
然后写一个用于获取信息的方法
通过这个discoveryClient就可以获取微服务信息了
@GetMapping("/payment/discovery")
public Object discovery(){
List<String> services = discoveryClient.getServices();//获取服务列表的信息
for (String service : services) {
log.info("****element"+service);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");//获取一个微服务名称下面的全部具体实例
for (ServiceInstance instance : instances) {
log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return discoveryClient;
}
2、在8001的主启动类上添加注解@EnableDiscoveryClient
3.9Eureka的自我保护机制与关闭
概念:一句话:某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存,属于CAP里面的AP分支,防止了因为网络分区发生故障而去住注销掉一个健康的微服务。
AP:当发生网络分区时,最大努力保证可用性
如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。 使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
那么怎么关闭自我保护机制?
此次演示以7001服务器和8001客户端为例
(1)修改7001的yml文件,关闭自我保护机制
在运行7001,发现系统提示我们自我保护机制已经关闭了
(2)修改8001的yml
4.zookeeper服务注册与发现
在写这篇文章的时候,博主本人之前并未学习过zookeeper,所以这里暂时先跳过,后续学习后回来补。
5.Consul服务注册与发现
官网:https://www.consul.io/intro/index.html
中文文档:https://www.springcloud.cc/spring-cloud-consul.html
安装说明:https://learn.hashicorp.com/consul/getting-started/install.html
下载地址:https://www.consul.io/downloads.html
Consul 是一套开源的分布式服务发现和配置管理系统,由 HashiCorp 公司用 Go 语言开发。
提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。
可以干的事情:
文件下载解压后:
在这个文件夹可以直接用cmd命令运行
启动以后,在浏览器中输入 http://localhost:8500就可以访问web界面了,还是很漂亮的
5.1新建支付服务提供者8006注册进consul
1、建module:cloud-providerconsul-payment8006
2、改pom
<dependencies>
<!--SpringCloud consul-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<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>
3.写yml
###consul服务端口号
server:
port: 8006
spring:
application:
name: consul-provider-payment
####consul注册中心地址
cloud:
consul:
host: localhost
port: 8500
discovery:
#hostname: 127.0.0.1
service-name: ${spring.application.name}
4、主启动
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8006.class,args);
}
}
5、业务类controller
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/payment/consul")
public String paymentConsul(){
return "springcloud with consul: "+serverPort+"\t\t"+ UUID.randomUUID().toString();
}
}
6、测试
运行我们的主启动类,然后去consul的web界面观察
程序访问也通过
5.2新建服务消费者80注册进consul
1、新建一个module:cloud-consumerconsul-order80
2、改pom
<dependencies>
<!--SpringCloud consul-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<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>
3、写yml
###consul服务端口号
server:
port: 80
spring:
application:
name: cloud-consumer-order
####consul注册中心地址
cloud:
consul:
host: localhost
port: 8500
discovery:
#hostname: 127.0.0.1
#注册进去的名字
service-name: ${spring.application.name}
4、主启动
@SpringBootApplication
@EnableDiscoveryClient
public class OrderConsul80 {
public static void main(String[] args) {
SpringApplication.run(OrderConsul80.class,args);
}
}
5、业务类
这里的业务类和我们之前的Eureka下的那个80消费者很相似,也是使用RestTemplate,所以我们拷贝原来的配置类就行
然后写一个controller
@RestController
@Slf4j
public class OrderConsulController {
public static final String INVOKE_URL = "http://consul-provider-payment"; //与Eureka类似,我们要转发的服务的注册名字
@Autowired
private RestTemplate restTemplate;
@GetMapping(value = "/consumer/payment/consul")
public String paymentInfo()
{
String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul", String.class);
System.out.println("消费者调用支付服务(consule)--->result:" + result);
return result;
}
}
6、测试
启动顺序:8006->80
测试访问,发现一切正常
6.三个注册中心的异同点
1、CAP是什么?
C:Consistency(数据一致性)
A:Availability(可用性)
P:Partition tolerance(分区容错性)
最多只能同时较好的满足两个。
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,
因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。
AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
三种注册中心的对比图