SpringCloud起步笔记教程
文章目录
1. 微服务架构和SpringCloud
多维度下的数字化生活
定义:
- 微 狭义来讲就是单个应用的体积小
- 微服务架构风格是一种使用一套小服务来开发单个应用的方式途径
- 提高扩展性,可靠性,降低维护成本
优势:
-
单体架构的问题:
主要有以下几点:
1.复杂性逐渐变高
- 比如有的项目有几十万行代码,各个模块之间区别比较模糊,逻辑比较混乱,代码越多复杂性越高,越难解决遇到的问题。
2.技术债务逐渐上升
- 公司的人员流动是再正常不过的事情,有的员工在离职之前,疏于代码质量的自我管束,导致留下来很多坑,由于单体项目代码量庞大的惊人,留下的坑很难被发觉,这就给新来的员工带来很大的烦恼,人员流动越大所留下的坑越多,也就是所谓的技术债务越来越多。
3.部署速度逐渐变慢
- 这个就很好理解了,单体架构模块非常多,代码量非常庞大,导致部署项目所花费的时间越来越多,曾经有的项目启动就要一二十分钟,这是多么恐怖的事情啊,启动几次项目一天的时间就过去了,留给开发者开发的时间就非常少了。
4.阻碍技术创新
- 比如以前的某个项目使用struts2写的,由于各个模块之间有着千丝万缕的联系,代码量大,逻辑不够清楚,如果现在想用spring mvc来重构这个项目将是非常困难的,付出的成本将非常大,所以更多的时候公司不得不硬着头皮继续使用老的struts架构,这就阻碍了技术的创新。
5.无法按需伸缩
- 比如说电影模块是CPU密集型的模块,而订单模块是IO密集型的模块,假如我们要提升订单模块的性能,比如加大内存、增加硬盘,但是由于所有的模块都在一个架构下,因此我们在扩展订单模块的性能时不得不考虑其它模块的因素,因为我们不能因为扩展某个模块的性能而损害其它模块的性能,从而无法按需进行伸缩。
而微服务每个模块就相当于一个单独的项目,代码量明显减少,遇到问题也相对来说比较好解决。
SpringCloud
主题词:基于分布式的微服务架构
- SpringCloud=分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶
- 通常是一个整体
- SpringBoot是一种服务开发技术(技术栈)
- 服务注册与发现:EUREKA
- 服务负载均衡与调用:NETFLIX OSS RIBBON
- 服务负载与调用:NETTFLIX
- 服务熔断降级:HYSTRIX
- 服务网关:Zuul
- 服务分布式配置:SpringCloud Config
- 服务开发:SpingBoot
2 Project工作空间
2.1 总父工程
-
约定>配置>编码
-
IDEA新建project工作空间
-
Rest微服务工作架构
2.1.1 IDEA新建project工作空间
-
new Project
-
聚合总父工程名字
-
Maven选版本
-
工程名字
-
字符编码
-
注册生效激活
- 表示支持我们的注解
-
java编译版本选8
-
File Type过滤(可不做)
2.2.2 父工程的pom配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mnZLd0M6-1662018234740)(C:\Users\86130\AppData\Roaming\Typora\typora-user-images\image-20220320152344554.png)]
加 <packaging>pom</packaging>
<?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>org.example</groupId>
<artifactId>springcloud</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.18</mysql.version>
<druid.verison>1.1.16</druid.verison>
<mybatis.spring.boot.verison>1.3.0</mybatis.spring.boot.verison>
</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.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.verison}</version>
</dependency>
<!-- mybatis-springboot整合 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.verison}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.4</version>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
dependencyManagement 中定义版本号,如果子项目不写不会从父项目中导入,类似与Java中的接口
2.2.3mvn install
用来打包
2.2Rest微服务工作架构
消费者80调用8001支付模块
微服务模块
- 建module
- 改pom
- 写yml
- 主启动
- 业务类
代码:D:\java笔记\SpringCloud\Project032001
2.3 自动热部署devtools
2.3.1 子工程pom插入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
2.3.2 父工程中插入plugin
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.4</version>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
2.3.3 Enabling automatic build
2.4 RestTemplate
80端对8001的远程调用
public class OrderController {
public static final String PAYMENT_URL = "http://localhost:8001";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> creat(Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "payment/add", 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.Eureka 基础知识
Eureka服务注册与发现
- 什么是服务注册
- Eureka采用CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心,而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接,这样系统的维护人员就可以通过Eureka Server来监控各个微服务是否正常运行
- 在服务注册于发现中,有一个注册中心,当服务器启动的时候,会把当前自己服务器的信息 比如 服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的通讯地址,然后在实现本地RPC调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理理念)。在任何RPC远程框架中,都会有一个注册中心
- Eureka包含两个组件:Eureka Server和Eureka Client
- Eureka Server提供服务注册服务
- 各个微服务节点通过配置启动后,会在EurekaServer中注册,这样EurekaServer中的服务注册表中将会存储所有 可用服务节点的信息,服务节点的信息可以在界面中直观看到
- Eureka Client通过注册中心进行访问
- 是一个java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期30s)。如果Eureka Server在多个心跳周期内没有接受到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90s)
- 类比物业
- Eureka Server提供服务注册服务
pom的配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>cloud-api-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
yml配置
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #其他微服务注册进来的地址
#defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
主启动类
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer//标记
public class EurekaMai7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMai7001.class,args);
}
}
启动localhost7001后
还未有入住
client端!!!!!!!!!!!!!!!!!
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>3.0.2</version>
</dependency>
启动测试
先Eureka 再Client
出现以下信息
Eurake集群原理说明
互相注册,相互调用
eureka7001 <----相互调用----> eureka7002
修改eureka的yml
DiscoveryClient
@Resource
private DiscoveryClient discoveryClient;
使用方法
@GetMapping("/discovery")
public Object discovery(){
List<String> services = discoveryClient.getServices();
for(String element:services)
{
log.info("*****element:"+element);
}
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 this.discoveryClient;
}
CLOUD-PAYMENT-SERVICE 10.162.51.222 8002 http://10.162.51.222:8002
CLOUD-PAYMENT-SERVICE 10.162.51.222 8001 http://10.162.51.222:8001
Eureka停更说明
停更不停用
4 zookeeper
- 是一个分布式协调工具,可以实现注册中心功能
- Eureka的替代品
提供的服务包括:
- 分布式消息同步和协调机制
- 服务器节点动态上下线
- 统一配置管理
- 负载均衡
- 集群管理
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
zookeeper命令行:
在服务器中打开zookeeper安装目录
打开bin目录
- ls /列出
- create /node ‘name’ 新建node
- get /node 获取文件:
配置zookeeper
application.yml
server:
port: 8006
spring:
application:
name: cloud-provider-payment
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/premission?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username: root
password: '010213'
cloud:
zookeeper:
connect-string: 121.4.111.212:2181
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul或者zookeeper作为注册中心时注册服务
public class PaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8006.class,args);
}
}
出现service
907996bd-6a91-4d86-aef2-442ffc6622be
内部编码流水号
获得基本信息
临时还是持久结点?
(Eureka的自我保护机制,会相对保存)
zookeeper会不断更新id
报错:
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
ps: zookeeper和log4j不能重复调用
5. Consul注册
与zookeeper和Eureka相同
主要功能;
-
服务发现:
提供HTTP和DNS两种发现方式 -
健康检测:
支持多种方式:
HTTP、TCP、Docker、Shell脚本定制化
-
KV存储:
Key、Value存储方式
-
多数据中心:
Consul支持多数据中心 -
可视化Web界面
好
略
三个注册中心的异同点
AP、CP、CP的值:
C:强一致
A:可用性
P:分区容错性
AP:奔溃了还能存活
Cp:奔溃了就没了
6. Ribbon
负载均衡服务调用
基于Nerflix Ribbon 实现的一套客户端,负载均衡的工具
主要功能是提供客户端的软件负载均衡算法和服务调用
Ribbon客户端组件提供了一系列完善的配置项:
- 连接超时
- 重试等
简单来说:就是在配置文件中列出Load Balancer(简称LB)后面所有的及其
Ribbon会自动的帮助你基于某种规则(如简单轮询、随机连接等)去连接及其
我们很容易使用Ribbon来实现自定义的负载均衡算法
LB负载均衡(Load Balance)是什么
简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。常见的负载均衡有软件Nginx,LVS,硬件F5等。
Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡区别
-
Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。
集中式LB
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx)由该设施负责把访问请求通过某种策略转发至服务的提供方; -
本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到 JVM本地,从而在本地实现RPC远程服务调用技术。
进程内LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址
负载均衡算法:
- 随机,通过随机选择服务进行执行,一般这种方式使用较少;
- 轮训,负载均衡默认实现方式,请求来之后排队处理;
- 加权轮训,通过对服务器性能的分型,给高配置,低负载的服务器分配更高的权重,均衡各个服务器的压力;
- 地址Hash,通过客户端请求的地址的HASH值取模映射进行服务器调度。
- 最小链接数;即使请求均衡了,压力不一定会均衡,最小连接数法就是根据服务器的情况,比如请求积压数等参数,将请求分配到当前压力最小的服务器上。
- 其他若干方式。
Ribbon 其实是一个软负载均衡的客户端组件
它可以和其他所需请求的客户端结合需求
依赖
eruka框架已经使用了Ribbon的依赖,引入eruka 的依赖则不需要引入ribbon
RestTemplate的使用
常用Get、Post
- getForObject():可以理解为返回json
- getForEntity():返回的是对象
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL + "payment/get/" + id, CommonResult.class);
}
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPayment2(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> entity=restTemplate.getForEntity(PAYMENT_URL + "payment/get/" + id, CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
return entity.getBody();
}
else{
return new CommonResult<>(444,"操作失败");
}
}
Ribbon负载均衡演示
IRule:
根据特定算法中选择其中一个
如何替换
自定义配置类不能放在@ComponentScan所扫描的及其子包下,以达到定制的目的
创建MySelfRule规则类
需要单独创建一个myrule,要和OrderMain81类分开(@SpringApplication注解会自动扫描子包)