SpringCloud学习笔记 02、SpringCloud六大基础组件使用
@[toc]
前言
本篇博客是SpringCloud的学习笔记,主要介绍SpringCloud中的基础组件,若文章中出现相关问题,请指出!
所有博客文件目录索引:博客目录索引(持续更新)
SpringCloud与Springboot版本对应
Spring Cloud 2020.0.0发布 再见了Netflix
简单一句话概括:Spring Cloud 2020.0.0版本彻底删除掉了Netflix除Eureka外的所有组件。
Release Train 发布时间 Spring Boot版本 SC Commons版本
2020.0.x 2020-12 2.4.x 3.0.0
Hoxton 2019-07 2.2.x, 2.3.x (从SR5起) 2.2.x
Greenwich 2018-11 2.1.x 2.1.x
Finchley 2017-10 2.0.x 2.0.x
Edgware 2017-08 1.5.x 1.3.x
Dalston 2017-05 1.5.x 1.2.x
Brixton 2016-09 1.3.x 1.1.x
Angel 2016-05 1.2.x 1.0.x
一、搭建Eureka服务(Eureka Server)
1.1、基础知识
微服务架构往往是相对于比较庞大的,复杂的工程。要建立微服务,首先要先建立好对应的微服务模块,也要建立一系列的基础设施与组件。
用于进行服务发现与注册。
是Netflix的一个组件,只需要使用服务的标识符就能够进行访问服务了,而不需要去修改服务的配置文件,其他的客户端可以使用Eurake client来进行连接服务端。
为什么需要服务注册与发现?
- IP变化
- 难以维护。例如服务之间调用,可能会互相进行远程调用接口,那么对应指定要调用的服务端口或ip地址发生改变那么就需要重新修改配置文件等等,此时我们就通过服务注册发现来进行对服务的统一管理。
- 改进,一般每个服务都会有对应的节点,有对应的消费者与服务者。首先消费者与服务者都在服务中心进行注册,之后消费者需要进行消费服务也都会通过服务中心进行。
架构
服务消费者、生产者都需要在Eureka Server上进行注册。服务者可以进行续约、取消操作
下面是Eureka的架构图,Eureka Server可以有多个节点,防止一个宕机服务就无法提供,不同的客户端可以向Server进行远程调用请求等等操作
1.2、开发一个Eureka Server服务
引入依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- SpringCloud指定版本 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 这里eureka-server依赖不需要指定版本,它会自动根据上面spring-cloud的对应版本依赖中去找 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
yaml配置文件编写:
server:
port: 8000
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
client:
fetch-registry: false #fetch-registry:获取注册表。不需要同步其他节点数据。(当前没有建立eureka server集群,这里不需要同步节点)
register-with-eureka: false # 代表是否将自己也注册到Eureka Server,这里不注册
service-url: # 设置服务的地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
最终在springboot启动类上添加启动注解:
@EnableEurekaServer //Eureka Server服务端
可以看到当前没有实例被注册,该页面是Eureka服务管理界面,下面包含了对应的相关信息等。
我们可以看一下当配置文件中register-with-eureka: true
设置为默认值true时,可以看到自己也会被注册到服务中心上去:
额外:Eurake Server整合Springboot版本问题
上面案例使用的springcloud的版本依赖(Greenwich.SR5)只能配对较低版本的springboot,若是配对springboot2.4.5就会报各种各样的错误,想要兼容2.4.5则需要引入对应其他版本的springcloud依赖,如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
已经测试可以兼容springboot2.4.5,对应2020.0.2是今年出的springcloud依赖版本!
注意:springcloud中2020.0.2版本里的Hystrix已经被替代掉了,建议学习使用的springcloud版本为上面green的。
二、服务改造(Eureka Client)
原本服务改造(实现注册)
①将原本提供的单个服务进行改造,引入对应的client依赖。
<!-- Eureka-client:进行注册到服务中心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 父模块声明即可,之后上面的依赖就会根据其进行导入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
②配置文件中进行配置client地址
# 指定注册的服务中心地址,一般与eureka-server中配置的对应,此时该服务启动就会将自己注册到服务中心
eureka:
client:
service-url:
defaultZone: http://localhost:8000/eureka/
启动项目效果:对应的client服务就会自动连接注册到服务中心去
注意其中的application名称如果你不在yaml配置文件配置spring.application.name的话就会像上面一样为UNKNOEW,下面是配置好了的:
其他的服务也是如何进行配置!
- 老的版本对应client服务需要加上注解@EnableEurekaClient,实际上不加也是ok的,原因就是我们依赖的是比较高级的依赖,如starter-netflix-eureka-client,所以可省略该注解。
之后让两个模块实现调用
三、Feign(远程调用)
3.1、Feign历史介绍
早期叫做Open Feign,早期叫做netflix-feign,netflix是最早开发以及维护feign的公司,后来将这个组件捐献出去了,就给到springcloud团队。其团队拿到netflix-feign之后在其基础上进行开发升级,之后才更改为open-feign.
声明式的模板式的HTTP客户端,若是我们在springcloud中使用这个组件能够非常方便的调用http的远程请求,调用远程方法如调用本地方法一样简单,基于接口与注解的方式实现的。
左边生产者提供服务的,首先进行注册,接着从注册中心将所有的服务地址拉取下来,接着consumer利用一定的算法(Ribbin)做负载均衡来去调用服务端
不利用feign需要手写http请求,包括去传入参数、请求头、cookie这一系列的工作,需要配置一系列之后才能够发送请求十分繁琐,而使用feign之后调用远程方法就像是调用本地方法一样容易
3.2、springboot服务集成Feign
集成过程及思路
前提:你需要先搭建eureka server服务接着还要进行调用的服务改造为client,确保多个服务能够被注册到服务中心上去之后我们才能够进行下面的步骤来进行远程调用!
步骤:
1、引入依赖:
<!-- 远程调用:feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringCloud指定版本 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
2、启动类上标注开启注解
@EnableFeignClients //开启远程调用
3、引入对应要调用的服务包依赖(若是对应的接口方法中使用到对应包中的一些类,则需要引入依赖)
4、创建client目录,添加client接口
/**
* @ClassName CourseListClient
* @Author ChangLu
* @Date 2021/10/4 16:36
* @Description TODO
*/
@FeignClient("course-list") //对应服务中心的application,也就是对应的服务配置进行声明spring.application.name=course-list
public interface CourseListClient {
@GetMapping("/course/list") //对应该接口的url路径
Object getList(); //对应controller方法,建议就是直接把原本的controller方法拿过来即可,把方法体和public去掉即可!!!
}
OK,准备步骤完成,我们来在该配置的服务中进行调用使用
@RestController
@RequestMapping("/courseprice")
public class CoursePriceController {
//同样通过Spring的依赖注入,将动态代理类注入进来
@Autowired
private CourseListClient courseListClient;
@GetMapping("/test")
public Object testFeignCourseService(){
//进行调用远程方法,真的就像调用本地方法一样!!!不再需要自己指定url路径、请求体、参数一系列东西,两个字舒服!!!
return courseListClient.getList();
}
}
测试一下,调用另外一个模块成功:
思考
本质:远程调用其实本质同样就是发送http请求,传送的对象最终会解析成消息体。
最核心的作用是为 HTTP 形式的 Rest API 提供了非常简洁高效的 RPC 调用方式。
好处:省去了我们编写 HTTP 连接,数据解析获取等一系列繁琐的操作。
四、实现负载均衡(Ribbon)
4.1、认识负载均衡
负载均衡类型
负载均衡两种类型:
- 客户端负载均衡:Ribbon
- 服务端负载均衡:Nginx
对于客户端:在调用的时能够看到对应的所有节点,我依次去调用即可,由我调用的人去考虑问题。
- 一般用于服务之间内部进行调用会使用Ribbon,而不是nginx(使用的话无非将我们的链路增长),
对于服务端:所有的请求先来到nginx,由它来进行分发转发
- 像用户来发送请求,统一Nginx收取进行分发。
认识策略
负载均衡策略三种
RandomRule
:表示随机策略,每次都会随机调用RoundRobinRule
:表示轮询策略,表示一个个进行调用,比较好的能够平衡各个节点压力ResponseTimeWeightedRule
加权:根据每一个Server的平均响应时间动态加权。也就是说会进行评估,例如三个节点,有一个节点机器比较差,后两个节点机器比较好,使用该策略就会去进行动态评估,机器不好的会少给几个请求
4.2、实操配置ribbon
如何配置策略?
- 通过在yaml中添加
Ribbon.NFLoadBalancerRuleClassName
进行配置。
实操配置:例如当前course-price服务要远程调用course-list服务,此时我们可以进行配置ribbon配置策略
course-price服务的配置内容:
# 服务id=>命名空间=>配置属性
course-list: # 当前远程调用的服务application名
ribbon:
NFLoadBanlancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule # 轮询策略
course-list服务配置:
ribbon:
eureka:
enabled: true
五、断路器Hystrix
5.1、认识断路器
进行远程调用的时候出现了延迟或者服务不可用就能够起到保护的功能。
当某一个单元发生故障的时候,就可以利用断路器把它隔离出去。帮助我们快速优雅的构建断路的功能,当访问异常的时候就返回默认的响应,而不用长时间的让用户去等待,避免在分布式系统中出现故障导致蔓延。
下面是某个请求调用多个服务时某个服务出现故障导致延迟一定时间;右边则是多个请求对同一个服务进行请求时,若是该服务故障则会导致连接数量急剧增多,容易导致服务器瘫痪。
5.2、集成Hystrix
前提:一般的话是集成好了Feign之后,我们才会对某个服务使用断路器。
配置:
1、引入依赖
<!-- 引入断路器模块 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- SpringCloud指定版本,springboot2.1.x -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
2、yaml打开断路器
feign:
hystrix:
enabled: true # 远程调用断路器
3、springboot启动器添加注解
@EnableCircuitBreaker //打开断路器
4、对默认情况进行配置,找到指定client接口,对应的注解配置fallback指定类,该类则是调用异常默认返回的方法,这里写的就是发送错误时的方法
//指定fallback,也就是当远程调用的服务不可用时对应接口的默认返回
@FeignClient(value = "course-list",fallback = CourseListClientHystrix.class)
public interface CourseListClient {
@GetMapping("/course/list")
List<Course> getList();
}
//1、同样实现CourseListClient接口,重写其中的对应的接口方法,该方法用于作为服务不可调用时返回的默认值
@Component
public class CourseListClientHystrix implements CourseListClient{
@Override
public List<Course> getList() {
Course course = new Course();
course.setId(0);
course.setCourseId(0);
course.setName("Java从入门到精通");
course.setValid(0);
return Collections.singletonList(course);
}
}
效果:一旦getListz()接口远程调用能够访问就会返回调用远程接口返回的值,若是对应服务不可用或者其他问题时就会返回我们指定接口的默认值。
六、网关Zuul(统一管理)
为什么需要网关?
- 对于分布式远程调用,服务与服务之间都能够进行互相调用,若是我们在某个服务上做鉴权或者说单个自己开个服务用于鉴权,都是会有一定的问题的,单个服务上做鉴权,那么其他服务都需要额外再写一套;单个服务做鉴权,你还要考虑转发的问题。此时就会出现签名校验、登陆校验冗余等问题。
- 此时就出现了Zuul网关,用于统一管理多个服务,想要访问某个服务必须先要通过网关之后才能够访问到服务。
6.1、springboot集成Zuul网关
引入依赖:
<dependencies>
<!-- 用于服务注册 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- zuul网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
添加一个启动器类:
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
/**
* @ClassName ZuulGatewayApplication
* @Author ChangLu
* @Date 2021/10/5 8:27
* @Description 网关启动类
*/
@EnableZuulProxy //开启Zuul代理
@SpringCloudApplication //包含springboot启动器注解、开启服务注册发现、开启断路器
public class ZuulGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApplication.class,args);
}
}
配置文件:
spring:
application:
name: zuul-gateway # 注册到服务中心的应用名称
server:
port: 9000
# 指定注册的服务中心地址,一般与eureka-server中配置的对应,此时该服务启动就会将自己注册到服务中心
eureka:
client:
service-url:
defaultZone: http://localhost:8000/eureka/
此时我们就已经搭建了一个网关Zuul的服务,我们将项目启动:可以看到zuul网关也被注册到服务中心去了
此时我们就可以通过这个zuul网关来进行访问其他两个服务:
①http://localhost:9000/course-list/course/list:9000端口(网关)、服务名(application name)、uri接口路径
②http://localhost:9000/course-price/courseprice/courseprice:9000端口(网关)、服务名(application name)、uri接口路径
我们可以看到通过走9000端口的网关服务也能够去访问到同在注册中心的其他服务,在这里我们是没有对zuul服务的访问前缀作限制,这里就是/。
再说明一下,不走网关的话像之前一样只要你知道对应的ip地址端口号以及服务名你同样能够访问到该服务,这都是没有影响的:
6.2、配置路由地址(针对于服务)
在6.1中,我们通过搭建一个zuul网关服务将其注册到服务中心,此时我们就可以通过该网关来访问到其他的服务,默认访问官网服务是没有对应的前缀/xxx的,并且访问指定的服务需要根据在服务中心上注册的application name
才能够进行访问到指定的服务,下面来进行配置zuul网关服务的前缀url以及对应其他服务的路由地址。
下面是在zuul服务的yaml配置中的内容:
# zuul的相关配置
zuul:
prefix: /changlu # 访问zuul网关的前缀url(由原来的/ => /changlu)
routes: # 路由配对,下面有两组服务
course-list:
path: /list/** # 2、指定的服务路由转换 (例如由原来的/course-list => /list)
service-id: course-list # 1、指定的服务名(application name):课程服务
course-price:
path: /price/**
service-id: course-price
配置好之后看效果:
# 配置之前访问服务地址
http://localhost:9000/course-list/course/list
http://localhost:9000/course-price/courseprice/courseprice
# 配置之后访问服务地址
http://localhost:9000/changlu/list/course/list
http://localhost:9000/changlu/price/courseprice/courseprice
同样说明一下,不走网关直接访问服务也是ok的!
6.3、实现网关过滤器(统计请求时长)
介绍过滤器
首先介绍下面几个过滤器:
- pre 过滤器在路由请求之前运行
- route 过滤器可以处理请求的实际路由
- post 路由请求后运行过滤器
- error 如果在处理请求的过程中发生错误,则过滤器将运行
实现pre、post过滤器,统计请求执行时间
PreRequestFilter
:前置请求过滤器
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
/**
* @ClassName PreRequestFilter
* @Author ChangLu
* @Date 2021/10/5 9:26
* @Description 前置请求过滤器
*/
@Component
public class PreRequestFilter extends ZuulFilter {
@Override
public String filterType() {
//过滤器类型
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
//过滤器顺序
return 0; //0-1000 从小到大顺序依次执行,这里表示第一个执行
}
@Override
public boolean shouldFilter() {
return true; //是否启动过滤器
}
//具体过滤器中执行的方法
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
currentContext.set("startTime",System.currentTimeMillis());
System.out.println("前缀过滤器pre已经记录时间");
return null;
}
}
PostRequestFilter
:后置器请求过滤器
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
/**
* @ClassName PostRequestFilter
* @Author ChangLu
* @Date 2021/10/5 9:31
* @Description 后置请求处理器
*/
@Component
public class PostRequestFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
/**
* filter执行顺序,值越小优先级越高
* 官方推荐使用x-1方式优先排序
* @return
*/
@Override
public int filterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER-1; //响应过滤器为1000,这里-1表示该过滤器越优先执行
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
Long startTime = (Long) currentContext.get("startTime");
Long duration = System.currentTimeMillis() - startTime;
String uri = currentContext.getRequest().getRequestURI();
System.out.println("uri:"+uri+",处理时长为:"+duration);
return null;
}
}
效果:统计访问一个请求时处理的时长!
参考文章
Spring Cloud 2020.0.0发布 再见了Netflix:关于SpringCloud的版本信息
[2]. Feign远程调用原理:仅仅是围绕Feign展开描述
[3]. Spring Cloud OpenFeign 工作原理解析:基于springcloud来进行讲述,包含原理
- 点赞
- 收藏
- 关注作者
评论(0)