工作日志-后端

day01

今天完成脚手架框架的搭建,RabbitMQ的访问端口要写成5672,15672只是管理页面的进入方式,并非配置端口。

day02

OAuth2认证服务的配置

  1. 需要配置 public class AuthorizationServer extends AuthorizationServerConfigurerAdapter{} 中的三个类
  • AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束。
  • ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。(比如clientid等,用以识别客户端身份)(一般有内存形式和)
  • AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
  1. 配置AuthorizationServerTokenServices tokenService类,设定令牌有效期等令牌配置。

Redis为什么快

  1. Redis将所有数据存储在内存中,这使得它能够提供非常快速的读写操作。
  2. Redis 单线程指的是「接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端」这个过程是由一个线程(主线程)来完成的,这也是我们常说 Redis 是单线程的原因。(采用单线程模型,避免多线程之间的竞争,单线程模型说明请看小林Coding)
    Redis 在 2.0 版本,会启动 2 个后台线程,分别处理关闭文件、AOF 刷盘这两个任务;
    Redis 在 4.0 版本之后,新增了一个新的后台线程,用来异步释放 Redis 内存,也就是 lazyfree 线程。
  3. Redis采用I/O多路复用机制处理Socket请求,而不是阻塞等待请求完成,这种架构使得Redis能够高效地处理大量并发连接。
  4. redis高效的数据结构设计,比如在ZSet中使用跳表、压缩列表这样的数据结构。

跳表(skiplists)是一种有序的数据结构,它通过在每个节点中维持多个指向其他的节点指针,从而达到快速访问队尾目的。

压缩列表实际上类似于一个数组,数组中的每一个元素都对应保存一个数据。和数组不同的是,压缩列表在表头有三个字段 zlbytes、zltail 和 zllen,分别表示列表长度、列表尾的偏移量和列表中的 entry 个数;压缩列表在表尾还有一个 zlend,表示列表结束。

alt text

StringRedisTemplate的相关操作

1
2
3
4
5
StringRedisTemplate.opsForValue();  //操作字符串
StringRedisTemplate.opsForHash();   //操作hash
StringRedisTemplate.opsForList();   //操作list
StringRedisTemplate.opsForSet();   //操作set
StringRedisTemplate.opsForZSet();   //操作有序set

Redis缓存雪崩、击穿、穿透

布隆过滤器通过添加的key需要根据k个无偏hash函数(一般是三个)计算得到多个hash值,然后对数组长度进行取模得到数组下标的位置,然后将对应数组下标的位置的值置为1,当有新元素进来时,只要通过hash计算的位置有一个为1则判断其已经存在于Redis中。
布隆过滤器判断存在则不一定存在,判断不存在则一定不存在。布隆过滤器有时候占太大内存,需要清空或者重新设置。

alt text

Redis与数据库一致性问题

(一般给缓存添加过期时间)
先删缓存,再更新数据库:采用延时双删除解决不一致问题
先更新数据库,再更新缓存(主流,Cache-aside)
消息队列重试机制,应用消息队列删除缓存时:

  • 如果应用删除缓存失败,可以从消息队列中重新读取数据,然后再次删除缓存。
  • 如果删除缓存成功,就要把数据从消息队列中移除,避免重复操作,否则就继续重试。
应用Cannal订阅MysqlBinlog删除缓存

Cannal是什么:

可以简单地把canal理解为一个用来同步MySQL增量数据(不同步全量数据)的一个工具,将增量数据同步给消息队列、缓存、搜索引擎(ElasticSearch)等。

Canal简单原理:
Canal模拟Mysql的主从复制的交互协议,把自己伪装成一个Mysql的从节点,向Mysql主节点发送dump请求,Mysql接受请求后就会推送Binlog给Canal,Canal解析Binlog字节流之后会将其转换为便于读取的结构化数据,供下游程序订阅使用。

数据一致性做法:
这里采用的是先更新数据库后更新缓存的策略,所以当数据库数据发生变更时,将binlog日志采集发送到MQ队列里面,然后编写一个简单的缓存删除消息者订阅binlog日志,根据更新log删除缓存,并且通过ACK机制确认处理这条更新log,保证数据缓存一致性。(必须删除缓存成功后再ACK给消息队列)

day03

@AllArgsConstructor 是 Lombok 库的一个注解,它用于生成一个包含所有参数的构造函数。这个注解通常与@NoArgsConstructor 一起使用,后者用于生成一个不包含任何参数的构造函数。

@Builder 是 Lombok 库的一个注解,它用于生成一个构建器(Builder)模式,用于简化对象创建的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
public class User {
private String name;
private int age;
private String email;
}

User user = User.builder()
.name("John Doe")
.age(30)
.email("john.doe@example.com")
.build();

在后端微服务项目中,字典数据到底是什么?

