目录

1,pom引入

2,整合mybatis

2.1 mybaits执行过程

2.2 sqlsession是线程不安全的。

2.3 springboot整合mybatis

3,异常统一处理

3.1 为什么要使用异常统一处理

3.2 处理方式

4,接口鉴权

5,jwt 生成token

6,单点登录 token

6.1 验证token过期的方式

6.2 单点登录token在不同域之间怎么传递

7, 断言

8,加密处理

9,统一http返回码

10,全局线程池

11,接口字段校验

12,导出excel

13,google缓存

1,pom引入

通常引入spring-boot-starter 和 spring-boot-starter-web两个即可

2,整合mybatis

pom再引入mybatis-spring-boot-starter

2.1 mybaits执行过程

读取配置文件中数据库连接信息 -》创建一个sqlsessionFactory工厂 -》调用工厂中指的方法创建一个sqlsession会话实例-》sqlssesion.getMapper(.class)获取对应的mapper映射实例-》调用mapper中的方法执行sql -> 提交、关闭。

2.2 sqlsession是线程不安全的。

因为connection是线程不安全的。为什么connection是线程不安全的呢?如果多个线程共享一个connection实例。可能一个线程刚开启事 con.setAutoCommit(true);而另一个线程直接提交事务con.commit();导致数据混乱。

        通常说的线程不安全指的是sqlsession的defaultSqlsession实现类,这种方式需要手动提交sql。spring为我们提供了sqlsessionTemplate,sqlsessionManager 两种线程安全的实例。二者使用了ThreadLocal 和动态代理的方式处理线程安全的问题。这两种不用手动提交,关注业务即可。

        使用原生的方式连接数据库,在线程安全的情况下,每次都要建立连接、销毁连接,比较耗性能。所以使用数据库连接池,在连接池中维持着多个连接。线程需要时从连接池中取即可,使用完放回。

        一个线程中如果有多次跟数据库的交互,没有事务时(通常都是查询),这些交互时的数据库连接可能不是同一个conncetion对象。如果是事务,那就是一个连接对象。

2.3 springboot整合mybatis

        简单理解:引入mybatis-spring-boot-starter,创建DataSource、sqlsessionFactory bean,完成对数据库的连接。创建sqlsessionTemplate bean。

3,异常统一处理

3.1 为什么要使用异常统一处理

当前普遍的项目接口处理的方式是返回错误码和对应的错误说明。对于正常的返回信息,是返回的实体字段信息,这个没啥问题。但是对已运行时异常和我们抛出的异常不能按照我们的预想是返回错误码和错误说明。

所以,我们要对捕获的异常和运行时异常也能返回错误码和对应的异常说明处理。对于抛出的异常可以指定错误码。但是对于运行时异常则返回统一的系统异常,在日志中打印真实的栈信息异常。

3.2 处理方式

参照如下博文:https://www.cnblogs.com/kire-cat/p/15350364.html

大致流程是:添加配置类,在配置类中采用aop的方式,对运行时异常进行拦截,获取到异常信息的错误码和错误信息,使用@ResponseBody注解返回给调用者。

关键注解是:类注解@RestControllerAdvice。方法注解@ExceptionHandler @ResponsesBody

@ExceptionHandler是对controller任何一处异常做拦截。

@Responsebody这个注解表示你的返回值将存在responsebody中返回到前端

@RestControllerAdvice 注解是作用是拦截所有@Controller注解,进行增强处理。

4,接口鉴权

给所有接口加鉴权处理。方式:创建一个配置类,也是一个bean (使用@Configuration, @Bean)。实现WebMvcConfigurer,添加一个拦截器方法,拦截器所有请求。并将拦截的请求转到我们自定义的处理器中出来。自定义拦截处理器需要实现HandlerInterceptor。

自定义拦截处理器主要处理内容:如:解析请求头中的token,判断能否正常解析,能否解析其中用户信息,秘钥信息。在拿到用户秘钥信息与数据库中真实的用户信息对比,一致则通过认证。

5,jwt 生成token

原理大概是使用令牌(即jwt,)和加密算法,以base64编码输出,生成token。可以添加需要加密的内容,如用户名,密码。可以设置过期时间;

