SpringCloud开发文档
自己使用springcloud时,组件搭配如下:服务发现eureka、负载均衡ribbon、Web服务客户端openfeign、断路器hystrix、网关zuul、配置中心config、消息总线bus。文章也将介绍这些组件的使用。
一、服务发现Eureka
Eureka是一个基于REST的服务注册中心,它可以让服务提供者和服务消费者在其中注册和发现对方,从而实现服务间的通信和负载均衡。Eureka由Eureka Server和Eureka Client组成,Eureka Server提供注册服务的功能,Eureka Client提供注册和发现的功能,以及内置的负载均衡器。Eureka还支持集群模式,可以提高注册中心的可用性和容错性。简言之,Eureka用于替换传统的RestTemplate远程调用。
(一)产生的业务场景
Eureka产生的业务场景是为了解决微服务架构中服务间的注册和发现问题。在微服务架构中,服务的数量和分布是动态变化的,因此需要一个中心化的服务来管理服务的信息,如服务名、IP、端口等。Eureka就是这样一个基于REST的服务发现框架。
(二)快速搭建
1.新建一个模块,名为eureka
2.服务端pom.xml文件引入依赖
<!--Eureka服务端依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
3.服务端启动类上添加@EnableEurekaServer注解
//启用Eureka服务端
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
4.服务端application.yml文件中配置相关参数
spring:
application:
name: eureka #挂载到eureka服务器上显示的名字
server:
port: 6061 #该资源模块的端口
eureka: #Eureka核心配置
instance:
hostname: localhost #主机名,服务器IP地址
client: #客户端配置,Eureka服务端也可以作为一个客户端
# registerWithEureka: false #默认true,eureka server服务器是否注册自己,单机版可以不用注册自己
# fetchRegistry: false #默认true,是否获取注册中心服务服务端的服务信息
serviceUrl: #配置eureka server服务器地址,可以打开这个网址
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://localhost:6061/eureka/ #挂载到服务器的地址
5.客户端pom.xml文件引入依赖
<!--Eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
6.客户端启动类上添加@EnableEurekaClient注解
@EnableEurekaClient
@SpringBootApplication
public class StuentProviderApplication {
public static void main(String[] args) {
SpringApplication.run(StuentProviderApplication.class, args);
}
}
7.客户端application.yml文件中配置相关参数
spring:
application:
name: student-provider #挂载到eureka服务器上显示的名字
eureka:
client:
# register-with-eureka: true 默认开启,将这个提供具体功能的模块 挂载 到eureka server服务器上
# fetch-registry: true 默认开启,默认true,是否获取注册中心服务服务端的服务信息
service-url:
defaultZone: http://localhost:6061/eureka #挂载到服务器的地址
这样,就能将各模块挂载到eureka上了,可以访问http://localhost:6061/eureka/查看。
8.eureka远程调用格式
远程调用依旧需要RestTemplate类的支持,将该类注入。
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
在controller层中,需要注入DiscoveryClient,并自己编写远程调用策略。实例如下:
@Autowired
private DiscoveryClient discoveryClient;
public String serviceUrl() {
List<ServiceInstance> list = discoveryClient.getInstances("student-provider");
if (list != null && list.size() > 0 ) {
return list.get(0).getUri()+"";
}
return null;
}
@Autowired
RestTemplate restTemplate;
@GetMapping("/all")
public Object all(){
return restTemplate.getForEntity(serviceUrl()+"/student/all", ResponseResault.class);
}
9.拓展:eureka服务端集群配置
只需修改配置文件,通过-Dport命令新开一个端口模拟集群效果。
spring:
application:
name: eureka
server:
port: ${port:6061}
eureka: #Eureka核心配置
instance: #实例,
hostname: ${hostname:a} #主机名,服务器IP地址,集群部署,不能有相同的名字
client: #客户端配置,Eureka服务端也可以作为一个客户端
# registerWithEureka: false #默认true,是否注册到Eureka服务端,单机版可以不用注册自己,集群配置,必须为真
# fetchRegistry: false #默认true,是否获取注册中心服务服务端的服务信息
serviceUrl: #客户端注册到服务端,这里就是配置服务端地址
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: ${defaultZone:http://localhost:6062/eureka/}
(三)技术要点
1.心跳机制
客户端每过30秒会向服务端发送心跳包,超过3轮90秒,没有发送,服务器可以认为服务故障,可以从服务列表中删除服务。在客户端配置:
eureka:
instance:
lease-renewal-interval-in-seconds: 30 #续约时间
lease-expiration-duration-in-seconds: 90 #失效续约时间
2.自我保护机制
默认是启动的,Eureka会将15分钟内心跳率低于85%的服务保护起来,为了保证服务可用。在服务端配置:
eureka:
server:
enable-self-preservation: true #自我保护
eviction-interval-timer-in-ms: 60000 #服务剔除时间,默认值60s,过多久执行剔除策略
3.剔除策略
Eureka服务端会创建一个定时任务,每隔一段时间(默认为60秒)检查当前清单中超时(默认为90秒)没有续约的服务实例,并将它们从清单中移除,这个操作被称为失效剔除。
二、负载均衡Ribbon
Ribbon是一个客户端负载均衡器,它可以根据一定的规则,从多个服务提供者中选择一个合适的服务进行调用。Ribbon可以与Eureka Client结合使用,从Eureka Server中获取服务列表,并根据负载均衡策略进行选择。Ribbon也提供了多种负载均衡策略,如轮询、随机、权重等,也可以自定义负载均衡策略。简言之,Ribbon进一步简化远程调用,同时支持负载均衡。
(一)产生的业务场景
Ribbon的诞生是为了解决服务端负载均衡器的局限性,例如无法满足不同消费者(客户端)的负载均衡策略需求,以及增加了网络延迟和故障风险等问题。
(二)快速搭建
1.客户端pom.xml引入依赖
客户端在引入eureka-client依赖时,已经自动引入Ribbon的依赖。不需要手动添加依赖。
2.在RestTemplate配置类的方法上加上@LoadBalanced注解
@Bean
@LoadBalanced //ribbon方式调用微服务,不需要再手写调用方法,负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
3.修改调用方法
不需要注入DiscoveryCLient了,并重写serviceUrl()方法。
public String serviceUrl() {
return "http://STUDENT-PROVIDER"+"/student/";
}
4.ribbon负载均衡算法,默认是轮询
student-provider: #调用的服务名
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #指定为随机
(三)技术要点
1.nginx和ribbon负载均衡的区别
-
nginx是一个服务端的负载均衡器,它可以作为一个反向代理服务器,拦截客户端的请求,并根据配置的策略转发给后端的服务器。
-
ribbon是一个客户端的负载均衡器,它可以从注册中心获取可用的服务列表,并根据自己的算法选择调用哪个服务。
ribbon在消费者(调用者)配置,由调用者决定选择哪一个生产者。(eureka server服务器可以做集群,同时生产者(提供服务的模块)也可以做集群)。
三、Web服务客户端OpenFeign
OpenFeign是一个声明式的Web服务客户端,它可以让服务消费者通过接口和注解的方式,轻松地调用服务提供者的接口。OpenFeign可以与Eureka和Ribbon结合使用,从而实现服务的注册、发现和负载均衡。OpenFeign还提供了对Hystrix的支持,可以实现服务的熔断和降级。简言之,OpenFeign又进一步简化了远程调用。
(一)产生的业务场景
OpenFeign的诞生是为了简化和优化Feign的使用。
(二)快速搭建
1.在调用模块的pom.xml中添加依赖
<!--OpenFeign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.启动类上添加@EnableFeignClients注解
@EnableFeignClients //启用openfeign调用
@EnableEurekaClient
@SpringBootApplication
public class StudentConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(StudentConsumerApplication.class, args);
}
}
3.定义远程调用接口
被调用模块中有什么方法,就声明什么方法。
//Feign的远程调用,定义接口,底层使用AOP,自动生成接口的实现类
@FeignClient("student-provider") //被调用模块的服务名
@RequestMapping("/student")
public interface StudentProviderFeignClient {
@GetMapping("/")
public ResponseResult getAllStudents();
@GetMapping("/{sid}")
public ResponseResult getStudentBySid(@PathVariable("sid") int sid);
@PostMapping("/")
public ResponseResult addStudent(@RequestBody Student student);
}
4.修改调用方法
@Autowired
StudentProviderFeignClient studentProviderFeignClient;
@GetMapping("/all")
public Object all() {
// return restTemplate.getForEntity(serviceUrl() + "/all", ResponseResault.class);
return studentProviderFeignClient.all();
}
(三)技术要点
1.微服务调用方式总结
- 原生RestTemplate远程调用
- Eureka格式的远程调用
- Ribbon格式的远程调用
- OpenFeign格式的远程调用
2.openfeign超时策略
openfeign远程调用另一个资源,默认时间超过一秒,会报读取时间超时异常。可以修改配置文件。在调用模块中配置。
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
四、断路器Hystrix
Hystrix是一个服务熔断和降级的组件,它可以在服务调用出现异常或超时的情况下,触发熔断机制,阻止继续调用该服务,从而避免雪崩效应。Hystrix还可以提供降级机制,当服务不可用时,返回一个预设的默认值或执行一个备用逻辑,从而保证服务的可用性。Hystrix还提供了一个仪表盘,用于监控服务的熔断和降级的状态和指标。简言之,Hystrix断路器提供降级、熔断、限流等功能,避免雪崩效应。
(一)产生的业务场景
Hystrix 的诞生是为了解决分布式系统中的联动故障问题,即当某个服务出现故障时,会导致依赖该服务的其他服务也出现故障,从而引发雪崩效应,影响整个系统的正常运行。
(二)快速搭建
1.在调用模块的pom.xml中添加依赖
<!--hystrix断路器依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.启动类上添加@EnableHystrix注解
//@EnableDiscoveryClient
//断路器通用:Hystrix和Sentinel都可以
//@EnableCircuitBreaker
//Hystrix专用注解
@EnableHystrix
@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class StudentConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(StudentConsumerApplication.class, args);
}
}
3.yml配置文件还需开启使用
feign:
hystrix:
enabled: true
4.接口加上fallback属性
在之前openfeign创建的接口的@FeignClient注解上加fallback属性,指定降级方法所在类。
@FeignClient(name = "student-provider",fallback = StudentProviderFallback.class) //根据名字寻找对应模块中的方法,然后自动生成实体类
//注意这里不能直接在类上加映射,否则会报重复生成类错误
public interface StudentProviderFeignClient {
@GetMapping("/student/all")
public Object all();
@GetMapping("/student/getById/{id}")
public Object getById(@PathVariable("id") String id);
@PostMapping("/student/add")
public Object add(@RequestBody Student student);
}
5.定义降级方法
在调用模块中,新建类实现我们创建的远程调用的接口 ,配置降级方法(一般返回响应结果),返回值、参数都必须与被调用模块中的方法一致。如下实例:
@Component
public class StudentProviderFallback implements StudentProviderFeignClient{
@Override
public Object all() {
return new ResponseResault(200,"all-fallback",null);
}
@Override
public Object getById(@PathVariable("id") String id) {
return new ResponseResault(200,"getById-fallback",null);
}
@Override
public Object add(@RequestBody Student student) {
return new ResponseResault(200,"add-fallback",null);
}
}
(三)技术细节
1.Hystrix和异常处理的区别
Hystrix如果已经出了问题,错了几次,跳过该方法,直接执行降级方法(减少服务器压力),过一会会再次执行看好了没有。而异常处理,每次都会执行异常部分,然后再catch处理异常,消耗了很多服务资源。
2.Hystrix底层原理
断路器有三个状态:关闭,打开,半开。
开始,断电器关闭。当请求接口,接口出现问题,执行降级方法(达到一定的阈值条件:10秒,20个请求,错误率超过50%),不会执行请求接口,直接调用降级方法。熔断5秒以后,进入半开状态:放一个请求尝试执行请求接口,如果可以正常请求,断路器关闭,请求链路恢复正常;如果还是出现问题,继续熔断。
五、网关Zuul
Zuul是一个服务网关,它可以提供路由、过滤、安全等功能,对外暴露统一的访问入口,对内屏蔽服务的细节。Zuul可以与Eureka和Ribbon结合使用,实现动态路由和负载均衡。Zuul还提供了多种过滤器,用于实现请求的校验、转换、限流等功能,也可以自定义过滤器。简言之,Zuul可以为微服务统一口(类似nginx),并且可以统一身份认证,鉴权。
(一)产生的业务场景
Zuul 的产生是为了解决微服务系统中的复杂性和多样性问题,例如如何管理多个服务的访问、如何保证服务的可用性和性能、如何保护服务的安全和隐私等。
(二)快速开发
1.新建一个模块,名为zuul
2.服务端pom.xml文件引入依赖
<!--zuul依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
3.服务端启动类上添加@EnableZuulProxy注解
@EnableZuulProxy //启用zuul网关
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,args);
}
}
4.服务端application.yml文件中配置相关参数
一共有四种方式,介绍最简洁的一种。(忘了翻笔记)
zuul:
routes:
provider-student: /student/** #请求路径,将会映射到provider-student名字对应的ip地址
5.zull的权限认证
实现ZuulFilter方法即可,包含了四个方法。
@Component
public class LoginFilter extends ZuulFilter {
//拦截类型
//per请求执行之前:认证授权
//post请求执行之后
//error发生错误时
//routing路由时执行
@Override
public String filterType() {
return "pre";
}
//执行顺序,数字越小,优先级越高
@Override
public int filterOrder() {
return 0;
}
//是否拦截,
//返回true,执行run()方法
//返回false,不执行run()方法,放行
@Override
public boolean shouldFilter() {
System.out.println("进入shouldFilter");
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
HttpServletResponse response = currentContext.getResponse();
//获取请求参数uname,有这个参数放行
if (!StringUtils.isEmpty(request.getParameter("uname"))) {
System.out.println("进入shouldFilter,有uname,直接放行,不需要run()方法继续判断");
return false;
}
System.out.println("进入shouldFilter,没有uname,进入run()方法继续判断");
return true;
}
//进入run(),可以拦截,也可放行
@Override
public Object run() throws ZuulException {
System.out.println("进入run()方法,继续判断");
//没有uname参数,再判断token参数,有token也方法
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
HttpServletResponse response = currentContext.getResponse();
response.setContentType("text/html;charset=utf-8");
if (!StringUtils.isEmpty(request.getParameter("token"))) {
System.out.println("进入run()方法,有token,放行");
//放行
return null;
} else {
//没有token拦截,网关拦截
System.out.println("进入run()方法,没有token,拦截");
currentContext.setSendZuulResponse(false);
// currentContext.setResponseStatusCode(401);
//响应前端信息,使用统一响应体
ResponseResult responseResult=new ResponseResult(401,"not login","先登录");
ObjectMapper objectMapper=new ObjectMapper();
try {
String strbody = objectMapper.writeValueAsString(responseResult);
// response.getWriter().write(strbody);
currentContext.setResponseBody(strbody);
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
}
}
六、配置中心Config
Config是一个分布式配置中心,它可以让服务从一个统一的外部源获取配置信息,而不是在本地配置文件中。Config可以与Git、SVN等版本控制工具结合使用,实现配置的版本管理和备份。Config还可以与Eureka和Bus结合使用,实现配置的动态刷新和广播。简言之,Config可以统一管理微服务的配置文件。
(一)产生的业务场景
为了统一管理项目中所有配置的系统,同时使得服务可以动态地获取和刷新配置,提高配置的灵活性、可维护性和安全性。
(二)快速搭建
1.创建git远程仓库
各模块的配置文件,按照规范传到git。配置文件起名格式如:/master/模块名-dev.yml
2.新建一个模块,名为config
3.服务端pom.xml文件引入依赖
<dependencies>
<!--配置中心服务端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
4.服务端启动类上添加@EnableConfigServer注解
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
5.服务端application.yml文件中配置相关参数
server:
port: 4041
spring:
cloud:
config:
server:
git:
uri: https://gitee.com/lbsttt/config.git
# username: 私有仓库,git账号密码
# password:
此时,配置中心服务端可以从远程仓库获取配置文件http://localhost:4041/master/studentconsumer-dev.yml
6.客户端pom.xml文件引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
7.客户端创建bootstrap.yml配置文件
SpringBoot的配置文件有两种:应用程序级别的配置文件application.yml,还有一种优先级更高的系统级别的配置文件,bootstrap.yml。因为现在application.yml在git上,我们还需要连接到配置中心服务端才能获取服务。
#连接到配置中心服务端
spring:
cloud:
config:
uri: http://localhost:4041
label: master
name: studentconsumer
profile: dev
现在客户端可以通过配置中心服务端获取配置文件信息,配置中心服务端又从远程git仓库读取出配置文件信息。但是客户端因为缓存的原因,还无法动态刷新,下面的步骤对配置进行动态刷新。
8.客户端pom.xml文件引入依赖
<!-- SpringBoot 监控插件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
9.客户端配置暴露监控头
#连接到配置中心服务端
spring:
cloud:
config:
uri: http://localhost:4041
label: master
name: studentconsumer
profile: dev
management:
endpoints:
web:
exposure:
include: "refresh" #actuator暴露刷新头,前端发送post请求,通知当前微服务读取修改后的最新的配置文件
#这里这个名字可以写include: "*",发送请求的时候必须是refresh这个名字
10.controller添加@RefreshScope 刷新注解
@RefreshScope //刷新注解
@RestController
@RequestMapping("/studentconsumer")
public class StudentController {
11.发送刷新配置请求
在需要刷新配置的时候,发送刷新请求,这里使用windows的curl命令模拟发送请求,其中 uri+/actuator/refresh是固定写法。
curl -X POST http://localhost:7071/actuator/refresh
七、消息总线Bus
Bus是一个消息总线,它可以用于在分布式系统中传递消息,实现服务间的通信和协作。Bus可以与RabbitMQ、Kafka这两个消息中间件结合使用,实现消息的可靠传输。Bus还可以与Config结合使用,实现配置的动态刷新和广播。简言之,Bus解决发送多次刷新配置请求问题。
(一)产生的业务场景
Bus的诞生是为了解决分布式系统中的配置更新和管理问题。多个微服务更新配置,都发送一个refresh请求,很麻烦,现在一个请求解决。
(二)快速搭建
1.服务端pom.xml文件引入依赖
<!-- 添加Spring Cloud Bus和RabbitMQ的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.服务端application.yml文件中配置相关参数
# 配置RabbitMQ的连接信息
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
# 暴露bus-refresh端点,用于接收刷新请求并广播给所有服务实例
management:
endpoints:
web:
exposure:
include: "bus-refresh"
3.客户端pom.xml文件引入依赖
<!-- 添加Spring Cloud Bus-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
4.客户端application.yml文件中配置相关参数
# 配置RabbitMQ的连接信息
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
到此配置中心就搭建完成,现在发送请求刷新配置文件即可。
5.通知连接到mq的所有微服都刷新
发送POST请求到配置中心服务端的bus-refresh端点,它会将请求转发给消息队列,然后消息队列会通知所有订阅了该队列的服务实例刷新配置
curl -X POST http://localhost:4041/actuator/bus-refresh
6.通知消息队列只给特定的微服务发送刷新请求
发送POST请求到配置中心服务端的bus-refresh/{destination}端点,其中{destination}是服务实例的名称和端口号,它会将请求转发给消息队列,然后消息队列会通知指定的服务实例刷新配置
curl -X POST http://localhost:4041/actuator/bus-refresh/微服务名:端口号
同时,config是配置中心的服务端,可以作为Eureka的客户端,注册到eureka。
7.将配置中心服务端注册到eureka
# 配置Eureka的注册中心地址
eureka:
client:
service-url:
defaultZone: http://localhost:4040/eureka/
# 配置服务的名称和端口号
spring:
application:
name: config
cloud:
config:
server:
git:
uri: https://github.com/xxx/config-repo.git # 配置Git仓库的地址
server:
port: 4041
8.配置中心客户端可以通过4041的服务名去查找服务地址
# 配置Eureka的注册中心地址
eureka:
client:
service-url:
defaultZone: http://localhost:4040/eureka/
# 配置服务的名称和端口号
spring:
application:
name: 服务名
cloud:
config:
uri: http://config:4041 # 通过配置中心服务端的服务名和端口号获取配置信息
server:
port: 端口号
- 感谢你赐予我前进的力量