字典数据(Dictionary Data)通常指的是存储在数据库或内存中的键值对集合,用于存储和提供对应用程序中常用术语、值或选项的统一引用。这些术语、值或选项在应用程序的不同部分被重复使用,例如,国家、省份、性别、产品类型等。

字典数据一般存放在两张表中:

  • sys_dict_type: 存储有哪些下拉框,比如用户性别,操作类型等。
  • sys_dict_data: 存储下拉框具体option的内容,比如性别男女,会有一列属性与dict-type对应。

day04

https://blog.csdn.net/itigoitie/article/details/127785659

分布式事务概念

不同服务需要网络通信,而网络存在不可到达性,这种分布式系统环境下,通过与不同的服务进行网络通信去完成事务称之为分布式事务。

分布式事务解决方案

满足CAP理论,CAP是Consistency、Availability、Partition tolerance三个词语的缩写,分别表示一致性、可用性、分区容忍性。在分布式系统中进行分布式事务控制,要么保证CP、要么保证AP。

  1. CP架构(刚性事务):如果要满足数据的强一致性,就必须在一个服务数据库锁定的同时,对分布式服务下的其他服务数据资源同时锁定。等待全部服务处理完业务,才可以释放资源。此时如果有其他请求想要操作被锁定的资源就会被阻塞,这样就是满足了CP。达到了强一致性和弱可用性。

  2. AP架构(柔性事务):如果要满足服务的的强可用性,每个服务就可以各自独立执行本地事务,而无需相互锁定其他服务的资源。在各个服务的事务尚未完全处理完毕时,如果去访问数据库,可能会遇到各个节点数据不一致的情况。然后我们还需要一些措施,使得经过一段时间后,各个节点的数据最终达到一致性。这样就是满足了AP。达到了弱一致性(最终一致性)和强可用性。(保障 AP 放弃 CP 是常见的一种做法)

满足CP的方案

二阶段提交(Two-Phase Commit,2PC)

满足AP的方案
1. TCC模式可以解决2PC中的资源锁定和阻塞问题,减少资源锁定时间。

Try:资源的检测和预留;
Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功;
Cancel:预留资源释放。

