springcloud 第二阶段总结

Seata

seata是什么?

seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。

img

本次拆分新生电子报道用的是 1.4.2 版本的 seata,是最新的一个版本

在项目中 Seata 具体搭建

  1. 首先我们需要现在官网上下载源码资源包 seata-1.4.2.zip 以及服务端包 seata-server-1.4.2

img

  1. 服务端前期工作

    1. 为 seata 建立数据库,导入资源包中的 sql 文件
    2. 修改配置文件 file.confregistry.conf
    3. 在源码包中修改 config.txt,并导入 nacos 配置中心,(单独给 seata 建立一个命名空间)
    4. 给需要进行全局事务控制的数据库导入 undo_log
    5. 双击 seata-server.bat 启动

为 seata 建立数据库,导入资源包中的 sql 文件

  • 在数据库中建立一个名字为 seata 的数据库,导入 sql 文件运行 script\server\db\mysql.sql

修改配置文件 file.confregistry.conf

  • file.conf,主要修改
 mode = "db"
 db {
    datasource = "druid"
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url ="jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true"
    user = "root"
    password = "li12345"
    minConn = 5
    maxConn = 100
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }
  • registry.conf 文件
registry {
  type = "nacos"
  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = "ab8ba48b-27f0-4051-8c4d-9118256a4faa"
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
}
config {
  type = "nacos"

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = "ab8ba48b-27f0-4051-8c4d-9118256a4faa"
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
    dataId = "seataServer.properties"
  }
}

其中namespace = "ab8ba48b-27f0-4051-8c4d-9118256a4faa" 是单独在 nacos 中建立的一个命名空间,用于后续导入 seata 配置,导入之后如下图所示

img

在源码包中修改 config.txt,并导入 nacos 配置中心

  • 修改源码包中的 script/config-center/config.txt
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=li12345
# 添加上此配置
service.vgroupMapping.fsp_tx_group=default
  • 在项目中配置 tx-service-group: 后面的值需要的nacos 里面TC 集群保持一致,例如 nacos 里有上面代码块中添加的 service.vgroupMapping.fsp_tx_group=default
  • 修改完之后打开 Git Bash 打开到目录 script\config-center\nacos 下,运行
sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t ab8ba48b-27f0-4051-8c4d-9118256a4faa -u nacos -w nacos
  • 其中 -t 后是命名空间,如果使用默认的 public 去掉该参数即可

给需要进行全局事务控制的数据库导入 undo_log

DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (
`id` bigint NOT NULL AUTO_INCREMENT,
`branch_id` bigint NOT NULL,
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;

双击运行 seata-server.bat 启动

img

项目配置文件

seata:
  #事务群组(可以每个应用独立取名,也可以使用相同的名字),要与服务端nacos-config.txt中service.vgroup_mapping中存在,并且要保证多个群组情况下后缀名要保持一致-tx_group
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: fsp_tx_group  #要与配置文件中的vgroupMapping一致
  service:
    vgroup-mapping:
      fsp_tx_group: default
    disable-global-transaction: false
  registry: #registry根据seata服务端的registry配置
    type: nacos #默认为file
    nacos:
      application: seata-server #配置自己的seata服务
      server-addr: ${spring.cloud.nacos.server-addr} #根据自己的seata服务配置
      group: SEATA_GROUP #根据自己的seata服务配置
      namespace: ab8ba48b-27f0-4051-8c4d-9118256a4faa
      username: nacos #根据自己的seata服务配置
      password: nacos #根据自己的seata服务配置
  config:
    type: nacos #默认file,如果使用file不配置下面的nacos,直接配置seata.service
    nacos:
      server-addr: ${spring.cloud.nacos.server-addr} #配置自己的nacos地址
      group: SEATA_GROUP #配置自己的dev
      namespace: ab8ba48b-27f0-4051-8c4d-9118256a4faa #改为自己的nacos的namespace,这里填写的是刚才创建seata命名空间的id
      username: nacos #配置自己的username
      password: nacos #配置自己的password

配置之后运行时遇到的问题

2022-04-29 14:22:29.536 ERROR 81060 --- [eoutChecker_2_1] i.s.c.r.netty.NettyClientChannelManager  : 0101 can not connect to 10.102.176.175:8091 cause:can not register RM,err:can not connect to services-server.

踩坑

百度查询时候,说的是 在启动 seata 时是以内网运行的,与nacos 连接出现了问题,解决了好长时间,并没有解决

造成这个问题的原因:我的 nacos 是配置在服务器上的,seata 是在本地启动的,两者之间出现了问题,最后将 nacos 也在本地启动,问题解决。之后在开发中,要在本地就全部在本地配置响应的内容,要么就全部在服务器上,否则有可能会出现意想不到的错误

Sentinel

Sentinel 是什么?

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

  • sentinel 是以 jar 包的形式启动的,启动之后如下所示

img

application.yml 配置文件

spring:
  application:
    name: API-DATA
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
    sentinel:
      transport:
        # 配置Sentinel dashboard地址
        dashboard: localhost:8080
        # 默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: ${spring.cloud.nacos.server-addr}
            dataId: ${spring.application.name}
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

配置 sentinel 持久化,对于有些接口需要进行服务降级/熔断,但是如果不配置持久化,我们只在 sentinel 上添加一个配置之后,项目一旦重启,这些配置的降级/熔断策略就没有,需要重新再次配置,这样很不友好,因此,持久化是非常必要的

  • 在 nacos 的配置列表中进行持久化配置

img

gateway配置全局过滤器

请求服务时首先是通过 gateway 然后再进行路由转发,因此再 gateWay 这里配置是最合适的

package com.example.filter.factory;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.example.entity.Admin;
import com.example.feign.AdminFeign;
import com.example.response.ResponseResult;
import com.example.utils.AdminUtils;
import com.example.utils.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;

/**
 * description:
 *
 * @author: lixianghong
 * @date: 2022/4/27 8:39
 */
@Slf4j
@Component  // 代表在工厂中创建对象 @configuration 配置
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {

  @Resource
  private AdminFeign adminFeign;

  @Value("${token.requestHeader}")
  private String requestHeader;

  @Value("${token.startWith}")
  private String startWith;

  public TokenGatewayFilterFactory() {
    super(Config.class);
  }

  @Override
  public GatewayFilter apply(Config config) {
    return new GatewayFilter() {
      @Override
      public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("进入 filter");
        // 1. 获取 token 信息
        List<String> authorization = exchange.getRequest().getHeaders().get(requestHeader);
        // 获取响应体
        ServerHttpResponse response = exchange.getResponse();
        // 判断请求头的内容是否为空:y:返回验证失败信息
        if (authorization == null) {
          return unAuthorizationResponse(response,HttpStatus.UNAUTHORIZED,"令牌验证失败");
        }
        String token = authorization.get(0);
        // 判断 token 是否为空,token 是否以规定字符开头 返回格式是否正确信息
        if (StrUtil.isEmpty(token) || !token.startsWith(startWith)) {
          return unAuthorizationResponse(response,HttpStatus.UNAUTHORIZED,"令牌格式错误");
        }
        token = token.substring(startWith.length());
        // 截取之后 token 如果为空,则返回格式错误信息
        if (StrUtil.isEmpty(token)) {
          return unAuthorizationResponse(response,HttpStatus.UNAUTHORIZED,"令牌格式错误");
        }
        // 判断 token 是否过期,如过期则返回过期信息
        if (TokenUtil.isExpire(token)) {
          return unAuthorizationResponse(response,HttpStatus.UNAUTHORIZED,"令牌已过期");
        }
        Admin admin = adminFeign.getAdmin(token);
        // 将当前用户存储先下来
        AdminUtils.setLoginUser(admin);
        // 放行
        return chain.filter(exchange);
      }
    };
  }

  public static Mono<Void> unAuthorizationResponse(ServerHttpResponse response, HttpStatus status, String message) {
    response.setStatusCode(HttpStatus.UNAUTHORIZED);
    ResponseResult<Void> result = new ResponseResult<>();
    result.setMessage(message);
    result.setStatus(401);
    DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(result).getBytes());
    return response.writeWith(Mono.just(dataBuffer));
  }

  public List<String> shortcutFieldOrder() {
    return super.shortcutFieldOrder();
  }

  // 内部类
  public static class Config {

  }
}

