Mojolicious模板缓存过期

4

我目前正在使用Mojolicious开发一个小型单页Web应用程序。该应用具有一个JavaScript前端(使用Backbone),它与REST-ish API交互。源代码的布局大致如下:

use Mojolicious::Lite;

# ... setup code ...

get '/' => sub {
    my $c = shift;
    # fetch+stash data for bootstrapped collections...
    $c->render('app_template');
};

get '/api_endpoint' => sub {
    my $c = shift;
    # fetch appropriate API data...
    $c->render(json => $response);
};

# ... more API endpoints ...

app->start;

该应用程序模板使用EP,但非常少量;仅有的服务器端模板指令是插入引导集合的JSON。它作为普通CGI脚本通过Apache部署。(这不是最佳选择,但它用于低流量的内部使用,并且更复杂的服务器配置在此情况下会出现问题。)Perl CGI通过mod_perl进行配置。
这通常有效,但有时呈现器会以某种方式认为它应该缓存模板并忽略对其所做的更改。error_log中的调试记录显示“渲染缓存模板”,而不是正常的“渲染模板”,我的模板新更改停止在浏览器中显示。我无法找到可靠的方法来阻止这种情况,尽管它最终会根据我无法辨别的条件自行停止。
如何使应用程序可靠地注意到模板更改?或者,如何完全禁用模板缓存?

你让我感到欣慰的是,“大多数时间都能正常工作”。 :) - simbabque
2个回答

8
我该如何使应用程序可靠地注意到模板更改?
这就是 morbo 开发服务器的作用。Morbo 不适用于您的实时代码部署,而是适用于开发环境,在该环境中您不断更改代码和模板。通常,对实时代码和模板的更改应通过重新启动应用程序服务器或在您的情况下重新启动 Apache 来处理。(Hypnotoad 具有热重启功能,以此来达到此目的)
另外,我如何完全禁用模板缓存?
要做到这一点,请添加以下设置代码(在路由之外,在 use Mojolicious::Lite 之后)。
app->renderer->cache->max_keys(0);

我已经习惯在这个环境中进行开发,没有任何特殊要求;理论上它是一个CGI环境,这意味着每次新页面加载都应该有自己的进程。我猜mod_perl以某种方式打破了这个不变量。无论如何,禁用缓存就可以解决问题。 - Tom Hunt

4

请参见下面的旧答案。

我将此答案的发现转化为一个插件,并在与Grinnz在IRC上讨论后,作为Mojolicious::Plugin::Renderer::WithoutCache发布到CPAN上,他们鼓励发布。

您可以像这样使用它:

use Mojolicious::Lite;
plugin 'Renderer::WithoutCache';

它将创建一个什么也不做的新缓存对象,并将其全局安装到渲染器中。这样,它就不需要像我的初始答案那样每次都创建。

理论上,这应该比 Grinnz的方法(更明智)更快,而且由于您明确不想缓存,显然希望尽可能快,对吧?据说更快,因为真正的 Mojo::Cache 仍然需要去尝试设置缓存,但然后会中止,因为没有更多的空闲键,并且它还会尝试每次从缓存中查找值。

我使用 DumbbenchBenchmark 进行了基准测试。它们两个都显示出可忽略的结果。我每个运行了几次,但它们波动很大,不清楚哪个更快。我包括了一次运行的输出,其中我的实现更快,但它仍然显示出差异微小。

使用 Dumbbench 进行基准测试:

use Dumbbench;
use Mojolicious::Renderer;
use Mojolicious::Controller;
use Mojolicious::Plugin::Renderer::WithoutCache::Cache;

my $controller         = Mojolicious::Controller->new;
my $renderer_zero_keys = Mojolicious::Renderer->new;
$renderer_zero_keys->cache->max_keys(0);

my $renderer_nocache = Mojolicious::Renderer->new;
$renderer_nocache->cache( Mojolicious::Plugin::Renderer::WithoutCache::Cache->new );

my $bench = Dumbbench->new(
    target_rel_precision => 0.005,
    initial_runs         => 5000,
);

