完整教程:spring-cloud使用

nacos -注册中心

nacos是alibaba提供的管理微服务架构的注册中心,在上面可以管理你的每一个服务内容。

引入

前往官网下载zip或者通过docker引入,下载好之后通过cmd命令启动。位置在bin目录下。

startup.cmd -m standalone

这个命令的意思是通过单体项目启动nacos注册中心,启动之后可以看到你的nacos生成网址。
在这里插入图片描述
然后我们就需要在自己的项目中进行配置了,主项目需要引入下面这个包

<dependency>
  <groupId>com.alibaba.cloud<
    /groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery<
      /artifactId>
      <
      /dependency>

让他们被发现nacos发现,然后在子model中,引入web包,让他们成为web服务。

<dependency>
  <groupId>org.springframework.boot<
    /groupId>
    <artifactId>spring-boot-starter-web<
      /artifactId>
      <
      /dependency>

在子model中进行yml配置,通常nacos识别的服务名称就是你设置的applicationName。然后设置nacos服务地址为localhost:8848。

server:
port: 8000
spring:
application:
name: service-order
cloud:
nacos:
server-addr: 127.0.0.1:8848
config:
import-check:
enabled: false

这样你就可以在网址上看到你这个model的服务了。
在这里插入图片描述
之前必须要在启动类上加上注解@EnableDiscoveryClient才可以服务发现,但是从Spring Cloud Edgware开始,@EnableDiscoveryClient 或@EnableEurekaClient 可省略。只需加上相关依赖,并进行相应配置,即可将微服务注册到服务发现组件上。

使用代码获取每个服务信息

通过spring-cloud-nacos提供的discoveryClient接口来获取服务信息。

package com.atguigu.product;
import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import com.alibaba.nacos.api.exception.NacosException;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import java.util.List;
@SpringBootTest
public class DiscoveryTest
{
@Resource
DiscoveryClient discoveryClient;
@Test
void discoveryTest(){
discoveryClient.getServices().forEach(service->
{
List<
ServiceInstance> instances = discoveryClient.getInstances(service);
instances.forEach(serviceInstance ->
{
System.out.println("serviceInstance.getInstanceId() = " + serviceInstance.getInstanceId());
System.out.println("serviceInstance.getHost() = " + serviceInstance.getHost());
System.out.println("serviceInstance.getServiceId() = " + serviceInstance.getServiceId());
System.out.println("serviceInstance.getPort() = " + serviceInstance.getPort());
});
});
}
}

通过获取每个服务下的多个实例,并且将实例内容展示出来。
在这里插入图片描述

discoveryClient和nacosServiceDiscovery

就是spring和nacos提供的区别,如果使用的是前者discoveryClient,不管使用的是什么注册中心,都可以进行调用,而后者是引入nacos后才可以使用的接口。

@Resource
NacosServiceDiscovery nacosServiceDiscovery;
@Test
void discoveryTest() throws NacosException {
nacosServiceDiscovery.getServices().forEach(service->
{
List<
ServiceInstance> instances = null;
try {
instances = nacosServiceDiscovery.getInstances(service);
} catch (NacosException e) {
throw new RuntimeException(e);
}
instances.forEach(serviceInstance ->
{
System.out.println("serviceInstance.getInstanceId() = " + serviceInstance.getInstanceId());
System.out.println("serviceInstance.getHost() = " + serviceInstance.getHost());
System.out.println("serviceInstance.getServiceId() = " + serviceInstance.getServiceId());
System.out.println("serviceInstance.getPort() = " + serviceInstance.getPort());
});
});
}

一样的,具体我就不多写了。

nacos实现远程调用

当我们服务去访问另一个服务的时候,通过nacos可以实现,我们通过获得服务实例,可以以负载均衡的方式,然后通过springboot提供的http请求api进行访问,拿到数据后返回给服务即可。

下面是一个生成订单的服务,需要去远程获取到商品信息,就可以通过下列方式实现。

package com.atguigu.order.service.impl;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import com.atguigu.order.bean.Order;
import com.atguigu.order.service.OrderService;
import com.atguigu.product.bean.Product;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Slf4j
@Service
public class OrderServiceImpl
implements OrderService {
@Autowired
DiscoveryClient discoveryClient;
@Resource
RestTemplate restTemplate;
@Override
public Order createOrder(Long productId, Long userId) {
// Product product = getProductFromRemoteWithLoadBalanceAnnotation(productId);
//使用Feign完成远程调用
Product product = getProFromRemote(productId);
Order order = new Order();
order.setId(1L);
// 总金额
order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum())));
order.setUserId(userId);
order.setNickName("zhangsan");
order.setAddress("尚硅谷");
//远程查询商品列表
order.setProductList(Arrays.asList(product));
return order;
}
private Product getProFromRemote(Long proId){
List<
ServiceInstance> instances = discoveryClient.getInstances("service-product");
ServiceInstance serviceInstance = instances.get(0);
String uri="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/product/"+proId;
log.info("远程调用:{}",uri);
return restTemplate.getForObject(uri, Product.class)
;
}
}

