2017-11-04 120 views
1

我有一个使用PlayFramework 2.6.5和Guice DI(libraryDependencies += guice)构建的Java web服务,正好是时间注入模式。所有依赖项通过构造函数注入,使用@Inject@ImplementedBy,而Guice Module为空。`ProvisionException`被高速缓存,构造函数代码永远不会被重试

由于瞬态错误,一些依赖可以在构造函数中抛出异常。发生这种情况时,服务将失败,并显示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; 
    } 
} 

顺便说一句,相同的基本代码还用于在控制台应用程序,它没有按”不会遇到这个问题,所以我认为Play + Guice集成有一些特殊之处。任何建议关闭异常缓存?

回答

0

我发现了一个解决方案,使PF的行为更加明确和可预测。由PF默认生成的路由缓存控制器,即使它们的实例被破坏,假设singleton控制器是用户想要的。

here所述,可以在路由配置中的每个操作之前添加@,更改默认行为。

例如前:

GET /test webservice.TestController.test 

后:

GET /test @webservice.TestController.test 

有了这个语法,控制器不能单按默认值,仍然可以使用@Singleton需要的地方。此外,单例控制器在异常情况下不会被缓存,从而在不重新启动服务的情况下恢复瞬态错误。 代码副本可用here

1

抛出异常并缓存异常看起来像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 ... 
} 
+0

这将工作。我不禁感到,重新设计'IClock'实现以避免在构造函数中引发异常会更加整洁。难道这种验证不能懒惰地完成吗? – Graham

+0

我同意,如果有可能解决根本原因的异常,那么这将是一个更好的方法。 –

+0

感谢您的建议,我同意理想情况下可以改进代码,但不幸的是,在真实情况下情况并非如此(上面的例子是说明问题的一个非常简单的例子)。 @RichDougherty:_抛出异常并缓存异常看起来像Guice中的内置行为。我仍然不明白为什么这个问题只影响Web服务而不影响控制台应用程序(请参阅我的最后一句),尽管,两人都在使用Guice。 Play框架控制器是否被实例化为单例? –

相关问题