一、SpringCloud的概念
业务场景介绍
开发一个电商网站,要实现支付订单的功能,流程如下:
1.创建一个订单之后,如果用户立刻支付了这个订单,我们需要将订单状态更新为“已支付”
2.扣减相应的商品库存
3.通知仓储中心,进行发货
4.给用户的这次购物增加相应的积分
服务分析
订单服务、库存服务、仓储服务、积分服务
流程调用
用户针对一个订单完成支付之后,就会去找订单服务,更新订单状态
订单服务调用库存服务,完成相应功能
订单服务调用仓储服务,完成相应功能
订单服务调用积分服务,完成相应功能
SpringCloud
SpringCloud核心组件:Eureka
Eureka是微服务架构中的注册中心,专门负责服务的注册与发现。
订单服务想要调用库存服务、仓储服务,或者是积分服务,怎么调用? 订单服务压根儿就不知道人家库存服务在哪台机器上啊!他就算想要发起一个请求,都不知道发送给 谁,有心无力!
Eureka Client:负责将这个服务的信息注册到Eureka Server中
Eureka Server:注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号
Spring Cloud核心组件:Feign
现在订单服务确实知道库存服务、积分服务、仓库服务在哪里了,同时也监听着哪些端口号了。 但是新问题又来了:如何从订单服务跟其他服务建立网络连接,接着发送请求过去。
Spring Cloud核心组件:Ribbon
集群服务:库存服务部署在了5台机器上
192.168.169:9000
192.168.170:9000
192.168.171:9000
192.168.172:9000
192.168.173:9000
Ribbon就是专门解决这个问题的。它的作用是负载均衡,会帮你在每次请求时选择一台机器,均匀的把 请求分发到各个机器上
首先Ribbon会从 Eureka Client里获取到对应的服务注册表,也就知道了所有的服务都部署在了哪 些机器上,在监听哪些端口号。
然后Ribbon就可以使用默认的Round Robin算法,从中选择一台机器 Feign就会针对这台机器,构造并发起请求。
Spring Cloud核心组件:Hystrix
在微服务架构里,一个系统会有很多的服务。 以上面的业务场景为例:订单服务在一个业务流程里需要调用三个服务。 现在假设订单服务自己最多只有100个线程可以处理请求,然后呢,积分服务不幸的挂了,每次订单服 务调用积分服务的时候,都会卡住几秒钟,然后抛出—个超时异常
出现问题:微服务架构中的服务雪崩问题
如果系统处于高并发的场景下,大量请求涌过来的时候,订单服务的100个线程都会卡在请求积分 服务这块。导致订单服务没有一个线程可以处理请求 然后就会导致别人请求订单服务的时候,发现订单服务也挂了,不响应任何请求了
Hystrix是隔离、熔断以及降级的一个框架。
比如订单服务请求库存服务是一个线程池,请求仓储服务是一个线程池,请求积分服务是一个线程池。 每个线程池里的线程就仅仅用于请求那个服务。
Spring Cloud核心组件:Zuul
这个组件是负责网络路由的。
一般微服务架构中都必然会设计一个网关在里面,像android、ios、pc前端、微信小程序、H5等等。 不用去关心后端有几百个服务,就知道有一个网关,所有请求都往网关走,网关会根据请求中的一些特 征,将请求转发给后端的各个服务。 有一个网关之后,还有很多好处,比如可以做统一的降级、限流、认证授权、安全,等等。
组件总和
Eureka:各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还 可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里
Ribbon:服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台 Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求 Hystrix:发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务 调用的隔离,避免了服务雪崩的问题 Zuul:如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务
流程
1. 请求统一通过API网关(Zuul)来访问内部服务.
2. 网关接收到请求后,从注册中心(Eureka)获取可用服务
3. 由Ribbon进行均衡负载后,分发到后端具体实例
4. 微服务之间通过Feign进行通信处理业务
5. Hystrix负责处理服务超时熔断
二、nacos搭建
1.下载nacos服务并启动
复制路径可以到nacos界面,用nacos作为账号的密码可进入
2.创建maven项目
3.修改pom文件
①修改packaging属性
②增加dependencyManagement标签
作用:用来管理jar包的版本,让子项目中引用一个依赖而不用显示的列出版本号
dependencyManagement与dependencies区别:
dependencies即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继 承) dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依 赖。 如果不在子项目中声明依赖,是不会从父项目中继承下来的。 只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和 scope都读取自父pom。 另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
③添加spring-boot,spring-cloud,spring-cloud-alibaba的版本号
<spring-boot.version>2.4.1</spring-boot.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
④添加dependencyManagement依赖和dependencies依赖
dependencyManagement依赖:
<dependencies>
<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-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
dependencies依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
⑤将pom依赖的插件全部删除
最终pom依赖内容
<?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.lj</groupId>
<artifactId>SpringCloud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>provider</module>
<module>consumer</module>
<module>commons</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-boot.version>2.4.1</spring-boot.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
</properties>
<!-- 子类会继承依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 远程通信 Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.cloud02</groupId>
<artifactId>code</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<dependencyManagement>
<!-- 子类会继承版本 -->
<dependencies>
<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>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
4.添加nacos数据库表
找到nacos-service的conf文件夹,运行sql脚本
5.新建新模块(springBoot项目)
pom依赖中继承springCloud
<parent>
<artifactId>SpringCloud</artifactId>
<groupId>org.lj</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
springCloud_01依赖中也需要modules标签(用来管理同个项目中的各个模块)
6.在生成模块中添加yml文件
spring:
application:
name: nacos-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8082
7.在启动类中添加注解@EnableDiscoveryClient
8.同样创建消费者模块
注意:yml文件的端口号要不同
运行各模块的启动类,nacos中
三、生产者消费者代码编写
1.创建生成者和消费者的controller层
生产者
发起请求
在消费者的启动类中添加方法RestTemplate
为了使消费者跨服务访问
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
消费者controller层
package com.consumer.code.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @author My
*/
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/run")
public String run (){
return restTemplate.getForObject("http://nacos-provider/run",String.class);
}
}
可以使用nacos访问 http://provider/run是生产者的请求路径,这个也可以http://127.0.0.1/8081/run
nacos-provider是nacos的名字
但是如果使用nacos访问的话要在添加pom依赖(负载均衡)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
此时消费者就可以拿到生产者数据了