代码之家  ›  专栏  ›  技术社区  ›  Devis L.

`ProvisionException`被缓存,并且从不重试构造函数代码

  •  1
  • Devis L.  · 技术社区  · 7 年前

    我有一个使用PlayFramework 2.6.5和Guice DI构建的Java web服务( libraryDependencies += guice ),即时注入模式。所有依赖项都通过构造函数注入,使用 @Inject @ImplementedBy ,和Guice Module 是空的。

    由于暂时性错误,某些依赖项可能会在构造函数中引发异常。当这种情况发生时,服务会以 ProvisionException (这没问题,客户端需要重试)。

    我发现这些异常是缓存的,即使在解决了异常的根本原因后,Play或Guice也从不重试实例化这些类,并一直处理相同的异常,直到web服务重新启动。

    例如,考虑以下类 Clock 如果是午夜(00:xx),构造函数将失败。系统时钟一到午夜,服务就无法实例化该类。当时钟到达凌晨1点时,会不断引发相同的异常。此外,异常消息总是相同的(在示例中,异常消息是第一次发生异常的时间)

    @ImplementedBy(OddClock.class)
    public interface IClock {
        //...
    }
    
    public class OddClock implements IClock {
        @Inject
        public OddClock() throws Exception {
            if (DateTime.now().hourOfDay().get() == 0) {
                throw new Exception(DateTime.now().toString());
            }
        }
    }
    
    public class TimeController {
        @Inject
        public TimeController(IClock clock) {
            this.clock = clock;
        }
    }
    

    2 回复  |  直到 7 年前
        1
  •  1
  •   Rich Dougherty    7 年前

    抛出异常和缓存异常似乎是Guice中的内置行为。这也是一种公平的行为,因为Guice希望它创建的对象能够避免IO和其他不确定的操作。

    https://github.com/google/guice/wiki/BeCarefulAboutIoInProviders

    提供程序未定义重试策略。当值不可用时,多次调用get()可能会导致多个失败的规定。

    您可以通过更改 范围

    在我看来,更好的解决方案是获取不可靠的对象,并将其封装在另一个对象中,该对象隐藏失败并处理重试。这样,当Guice尝试创建可靠对象时,它总是会成功,并且您可以在可靠包装器中添加自己的故障处理代码。

    public class ReliableClock { 
      private Factory<Clock> clockFactory;
      private Clock internalClock;
      public ReliableClock(Factory<Clock> clockFactory) {
        this.clockFactory = clockFactory;
      }
      private synchronized Clock currentClock() throws Exception {
        if (clock == null) {
          clock = clockFactory.create() // May throw exception
        }
        return clock;
      }
      // ... methods ...
    }
    
        2
  •  0
  •   Devis L.    7 年前

    我找到了一个解决方案,使PF的行为更加明确和可预测。默认情况下,PF生成的路由会缓存控制器,即使其实例被破坏,假设单例控制器是用户想要的。

    如前所述 here ,可以通过添加 @

    e、 g.之前:

    GET    /test    webservice.TestController.test
    

    之后:

    GET    /test    @webservice.TestController.test
    

    使用这种语法,默认情况下控制器不是单例的,并且仍然可以使用 @Singleton 在需要的地方。此外,在异常情况下不会缓存单例控制器,从而允许在不重新启动服务的情况下恢复瞬时错误。 代码副本可用 here .