【问题标题】:Mojolicious template cache is staleMojolicious 模板缓存已过时
【发布时间】:2017-06-04 15:37:27
【问题描述】:

我目前正在使用 Mojolicious 开发一个小型单页 Web 应用程序。该应用程序有一个与 REST-ish API 对话的 Javascript 前端(使用 Backbone);源码的布局大致是:

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 中的调试记录显示“正在渲染缓存的模板”而不是正常的“渲染模板”,并且我对模板的新更改停止出现在浏览器中。我找不到可靠的方法来阻止它,尽管它最终会根据我无法辨别的情况自行停止。

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

【问题讨论】:

  • 你让我大部分时间都在工作。 :)

标签: apache perl mojolicious mod-perl


【解决方案1】:

如何使应用通知模板可靠地更改?

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

或者,我怎样才能完全禁用模板缓存?

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

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

【讨论】:

  • 我一直有在这种环境下做开发的习惯,没有什么特别的;理论上它是一个 CGI 环境,这意味着每个新页面加载都应该有自己的进程。我猜 mod_perl 以某种方式打破了这个不变量。无论如何,禁用缓存是可行的。
【解决方案2】:

旧答案见下文。

在 IRC 上讨论了 Grinnz 的机智之后,我将这个答案的发现变成了一个插件,并在 CPAN 上以 Mojolicious::Plugin::Renderer::WithoutCache 发布,他们鼓励发布。

你可以这样使用它:

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 进行基准测试:

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 $renderer,即Mojolicious::Renderer。它拥有 Mojo::Cache 实例。我查看了$cData::Printer,发现有一个$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,所以它可能没有什么不同。


您也可以只对getMojo::Cache 进行修补。这种更 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 的应用程序中的每个缓存获取。这可能不是你想要的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    • 2012-12-31
    • 1970-01-01
    • 1970-01-01
    • 2010-09-19
    • 2015-04-23
    • 1970-01-01
    相关资源
    最近更新 更多