上面的写法全部是访问第一个服务实例,那我们如何使用负载均衡的方式进行获取服务实例呢?

负载均衡方式

首先需要引入spring-cloud为我们提供的实现负载均衡api

<dependency>
  <groupId>org.springframework.cloud<
    /groupId>
    <artifactId>spring-cloud-starter-loadbalancer<
      /artifactId>
      <
      /dependency>

引入loadBalancerClient,使用方法choose即可实现负载均衡。

@Autowired
LoadBalancerClient loadBalancerClient;
private Product getProFromRemoteWithLoadBalance(Long proId){
ServiceInstance serviceInstance = loadBalancerClient.choose("service-product");
String uri="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/product/"+proId;
log.info("远程调用以负载均衡的方式,实例为,{},uri:{}",serviceInstance.getInstanceId(),uri);
return restTemplate.getForObject(uri, Product.class)
;
}
通过注解方式实现负载均衡

使用 @LoadBalanced放到你要远程调用的实例上,然后通过将网址改成你要远程调用的实例就可以实现了。

private Product getProFromRemoteWithLoadBalanceAnnoation(Long proId){
String uri="http://servce-product/product/"+proId;
log.info("远程调用以负载均衡的方式,uri:{}",uri);
return restTemplate.getForObject(uri, Product.class)
;
}
}

nacos-配置中心

nacos核心是什么,就是将各个分开的服务进行统一管理,配置中心就是将服务的配置进行统一管理。

首先要引入依赖

<
!--配置中心-->
<dependency>
  <groupId>com.alibaba.cloud<
    /groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config<
      /artifactId>
      <
      /dependency

然后在nacos中创建一个配置文件。
在这里插入图片描述
通过spring提供的外部配置文件绑定属性,来和nacos创建的文件进行绑定。

spring:
application:
name: service-order
cloud:
nacos:
server-addr: 127.0.0.1:8848
config:
import-check:
enabled: false
config:
import: nacos:service-order.properties

然后就可以获取到里面内容并且进行操作了。

@Value("${order.timeout}")
private String timeout;
@Value("${order.auto-confirm}")
private String autoConfirm;

在这里插入图片描述
在这里有一点,如果你更改了nacos配置文件的内容,项目里并不会自动改变,只有重新启动或者配置了 @RefreshScope才可以刷新。
@RefreshScope写到使用配置文件内容的类上面

@RefreshScope//自动刷新
@RestController
public class OrderController
{
@Autowired
OrderService orderService;
@Value("${order.timeout}")
private String timeout;
@Value("${order.auto-confirm}")
private String autoConfirm;
@GetMapping("/config")
public String config(){
return "autoConfirm = " + autoConfirm+",timeout="+timeout;
}
}

也可以用**@ConfigurationProperties**,这个直接有自动刷新方式。

package com.atguigu.order.properties;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "order")
@Data
public class OrderProperties
{
private String timeout;
private String autoConfirm;
}

也是更推荐这种方式,解耦更好。

配置中心监听

我们可以通过下列代码实现监听

@Bean
ApplicationRunner applicationRunner(NacosConfigManager nacosConfigManager) {
return (args) ->
{
ConfigService configService = nacosConfigManager.getConfigService();
configService.addListener("service-order.properties",
"DEFAULT_GROUP", new Listener() {
//声明一个线程
@Override
public Executor getExecutor() {
return Executors.newFixedThreadPool(4);
}
@Override
public void receiveConfigInfo(String s) {
System.out.println("变化的配置信息:"+s);
System.out.println("邮件通知.........");
}
});
};
}

具体意思就是通过nacosConfigManager来获取配置服务类,通过配置服务类的添加监控方法,将需要监控的配置文件写到里面。

PS:配置中心的配置会覆盖本地配置,因为本地先配置中心后,不过为了统一配置,配置中心的配置应该才是优先级最高的配置。

配置中心-数据隔离

nacos可以通过设置命名空间来进行配置文件隔离。
在这里插入图片描述
设置完命名空间,你在创建配置文件的时候就可以把他们放到不同的 命名空间中,相当于一个文件夹,然后可以用配置文件的group来进行细分。
在这里插入图片描述
然后在项目yml中进行配置。

