Mojolicious模板缓存陈旧

时间:2017-01-19 19:38:09

标签: apache perl mojolicious mod-perl

我目前正在使用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。它通过Apache作为普通的CGI脚本部署。 (这不是最佳选择,但它适用于低流量内部使用,更复杂的服务器配置在上下文中存在问题。)Perl CGI通过mod_perl进行配置。

这在大多数情况下都有效,但有时候渲染器会以某种方式获得它应该缓存模板并忽略对它的更改的想法。 error_log中的调试记录显示“渲染缓存模板”而不是普通的“渲染模板”,我对模板的新更改停止显示在浏览器中。我无法找到一种可靠的方法来阻止它,尽管它最终会根据我无法辨别的条件自行停止。

如何让应用程序通知模板可靠地更改?或者,如何完全禁用模板缓存?

2 个答案:

答案 0 :(得分:6)

  

如何让应用程序通知模板可靠地更改?

这是morbo开发服务器的用途。 Morbo不会用于您的实时代码部署,但适用于您不断更改代码和模板的开发环境。通常,对实时代码和模板的更改应通过重新启动应用程序服务器或Apache来处理。 (Hypnotoad具有用于此目的的热重启功能)

  

或者,如何完全禁用模板缓存?

为此,请添加以下设置代码(在use Mojolicious::Lite之后的路径之外):

app->renderer->cache->max_keys(0);

答案 1 :(得分:4)

旧答案见下文。

我将此答案的结果转换为插件,并在IRC上讨论智慧Mojolicious::Plugin::Renderer::WithoutCache之后以Grinnz在CPAN上发布,鼓励他们发布。

你可以像这样使用它:

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

它将创建一个不执行任何操作的新Cache对象,并将其全局安装到渲染器中。这样,每次都不需要像下面的初步答案那样创建它。

理论上,这应该比Grinnz' approach更快(这更明智),而且由于你明确地不想缓存,你显然希望事情尽可能快,对吧?它应该更快,因为真正的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 with 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中的所有内容一样)。

::EPRenderer gets a $rendererMojolicious::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 =%>.

现在,每次尝试getset缓存都不会做任何事情。它将尝试缓存,但它永远不会找到任何东西。

当然每次制作一个新物体并不好。最好在启动时创建一次该对象并将其置于app的内部永久版本中。你有CGI,所以它可能没什么区别。

你也可以在get之外修补Mojo::Cache。这种更黑客的方法也会做同样的事情:

package Foo;
use Mojolicious::Lite;

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

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

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

app->start;

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