6,单点登录 token

关键点:解决不同域名用户信息不能共享的问题。

跨域:先知道啥是跨域,简而言之,就是ip和端口或者域名是一样的。这样在浏览器中就能共享cookie了。

单点登录,分两种情况,即浏览器访问时域名是否相同。

相同域名:这种情况一般是多系统后台服务有一个统一的转发入口服务,类似于网关,在网关中做token验证和登录。

不同域名:(自己项目的情况)这种就需要跨域访问的时候url携带上当前域cookie中的token,然后在新的页面中先掉后端接口检验token是否正确。检验通过后,前端再重定向刷新页面(使用当前token,访问新页面接口),去掉地址栏中的token,正式访问。

总结,域名相同操作主要在后端,域名不同操作主要在前端。

6.0 怎么刷新token

前端登录时把token放localstorage中,每次请求时后台会重置token的过期时间,并放到响应头中。这些操作都是在前后端各自的拦截器中实现。

当然这种方式可能全新的太频繁了,因为一次一般要请求很多接口。可以考虑加个时间判断,快过期的时候后台再把新的token放到响应头中。

6.1无论哪种登录方式,都要带有用户信息。单纯一个地址是不行的。一般单点登录是不通系统间的跳转,都能够拿到token的。

7, 断言

断言可以理解一个工具类,通常用来做判空、是否等于等处理。

断言和统一异常结合起来使用,可以减少大量try catch的冗余处理。适合用于不满足条件时直接返回接口异常信息。

8,加密处理

给数据库等密码做简单加密处理。

springboot 提供了jasypt加密方式,使用方便

参照:SpringBoot使用jasypt加解密密码

8.2 登录密码加密处理:

使用RSA非对称加密处理,后端可直接生成公钥密钥对(简单的形式,不用jdk或申请证书),把公钥给前端。后端接收加密后的数据,使用私钥,解密即可。

另:md5加密是不可逆的,所以无法解密。对于需要原字符串对比情况,md5不适用。但是md5每次加密后的结果是一样的。

9,统一http返回码

背景:接口返回码分 业务状态码和http状态码。业务状态码可以随意定义。但是http状态码只有固定的一些,如200,404,500。有时候,虽然接口状态码返回200,但实际接口业务处理异常了。在浏览器控制台这种报错的接口是不爆红的,给我们定位哪个接口出错带来不便。所以要求在接口业务处理异常时,http状态码也返回异常。

定义一个配置类,实现ResponseBodyAdvice接口,使用@ControllerAdvice声明切面。

注意:也不是当自定义的业务码不在固定http状态码时会报错。所以要使用try catch捕获异常处理。

10,全局线程池

使用类注解@Configration,方法注解@Bean("asyncThreadPool"),声明一个bean。然后ThreadPoolTaskExceutor exector = new ThreadPoolTaskExcecutor();声明一个线程池bean对象。

在需要异步处理的方法上使用注解@Async("asyncThreadPool") 即可。这个方法的内容就可以异步操作了。

11,接口字段校验

背景:要对接口的字段判空、判断长度。

1,首先控制器层面:@Post 请求的参数要使用@RequestBody ,否则不识别。再者要声明@Validated才会对参数对象中的字段判断。@Length 限制长度 @Notnull 不空,@Min 数值型最小值,@Max 数值型最大值

2,每个字段注解也可以使用groups分组使用如@Length(max =20,groups = {Save.class, Update.class}

3,Get 请求可以使用@RequestParam(defaultValue= "") 声明默认值。

12,导出excel

推荐使用alibaba.easyexcel。注解声明表头、列。可以自定义样式策略。

其他导出excel的包,容易卡死。不推荐。

13,google缓存

google提供的loadingCache缓存包在查不到缓存时会抛出异常,极不友好。所以建议结合stream流处理。

// 声明缓存
private LoadingCache<String, Optional<String>> cache = CacheBuilder.newBuilder().maximumSize(200)
.expireAfterWrite(14400, TimeUnit.SECOND)
.build(
    List<Model> list = service.queryData();
    if(list ==null){
          return Optional.empty();
       }
       list.forEach(m-> cache.put(key,value));
        Optional<String> value = cache.getIfParent(key);
        if(value == null){
            return Optional.empty();
           }
        return value;
});