server:
port: 8000
spring:
application:
name: service-order
cloud:
nacos:
server-addr: 127.0.0.1:8848
config:
namespace: a44d4fc8-aee8-4f5f-ab40-85a0db6d0998
#这里的namespace写的是命名空间的id
config:
import:
- nacos:common.properties?group=order
- nacos:database.properties?group=order

要记住yml中配置的namespace是命名空间id,而不是名称。
在这里插入图片描述

OpenFeign

OpenFeign 是 Spring Cloud 生态中声明式的 HTTP 客户端框架,通过注解简化微服务间的远程调用‌。它基于原生 Feign 扩展,深度集成 SpringMVC 注解和负载均衡能力,使开发者能以调用本地方法的方式实现跨服务接口访问。‌‌‌‌
区别于编程式restTemplate,这个实现起来更加轻松。

使用

首先引入包

<
!-- 声明式远程调用-->
<dependency>
  <groupId>org.springframework.cloud<
    /groupId>
    <artifactId>spring-cloud-starter-openfeign<
      /artifactId>
      <
      /dependency>

然后在启动类上**@EnableFeignClients**注解启动feign

@SpringBootApplication
@EnableFeignClients //启动feign
public class OrderMainApplication
{

创建一个feign接口,并且实现远程调用api。

package com.atguigu.order.feign;
import com.atguigu.product.bean.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "service-product")
public interface ProductFeignClient {
@GetMapping("/product/{id}")
Product getOne(@PathVariable Long id);
}

spring会将带**@FeignClient的接口类里面的mvc格式接收头变为请求头,发送远程请求。
所以这个的意思就是去注册中心寻找
service-product**的服务,获取到他的url,并拼接param,获取内容返回给使用方。

第三方api

也可以通过feign实现第三方请求,区别于自己的服务,需要在注解上多增加一个url,来告诉spring,这个是外部请求。

package com.atguigu.order.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "service-weather",url = "http://t.weather.itboy.net/api/weather/city")
public interface WeatherFeignClient {
@GetMapping("/{cityId}")
public String weather(@PathVariable String cityId);
}

feign 日志

feign如果打开日志呢,只需要在配置类里面写上feignLogging的日志显示级别就行。

@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}

他还有NONE、BASIC、HEADERS。
分别是不显示内容。
显示请求方法和url和结果状态码和执行时间。
headers是显示basic的信息再加上request和response的headers。
full就是全有。

超时控制

如果feign请求的服务当机了,或者速度慢读取不到,我们都需要对feign进行一个超时控制。
对feign规定一个限时等待,如果超时就返回错误或者兜底数据,未超时就正常返回。
feign有一个默认设置。
在这里插入图片描述
连接时间10秒,等待时间60秒,那我们如何手动更改这些时间呢,其实就是更改options。

可以通过编码格式注入,也可以通过配置文件注入。

spring:
openfeign:
client:
config:
default:
connect-timeout: 1000
read-timeout: 2000
@Configuration
public class FeignConfiguration
{
@Bean
public Request.Options options() {
return new Request.Options(8, TimeUnit.SECONDS, 8, TimeUnit.SECONDS, true);
}

但是一定是约定>配置>编码的,所以配置文件的优先级会更高一些。

拦截器

可以在feign上用拦截器加一层,请求拦截器和响应拦截器,用feign提供的RequestInterceptor就可以。

package com.atguigu.order.interceptor;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class XTokenRequestInterceptor
implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header("token", UUID.randomUUID().toString());
}
}

那如何让spring知道这个拦截器呢,有两种方法,一种是直接将他放入容器中, feign每次都会从容器中读取requestInterceptor,或者使用配置文件。

spring:
openfeign:
client:
config:
service-product:
connect-timeout: 3000
read-timeout: 3000
request-interceptors:
- com.atguigu.order.interceptor.XTokenRequestInterceptor

兜底回调

就是如果没有获得本身内容,不返回给用户错误,而是一些假数据,让程序可以继续进行下去,也可以让用户体验好一些,那应该如何操作呢。需要使用sentinel来进行操作。

sentinel

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。

使用

首先下载sentinel的jar包,并且启动。

java -jar sentinel.jar

他默认占用8080端口,访问就可以看见sentinel的web页面。账号和密码都是sentinel
在这里插入图片描述
如何让服务放入到里面呢,就是引入依赖,并且配置文件配置就行了,具体如下。

<
!--sentinel-->
<dependency>
  <groupId>com.alibaba.cloud<
    /groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel<
      /artifactId>
      <
      /dependency>

