vieyahn2017 / iBlog

44 stars 0 forks source link

4.10 quartz报错invalid stream header #354

Closed vieyahn2017 closed 3 months ago

vieyahn2017 commented 4 years ago

quartz报错 invalid stream header

vieyahn2017 commented 4 years ago

quartz需要创建几张数据库表,比如

CREATE TABLE qrtz_triggers
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT NULL,
    PREV_FIRE_TIME BIGINT NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT NOT NULL,
    END_TIME BIGINT NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT NULL,
    JOB_DATA BYTEA NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
    REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);
vieyahn2017 commented 4 years ago

其中使用了bytea字段

网上帖子这么说:

PostgreSQL-Bytea字段读取出错 在postgreSQL中,用bytea存放二进制数据(mySQL中用mediumBlob..),用ifstream读取一张图片,转成base64,然后保存进postgreSQL数据库,取出后再把base64转成原来的编码,使用ofstream保存成图片格式,打不开图片显示绘图失败,原来的数据被破坏了,doc,excel文档也是如此,出现读取数据混乱。

解决办法:修改postgres的postgresql.conf配置文件 bytea_output = 'escape' # hex, escape 意思是设置bytea_output的输出类型设置为转义类型输出,而postgres默认是hex类型输出,所以导致转换数据混乱问题

vieyahn2017 commented 4 years ago

另外该错误,参考

java.io.StreamCorruptedException: invalid stream header: EFBFBDEF 问题解决

错误方式

   @Test
    public void testDeserializeTest() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        BigInteger bi = new BigInteger("0");
        oos.writeObject(bi);
        String str = baos.toString();
        System.out.println(str);
        ObjectInputStream ois = new ObjectInputStream(
                new BufferedInputStream(new ByteArrayInputStream(str.getBytes())));
        Object obj = ois.readObject();
        assertEquals(obj.getClass().getName(), "java.math.BigInteger");
        assertEquals(((BigInteger) obj).intValue(), 0);
    }

正确方式

 @Test
    public void testDeserialize() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        BigInteger bi = new BigInteger("0");
        oos.writeObject(bi);
        byte[] str = baos.toByteArray();
        ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(str)));
        Object obj = ois.readObject();
        assertNotNull(obj);
        assertEquals(obj.getClass().getName(), "java.math.BigInteger");
        assertEquals(((BigInteger) obj).intValue(), 0);
    }

原因是由于:

将字 ByteArrayOutputStream对象调用为toString转为为字符串时,会将 ObjectOutputStream对象放置在对象流头部的前两个字节(0xac)(0xed)序列化为两个“?”

当这个字符串使用getByte()时会将两个“?”变为(0x3f )(0x3f) 。然而这两个字符并不构成有效的对象流头。所以转化对象时候会失败。

vieyahn2017 commented 4 years ago

PostgreSQL抛错“不良的类型值: long”之解决

https://www.cnblogs.com/wggj/p/8023222.html

一、前言

项目中有一个独立程序,负责从主库同步部分数据到分库。由于混合使用了JPA和JDBC两种操作方式,该程序移植到后PostgreSQL错误不断且不好诊断,其中耗时耗力最多的就是:“org.postgresql.util.PSQLException: 不良的类型值 long ”。

二、原因分析

以下是PostgreSQL抛出例外处的日志片段:

Caused by: org.postgresql.util.PSQLException: 不良的类型值 long : \x0040010346504d4e00000001000003900101000000000000000002800000028001f4007d000202040000000200000000000000000000000000000000000000005041
        at org.postgresql.jdbc.PgResultSet.toLong(PgResultSet.java:2860)
        at org.postgresql.jdbc.PgResultSet.getLong(PgResultSet.java:2114)
        at org.postgresql.jdbc.PgResultSet.getBlob(PgResultSet.java:418)
        at org.postgresql.jdbc.PgResultSet.getBlob(PgResultSet.java:405)
        at org.apache.commons.dbcp.DelegatingResultSet.getBlob(DelegatingResultSet.java:565)
        at org.apache.commons.dbcp.DelegatingResultSet.getBlob(DelegatingResultSet.java:565)
        at org.hibernate.type.descriptor.sql.BlobTypeDescriptor$1.doExtract(BlobTypeDescriptor.java:48)
        at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:47)
        at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:258)
        at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:254)
        at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:244)
        at org.hibernate.type.AbstractStandardBasicType.hydrate(AbstractStandardBasicType.java:327)
        at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2775)
        at org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl.loadFromResultSet(EntityReferenceInitializerImpl.java:305)
        ... 64 more

可以看出,这是在读取BLOB(即BYTEA)类型数据时出的错,具体是把想byte[]当作long来读取。