application.yml 中配置,上面创建的类表示我们时自定义 token 工厂,使用 @Component注解代表在工厂中创建对象,class:TokenGatewayFilterFactory

 gateway:
      routes:
        - id: data_router
          uri: lb://API-DATA
          predicates:
            - Path=/api/ScreenData/**
          filters:
            - Token

给过滤其中 配置上 filters: - Token 表示我们这个模块中的接口访问需要走过滤器这一步

配置完成之后进行接口测试

出现的问题

在使用 openfeign 组件通信时,因为需要调用其他模块的接口,但是有没有token,出现报错异常

feign.codec.DecodeException: No qualifying bean of type 'org.springframework.boot.autoconfigure.http.HttpMessageConverters' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at feign.AsyncResponseHandler.decode(AsyncResponseHandler.java:119) ~[feign-core-10.10.1.jar:na]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
	|_ checkpoint ⇢ HTTP GET "/java/sayHi" [ExceptionHandlingWebHandler]

解决方法:创建gateWay配置

之前没有遇到过,记录一下。从报错的内容来看是找不到装配的bean,既然找不到就尝试手动注入,代码如下:

@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
    return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}

放入configuration注解的配置类中,问题解决。

权限管理

权限这块搞了一天没有弄进去,这块的内容还需要好好学习一下,这个阶段就先不做了。

尚硅谷springcloud第二季笔记是一份非常有价值的学习资料,对于学习和理解Spring Cloud框架的人来说非常重要。通过这份笔记,我们可以更全面地了解Spring Cloud框架的各个组件和模块,掌握其核心原理和使用方法。 这份笔记包含了Spring Cloud的各个核心组件,如Eureka、Ribbon、Feign、Hystrix等,每个组件都有详细的介绍和实践示例。通过学习这些组件,我们可以了解到Spring Cloud是如何实现微服务架构的,各个组件是如何协同工作的,以及如何解决分布式系统中的常见问题。 此外,笔记中也涉及到了一些最佳实践和常见的坑。通过学习这部分内容,我们可以避免一些常见的错误,提高开发效率和代码质量。同时,笔记中也提供了一些扩展知识和参考资料,便于我们进一步深入学习和研究。 值得一提的是,这份笔记是分享在网盘上的,这意味着我们可以随时随地进行学习和查阅。无论是在家、在公司还是在路上,我们都可以方便地访问这份资料。这对于那些工作繁忙、时间有限的人来说尤为重要,可以大大提高学习效率和灵活性。 总之,尚硅谷springcloud第二季笔记的网盘分享是一件非常有意义的事情。它可以帮助我们更好地学习和理解Spring Cloud框架,提升自己的技术水平。相信通过学习这份笔记,我们可以更加轻松地应对日常的开发工作,并在实际项目中取得更好的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值