可乐
一 排查思路
平台错误提示
发布平台获取错误信息(平台会有相关发布失败信息,用于辅助排查问题+8000咨询提供给值班同事,便与值班同事定位问题)
应用程序是否启动
ps -ef|grep 应用名称
业务日志排查
查看控制台日志 ( /home/publish_product/server_java/${application}/${version}/logs/stdout.log,主要输出启动过程中输出到终端的日志,应用启动失败,首要看这个日志( prod不生成,每次启动成功会删除,启动过程中去查看 )
框架error日志
(/home/product/logs/${application}_logs/frame/error_dubbo_framework.log,在框架层面记录下来的日志,一般启动失败,在这里也会有错误信息,不止是启动失败,应用运行过程中出现错误,也可以先去这个日志文件查看。)
(/var/log/cli.log从发布系统执行命令后,运维通道调用启动脚本过程中输出的日志)
(/var/log/message, 如果应用启动是申请的内存超过了OS 可用的内存,那么操作系统出于自我保护,会直接kill掉应用进程,同时会记录Kill process的日志,如果是docker进程,日志是记录在宿主机上)
/home/product/logs/{应用名_logs}/dolphin-executor-error.log
二 常见问题汇总
1 虚拟机类
1.1 代码异常问题
问题现场
解决方案
业务jar包类找不到
集团架构提供的组件jar包类找不到
base_component_all、dolphin等咨询架构同事辅助解决
1.2 健康检查不通过问题
问题现场
解决方案
查看业务日志,确认是否有异常 , ps -ef |grep 应用名 查看进程是否存在(如果日志没明显错误且进程存在,则排查端口)
端口是否启动(以下两种都可以查看端口暴露情况)
netstat -nlpt
telnet 127.0.0.1 应用rpc端口
端口没暴露
本地启动,看看端口是否暴露,本地调试解决问题(一般是dubbo-provider.xml文件中没有暴露的服务,没服务及时是dubbo应用,端口也不会启动)
端口暴露
说明程序正常运行 & 端口号暴露 ,但是启动耗时过程
排查应用启动过程中是否有异常,导致启动耗时过长。如果没问题,业务启动耗时变成是正常的,可以调整应用发布超时时间
1.3 获取动态参数异常问题
问题现场
解决方案
若jvm参数未配置
jvm参数已配置
查看app.properties里的application.name是否等于应用名
不等于,则修改保持一致
等于&jvm参数配置了,则重试几次,若几次还不行,咨询8000,问乐效值班同事
1.4 发布失败,缺少启动脚本文件
问题现场
解决方案
确认是否需要启动脚本文件
启动类应用(需要)
java
go
python
c/c++
nodejs
非启动类应用(不需要)
启动类解决方案(补全缺少脚本文件,参考通组其他工程)
1.5 发布失败,服务未启动问题
问题现场
问题原因
restart脚本复用了shell上下文,stop脚本exit 1时,会退出shell上下文,导致start脚本不执行
类似案例:
(查看工程restart.sh脚本,核对真实执行启动脚本)
解决方案
临时方案:乐效启停页面,先把应用程序启动
长期方案:修改restart脚本实现
1.6 发布失败, 后端服务超时未回应问题
问题现场
问题原因
调用cli通道执行发布操作,cli通过没响应,可以登录机器,查看/var/log/cli.logs
解决方案
查看cli日志,分析是否cli返回有延迟
重试(若重试还不行,找8000咨询乐效值班)
1.7 发布失败,提示check old xxxx failed问题
问题现场
解决方案
联系8000找架构处理,组件包检测不通过
1.8 发布失败,实际应用程序启动成功
问题现场
问题原因
start脚本自定义了发布检测超时时间,检测逻辑不通过,导致发布失败
解决方案
1.9 发布失败,后端服务超时未回应,命令超时
问题现场
问题原因
推送、发布实际真实执行过程:乐效调用cli服务器,把推送、发布脚本“推送”到宿主机上,然后在宿主机上执行脚本,返回执行结果
如果脚本执行异常,没有回调发布系统,页面会显示命令超时
如果脚本执行成功or失败,会返回对应信息,在乐效上会看到对应状态和信息提示
排查过程
登录部署机器,监听cli执行过程日志(tail -f /var/log/cli.log)
乐效重试推送or发布操作,在cli日志中会输出脚本执行过程日志
解决方案
2 容器发类
配置
provider配置xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo= "http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd" >
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name= "service-provider2" owner= "xiaoming" />
<!-- 使用zookeeper注册中心暴露服务地址 -->
<dubbo:registry address= "zookeeper://127.0.0.1:2181" id= "r1" timeout= "10000" />
<!-- 用dubbo协议在20882端口暴露服务
<dubbo:protocol name="dubbo" port="20882" /> -->
<!-- 用dubbo协议在20883端口暴露服务 -->
<dubbo:protocol name= "dubbo" port= "20883" />
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface= "com.lagou.service.HelloService" ref= "helloService" />
<!-- 和本地bean一样实现服务 -->
<bean id= "helloService" class= "com.lagou.service.impl.HelloServiceImpl" />
</beans>
consumer配置xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo= "http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd" >
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name= "dubbo-consumer" owner= "lisi" >
<dubbo:parameter key= "qos.enable" value= "true" ></dubbo:parameter>
<dubbo:parameter key= "qos.port" value= "33333" ></dubbo:parameter>
<dubbo:parameter key= "qos.accept.foreign.ip" value= "true" ></dubbo:parameter>
</dubbo:application>
<!-- -->
<dubbo:consumer timeout= "2000" check= "false" >
</dubbo:consumer>
<!-- 使用zookeeper注册中心暴露发现服务地址 -->
<dubbo:registry address= "106.75.144.210:2181" timeout= "10000" protocol= "zookeeper" id= "myRegistry" >
</dubbo:registry>
<dubbo:protocol name= "dubbo" />
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id= "service" interface= "com.my.study.service.Service" registry= "myRegistry" timeout= "4000" retries= "2" version= "0.0.0" />
<dubbo:annotation package= "com.my.study" ></dubbo:annotation>
</beans>
配置讲解xml形式
1.dubbo:application 当前应用的信息
1. name : 当前应用程序的名称,在dubbo-admin中我们也可以看到,这个代表这个应用名称。我们 在真正时是时也会根据这个参数来进行聚合应用请求。
2. owner : 当前应用程序的负责人,可以通过这个负责人找到其相关的应用列表,用于快速定位到责 任人。
3. qosEnable : 是否启动QoS 默认true。
4. qosPort : 启动QoS绑定的端口 默认22222 5. qosAcceptForeignIp: 是否允许远程访问 默认是false。
2.dubbo:registry 注册中心的信息
1. id : 当当前服务中provider或者consumer中存在多个注册中心时,则使用需要增加该配置。在一 些公司,会通过业务线的不同选择不同的注册中心,所以一般都会配置该值。
2. address : 当前注册中心的访问地址。
3. protocol : 当前注册中心所使用的协议是什么。也可以直接在 address 中写入,比如使用 zookeeper,就可以写成 zookeeper://xx.xx.xx.xx:2181。
4. timeout : 当与注册中心不再同一个机房时,大多会把该参数延长。
3.dubbo:protocol 数据传输使用的协议
1. id : 在大公司,可能因为各个部门技术栈不同,所以可能会选择使用不同的协议进行交互。这里 在多个协议使用时,需要指定。
2. name : 指定协议名称。默认使用 dubbo 。
4.dubbo:service 提供者能提供的服务
1. interface : 指定当前需要进行对外暴露的接口是什么。
2. ref : 具体实现对象的引用,一般我们在生产级别都是使用Spring去进行Bean托管的,所以这里面 一般也指的是Spring中的BeanId。
3. version : 对外暴露的版本号。不同的版本号,消费者在消费的时候只会根据固定的版本号进行消费。
4. executes: 用于在提供者做配置,来确保最大的并行度。
1. 可能导致集群功能无法充分利用或者堵塞 。
2. 但是也可以启动部分对应用的保护功能 。
3. 可以不做配置,结合后面的熔断限流使用。
5.dubbo:reference 消费者 配置
1. mock : 用于在方法调用出现错误时,当做服务降级来统一对外返回结果,后面我们也会对这个方 法做更多的介绍。
2. timeout : 用于指定当前方法或者接口中所有方法的超时时间。我们一般都会根据提供者的时长来 具体规定。比如我们在进行第三方服务依赖时可能会对接口的时长做放宽,防止第三方服务不稳定 导致服务受损。
3. check : 用于在启动时,检查生产者是否有该服务。我们一般都会将这个值设置为false,不让其进 行检查。因为如果出现模块之间循环引用的话,那么则可能会出现相互依赖,都进行check的话, 那么这两个服务永远也启动不起来。
4. retries : 用于指定当前服务在执行时出现错误或者超时时的重试机制。
1. 注意提供者是否有幂等,否则可能出现数据一致性问题。
2. 注意提供者是否有类似缓存机制,如出现大面积错误时,可能因为不停重试导致雪崩。
5. id : 指定该Bean在注册到Spring中的id。
6. interface: 服务接口名 3. version : 指定当前服务版本,与服务提供者的版本一致。
7. registry : 指定所具体使用的注册中心地址。这里面也就是使用上面在 dubbo:registry 中所声 明的id。
8.cache: 声明式缓存。
6 dubbo:method 指定具体方法级别在进行RPC操作时候的配置
1. name : 指定方法名称,用于对这个方法名称的RPC调用进行特殊配置。
2. async: 是否异步 默认false
配置讲解注解+properties形式
consumer配置
配置和启动类
public class ConsumerMain {
// 启动方法
public static void main ( String [] args ) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext ( ConsumerConfiguration . class );
context . start ();
// 获取消费者组件
ConsumerComponent service = context . getBean ( ConsumerComponent . class );
Executor executor = ( Executor ) context . getBean ( "taskExecutor" );
while ( true ){
try {
Thread . sleep ( 1000 );
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
for ( int i = 0 ; i < 35 ; i ++) {
executor . execute ( new Runnable () {
@Override
public void run () {
String result = service . methodA ();
}
});
executor . execute ( new Runnable () {
@Override
public void run () {
String result = service . methodB ();
}
});
executor . execute ( new Runnable () {
@Override
public void run () {
String result = service . methodC ();
}
});
}
}
}
// 配置静态类
@Configuration
@PropertySource ( "classpath:/dubbo-consumer.properties" )
@ComponentScan ( basePackages = "com.my.study" )
@EnableDubbo
static class ConsumerConfiguration {
}
}
properties文件
dubbo . application . name = dubbo - provider
dubbo . protocol . name = dubbo
dubbo . protocol . port = 20889
dubbo . application . owner = zhangsan
dubbo . registry . address = zookeeper : //106.75.144.210:2181
dubbo . application . qosEnable = false
dubbo . application . qosPort = 33334
dubbo . application . qosAcceptForeignIp = false
provider配置
provider配置application
dubbo . application . name = dubbo - provider
dubbo . protocol . name = dubbo
dubbo . protocol . port = 20889
dubbo . application . owner = zhangsan
dubbo . registry . address = zookeeper : //106.75.144.210:2181
dubbo . application . qosEnable = false
dubbo . application . qosPort = 33334
dubbo . application . qosAcceptForeignIp = false
provider配置和启动类
public class DubboPureMain {
// 启动方法
public static void main ( String [] args ) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext ( ProviderConfiguration . class );
context . start ();
System . in . read ();
}
// 配置静态类
@Configuration
@EnableDubbo ( scanBasePackages = "com.my.study.service" )
@PropertySource ( "classpath:/dubbo-provider.properties" )
static class ProviderConfiguration {
@Bean
public RegistryConfig registryConfig (){
RegistryConfig registryConfig = new RegistryConfig ();
registryConfig . setAddress ( "zookeeper://106.75.144.210:2181?timeout=10000" );
//registryConfig.setTimeout(10000);
return registryConfig ;
}
}
}}
1.@EnableDubbo(scanBasePackages =”com.my.study.service”)
提供者需要对外提供的方法直接通过包扫描的方式去进行注册到ioc中。
2.@Configuration
代表当前是个配置类
3.@PropertySource(“classpath:/dubbo-provider.properties”)
导入外部的文件并解析
4.@ComponentScan(basePackages =”com.my.study”)
将需要的类包扫描的方式去进行注册到ioc中。
总结
本文介绍的只是部分常用的配置,更多详细配置可以参考dubbo官方文档,配置模块的文档去了解学习。
深夜,凝芳殿中。
"奴婢参见玉妃,玉妃娘娘万福金安",林婉拜跪着说道。
身前不远处,坐着一位天生媚骨,身材极好的女子,此刻她正慵懒的抱着一只波斯猫,轻轻的抚摸着。
"本宫听闻浣衣监新来了个婢子,是工部尚书林任之的侄女,年纪不大,样貌倒是极美,还未出阁就引得京城王李两家的公子争相提亲",
玉妃将怀里的波斯猫放下,站起身来,端起一盏灯,缓缓向林婉走来。
虽然被发配到浣衣监才几日光景,林婉却也听其他人说起过,在后宫行事千万不要得罪玉妃,当日她好奇追问这是为何,众人却死活不敢说下去,
此刻已是亥时,无论是浣衣监还是凝芳殿都应该早早休息了才对,可偏偏玉妃差人唤自己进殿,过来的一路上,林婉思索了一番,实在想不到自己何时招惹了这尊大佛,只得在心里暗暗祈祷不要出什么事才好。
"本宫倒要看看,这大名鼎鼎的林婉,是不是生的似仙女一般夺人心魄"玉妃轻轻笑了几声,慕然间已然走到了林婉的面前。
"抬起头来"玉妃的声音突然从林婉耳边响起,既不温柔也不冰冷,彷佛没有感情的机器。
林婉乖乖的把头抬起,这才看清玉妃的模样,烛火映照之下,玉妃容色晶莹如玉,如新月生晕,花树堆雪,明眸皓齿,好似九重宫阙的谪仙子一般,难怪在这后宫能如此受宠!
玉妃也在看着她,二八年纪,生得浑然天成的绝美脸庞,朱唇皓齿,亦是美得不可方艳。
两人微微出神,还是林婉先反应过来,连忙低下头,生怕冒犯了玉妃。
玉妃出身高贵,自然眼界不低,何况在这后宫中最不缺的就是各样各式的美女,饶是如此她也不得不承认眼前婢女的容貌称得上祸国殃民。
玉妃深深的瞥了林婉一眼,随即说道,"本宫今夜唤你前来,就是想见识下你的样貌,现在看来,也不过如此,下去吧"。
林婉闻言如释重负,立马起身,顾不得那么许多退出了凝芳殿。
回到浣衣监,林婉还在思索着刚刚发生的事情,她觉得玉妃深夜唤她进宫一定不是看她的容貌这般简单,一夜无眠。
林婉不知道的是,玉妃今夜也失眠了...
--------------------------------------------- 我是分割线 --------------------------------------------------------------------------------------------------------------
次日叫醒林婉的不是公公,而是凝芳殿的婢子秋莹,辰时未至她就到了浣衣监,在门口等了半个时辰,
开门的婢子一来,秋莹就急匆匆的让她带自己去找林婉。
吱呀一声,林婉的房门被推开,此时屋内还是
背景:搭建业务异常的监控告警系统,要求支持两种告警类型
一个是首次告警,好理解,接收到上报的异常就告警
另一个则是统计告警:统计在首次告警后五分钟内该异常出现的总次数
给你五分钟,你会想到什么方式实现统计告警?我想到了定时任务…
其实这是一个典型的延时队列的使用场景
什么是延时队列?
延时队列,首先,它是一种队列,队列意味着内部的元素是有序的,元素出队和入队是有方向性的,元素从一端进入,从另一端取出。
其次,延时队列,最重要的特性就体现在它的延时属性上,跟普通的队列不一样的是,普通队列中的元素总是等着希望被早点取出处理,而延时队列中的元素则是希望被在指定时间得到取出和处理,所以延时队列中的元素是都是带时间属性的,通常来说是需要被处理的消息或者任务。
简单来说,延时队列就是用来存放需要在指定时间被处理的元素的队列。
延时队列的应用场景
在淘宝、京东等购物平台上下单,超过一定时间未付款,订单会自动取消。
打车的时候,在规定时间没有车主接单,平台会取消你的单并提醒你暂时没有车主接单。
点外卖的时候,如果商家在10分钟还没接单,就会自动取消订单。
收快递的时候,如果我们没有点确认收货,在一段时间后程序会自动完成订单。
在平台完成订单后,如果我们没有在规定时间评论商品,会自动默认买家不评论。
延时队列的实现方式
1、DelayQueue 延时队列
JDK
中提供了一组实现延迟队列的API
,位于Java.util.concurrent
包下DelayQueue
。
DelayQueue
是一个BlockingQueue
(无界阻塞)队列,它本质就是封装了一个PriorityQueue
(优先队列),PriorityQueue
内部使用完全二叉堆
(不知道的自行了解哈)来实现队列元素排序,我们在向DelayQueue
队列中添加元素时,会给元素一个Delay
(延迟时间)作为排序条件,队列中最小的元素会优先放在队首。队列中的元素只有到了Delay
时间才允许从队列中取出。队列中可以放基本数据类型或自定义实体类,在存放基本数据类型时,优先队列中元素默认升序排列,自定义实体类就需要我们根据类属性值比较计算了。
先简单实现一下看看效果,添加三个order
入队DelayQueue
,分别设置订单在当前时间的5秒
、10秒
、15秒
后取消。
要实现DelayQueue
延时队列,队中元素要implements
Delayed
接口,这个接口里只有一个getDelay
方法,用于设置延期时间。Order
类中compareTo
方法负责对队列中的元素进行排序。
public class Order implements Delayed {
@JsonFormat (locale = "zh" , timezone = "GMT+8" , pattern = "yyyy-MM-dd HH:mm:ss" )
private long time;
String name;
public Order (String name, long time, TimeUnit unit) {
this .name = name;
this .time = System.currentTimeMillis () + (time > 0 ? unit.toMillis (time) : 0 );
}
@Override
public long getDelay (TimeUnit unit) {
return time - System.currentTimeMillis ();
}
@Override
public int compareTo (Delayed o) {
Order Order = (Order) o;
long diff = this .time - Order.time;
if (diff <= 0 ) {
return -1 ;
} else {
return 1 ;
}
}
}
DelayQueue
的put
方法是线程安全的,因为put
方法内部使用了ReentrantLock
锁进行线程同步。DelayQueue
还提供了两种出队的方法 poll()
和 take()
, poll()
为非阻塞获取,没有到期的元素直接返回null;take()
阻塞方式获取,没有到期的元素线程将会等待。
public class DelayQueueDemo {
public static void main (String[] args) throws InterruptedException {
Order Order1 = new Order ("Order1", 5 , TimeUnit.SECONDS);
Order Order2 = new Order ("Order2", 10 , TimeUnit.SECONDS);
Order Order3 = new Order ("Order3", 15 , TimeUnit.SECONDS);
DelayQueue<Order > delayQueue = new DelayQueue<>();
delayQueue.put (Order1);
delayQueue.put (Order2);
delayQueue.put (Order3);
System.out .println ("订单延迟队列开始时间:" + LocalDateTime.now().format (DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
while (delayQueue.size() != 0 ) {
Order task = delayQueue.poll ();
if (task != null) {
System.out .format ("订单:{%s}被取消, 取消时间:{%s}\n", task.name, LocalDateTime.now().format (DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
Thread.sleep (1000 );
}
}
}
上边只是简单的实现入队与出队的操作,实际开发中会有专门的线程,负责消息的入队与消费。
执行后看到结果如下,Order1
、Order2
、Order3
分别在 5秒
、10秒
、15秒
后被执行,至此就用DelayQueue
实现了延时队列。
订单延迟队列开始时间:2020 -05 -06 14 :59 :09
订单:{Order1}被取消, 取消时间:{2020 -05 -06 14 :59 :14 }
订单:{Order2}被取消, 取消时间:{2020 -05 -06 14 :59 :19 }
订单:{Order3}被取消, 取消时间:{2020 -05 -06 14 :59 :24
深夜,凝芳殿中。
“奴婢参见玉妃,玉妃娘娘万福金安”,林婉拜跪着说道。
身前不远处,坐着一位天生媚骨,身材极好的女子,此刻她正慵懒的抱着一只波斯猫,轻轻的抚摸着。
“本宫听闻浣衣监新来了个婢子,是工部尚书林任之的侄女,年纪不大,样貌倒是极美,还未出阁就引得京城王李两家的公子争相提亲”, 玉妃将怀里的波斯猫放下,站起身来,端起一盏灯,缓缓向林婉走来。
虽然被发配到浣衣监才几日光景,林婉却也听其他人说起过,在后宫行事千万不要得罪玉妃,当日她好奇追问这是为何,众人却死活不敢说下去,
此刻已是亥时,无论是浣衣监还是凝芳殿都应该早早休息了才对,可偏偏玉妃差人唤自己进殿,过来的一路上,林婉思索了一番,实在想不到自己何时招惹了这尊大佛,只得在心里暗暗祈祷不要出什么事才好。
“本宫倒要看看,这大名鼎鼎的林婉,是不是生的似仙女一般夺人心魄”玉妃轻轻笑了几声,慕然间已然走到了林婉的面前。
前情概要
最近在一次发版前,代码被安全扫描出有漏洞,定睛一看,原来是fastjson的版本不安全,当时用的好像是1.2.57.sec10,安全推荐的包版本是1.2.83,因此开启了我的第一次依赖版本升级之旅。
头次碰壁
之前也有过升级依赖版本的操作,但都是自己公司的内部包,操作类似于在pom文件里面把1.0.0改成1.0.1这样子就行了;
于是这次我也是这么操作的,全局搜索fastjson的依赖,手动修改 <version>1.2.83<version>;看起来好像所有的fastjson都已经被升级成了1.2.83版本
可是当我查看Maven依赖时,不仅有1.2.83版本,原版本1.2.57.sec10的包也还在
我以为是缓存之类的问题,手动移除1.2.57.sec10的包,之后重新编译,1.2.57.sec10的依赖包居然又死灰复燃导入进来了
找出真相
那么…真相只有一个,在我们的项目里面有地方导入了fastjson – 1.2.57sec10的包,结合安全扫描给出的图,对fastjson不仅有直接引用,也有间接引用
我们全局搜索并手动升级fastjson的版本,并不能改掉所有的,可是至少应该改掉直接引入里面的呀,为什么直接引入、间接引入的都还在呢?
引入依赖优先级介绍
当依赖一个jar包多个版本时,优先使用哪个版本,原则如下:
1.本级优先于上级,上级优先于下级;
2.本级依赖版本优先于管理版本;
3.同一个包中后加载的版本覆盖先加载的版本;
4.上级管理版本和本级管理版本会覆盖下级依赖版本;
5.不同下级jar中依赖了不同版本,优先使用先加载下级jar中的版本;
6.与版本号大小没有关系
7.本级无法使用下级管理版本
总结起来就两条:路径最短,申明顺序其次
参考链接:(56条消息) maven中pom依赖相同jar包优先顺序加载版本_lizz666的博客-CSDN博客_mvn parent 相同jar包
Maven依赖传递 (biancheng.net)
一、字符串转指定格式的Date
开发时遇到一个问题,前端传入时间戳字符串,放进一个JSONObject对象中
后端使用alibaba的注解@JSONField(format = “yyyy-MM-dd HH:mm:ss”)去转换格式,得到的结果不正确
目标对象
这样格式转换倒是没有问题,但是转换后的值不正确
查阅资料得知,时间戳转换成日期也是要区分单位的(秒,毫秒)
资料链接:关于时间戳转换时间总是1970的问题__Cat_的博客-CSDN博客
obj.put("operateTime","1675048249");
@JSONField(format = "yyyy-MM-dd HH:mm:ss")转换的时候使用的单位是毫秒 所以如果要得到正确的时间格式,应该在时间戳字符串后面加上3个0(如果是时间戳的话则*1000) 正确的:obj.put("operateTime","1675048249" + 000);
注:时间戳,格林威治时间1970年01月01日00时00分00秒 (北京时间1970年01月01日08时00分00秒)起至现在的总秒数;
北京时间,可以理解为Date对象;
某周一,我正愉快的编写测试用例。既然是在Springboot框架下,那肯定是要调用其他类的
信心满满的使用@Autowried注解进行注入,启动测试类,这个时候报了注入类的空指针
开始排查…
一开始感觉是注入类没有打注解,看了之后发现是有的,那类应该是正常注入Springboot容器了,但是测试类没拿到。百度了一下,都不是对应的问题所在。
突然灵光一现,感觉会不会是注解用错了…
import org.junit.Test;
import org.testng.annotations.Test;
使用第一个注解时,无法注入,会报空指针;使用第二个注解就OK了,而且很明显看到第二个注解会启动容器
详细分析…
(待补充)
Integer的装箱,拆箱及缓存机制:(54条消息) Integer 的缓存机制_MrYuTing的博客-CSDN博客
当我们了解Integer装拆箱及缓存机制后,再来观察Integer和Int的相等判断
一、Integer 和 Integer对比
== 比较的对象(地址)是否相等
equals 比较的是值是否相等
说明:
Integer重写了equals方法,用于比较两个Integer对象的value值是否相等;如果没有重写equals方法的话,equals比较的也是对象地址是否相等(与==相同)。
在对比的两个Integer对象中,如果有对象是new出的,那么用 == 判断一定不相等
如果对比的两个Integer对象都不是new出的,且值范围在[-128 ~ 127]之间,判由于Integer的缓存机制,两个对象先装箱,指向同一个缓存对象,此时==方法会返回true,equals也返回true;如果值范围不在[-128 ~ 127]之间,将会直接 new Integer();进行新创建一个对象, == 方法会返回false,而equals方法会返回true;
总结:Integer对象的相等判断推荐使用 equals方法
二、Integer 和 Int 对比
Integer引用和int对比,和两个 int 判断是否相等是一样的,因为在编译期间编译器会把其中的一个 Integer 自动拆箱(Java 1.5 以后)给转换成一个 int,所以在字节码层面仍然是两个int做比较。
如果是1个Integer和1个int,==和equals是一样的,因为Integer会有一个拆箱的过程。只要这两个值一样,不管是==还是equals都是相等的。
下面是验证代码:
public class Test {
public static void main ( String [] args ) {
// 2个Integer
Integer i1 = new Integer ( 12 );
Integer i2 = new Integer ( 12 );
System . out . println ( "i1 == i2 ? " + ( i1 == i2 )); // false
System . out . println ( "i1.equals(i2) ? " + i1 . equals ( i2 )); //true
// 1个Integer 1个int
Integer i3 = new Integer ( 13 );
int i4 = 13 ;
System . out . println ( "i3 == i4 ? " + ( i3 == i4 )); // true
System . out . println ( "i3.equals(i4) ? " + i3 . equals ( i4 )); //true
}
}
为什么Integer的equals是判断的值呢?在Integer.class中可以看到Integer类改写了equals方法,就是判断的值是否存在。代码如下:
/**
* Compares this object to the specified object. The result is
* {@code true} if and only if the argument is not
* {@code null} and is an {@code Integer} object that
* contains the same {@code int} value as this object.
*
* @param obj the object to compare with.
* @return {@code true} if the objects are the same;
* {@code false} otherwise.
*/
public boolean equals ( Object obj ) {
if ( obj instanceof Integer ) {
return value == (( Integer ) obj ). intValue ();
}
return false ;
}
注意:Object中的equals方法 就是判断的==,所以如果一个对象没有重写equals方法,那么equals和==的结果是一样的。下面是Object中equals的代码:
public boolean equals ( Object obj ) {
return ( this == obj );
}
补充:Integer 与 Integer的对比
Integer t1 = new Integer(1000);
Integer t2 = new Integer(1000);
System.out.println(t1<=t2);
==结果为false但是t1<=t2结果为true
原因: 因为>、<、<=、>=是作用在基本类型上的,而不是对象类型。所以使用>、<、<=、>=的时候,Integer会自动拆箱成int,然后进行值的对比。但是==就是比较的是对象的地址,是说这2个对象在内存中是否占用同一个内存地址。
为什么子类引用不能指向父类对象 – 腾讯云开发者社区-腾讯云 (tencent.com)
父类引用可以指向子类对象,子类引用不能指向父类对象 – 简书 (jianshu.com)
因为在实例化申请空间时,是由对象大小决定的
[短文速读-3] 实例化子类会实例化父类么? – 简书 (jianshu.com)
会,会先调用父类的构造方法,而构造方法是用于对象的实例化并初始化
应该是实例化并初始化的过程,(我说的是JAVA)如:
Text t=new Text();
其中new 是为对象分配内存空间,Text();是初始化成员变量用的.t是此对象的一个引用.
(54条消息) 【避坑】FastJson稍微使用不当就会导致StackOverflow_Bolon0708的博客-CSDN博客
(87 封私信 / 80 条消息) json坑点 – 搜索结果 – 知乎 (zhihu.com)
记一次 FastJson 的踩坑经历 – 腾讯云开发者社区-腾讯云 (tencent.com)
fastjson的这些坑,你误入了没? | 码农家园 (codenong.com)
写代码时候还是需要注意点,能稍微规范些,就尽量按照规范,就如本次提到的问题,向 JSONObject 中加入 List<map<string,object> 时,不妨先提前 toJSON 转换一番,这样版本升级也不会有问题。</map<string,object>
由于依赖包升级导致不兼容的情况很常见,不过绝大多数都是向下兼容的,例如 JDK … 5、6、7、8 …,所以如果你正在开发核心代码,若涉及到版本更新,尽量考虑兼容性问题,如果涉及到老功能废弃时,不妨采用注解标注一下,这样后人会尽早发现问题。
在使用三方轮子时,尽可能做对三方的轮子了如之掌,知己知彼方能百战不殆。
知识记录
1.fastjson反序列化时,是能自动下划线转驼峰的。
例如:ReportExceptionDto dto = JSONObject.parseObject (jsonObject.toJSONString(), ReportExceptionDto.class);
参考文章:使用FastJson进行驼峰下划线相互转换写法及误区_氵奄不死的鱼的博客-CSDN博客_fastjson 驼峰转下划线
2.JSONObject.toJSONString()默认忽略值为null的属性
如下图:new出的对象中,如果存在属性的值为null,那么toJSONString生成的字符串不会有该属性;
怎么设置不忽略呢?让值为null的属性,写入空值,如下图
/**
* 过滤规则 - 避免toJSONString过滤空值对应的key
*/
private static ValueFilter valueFilter = (o,s,o1) -> o1 == null ? "" : o1;
Prev1 ... 2 3 4 ... 42 Next Page 3 of 42