$bench->add_instances(
    Dumbbench::Instance::PerlSub->new(
        name => 'max_keys',
        code => sub {
            $renderer_zero_keys->render( $controller, { text => 'foobar' } );
        }
    ),
    Dumbbench::Instance::PerlSub->new(
        name => 'WithoutCache',
        code => sub {
            $renderer_nocache->render( $controller, { text => 'foobar' } );
        }
    ),
);

$bench->run;
$bench->report;

__END__
max_keys: Ran 8544 iterations (3335 outliers).
max_keys: Rounded run time per iteration: 5.19018e-06 +/- 4.1e-10 (0.0%)
WithoutCache: Ran 5512 iterations (341 outliers).
WithoutCache: Rounded run time per iteration: 5.0802e-06 +/- 5.6e-09 (0.1%)

使用Benchmark进行基准测试:

use Benchmark 'cmpthese';
use Mojolicious::Renderer;
use Mojolicious::Controller;
use Mojolicious::Plugin::Renderer::WithoutCache::Cache;

my $controller         = Mojolicious::Controller->new;
my $renderer_zero_keys = Mojolicious::Renderer->new;
$renderer_zero_keys->cache->max_keys(0);

my $renderer_nocache = Mojolicious::Renderer->new;
$renderer_nocache->cache( Mojolicious::Plugin::Renderer::WithoutCache::Cache->new );

cmpthese(
    -5,
    {
        'max_keys' => sub {
            $renderer_zero_keys->render( $controller, { text => 'foobar' } );
        },
        'WithoutCache' => sub {
            $renderer_nocache->render( $controller, { text => 'foobar' } );
        },
    }
);

__END__
                 Rate     max_keys WithoutCache
max_keys     190934/s           --          -2%
WithoutCache 193846/s           2%           --

我认为在大量调用的高负载环境中,这可能会产生影响,但这很难证明。因此,如果您不想考虑缓存的内部情况,这个插件可能会有用。


旧回答:

查看 Mojolicious::Plugin::EPRenderer 后我发现有一个 cache。它是一个 Mojo::Cache 实例,具有方法 getsetmax_keys,并继承自 Mojo::Base(就像 Mojolicious 中的所有东西一样)。

New answer:

通过查看 Mojolicious::Plugin::EPRenderer 我发现有一个名为 cache 的缓存。它是一个 Mojo::Cache 实例,拥有 getsetmax_keys 等方法,并且像 Mojolicious 中的所有东西一样继承自 Mojo::Base

:EPRenderer获取一个$renderer, 它是一个Mojolicious::Renderer,它持有Mojo::Cache实例。我使用Data::Printer查看了$c,发现有一个$c->app持有所有这些信息。

知道这一点后,你可以很容易地创建自己的缓存类,什么都不做。

package Renderer::NoCache;
use Mojo::Base -base;

sub get {}
sub set {}
sub max_keys {}

现在将其插入到$c中。
package Foo;
use Mojolicious::Lite;

get '/' => sub {
    my $c = shift;

    $c->app->renderer->cache( Renderer::NoCache->new );

    $c->render(template => 'foo', name => 'World');
};

app->start;

__DATA__

@@ foo.html.ep
Hello <%= $name =%>.

现在每次尝试获取或设置缓存时都不会有任何反应。它会尝试缓存,但永远找不到任何东西。
当然,每次创建一个新对象并不是很好。最好在启动时只创建一次该对象,并将其放入内部永久版本的app中。由于您使用CGI,因此可能不会有什么区别。
你也可以直接对 Mojo::Cache 中的 get 进行猴子补丁。这种更加hacky的方法将实现相同的效果:
package Foo;
use Mojolicious::Lite;

*Mojo::Cache::get = sub { };

get '/' => sub {
    my $c = shift;

    $c->render(template => 'foo', name => 'World');
};

app->start;

但要注意:我们刚刚禁用了使用 Mojo::Cache 的应用程序中每个缓存的获取。这可能不是您想要的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接