Springcloud+Seata+nacos 分布式事务项目搭建 AT模式

前言本文要求对Springcloud有一定了解,对分布式事务本身有一定认知,如果不了解微服务,建议先看看Spring Cloud的基本认识和使用Spring Cloud的基本教程,再回头学习本文 为什么会出现分布式事务开发过程中,随着项目模块的增加以及分库分表的出现,传统事务已经无法满足业务需求,如分库,由于有多个数据源,而数据库事务又是基于数据库层,所以如果只用数据库原生事务,会导致数据库A成功提交,数据库B回滚,导致数据不一致,又比如多模块下,常见的订单流程,订单服务成功提交订单,调用库存服务扣减库存,由于是链式调用,库存成功扣减,然后回到订单服务时,出现异常,导致订单回滚,但是此时库存却未回滚,也会导致数据不一致,所以这些情况都需要分布式事务来解决这个问题(当然一般开发中,我们常用的做法是能避免就尽量避免,实在避免不了才使用分布式事务,因为分布式事务不管怎么样,性能,一致性,原子性等都会收到影响) 分布式事务目前的几种方案2PC(二阶段提交) 3PC(三阶段提交) TCC(Try - Confirm - Cancel) 最终一致性(消息队列等方式) 最大努力通知(数据不要求强一致)…

Springboot Druid 自定义加密数据库密码

前言开发过程中,配置的数据库密码通常是明文形式,这样首先第一个安全性不好(相对来说),不符合一个开发规范(如项目中不能出现明文账号密码),其实就是当出现特殊需求时,比如要对非运维人员开方服务器部分权限,但是又涉及项目部署的目录时,容易泄漏数据库密码,虽然一般生产环境中,数据库往往放入内网,访问只能通过内网访问,但是不管怎么说账号密码直接让人知道总归不好,甚至有些项目需要部署到客户环境中,但是可能共用一个公共数据库(数据库只向指定服务器开放外网端口或组建内网环境),这样的情况下,如果数据库密码再以明文形式存在,就非常危险了,常见的避免这些方式据我了解有2种 第一种:通过网络获取密码 这种是让密码通过接口获取,当然接口中可以做加密认证,但是如果是把获取地址和认证放在了配置文件中,那么稍微用心也能获取到密码,不过也可以跟第二种方法配合使用 第二种:密码加密 通过对密码加密,使得配置文件中配置的密码为密文,从而提升安全性,当然网络获取密码也可以获取加密后的密码,而国内常用的数据库连接池是Druid,所以此文主要是针对Druid的数据库密码加密方案,Druid默认再带了一个密码加密,所以Druid加密密码也有2种方案 Druid加解密密码的2种方案第一种:自带加密 优点:简单快捷,配置方便,无需改动代码(只需要改动配置文件即可) 缺点:自欺欺人式加密,因为密码和公钥都需要配置到配置文件中,只要别人有了配置文件,看出来了你是用的Druid,…

Java 浮点型(Double,Float)精度丢失解决方案