application.yml

spring:
sentinel:
transport:
dashboard: localhost:8080 #面板位置
eager: true #自动刷新

注入之后就可以看见他在里面显示出来了。

簇点链路

用户去访问资源的时候,如果这个资源配置了规则,sentinel就会去检查规则,如何符合则放行,如果不符合则抛出异常或者执行兜底回调。簇点链路就是sentinel在读取资源操作显示在页面上的链路操作,如下图。
在这里插入图片描述

下列配置就是将服务注册到sentinel中,可以被簇点链路获取。

spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.eager=true

eager是热加载,本身是要进行请求才会在sentinel中显示服务,将这个设置为true,可以直接出现。

异常处理

当我们给簇点链路设置规则之后,拦截的内容为sentinel为我们设定的,那我们如何自定义呢,这就要看我们如何设置得了。

根据下图,我们看到,如果是在浏览器端设置的,统一由BlockExceptionHandler来处理。
在这里插入图片描述
我们可以实现这个接口,自定义异常处理。
具体代码如下:

package com.atguigu.order.exception;
import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.common.R;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import java.io.PrintWriter;
@Component
public class MyBlockExceptionHandler
implements BlockExceptionHandler {
ObjectMapper objectMapper = new ObjectMapper();
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String s, BlockException e) throws Exception {
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter writer = httpServletResponse.getWriter();
R r = R.error(500, s + "流量超载,原因是" + e.getCause().getMessage());
String json = objectMapper.writeValueAsString(r);
writer.write(json);
}
}

@SentinelResource
一般是为非controller层设置的规则注解,设置之后可以被sentinel识别,继而可以配置规则,如果在这里出现规则限制,这去返回异常,异常可以被注解你的blockHandler包裹的方法解决,或者全局异常解决,具体如下。

@SentinelResource(value = "createOrder",blockHandler = "orderFallback")
public Order createOrder(Long productId, Long userId) {
// Product product = getProductFromRemoteWithLoadBalanceAnnotation(productId);
//使用Feign完成远程调用
Product product = productFeignClient.getProductById(productId);
Order order = new Order();
order.setId(1L);
// 总金额
order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum())));
order.setUserId(userId);
order.setNickName("zhangsan");
order.setAddress("尚硅谷");
//远程查询商品列表
order.setProductList(Arrays.asList(product));
return order;
}
public Order orderFallback(Long productId, Long userId,BlockException e) {
return new Order();
}

剩下两个一个是openfeign远程调用,通过注解的fallback解决,一个是SphU,可以用来访问资源文件,通过try/cache解决。

规则-流浪控制

限制多余的请求,从而保护系统资源不被耗尽。
在这里插入图片描述

阙值类型

qps:统计每一秒的请求数
并发线程数:统计并发线程数
在这里插入图片描述
勾选是否集群,就可以选择单机阙值和总体阙值。
单机阙值就是每一个集群每秒可以访问的数量,总体阙值就是这几个集群一共可以访问的数量。

流控模式

流控模式可以选择3种模式,分别是直接、关联、链路。

直接策略就是上面的直接拦截,这里就不讲了。

链路策略:可以规定在在这个链路上的,访问量大的请求被拦截,就是更加灵活,不用直接拦截,只拦截请求量这个链路上请求量大的了。

关联策略:就是可以绑定另一个请求,当你在访问本请求时候,可以关联让另一个请求被拦截,这种可以用在数据校准度比较高的时候,在更改访问量大的时候,让访问的人少一些。

流控效果

流控效果有3个,快速失败、warm up、排队等待。

快速失败:就是当访问量超过阈值的时候,直接拦截失败。

warmup:我称之为慢慢起床,就是假如有10个请求,我们先走3个,过一会儿再走剩下的,这个数量是逐步递增的。

排队等待:就是字面意思,大家都排队,每次进来几个,然后有一个timeout,如果过了,剩下排队的请求都挂掉了。

sentinel具体内容可以查看官方文档

gateway

gateway,相当于统一管理的后端的入口,你可以在后端入口进行拦截、保护、过滤等等请求。

使用

首先,使用gateway要引入依赖。

<dependencies>
  <
  !--网关-->
  <dependency>
    <groupId>org.springframework.cloud<
      /groupId>
      <artifactId>spring-cloud-starter-gateway<
        /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-loadbalancer<
                    /artifactId>
                    <
                    /dependency>
                    <
                    /dependencies>

将gateway注册到nacos服务中,配置文件中进行nacos的配置。
然后在配置文件中,可以直接进行路由的配置。