在另一篇随笔《JPA/Hibernate移植到PostgreSQL时关于CLOB, BLOB及JSON类型的处理》中,解释了PostgreSQL在处理LOB数据的两种方式:oid + bigobject方式和二进制数组方式。oid + bigobject方式是在LOB字段存取一个oid(BIGINT类型)值,而将真正的byte[]数据存放在公用的pg_largeobject,在PostgreSQL的JDBC中的接口是setBlob()/getBlob()、setClob()/getClob();而二进制数组方式则直接存取byte[],在JDBC中的接口是setBinaryStream()、setCharacterStream()等。

至此原因已经基本明朗,该独立程序在读取主库的LOB数据(二进制数组方式)时,仍然按oid + bigobject方式进行,由此导致出错。

三、解决方法

也在那篇随笔中,解决方法是重写PostgreSQL94Dialect的remapSqlTypeDescriptor()接口,分别将CLOB和BLOB按LongVarchar和LongVarBinary类型来处理,效果良好,解决了"column xxx is of type text but expression is of type bigint"的错误。

一开始以同样的思路期待解决问题,但错误依然存在,让人头疼不已。后来想到,该独立程序的某些操作在底层可能没用到remapSqlTypeDescriptor()接口,最终仍按默认的oid + bigobject方式来调用setBlob(),因此还需重写其它接口。但前前后后试了好几天,还是没有进展。

