Home Authors Posts by 可乐

可乐

419 POSTS 5 COMMENTS

0 102
一 排查思路
  • 平台错误提示
    • 发布平台获取错误信息(平台会有相关发布失败信息,用于辅助排查问题+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,在框架层面记录下来的日志,一般启动失败,在这里也会有错误信息,不止是启动失败,应用运行过程中出现错误,也可以先去这个日志文件查看。)

    • 运维通道cli日志

(/var/log/cli.log从发布系统执行命令后,运维通道调用启动脚本过程中输出的日志)

    • 操作系统日志

(/var/log/message, 如果应用启动是申请的内存超过了OS 可用的内存,那么操作系统出于自我保护,会直接kill掉应用进程,同时会记录Kill process的日志,如果是docker进程,日志是记录在宿主机上)

    • dolphin启动日志

 /home/product/logs/{应用名_logs}/dolphin-executor-error.log

二 常见问题汇总
1 虚拟机类
1.1 代码异常问题

问题现场

解决方案

  • 业务jar包类找不到
    • 排查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参数未配置
      • 配置对应环境jvm参数后,重新发布即可
    • jvm参数已配置
      • 查看app.properties里的application.name是否等于应用名
        • 不等于,则修改保持一致
        • 等于&jvm参数配置了,则重试几次,若几次还不行,咨询8000,问乐效值班同事
1.4 发布失败,缺少启动脚本文件

问题现场

解决方案

  • 确认是否需要启动脚本文件
    • 启动类应用(需要)
      • java
      • go
      • python
      • c/c++
      • nodejs
    • 非启动类应用(不需要)
      • config
      • static
  • 启动类解决方案(补全缺少脚本文件,参考通组其他工程)

  • 非启动类解决方案

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日志中会输出脚本执行过程日志

解决方案

  • 重试
  • 联系8000值班
2 容器发类

 

0 78

配置

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;
        }
    }
}

DelayQueueput方法是线程安全的,因为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);
        }
    }
}

上边只是简单的实现入队与出队的操作,实际开发中会有专门的线程,负责消息的入队与消费。

执行后看到结果如下,Order1Order2Order3 分别在 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

 

 

深夜,凝芳殿中。

“奴婢参见玉妃,玉妃娘娘万福金安”,林婉拜跪着说道。

身前不远处,坐着一位天生媚骨,身材极好的女子,此刻她正慵懒的抱着一只波斯猫,轻轻的抚摸着。

“本宫听闻浣衣监新来了个婢子,是工部尚书林任之的侄女,年纪不大,样貌倒是极美,还未出阁就引得京城王李两家的公子争相提亲”, 玉妃将怀里的波斯猫放下,站起身来,端起一盏灯,缓缓向林婉走来。

虽然被发配到浣衣监才几日光景,林婉却也听其他人说起过,在后宫行事千万不要得罪玉妃,当日她好奇追问这是为何,众人却死活不敢说下去,

此刻已是亥时,无论是浣衣监还是凝芳殿都应该早早休息了才对,可偏偏玉妃差人唤自己进殿,过来的一路上,林婉思索了一番,实在想不到自己何时招惹了这尊大佛,只得在心里暗暗祈祷不要出什么事才好。

“本宫倒要看看,这大名鼎鼎的林婉,是不是生的似仙女一般夺人心魄”玉妃轻轻笑了几声,慕然间已然走到了林婉的面前。

0 106

前情概要

最近在一次发版前,代码被安全扫描出有漏洞,定睛一看,原来是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)

0 101

一、字符串转指定格式的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对象;

0 90

某周一,我正愉快的编写测试用例。既然是在Springboot框架下,那肯定是要调用其他类的

信心满满的使用@Autowried注解进行注入,启动测试类,这个时候报了注入类的空指针


开始排查…

一开始感觉是注入类没有打注解,看了之后发现是有的,那类应该是正常注入Springboot容器了,但是测试类没拿到。百度了一下,都不是对应的问题所在。

突然灵光一现,感觉会不会是注解用错了…

import org.junit.Test;

import org.testng.annotations.Test;
使用第一个注解时,无法注入,会报空指针;使用第二个注解就OK了,而且很明显看到第二个注解会启动容器


详细分析…

(待补充)

 

 

0 191

Integer的装箱,拆箱及缓存机制:(54条消息) Integer 的缓存机制_MrYuTing的博客-CSDN博客

当我们了解Integer装拆箱及缓存机制后,再来观察Integer和Int的相等判断

一、Integer 和 Integer对比

  • == 比较的对象(地址)是否相等
  • equals 比较的是值是否相等

说明:

  1. Integer重写了equals方法,用于比较两个Integer对象的value值是否相等;如果没有重写equals方法的话,equals比较的也是对象地址是否相等(与==相同)。
  2. 在对比的两个Integer对象中,如果有对象是new出的,那么用 == 判断一定不相等
  3. 如果对比的两个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个对象在内存中是否占用同一个内存地址。

0 76

为什么子类引用不能指向父类对象 – 腾讯云开发者社区-腾讯云 (tencent.com)

父类引用可以指向子类对象,子类引用不能指向父类对象 – 简书 (jianshu.com)

因为在实例化申请空间时,是由对象大小决定的

[短文速读-3] 实例化子类会实例化父类么? – 简书 (jianshu.com)

会,会先调用父类的构造方法,而构造方法是用于对象的实例化并初始化

应该是实例化并初始化的过程,(我说的是JAVA)如:
Text t=new Text();
其中new 是为对象分配内存空间,Text();是初始化成员变量用的.t是此对象的一个引用.

0 176

(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;