Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented load balancing support #865

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/**
* Nexcess.net Turpentine Extension for Magento
* Copyright (C) 2012 Nexcess.net L.L.C.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

class Nexcessnet_Turpentine_Model_Config_Select_LoadBalancing {
/**
* @return array
*/
public function toOptionArray() {
$helper = Mage::helper('turpentine');
return array(
array('value'=>'no', 'label'=>$helper->__('No, use only one backend server')),
array('value'=>'yes', 'label'=>$helper->__('Yes, use load balancing')),
array('value'=>'yes_admin', 'label'=>$helper->__('Yes, with separate settings for Admin')),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,14 @@ protected function _getDefaultBackend() {
'first_byte_timeout' => $timeout . 's',
'between_bytes_timeout' => $timeout . 's',
);
return $this->_vcl_backend( 'default',
Mage::getStoreConfig( 'turpentine_vcl/backend/backend_host' ),
Mage::getStoreConfig( 'turpentine_vcl/backend/backend_port' ),
$default_options );
if ( Mage::getStoreConfigFlag( 'turpentine_vcl/backend/load_balancing' ) ) {
return $this->_vcl_director( 'default', $default_options );
} else {
return $this->_vcl_backend( 'default',
Mage::getStoreConfig( 'turpentine_vcl/backend/backend_host' ),
Mage::getStoreConfig( 'turpentine_vcl/backend/backend_port' ),
$default_options );
}
}

/**
Expand All @@ -304,10 +308,14 @@ protected function _getAdminBackend() {
'first_byte_timeout' => $timeout . 's',
'between_bytes_timeout' => $timeout . 's',
);
return $this->_vcl_backend( 'admin',
Mage::getStoreConfig( 'turpentine_vcl/backend/backend_host' ),
Mage::getStoreConfig( 'turpentine_vcl/backend/backend_port' ),
$admin_options );
if ( Mage::getStoreConfigFlag( 'turpentine_vcl/backend/load_balancing' ) ) {
return $this->_vcl_director( 'admin', $admin_options );
} else {
return $this->_vcl_backend( 'admin',
Mage::getStoreConfig( 'turpentine_vcl/backend/backend_host' ),
Mage::getStoreConfig( 'turpentine_vcl/backend/backend_port' ),
$admin_options );
}
}

/**
Expand Down Expand Up @@ -545,6 +553,107 @@ protected function _vcl_backend( $name, $host, $port, $options=array() ) {
return $str;
}

/**
* Format a VCL director declaration, for load balancing
*
* @param string $name name of the director, also used to select config settings
* @param array $backendOptions options for each backend
* @return string
*/
protected function _vcl_director( $name, $backendOptions ) {
$tpl = <<<EOS
director {{name}} round-robin {
{{backends}}
}
EOS;
if ( 'admin' == $name && 'yes_admin' == Mage::getStoreConfig( 'turpentine_vcl/backend/load_balancing' ) ) {
$backendNodes = Mage::helper( 'turpentine/data' )->cleanExplode( PHP_EOL,
Mage::getStoreConfig( 'turpentine_vcl/backend/backend_nodes_admin' ) );
$probeUrl = Mage::getStoreConfig( 'turpentine_vcl/backend/backend_probe_url_admin' );
} else {
$backendNodes = Mage::helper( 'turpentine/data' )->cleanExplode( PHP_EOL,
Mage::getStoreConfig( 'turpentine_vcl/backend/backend_nodes' ) );
$probeUrl = Mage::getStoreConfig( 'turpentine_vcl/backend/backend_probe_url' );
}
$backends = '';
foreach ( $backendNodes as $backendNode ) {
$parts = explode( ':', $backendNode, 2 );
$host = ( empty($parts[0]) ) ? '127.0.0.1' : $parts[0];
$port = ( empty($parts[1]) ) ? '80' : $parts[1];
$backends .= $this->_vcl_director_backend( $host, $port, $probeUrl, $backendOptions );
}
$vars = array(
'name' => $name,
'backends' => $backends
);
return $this->_formatTemplate( $tpl, $vars );
}

/**
* Format a VCL backend declaration to put inside director
*
* @param string $host backend host
* @param string $port backend port
* @param string $probeUrl URL to check if backend is up
* @param array $options extra options for backend
* @return string
*/
protected function _vcl_director_backend( $host, $port, $probeUrl='', $options=array() ) {
$tpl = <<<EOS
{
.backend = {
.host = "{{host}}";
.port = "{{port}}";
{{probe}}

EOS;
$vars = array(
'host' => $host,
'port' => $port,
'probe' => ''
);
if ( !empty( $probeUrl ) ) {
$vars['probe'] = $this->_vcl_get_probe( $probeUrl );
}
$str = $this->_formatTemplate( $tpl, $vars );
foreach( $options as $key => $value ) {
$str .= sprintf( ' .%s = %s;', $key, $value ) . PHP_EOL;
}
$str .= <<<EOS
}
}
EOS;
return $str;
}

/**
* Format a VCL probe declaration to put in backend which is in director
*
* @param string $probeUrl URL to check if backend is up
* @return string
*/
protected function _vcl_get_probe( $probeUrl ) {
$urlParts = parse_url( $probeUrl );
if ( empty( $urlParts ) ) {
// Malformed URL
return '';
} else {
$tpl = <<<EOS
.probe = {
.request =
"GET {{probe_path}} HTTP/1.1"
"Host: {{probe_host}}"
"Connection: close";
}
EOS;
$vars = array(
'probe_host' => $urlParts['host'],
'probe_path' => $urlParts['path']
);
return $this->_formatTemplate( $tpl, $vars );
}
}

/**
* Format a VCL ACL declaration
*
Expand Down
5 changes: 5 additions & 0 deletions app/code/community/Nexcessnet/Turpentine/etc/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,13 @@
</turpentine_varnish>
<turpentine_vcl>
<backend>
<load_balancing>no</load_balancing>
<backend_host>127.0.0.1</backend_host>
<backend_port>8080</backend_port>
<backend_nodes>127.0.0.1:8080</backend_nodes>
<backend_probe_url></backend_probe_url>
<backend_nodes_admin>127.0.0.1:8080</backend_nodes_admin>
<backend_probe_url_admin></backend_probe_url_admin>
<frontend_timeout>300</frontend_timeout>
<admin_timeout>21600</admin_timeout>
<crawlers>127.0.0.1</crawlers>
Expand Down
61 changes: 61 additions & 0 deletions app/code/community/Nexcessnet/Turpentine/etc/system.xml
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,25 @@
<show_in_website>0</show_in_website>
<show_in_store>0</show_in_store>
<fields>
<load_balancing translate="label" module="turpentine">
<label>Load Balancing</label>
<frontend_type>select</frontend_type>
<source_model>turpentine/config_select_loadBalancing</source_model>
<sort_order>05</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>0</show_in_website>
<show_in_store>0</show_in_store>
</load_balancing>
<backend_host translate="label" module="turpentine">
<label>Backend Host</label>
<frontend_type>text</frontend_type>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>0</show_in_website>
<show_in_store>0</show_in_store>
<depends>
<load_balancing>no</load_balancing>
</depends>
</backend_host>
<backend_port translate="label" module="turpentine">
<label>Backend Port</label>
Expand All @@ -245,7 +257,56 @@
<show_in_default>1</show_in_default>
<show_in_website>0</show_in_website>
<show_in_store>0</show_in_store>
<depends>
<load_balancing>no</load_balancing>
</depends>
</backend_port>
<backend_nodes translate="label comment" module="turpentine">
<label>Backend Server List</label>
<frontend_type>textarea</frontend_type>
<comment>A list of HOST:PORT pairs of the backend web servers, one per line</comment>
<sort_order>22</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>0</show_in_website>
<show_in_store>0</show_in_store>
<depends>
<load_balancing separator="|">yes|yes_admin</load_balancing>
</depends>
</backend_nodes>
<backend_probe_url translate="label comment" module="turpentine">
<label>Backend Check URL</label>
<comment>URL where Varnish can probe if a node is available. Leave empty to disable probing.</comment>
<frontend_type>text</frontend_type>
<sort_order>23</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>0</show_in_website>
<show_in_store>0</show_in_store>
<depends>
<load_balancing separator="|">yes|yes_admin</load_balancing>
</depends>
</backend_probe_url>
<backend_nodes_admin translate="label" module="turpentine">
<label>Backend Server List for Admin</label>
<frontend_type>textarea</frontend_type>
<sort_order>25</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>0</show_in_website>
<show_in_store>0</show_in_store>
<depends>
<load_balancing>yes_admin</load_balancing>
</depends>
</backend_nodes_admin>
<backend_probe_url_admin translate="label" module="turpentine">
<label>Backend Check URL for Admin</label>
<frontend_type>text</frontend_type>
<sort_order>27</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>0</show_in_website>
<show_in_store>0</show_in_store>
<depends>
<load_balancing>yes_admin</load_balancing>
</depends>
</backend_probe_url_admin>
<frontend_timeout translate="label" module="turpentine">
<label>Frontend Timeout</label>
<frontend_type>text</frontend_type>
Expand Down
3 changes: 3 additions & 0 deletions app/code/community/Nexcessnet/Turpentine/misc/version-2.vcl
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ sub vcl_recv {
if (req.http.X-Opt-Enable-Caching != "true" || req.http.Authorization ||
!(req.request ~ "^(GET|HEAD|OPTIONS)$") ||
req.http.Cookie ~ "varnish_bypass={{secret_handshake}}") {
if (req.url ~ "{{url_base_regex}}{{admin_frontname}}") {
set req.backend = admin;
}
return (pipe);
}

Expand Down
3 changes: 3 additions & 0 deletions app/code/community/Nexcessnet/Turpentine/misc/version-3.vcl
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ sub vcl_recv {
if (!{{enable_caching}} || req.http.Authorization ||
req.request !~ "^(GET|HEAD|OPTIONS)$" ||
req.http.Cookie ~ "varnish_bypass={{secret_handshake}}") {
if (req.url ~ "{{url_base_regex}}{{admin_frontname}}") {
set req.backend = admin;
}
return (pipe);
}

Expand Down