不得已分析hibernate-core源码(https://github.com/hibernate/hibernate-orm/tree/master/hibernate-core),发现org.hibernate.type.descriptor.sql.BlobTypeDescriptor.java里有一段逻辑,大致是当某变量设置为BLOB_BIND时调用setBlob(),设置为PRIMARY_ARRAY_BINDING时调用setBytes(),设置为STREAM_BINDING时调用setBinaryStream()。CLOB的情况也类似。有戏

再回到org.hibernate.dialect.PostgreSQLxxDialect,经一层层追溯,终于在最底层的PostgreSQL81Dialect(藏的太深了),在getSqlTypeDescriptorOverride()接口中找到对应的内容,而且发现默认的oid + bigobject方式是在此定义的。于是重写该接口:

    @Override
    public SqlTypeDescriptor getSqlTypeDescriptorOverride(int sqlCode)
    {
        SqlTypeDescriptor descriptor;
        switch (sqlCode)
        {
        case Types.BLOB:
            // Force BLOB binding. Otherwise, byte[] fields annotated
            // with @Lob will attempt to use
            // BlobTypeDescriptor.PRIMITIVE_ARRAY_BINDING. Since the
            // dialect uses oid for Blobs, byte arrays cannot be used.
            //descriptor = BlobTypeDescriptor.BLOB_BINDING;
            descriptor = BlobTypeDescriptor.STREAM_BINDING;
            break;
        case Types.CLOB:
            //descriptor = ClobTypeDescriptor.CLOB_BINDING;
            descriptor = ClobTypeDescriptor.STREAM_BINDING;
            break;
        default:
            descriptor = super.getSqlTypeDescriptorOverride(sqlCode);
            break;
        }
        return descriptor;
    }

问题终于得到解决!

PS: 推荐使用二进制数组方式存取LOB,而不是默认的oid + bigobject; 如果抛错信息“不良的类型值”后是long,几乎可断定是LOB调用模式的问题;如果是其它类型,需进一步分析。 如果直接用JDBC,对LOB的调用接口是setBinaryStream()、setCharacterStream()等。

vieyahn2017 commented 4 years ago

quartz 定时任务的总结(一)

https://www.cnblogs.com/laosunlaiye/p/9395507.html

1. 介绍

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。 Quartz用一个小Java库发布文件(.jar文件),这个库文件包含了所有Quartz核心功能。这些功能的主要接口(API)是Scheduler接口。它提供了简单的操作,例如:将任务纳入日程或者从日程中取消,开始/停止/暂停日程进度。

2. 定时器种类

Quartz 中五种类型的 Trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,NthIncludedDayTrigger和Calendar 类( org.quartz.Calendar)。 最常用的: SimpleTrigger:用来触发只需执行一次或者在给定时间触发并且重复N次且每次执行延迟一定时间的任务。 CronTrigger:按照日历触发,例如“每个周五”,每个月10日中午或者10:15分。

3. 存储方式

RAMJobStore和JDBCJobStore 对比:

类型 优点 缺点 RAMJobStore 不要外部数据库,配置容易,运行速度快 因为调度程序信息是存储在被分配给JVM的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。另外因为存储到JVM内存里面,所以可以存储多少个Job和Trigger将会受到限制 JDBCJobStore 支持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务 运行速度的快慢取决与连接数据库的快慢

4. 表关系和解释

表关系

这里写图片描述

解释

表名称 说明 qrtz_blob_triggers Trigger作为Blob类型存储(用于Quartz用户用JDBC创建他们自己定制的Trigger类型,JobStore 并不知道如何存储实例的时候) qrtz_calendars 以Blob类型存储Quartz的Calendar日历信息, quartz可配置一个日历来指定一个时间范围 qrtz_cron_triggers 存储Cron Trigger,包括Cron表达式和时区信息。 qrtz_fired_triggers 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息 qrtz_job_details 存储每一个已配置的Job的详细信息 qrtz_locks 存储程序的非观锁的信息(假如使用了悲观锁) qrtz_paused_trigger_graps 存储已暂停的Trigger组的信息 qrtz_scheduler_state 存储少量的有关 Scheduler的状态信息,和别的 Scheduler 实例(假如是用于一个集群中) qrtz_simple_triggers 存储简单的 Trigger,包括重复次数,间隔,以及已触的次数 qrtz_triggers 存储已配置的 Trigger的信息 qrzt_simprop_triggers

5. 核心类和关系

核心类 (1)核心类 QuartzSchedulerThread :负责执行向QuartzScheduler注册的触发Trigger的工作的线程。 ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提供运行效率。 QuartzSchedulerResources:包含创建QuartzScheduler实例所需的所有资源(JobStore,ThreadPool等)。 SchedulerFactory :提供用于获取调度程序实例的客户端可用句柄的机制。 JobStore: 通过类实现的接口,这些类要为org.quartz.core.QuartzScheduler的使用提供一个org.quartz.Job和org.quartz.Trigger存储机制。作业和触发器的存储应该以其名称和组的组合为唯一性。 QuartzScheduler :这是Quartz的核心,它是org.quartz.Scheduler接口的间接实现,包含调度org.quartz.Jobs,注册org.quartz.JobListener实例等的方法。 Scheduler :这是Quartz Scheduler的主要接口,代表一个独立运行容器。调度程序维护JobDetails和触发器的注册表。 一旦注册,调度程序负责执行作业,当他们的相关联的触发器触发(当他们的预定时间到达时)。 Trigger :具有所有触发器通用属性的基本接口,描述了job执行的时间出发规则。 - 使用TriggerBuilder实例化实际触发器。 JobDetail :传递给定作业实例的详细信息属性。 JobDetails将使用JobBuilder创建/定义。 Job:要由表示要执行的“作业”的类实现的接口。只有一个方法 void execute(jobExecutionContext context) (jobExecutionContext 提供调度上下文各种信息,运行时数据保存在jobDataMap中) Job有个子接口StatefulJob ,代表有状态任务。 有状态任务不可并发,前次任务没有执行完,后面任务处于阻塞等到。 关系-自己理解 这里写图片描述

一个job可以被多个Trigger 绑定,但是一个Trigger只能绑定一个job!

6. 配置文件

quartz.properties //调度标识名 集群中每一个实例都必须使用相同的名称 (区分特定的调度器实例) org.quartz.scheduler.instanceName:DefaultQuartzScheduler //ID设置为自动获取 每一个必须不同 (所有调度器实例中是唯一的) org.quartz.scheduler.instanceId :AUTO //数据保存方式为持久化 org.quartz.jobStore.class :org.quartz.impl.jdbcjobstore.JobStoreTX //表的前缀 org.quartz.jobStore.tablePrefix : QRTZ_ //设置为TRUE不会出现序列化非字符串类到 BLOB 时产生的类版本问题 //org.quartz.jobStore.useProperties : true //加入集群 true 为集群 false不是集群 org.quartz.jobStore.isClustered : false //调度实例失效的检查时间间隔 org.quartz.jobStore.clusterCheckinInterval:20000 //容许的最大作业延长时间 org.quartz.jobStore.misfireThreshold :60000 //ThreadPool 实现的类名 org.quartz.threadPool.class:org.quartz.simpl.SimpleThreadPool //线程数量 org.quartz.threadPool.threadCount : 10 //线程优先级 org.quartz.threadPool.threadPriority : 5(threadPriority 属性的最大值是常量 java.lang.Thread.MAX_PRIORITY,等于10。最小值为常量 java.lang.Thread.MIN_PRIORITY,为1) //自创建父线程 //org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true //数据库别名 org.quartz.jobStore.dataSource : qzDS //设置数据源 org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartz org.quartz.dataSource.qzDS.user:root org.quartz.dataSource.qzDS.password:123456 org.quartz.dataSource.qzDS.maxConnection:10

7.JDBC插入表顺序

主要的JDBC操作类,执行sql顺序。 Simple_trigger :插入顺序 qrtz_job_details —> qrtz_triggers —> qrtz_simple_triggers qrtz_fired_triggers Cron_Trigger:插入顺序 qrtz_job_details —> qrtz_triggers —> qrtz_cron_triggers qrtz_fired_triggers

8.参考文章

官网: http://www.quartz-scheduler.org/

深入解读Quartz的原理 :http://lavasoft.blog.51cto.com/62575/181907/ 基于 Quartz 开发企业级任务调度应用 :http://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/ quartz 数据库表含义解释 :http://blog.csdn.net/tengdazhang770960436/article/details/51019291 Quartz源码分析: https://my.oschina.net/chengxiaoyuan/blog/664833 http://blog.csdn.net/u010648555/article/category/6601767 Quartz系列:http://blog.csdn.net/Evankaka/article/category/3155529