前言最近公司某小伙子做了一个商城的微信支付相关的接口,其中包含退款,在测试过程中发现部分单据没有退款,微信支付提示退款金额跟支付金额不匹配(大于支付金额),检查数据库和调试过程中,发现商品的单价和手工计算出来的总价是跟订单金额匹配的,实在无法确认问题原因最终bug转向我来排查,于是有了此文 排查由于手工计算出来的金额跟实际支付的金额是能匹配上的,所以一开始我以为是订单已经进行了部分退款,再次全部退款的话肯定会被微信阻止,因为这样的话就会出现超退。 第一步:是否已经部分退款 去微信支付平台排查是否有退款,结果发现并没有退款 由于微信平台没有查询到此单的退款,那么说明不是超退的问题,可能是传入的订单金额有问题,但是大部分单据又可以退款,所以也暂时陷入了僵局,不过为了保险起见,还是去看了一下传参处 第二步:检查传参 检查微信退款方法传参时,看了传入参数,由于微信传参的单位是分,所以看到传参处传入退款总金额代码(int)(100)*商品单价*退货数量(这里只是伪代码,多个商品肯定还要相加计算的),粗略一看没有问题,仔细一看不对,总价是直接double类型的乘以数量在乘以100再强转为int类型传入的,按照理论上来说是没有问题的,因为我们商品价格都是精确到分的,但是只是理论上,实际上就是Java的double类型有个大坑(不只是Java,JavaScript,c#等很多语言都有这个问题),最终在代码中进行总价计算,发现确定是因为这个原因,导致计算出来的退款金额比应退金额多了1分钱,…

基于IDEA热部署更新服务器Tomcat类,服务器Tomcat热更新

前言在开发过程中,如果我们是使用的IDEA,就会知道IDEA有一个热更新的功能,何为热更新?就是在不重启Tomcat的情况下让服务器中的代码变更为最新的。这样既能快速的更新代码,又不用担心Tomcat重启session过期的问题,如同更新jsp一般的方便,当然IDEA里面的热更新还是没有更新jsp那么方便,具体限制本文后面有提(只是我目前了解到的限制)。如果说本地更新还算快捷,那么服务器更新就更加麻烦了,一般我们更新服务器上面的程序有2种情况,一是全量更新,就是打包war包去更新,二是部分更新,就是修改了那个class,就去服务器替换某个class文件,然后重启服务器(jsp文件不算,因为更新jsp是不需要重启Tomcat的,具体原因自己行百度)。这样常规的更新有2个弊端,第一:耗费时间长,不管是文件上传的时间,还是重启服务的时间,尤其是在项目很大启动很慢的时候,有时候只是更新一个简单的东西,却要等待数分钟,而且这数分钟还不是一个人的时间,是所有当时在用这台服务器的人的时间,第二:session失效的问题,服务器一旦重启,所有登录的用户就需要重新登录(当然如果session不是保存在Tomcat中的就另当别论了)。 基于以上种种,如果更新服务器能像我们本地IDEA一样方便,那么会节省我们很多的时间,加快各方人马的开发进度。幸好这种方式是有的,可以实现在IDEA中直接更新服务器代码,此方式是基于Tomcat远程调试的(不清楚远程调试的同学建议百度,也是一个挺好用的功能),而且不需要做其他配置,所以直接进入配置环节 Tomcat远程调试配置因为只要配置好了IDEA的远程调试,…

Springboot Druid 使用Slf4j输出可执行SQL

前言在开发中,为了数据安全,所有SQL语句肯定是用占位符的,但是在实际开发中,为了方便追踪问题,经常需要查看具体执行的SQL语句内容,而用了占位符之后,每次真实执行的语句只能靠自己根据参数拼接出来,耗时耗力,所以需要一个方法输出每次执行的SQL语句,并且还是有参数的(替换了占位符),而Druid就支持此功能(Druid的主要功能可不是这个),所以此文主要讲Springboot环境下使用Druid输出可执行SQL,并且是使用的Springboot的默认日志配置格式,因为一旦修改了Springboot的日志配置格式,输出来的日志一般是没有颜色区分的,如下: 而springboot默认配置的日志如下: 当然也可以自己配置出来颜色,只是我个人觉得springboot的默认配置已经很好看了,不用再配置了 当然Springboot配置Druid输出可执行的SQL文档太多了,只是他们都需要配置日志本身,也就是配置后日志输出的样子我不喜欢,所以各种研究后,才有了此文(之前百度是真没有找到用默认配置输出的方法) Druid 介绍Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。以上是官方的自我介绍,也侧面说明了Druid是一个功能强大,自带监控扩展的数据库连接池,具体想了解Druid的话建议去全球最大的同性交友基地了解 配置使用Springboot默认的日志配置文件,意味着就没有log4j.properties之类的文件给我们配置了,那就只有在application.yml或者application.properties中配置springboot告诉它我们哪些地方需要打印日志,需要打印什么级别的日志 首先要配置连接池为Druid spring: application: name: admin-managee datasource: druid:…

SpringMVC,SpringBoot快速验证参数是否完整 基于@Valid注解

前言在接口开发中,经常是需要对传入参数完整性进行验证的,或者对手机号格式进行验证等,而一般不了解@Valid注解之前,都是通过自己封装方法来验证,都是一堆if else集合,这样判断对于代码结构很不友好,开发效率也不高,使用@Valid注解可以快速的对参数进行验证,并且可以通过抓取全局异常,从而实现统一返回 引用@Valid是使用hibernate validation的时候使用,其中java的jsr303声明了这类接口,hibernate-validator对其进行了实现 在进行maven整合时,需要引入的依赖 <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.13.Final</version>…

定时同步数据的任务优化

前言定时任务在系统中并不少见,主要目的是用于需要定时处理数据或者执行某个操作的情况下,如定时关闭订单,或者定时备份。而常见的定时任务分为2种,第一种:固定时间执行,如:每分钟执行一次,每天执行一次。第二种:延时多久执行,就是当发生一件事情后,根据这件时间发生的时间定时多久后执行任务,如:15分钟后关闭订单付款状态,24小时候后关闭订单并且释放库存,而由于第二种一般都是单一数据的处理(主要是指数据量不大,一般情况下只有一个主体处理对象,如:一个订单以及订单中的N个商品),所以一般情况下第二种出现性能问题的几率不大(不代表没有),所以本文主要是针对第一种定时任务来进行优化,而且主要是针对数据同步或者传递数据来进行优化,而优化的方式也是根据实际项目中的情况在不同阶段进行优化的 第一阶段第一阶段属于原始阶段,逻辑也最为简单,由于同步分为数据同步和传递数据,而且2种的需求各不一致(主要是在于是否允许丢失),所以分开分析 第一种类型:传递数据由于传递数据可以允许丢失,常见的场景如调用凭证推送(常见于接口需要暴露给第三方,为了安全性,可以定时推送调用凭证来保证接口安全性),消息推送(订单消费成功后推送消息,由于可能推送失败,所以需要进入定时任务进行重试,但是因为消息实时性,所以重试到一定次数后放弃重试) 传递数据在第一阶段设计非常简单,定时推送,有限的错误次数,同步成功后修改状态,同步失败后对失败次数+…

记一次ajax的JSESSIONID 变化解决、非跨域变化

前言某一天测试提了一个bug,系统进入到某个页面中后,出现登录失效,不管怎么样,只要进入这个页面再点击其他链接就会去到登录页面,测试环境没有问题。 开始解决第一步:怀疑代码问题因为测试环境没有任何问题,所以怀疑是代码本身的问题,于是在本地测试,发现本地没有问题,于是重新打包部署到正式服务器测试,发现还是出现异常。 第二步:确认为何会登录失效因为代码和测试环境与本地都是一样的,所以怀疑是数据原因导致登录失效,所以在本地将配置切换到正式环境,发现本地还是没有任何问题,排除这一问题,同时后台也没有任何异常产生,最终追踪到页面,发现页面发出的ajax请求的JSESSIONID 发生了改变,于是开始排查JSESSIONID 为什么会改变,百度到了2种原因: 一、response调用了reset方法导致登录失效二、ajax跨域导致登录失效首先第一个,如果是因为调用reset方法导致登录失效,那么所有的环境理论上都会登录失效,但是为了保险起见,还是检查一下,最后发现并没有地方调用reset方法,所以排除此项。于是检查第二个,但是整个系统并没有跨域,但是为了保险,还是将网上说的方法在ajax请求中添加 beforeSend: function(xhr) { xhr.withCredentials = true; } crossDomain:true, 加上之后发现还是不行,于是以上2种原因被排除 第三步:…