// 使用缓存 捕获异常,查不到也影响业务
try{
    return cache.get(key).orElse(null);\
}cache(Exception e){
    return null;
}

14,redis缓存和分布式锁

1,redis 使用redisTemplate 操作redis 

2,缓存企业、部门、人员、字典数据,过期时间半天、一天不等。

3,使用string格式即可,key使用固定前缀加数据的键。值如果是复杂结构的,则存储为json格式。

4,分布式锁:1,用途 防止反复推送数据,2,项目启动时,缓存数据到redis时,先上锁再开启缓存,因为缓存时间较长,防止其他节点重复缓存相同的数据。 锁超期时间设置为100s。

使用,1加锁逻辑,先获取key,如果存在说明有其他节点在执行了,放弃执行。如果没有则加缓存加锁。2删除锁,任务执行完后,删除锁,删除前先判断锁的值是否一样。记住 锁的值是随机生成的。一样才能删除锁。否则可能出现(任务执行的时间很长,锁超时自动释放了。任务执行完了去释放锁,可能把另外一个节点正在执行任务的锁给释放了。)

3,使用rediscallback 使加锁和设置过期时间在一个redis链接中,保证事务原子性,解决设置过期时间失败的问题。

使用lua脚本解锁。if redis.call("get", keys)= argv[1] then return redis.call(del, keys[1])。先判断锁的值是否一样,一样才能删除。redis执行lua脚本能保证查询和删除在一个原子操作。

redis序列化要注意,一般有两种序列化方式,一种是json,一种是string。通常key是字符串,value是json。存储时用的哪种方式,取值时也要用哪种

4.使用spring提供的redistemplate类,获取锁,执行execute方法,返回成功就加锁成功。解锁使用lua脚本,也是通过redistemplate执行。

也可以使用setnx提供的原子性操作方法,保证线程安全。

集群使用主从复制做容灾。主节点写,从节点读。

15,幂等

处理幂等的方式有很多种,最简单的是主键(包括业务唯一索引)限制和前端防抖。

2,较复杂的是token去重,需要前后端配合,比如某个更新接口,当进入到这个接口所在的页面时,前端请求后端获得一个token,然后拿这个token调更新接口。此时,前后端各自保存一个相同的token,后端接口会先拿token去redis中找(要考虑到分布式,redis结构是string,键可以是这个接口名,值就是token。),如果token一致说明,是首次操作,然后更新后端的token, 如果前端再次发来请求,两边token就会对不上,说明请求就重复了。

3,悲观锁,只适合更新操作,对同一条数据使用fot update加锁。但这里的查询条件一定要是主键,否则会锁全表。

16, nginx转发,分流

17,处理的问题。

1,项目启动找不到bean。处理,发现在使用@Async异步处理时,在bean有循环依赖时,会出现找不到bean的情况。解决,使用@lazy懒加载,但保险起见,还是不使用循环依赖的书写方式,可增加业务处理层数。

18,  查看服务所在目录属于哪个文件系统,以及剩余磁盘空间  

命令: dh -lh 目录  

如 dh -lh /home

20 redis删除策略

定时删除,给键设置过期时间。

惰性删除,如果没有设置过期时间,就统一设置一个,在请求时先看有没有过期。

定期淘汰:内存达到阈值就使用淘汰策略,比如 最近不常用的,使用最少的,时间最长的,随机删除等。

21 kafka吞吐快的原因,这个问题没思考过,记一下。kafka是对文件做读写,追加顺序操作,反正这样就是快。2.零拷贝 正常写磁盘数据要经过缓冲区,但人家不需要直接写。3,分区分段,可并行写。4可批量提交,压缩传输。

22 kafka消费者组的概念。就是多个消费者共享一个groupid,共同消费。可以提升消费速度,也能保证有序性,也支持容灾,有的消费者挂了,也不影响。

22. redis自动续期 策略。就使用redission提供的watch dog。首先要开启这个功能。然后会开启定时任务,隔个十几秒扫一下,锁还在被持有,就继续续期。听说这个源码有用到可重入锁。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