代码之家  ›  专栏  ›  技术社区  ›  Ganesh Satpute

微服务实例之间的JPA同步

  •  1
  • Ganesh Satpute  · 技术社区  · 6 年前

    我有一份定时的工作

    @Component
    public class Worker {
    
        @Scheduled(fixedDelay = 100)
        public void processEnvironmentActions() {
            Job job = pickJob();
        }
    
        public Job pickJob() {
            Job job = jobRepository.findFirstByStatus(Status.NOT_PROCESSED);
            job.setStatus(Status.PROCESSING);
            jobRepository.save(job);
            return job;
        }
    }
    

    现在,在大多数情况下,这会给我正确的结果。但是,如果有两个微服务实例同时执行这段代码,会发生什么呢?

    编辑: @Transactional 所以把它拿走了。问题仍然是一样的。

    4 回复  |  直到 6 年前
        1
  •  5
  •   Jens Schauder    6 年前

    但是,如果有两个微服务实例同时执行这段代码,会发生什么呢?

    答案往往是:视情况而定。

    所有这些都假设代码在事务内部运行

    1. 乐观锁定。

      如果你的 Job 实体具有版本属性,即使用 @Version 注释。 OptimisticLockingException . 您所要做的就是处理该异常,这样您的进程就不会死掉,而是再次尝试获取下一个异常 工作

    2. 如果 工作 实体没有版本属性,JPA将默认不应用任何锁定。 第二个进程访问 工作 将发布一个更新,本质上是一个NOOP,因为第一个进程已经更新了它。 你可能想避免这种情况。

    3. 悲观锁定

      因此,这应该避免第二个进程在第一个进程写入行之前选择该行。 这可能会阻碍整个第二个过程。 因此,请确保持有这样一个锁的事务是短的。

      为了获得这样的锁,请注释repository方法 findFirstByStatus 具有 @Lock(LockModeType.PESSIMISTIC_WRITE) .

    当然,可能会有一些库或框架为您处理此类细节。

        2
  •  2
  •   Ganesh Satpute    6 年前

    @Jens Schauder的回答为我指明了正确的方向。让我分享代码,这样它就能指导其他人。这就是我如何解决我的问题,我改变了我的职业类别如下

    @Entity 
    public class Job {
       @Version
       private Long version = null; 
       // other fields omitted for bervity
    }
    

    现在,让我们跟踪以下代码

    @Transactional
    public Job pickJob() {
        Job job = jobRepository.findFirstByStatus(Status.NOT_PROCESSED);
        job.setStatus(Status.PROCESSING);
        Job saved jobRepository.save(job);
        return saved;
    }
    

    笔记 :请确保您返回 saved 对象而不是 job save 操作的版本计数 工作 将落后于 那是为了 保存的

    .

    Service 1                                 Service 2 
    
    1. Read Object (version = 1)              1. Read Object (version = 1)
    2. Change the object and save 
          (changes the version)
    3. Continues to process                   2. Change the object and save 
                                                   (this operation fails as 
                                                    the version that was read 
                                                    was 1 but in the DB version is 2)
                                              3. Skip the job processing 
    

    这样,作业将仅由一个进程执行。

        3
  •  1
  •   Conffusion    6 年前
        4
  •  1
  •   gtosto    6 年前

    我同意 @Conffusion 回答,但不知道应该使用的冲水策略 jobRepository.saveAndFlush(job) 方法,以便确保sql语句被下推到数据库中。

    另见 Difference between save and saveAndFlush in Spring data jpa