一:简介
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
spring cloud 五大核心组件(后续使用讲解)
服务发现 - Eureka
客服端负载均衡 - Ribbon/Feign
断路器 - Hystrix
服务网关 - Zuul / Gateway
分布式配置 - Spring Cloud Config
二:nacos(阿里的一款注册中心) 与 eureka的区别
1:提到注册中心,那么 必定遵循CAP原则
1、C (强一致性): 在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
2、A (高可用性): 在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
3、P (分区容错性):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。如果在某个分布式系统中数据无副本, 那么系统必然满足强一致性条件, 因为只有独一数据,不会出现数据不一致的情况,此时C和P两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了CP系统,但是CAP不可同时满足
2:eureka+spring cloud config:
eureka: 可以做注册中心,完全AP,支持注册中心之间的节点复制,同时支持服务端同时注册多个注册中心节点,
所以不存节点信息不一致的情况
config: 单独服务,是从git仓库拉取配置信息,然后服务端从config服务里面拉取配置信息缓存到本地仓库
这里配置的变更比较麻烦,他需要结合bus组件,同时约束了只能用rabbitmq和kafka来进行通知服务端进行配置变更
但是保证了数据的一致性,因为他的配置信息在git仓库上,git仓库只有一个,就会数据一致
注:对于服务数量小于上千台来说,性能没有问题
3:nacos
他同时支持AP和CP模式,他根据服务注册选择临时和永久来决定走AP模式还是CP模式,
他这里支持CP模式对于我的理解来说,应该是为了配置中心集群,因为nacos可以同时作为注册中心和配置中心,
因为他的配置中心信息是保存在nacos里面的,假如因为nacos其中一台挂掉后,还没有同步配置信息,
就可能发生配置不一致的情况., 配置中心的配置变更是服务端有监听器,配置中心发生配置变化,
然后服务端会监听到配置发生变化,从而做出改变
三:入门案列
1:安装nacos到本地
首先 nacos工具包下载 提取码:hqrd 下载好后直接解压即可,
到官网下载的话 记得找到对应 /bin/startup.cmd 文件 右键编辑 --server.port=9090 为设置nacos端口 默认端口为8848
"%JAVA%" %JAVA_OPT% nacos.nacos %* --server.port=9090
双击执行.cmd文件 出现如下图 即可
可以直接 访问nacos的可视化界面 在网页地址栏输入 http://127.0.0.1:9090/nacos/index.html 用户名密码默认均为:nacos
2:创建测试项目 (项目目录结构如下)
2.1:创建项目父工程(spring-cloud-demo) 修改 pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zjw</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>spring-cloud-demo</name>
<packaging>pom</packaging>
<description>Demo project for Spring Boot</description>
<modules>
<module>provider</module>
<module>consumer</module>
</modules>
<properties>
<spring-boot.version>2.2.2.RELEASE</spring-boot.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
<nacos.version>0.2.2.RELEASE</nacos.version>
<openfeign.version>2.1.2.RELEASE</openfeign.version>
<nacos-discovery.version>0.9.0.RELEASE</nacos-discovery.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${openfeign.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
<version>${nacos-discovery.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${nacos.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2.2:添加服务提供者 provider
2.2.1:在父工程中添加一个module,作为服务的提供者(provider) pom.xml文件如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zjw</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>provider</artifactId>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2.2:配置提供者application.yml
################# cloud 初始化参数 ############################
oa:
application: provider-server #应用名
port: 8081 #项目端口号
# nacos-ip: 100.168.1.151 #注册中心ip地址
nacos-ip: 127.0.0.1 #注册中心ip地址
nacos-port: 9090 #注册中心端口号
################# cloud 初始化参数 end ############################
server:
port: ${oa.port}
tomcat:
uri-encoding: UTF-8
spring:
application:
name: ${oa.application}
cloud:
nacos:
discovery:
server-addr: ${oa.nacos-ip}:${oa.nacos-port}
2.2.3:在启动类上添加@EnableDiscoveryClient注解
package com.zjw.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
2.2.4:服务提供者写一个对外访问的接口
package com.zjw.provider.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
private final String name = "刚正青";
private final String sex = "男";
@GetMapping("/test")
public String test(){
String temp = "服务注册-->" + "\r" + "姓名:" + name + " 性别:" + sex;
return temp;
}
}
2.3:添加一个服务消费者 consumer
2.3.1:服务消费者添加 Fegin 与 熔断机制 hystrix 和提供者关联 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zjw</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>consumer</artifactId>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.3.2:配置消费者application.yml
oa:
application: consumer-server
prot: 8082
nacos-ip: 127.0.0.1
nacos-prot: 9090
server:
port: ${oa.prot}
tomcat:
uri-encoding: UTF-8
spring:
application:
name: ${oa.application}
cloud:
nacos:
discovery:
server-addr: ${oa.nacos-ip}:${oa.nacos-prot}
feign:
hystrix:
enabled: true
2.3.3:消费者 核心启动类 需要添加@EnableDiscovery 和 @EnableFeignClients
package com.zjw.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
2.3.4:rpc远程调用 服务提供者 service接口 和 serviceImpl实现
package com.zjw.consumer.service;
import com.zjw.consumer.service.impl.TestServiceImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "provider-server",fallback = TestServiceImpl.class)
public interface ITestService {
@GetMapping("/test")
String test();
}
package com.zjw.consumer.service.impl;
import com.zjw.consumer.service.ITestService;
import org.springframework.stereotype.Component;
@Component
public class TestServiceImpl implements ITestService {
@Override
public String test(){
return "远程调用服务失败";
}
}
2.3.5:消费者服务添加一个对外访问接口
package com.zjw.consumer.controller;
import com.zjw.consumer.service.ITestService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class TestController {
@Resource
ITestService testService;
@GetMapping("/hello")
public String test(){
String temp = "服务发现-->";
return temp + testService.test();
}
}
3.启动服务
编译器出现如下样式:则服务注册成功
在nacos中 服务->列表 查看 健康实例数为1则说明服务注册成功
4.访问服务
这里可以直接访问 提供者 -->在访问 消费者
四:总结
这里可能有小伙伴不明白
1.访问消费者服务是为啥直接去掉了消费者 回来后有不走自己本身实现呢,那你自己写一个实现有什么有呢?
@FeignClient(name = "provider-server",fallback = TestServiceImpl.class)
其实只要理解这句就好,当外部调用消费者接口时到service层 通过spring cloud Feign远程调用到另一服务上 也就是provider,在那边完成调用返回自然不会 在回到本身再次执行本身实现了。
2.那你自己写一个实现有什么有呢?
在任何时候都可能出现 服务宕机,为了以防万一,方便查找。
当provider宕机时,也就是消费者找不到提供者,这时候 4-5秒 nacos发现异常 provider的健康实例就会变成0;
这时就会触发 fallback = TestServiceImpl.class 变换到 应用本身实现
(我这里先把 provider服务关闭 后再页面访问消费者接口 如图下)
3.分析流程
1.当外部访问消费者consumer接口时,服务调用到service层,通过rpc远程服务调用到 服务提供者provider中
我这边写的比较简单
但是第一:consumer中 实现其实是远程调用失败后 自动变换 到原先定义好的实现 (实际的业务操作是在 provider上的)
第二:provider才是 业务处理方。实际业务中 这里会发生 service dao等的 数据操作
应用场景:consumer 要去那一条数据 但是这条数据在test2数据库中,而consumer是绑定了 test1数据库
假如不配置动态数据源 切换
我们可以provider上绑定数据库test2 然后只要consumer远程调用到 provider上操作 test2数据即可
2.不用纠结 消费者 和 提供者 这两个概念
其实在这里 provider及是消费者 也是 提供者
可以说是 A 服务要调用 B 服务 这是,我们就对AB关系,定义成A是B的消费者,B是A的提供者