2. MQ事务消息方案(消息通知型,采用确认机制

事务发起者A执行本地事务;
事务发起者A通过MQ将需要执行的事务信息发送给事务参与者B;
事务参与者B接收到消息后执行本地事务;

注意事项:

  • 事务发起者A必须确保本地事务成功后,消息一定发送成功;
  • MQ必须保证消息正确投递和持久化保存;
  • 事务参与者B必须确保消息最终一定能消费,如果失败需要多次重试;
  • 事务B执行失败,会重试,但不会导致事务A回滚;
3. 本地消息表方案(消息通知型)

事务发起者:

  • 开启本地事务;
  • 执行事务相关业务;
  • 发送消息到MQ;
  • 把消息持久化到数据库,标记为已发送;
  • 提交本地事务;

事务消费者:

  • 接收消息;
  • 开启本地事务;
  • 修改数据库消息状态为已消费;
  • 提交本地事务

开启额外的定时任务(xxl-job):

  • 定时扫描消息表中超时未消费消息

注意事项:

  • 数据一致性完全依赖于消息服务,因此消息服务必须是可靠的;
  • 需要处理被动业务方的幂等问题
  • 被动业务失败不会导致主动业务的回滚,而是重试被动的业务;
  • 事务业务与消息发送业务耦合、业务数据与消息表要在一起;
  • 准备阶段(try):资源的检测和预留;
  • 执行阶段(confirm/cancel):根据上一步结果,判断下面的执行方法。如果上一步中所有事务参与者都成功,则这里执行confirm。反之,执行cancel;
    消息幂等性:即使多次收到了消息,也不会重复消费。所以保证消息的幂等性就是保证消息不会重复消费。

保证消息幂等性的方法:

  1. mq内部可以为每条消息生成一个全局唯一、与业务无关的消息id,当mq接收到消息时,会先根据该id判断消息是否重复发送,mq再决定是否接收该消息。
  2. 如果从MQ拿到数据是要存到数据库(Mysql或者Redis中的Set),那么可以根据数据创建唯一约束,这样的话,同样的数据从MQ发送过来之后,当插入数据库的时候,会报违反唯一约束,不会插入成功的。(或者可以先查一次,是否在数据库中已经保存了,如果能查到,那就直接丢弃就好了)。

本地消息表方案优化版: 引入一个独立的消息服务,来完成对消息的持久化、发送、确认、失败重试等一系列行为,但是实现会比较复杂。

4. AT模式(SeaTa)

AT 模式是一种无侵入的分布式事务解决方案。可以看做是对TCC或者二阶段提交模型的一种优化,解决了TCC模式中的代码侵入、编码复杂等问题。

在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

基本原理:

  • 一阶段:执行本地事务,并返回执行结果
  • 二阶段:根据一阶段的结果,判断二阶段做法:提交或回滚
    但AT模式底层做的事情可完全不同,而且第二阶段根本不需要我们编写,全部有Seata自己实现了。也就是说:我们写的代码与本地事务时代码一样,无需手动处理分布式事务。

@Valid注解和VO的联动

在VO实体中可以加入如下图中的类似@Length、@Pattern之类的注解,这样在Controller传参时加入@Valid注解就可以自动校验参数是否合法。

day05

学习Feign之前先学习Ribbon,Feign是整合了Ribbon做负载均衡的(很多微服务组件都整合了Ribbon,比如Nacos-Discovery服务)。

Ribbon:

负载均衡是从多个服务中根据某个策略选择一个进行访问,常见的负载均衡分为两种:

  1. 客户端负载均衡:即在客户端就进行负载均衡算法分配。例如spring cloud中的ribbon,客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择 一个服务器,然后进行访问
  2. 服务端负载均衡:在消费者和服务提供方中间使用独立的代理方式进行负载。例如Nginx,先发送请求,然后通过Nginx的负载均衡算法,在多个服务器之间选择一 个进行访问!

使用Ribbon时只需添加**@LoadBalanced注解**即可。

Ribbon可以把微服务的服务名通过负载均衡策略替换成某一台机器的IP地址,然后通过http请求进行访问!(服务名->ip地址)

常见的负载均衡算法

  • 随机:通过随机选择服务进行执行,一般这种方式使用较少;
  • 轮询:请求来之后排队处理,轮着来
  • 加权轮询:通过对服务器性能的分型,给高配置,低负载的服务器分配更高的权重,均衡各个服务器的压力;
  • 一致性hash:通过客户端请求的地址的HASH值取模映射进行服务器调度。
  • 最少并发:将请求分配到当前压力最小的服务器上
  • Ribbon默认的负载均衡算法:ZoneAvoidanceRule:区域权重策略。默认的负载均衡策略,综合判断server所在区域的性能和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server。在没有区域的环境下,类似于轮询(RandomRule)

自定义负载均衡策略方式有多种:

  • 实现 IRule 接口
  • 继承AbstractLoadBalancerRule类

Feign:

Feign和OpenFeign区别:

  • Feign:Feign是Netflix开发的声明式、模板化的HTTP客户端,Feign可帮助我们更加便捷、优雅地调用HTTP API。可以单独使用
  • OpenFeign:Spring Cloud openfeign对Feign进行了 增强,使其支持Spring MVC注解,另外还整合了Ribbon和Eureka,从而使得Feign的使用更加方便。

Feign常用配置:

  • 日志配置:有时候我们遇到 Bug,比如接口调用失败、参数没收到等问题,或者想看看调用性能,就需要配置 Feign 的 日志了,以此让 Feign 把请求信息输出来。日志配置分为局部配置和全局配置!
  • 拦截器配置:每次 feign 发起http调用之前,会去执行拦截器中的逻辑,就类似mvc中的拦截器。比如:做权限认证。
  • 超时时间配置:通过 Options 可以配置连接超时时间(默认2秒)和读取超时时间(默认5秒),注意:Feign的底层用的是Ribbon,但超时时间以Feign配置为准

Feign在Ribbon基础上进一步把参数组装到url中去,实现一个完整的RPC调用。

各微服务提供给别的服务调用的接口url要加上服务名,即:Nacos配置文件中的Spring-Application-name/xxx(GetMapping(“”))/xxx

FeignClient使用:

下面的例子:比如我的content微服务想要调用Search服务,就在content微服务的包下面

alt text

SpringGateway的作用

各微服务首先要在Nacos中被发现。

alt text

  • 如果不配置网关:
    alt text

Captcha-Plus(AJ-Captcha)的使用:

  1. 源码解读
  2. 脚手架中继承一下,因为管理使用 /admin-api/system-user/* 作为前缀,所以需要继承(Url问题)
  3. 如下图,获取验证码与校验验证码(Get/Check)
    alt text
  4. 二次校验(需要自己进行二次验证签名,需要传captchaVerification)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@PostMapping("/login")
public ResponseModel get(@RequestParam("captchaVerification") String captchaVerification) {
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(captchaVerification);
ResponseModel response = captchaService.verification(captchaVO);
if(response.isSuccess() == false){
//验证码校验失败,返回信息告诉前端
//repCode 0000 无异常,代表成功
//repCode 9999 服务器内部异常
//repCode 0011 参数不能为空
//repCode 6110 验证码已失效,请重新获取
//repCode 6111 验证失败
//repCode 6112 获取验证码失败,请联系管理员
}else{
//自己的业务代码
}
return response;
}