server:
port: 80
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: order-route
uri: lb://service-order
predicates:
- Path=/api/order/**
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/**

gateway默认优先级是顺序排列,如果你在第一个设置了非常大范围的Path,那么下面的内容就不会显示,可以使用order解决

gateway:
routes:
- id: order-bing
uri: https://cn.bing.com/
predicates:
- Path=/**
order: 10

predicates

断言,给路径可以设置规则。

predicates参数

在这里插入图片描述

自定义断言

如果我们想自定义断言,那应该如何进行呢。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.cloud.gateway.handler.predicate;
import jakarta.validation.constraints.NotEmpty;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
public class QueryRoutePredicateFactory
extends AbstractRoutePredicateFactory<
Config> {
public static final String PARAM_KEY = "param";
public static final String REGEXP_KEY = "regexp";
public QueryRoutePredicateFactory() {
super(Config.class)
;
}
public List<
String> shortcutFieldOrder() {
return Arrays.asList("param", "regexp");
}
public Predicate<
ServerWebExchange> apply(final Config config) {
return new GatewayPredicate() {
public boolean test(ServerWebExchange exchange) {
if (!StringUtils.hasText(config.regexp)) {
return exchange.getRequest().getQueryParams().containsKey(config.param);
} else {
List<
String> values = (List)exchange.getRequest().getQueryParams().get(config.param);
if (values == null) {
return false;
} else {
for(String value : values) {
if (value != null && value.matches(config.regexp)) {
return true;
}
}
return false;
}
}
}
public Object getConfig() {
return config;
}
public String toString() {
return String.format("Query: param=%s regexp=%s", config.getParam(), config.getRegexp());
}
};
}
@Validated
public static class Config
{
private @NotEmpty String param;
private String regexp;
public String getParam() {
return this.param;
}
public Config setParam(String param) {
this.param = param;
return this;
}
public String getRegexp() {
return this.regexp;
}
public Config setRegexp(String regexp) {
this.regexp = regexp;
return this;
}
}
}

filter

过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。过滤器可以限定作用在某些特定请求路径上。 Spring Cloud Gateway包含许多内置的GatewayFilter工厂。

GatewayFilter工厂同上一篇介绍的Predicate工厂类似,都是在配置文件application.yml中配置,遵循了约定大于配置的思想,只需要在配置文件配置GatewayFilter Factory的名称,而不需要写全部的类名,比如AddRequestHeaderGatewayFilterFactory只需要在配置文件中写AddRequestHeader,而不是全部类名。在配置文件中配置的GatewayFilter Factory最终都会相应的过滤器工厂类处理。

server:
port: 8081
spring:
profiles:
active: add_request_header_route
---
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://httpbin.org:80/get
filters:
- AddRequestHeader=X-Request-Foo, Bar
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
profiles: add_request_header_route

cors

跨域问题可以通过gateway来解决

spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allow-credentials: true
allowedOrigins: "*"
allowedMethods: "*"
allowedHeaders: "*"

也可以通过meta进行单个的配置,不过一般不需要。

seata

分布式事务,用来解决分布式服务出现的数据不一致问题。

本身的服务是各自操作各自的,但我们在一个服务中通过openFeign去访问另一个服务的操作,并不会被本地的事务回滚。

seata拥有三个对象
1.TC transaction coordinator 事务协调者
2.TM transaction manager 事务管理器 全局事务
3.RM Resource manager 资源管理器

在这里插入图片描述
也就是当business开始这个服务的事务,会首先通知tc,tc给予一个全局事务,business去rpc访问其他服务,其他服务会创建分支事务,并且告诉tc,tc会知道所有服务的状态。

使用

首先安装seata

<dependency>
  <groupId>com.alibaba.cloud<
    /groupId>
    <artifactId>spring-cloud-starter-alibaba-seata<
      /artifactId>
      <
      /dependency>

使用注解**@GlobalTransaction**

seata使用二阶提交方式,首先每个服务先在本地进行操作,并且向tc申请全局锁,并且将前镜像和后镜像存储到undo_log当中,并将结果返回给tc。
第二阶段,tc根据返回的结果来进行回滚,回滚通过undo_log存储的数据,恢复到前镜像,并且这时候还会判断目前数据库的内容是否跟后镜像一样。

seata 四种事务模式

auto模式,全程有seata控制,从主事务到分支事务。
ax:全局锁是互斥锁。
tcc:具体实现要由程序员实现,prepare、commit、rollback
saga:结合消息队列,就是执行完成后,会通知下一个服务。

这就是基本使用的cloud了,完事。

posted @ 2025-07-25 12:35  yfceshi  阅读(69)  评论(0)    收藏  举报