I've had to implement an IUrlProvider and IContentFinder (took me a while but I got there) and one of the things my contentfinder does is build a hashtree to match our old CMS page IDs with our new one.
It only builds the hashtree on the first load... from then on it's cached (I've used LogHelper to make sure it does just that).
I guess I could try the sites and switch off the code to build the hashtree and see how quickly they each load.
I notice that Jeroen Breuer uses caching in both his UrlProvider and ContentFinder code (https://www.youtube.com/watch?v=DWjbJiIUQdk)
Perhaps I should try that out.
Alex,
our site will host our main site and 25 smaller sites. I have tried an individual Application Pool for each microsite but I'm not so sure it made much of a difference.
We are not live yet - it's been a while and we're really close.
We have a large number of pages within the Umbraco CMS and a huge amount of media nodes as well, but from what I can see here on the Umbraco forums, there are some pretty big sites out there.
I've tried tidying my contentfinder and getting rid of all the identifiable code, so here goes:
namespace myNameSpace.Library.ContentFinders
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web;
using Umbraco.Web.Routing;
public class FourOhFourContentFinder : IContentFinder
{
//create a global hashtable as this can be re-used
public static Hashtable newPageIDHashTable = new Hashtable();
public bool TryFindContent(PublishedContentRequest contentRequest)
{
// **** 1) Start - check for the old CMS page ID
String currurl = HttpContext.Current.Request.RawUrl;
String querystring = null;
// Check to make sure some query string variables exist and if not add some and redirect.
// start by creating a few variables
int iqs = currurl.IndexOf('?');
int oldPageId;
int newPageId = -1;
int homepageId = 1059;
string oldPageIdent = "";
string newUrl = "";
//if there is a querystring...
if (iqs >= 0)
{
//if the querystring contains the word "page" it indicates an old CMS page
querystring = (iqs < currurl.Length - 1) ? currurl.Substring(iqs + 1) : String.Empty;
oldPageIdent = HttpUtility.ParseQueryString(querystring).Get("page");
// Parse the query string variables into a NameValueCollection.
bool result = int.TryParse(oldPageIdent, out oldPageId);
if (result)
{
if (querystring.Length > 1)
{
//strip out the old page id
querystring = querystring.Replace("page=" + oldPageId, "");
//strip out a leading ampersand
if (querystring.Length > 1)
{
querystring = (querystring.Substring(0, 1) == "&" ? "?" + querystring.Substring(1, querystring.Length - 1) : "?" + querystring);
}
}
//check to see if the hashtable has already been created by looking for the homepage
if ((newPageIDHashTable.ContainsKey(0) && (int)newPageIDHashTable[0] == homepageId))
{
LogHelper.Info(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, currurl + "HashTable Exists!");
//check to see if the oldPageId (i.e. the old CMS ID) exists
if (newPageIDHashTable.ContainsKey(oldPageId))
{
newPageId = (int)newPageIDHashTable[oldPageId];
newUrl = System.Web.HttpContext.Current.Request.Url.Scheme + "://" + System.Web.HttpContext.Current.Request.Url.Host + ":" + System.Web.HttpContext.Current.Request.Url.Port + "/" + newPageId + querystring;
UmbracoContext.Current.HttpContext.Response.RedirectPermanent(newUrl);
}
}
else
{
LogHelper.Info(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, currurl + "HashTable Needs to be Created!");
// the hashtable doesn't exist so create it
makePageIDHashTable();
//check to see if the oldPageId (i.e. the old CMS ID) exists
if (newPageIDHashTable.ContainsKey(oldPageId))
{
newPageId = (int)newPageIDHashTable[oldPageId];
newUrl = System.Web.HttpContext.Current.Request.Url.Scheme + "://" + System.Web.HttpContext.Current.Request.Url.Host + ":" + System.Web.HttpContext.Current.Request.Url.Port + "/" + newPageId + querystring;
UmbracoContext.Current.HttpContext.Response.RedirectPermanent(newUrl);
}
}
}
}
////End - check for the old page ID
// ***** 2) use contentRequest.Uri to get the current node
// this has to happen after 1) above as otherwise we'd never find old CMS pages
string _path = contentRequest.Uri.LocalPath.Replace("/", "");
//if the path is non-existent, then return the homepage for this domain - let something else handle the request
if (_path.Length < 1) return false;
// ***** 3) get the page (or node) id from the Uri
int nodeId;
// if it's not a node id then let something else handle the request
if (!int.TryParse(contentRequest.Uri.LocalPath.Replace("/", ""), out nodeId)) return false;
// now check if we are in the right docmain or we will have to return a page 404
// ***** 4) set up the page 404s
if (contentRequest != null)
{
// Get the current domain name
var ds = ApplicationContext.Current.Services.DomainService;
string domainName = HttpContext.Current.Request.ServerVariables["SERVER_NAME"];
// myOrgDomain is based on the request coming in
var myOrgDomain = ds.GetByName("http://" + domainName);
// 4a) return if there is no domain
if (myOrgDomain == null) return false;
//ignore intranet addresses as we don't need to return a 404 for the wrong domain
if ((domainName.ToLower() == "myorglive7514.local") || (domainName.ToLower() == "internalserver.myorg.co.uk")) return false;
// 4b) Get the root node id of the domain
// rootNodeId is based on the domain in the request coming in
int rootNodeId = (int)myOrgDomain.RootContentId;
// Return if a root node couldn't be found
if (rootNodeId <= 0) return false;
// 4c) Get the root node id of the domain
// Find the root node from the ID
IPublishedContent root = (rootNodeId > 0 ? UmbracoContext.Current.ContentCache.GetById(rootNodeId) : null);
// Return FALSE if the root node wasn't found (AKA move on to the next content finder)
if (root == null) return false;
//get this page's root node so we can compare the root node for this page with the root of the domain
//get the content from the node id
IPublishedContent currentPageContent = (nodeId > 0 ? UmbracoContext.Current.ContentCache.GetById(nodeId) : null);
// if 1) there is no content
// or 2) the root's template in this domain doesn't match the root's template for the page passed in
// then we move on to the final stage of assigning the correct 404 for each domain
string templateForThisPage = "";
bool pageExists = true;
if (currentPageContent == null)
{
pageExists = false;
}
else
{
IPublishedContent rootForThisPage = currentPageContent.AncestorOrSelf(1);
templateForThisPage = rootForThisPage.GetTemplateAlias();
}
// looks like nothing can find this page so now we decide which 404 to use
if (templateForThisPage != root.GetTemplateAlias() || !pageExists)
{
// Handle error pages for each site type
switch (root.GetTemplateAlias())
{
case "myOrg_site1":
{
// Get the error page from the hardcoded ID
contentRequest.PublishedContent = UmbracoContext.Current.ContentCache.GetById(1446);
break;
}
case "myOrg_site2":
{
contentRequest.PublishedContent = UmbracoContext.Current.ContentCache.GetById(2010);
break;
}
case "myOrg_site3":
{
contentRequest.PublishedContent = UmbracoContext.Current.ContentCache.GetById(4654);
break;
}
case "myOrg_site4":
{
contentRequest.PublishedContent = UmbracoContext.Current.ContentCache.GetById(2494);
break;
}
}
}
}
// Return whether an error page was found
return contentRequest.PublishedContent != null;
}
//get the new Umbraco ID for the old CMS page id
public static void makePageIDHashTable()
{
IContentService cs = ApplicationContext.Current.Services.ContentService;
IContentTypeService cts = ApplicationContext.Current.Services.ContentTypeService;
int dt = cts.GetContentType("myOrgDocType").Id;
IEnumerable<IContent> d = default(IEnumerable<IContent>);
d = cs.GetContentOfContentType(dt);
//set up some variables
int oldPageID = 0;
int newPageID = 0;
//the hash table is a global variable
//first iteration gets all the values we need
foreach (IContent doc in d)
{
//get the existing page id
if (doc.GetValue("p_page_id") == null)
{
//if p_page_id is null then this is either
// a) a new page in Umbraco or
// b) has been edited and the p_page_id has been deleted
continue;
}
else
{
oldPageID = Convert.ToInt32(doc.GetValue("p_page_id"));
}
//get the new page id
newPageID = doc.Id;
//check for duplicates - the hash table may already have the old and new page ids
if ((newPageIDHashTable.ContainsKey(oldPageID) && (int)newPageIDHashTable[oldPageID] == newPageID))
{
continue;
}
try
{
//add the page to the hash-table
newPageIDHashTable.Add(oldPageID, newPageID);
}
catch (Exception ex)
{
LogHelper.Error(typeof(FourOhFourContentFinder), "Error in FourOhFourContentFinder.makePageIDHashTable", ex);
continue;
}
}
}
}
}
I think the problem is that you are using the ContentService to query your content. This will bypass the published content cache stored in memory and will query the database direclty.
I think I might have found one way to speed up the sites.
I've changed my setup in IIS... instead of having a site for each domain (with the appropriate host header), I've tried one site only with multiple host headers.
Yet again, our previous setup allowed the former configuration with no performance issues.
Which is better?
These are not huge sites and so should all run together in harmony.
One Umbraco installation (even if it hosts multiple sites) should be one IIS website and one application pool. Having multiple IIS sites will have you run in to trouble.
Multiple sites on one Umbraco instance
We have multiple sites on one Umbraco instance and have noticed that start-up times are a bit slow.
We're using
Umbraco v7.5.14
Should I be using the
umbraco.config
?How do all the sites compete for the same config file?
Thanks
Muiris
Hi MuirisOG
Do you have some custom logic in Umbraco start event?
Is it a big site?
Thanks,
Alex
Alex Yes, I do.
I've had to implement an IUrlProvider and IContentFinder (took me a while but I got there) and one of the things my contentfinder does is build a hashtree to match our old CMS page IDs with our new one.
It only builds the hashtree on the first load... from then on it's cached (I've used LogHelper to make sure it does just that).
I guess I could try the sites and switch off the code to build the hashtree and see how quickly they each load.
Thanks
Muiris
It looks like the code that you have is the reason slow start.
Please, share how did you solve the issue.
Thanks,
Alex
Alex
sorry, I have mislead you.
I've checked and it only hits that section of the
ContentFinder
if one of our old CMS page URLs is detected.However, I'll prepare the ContentFinder to post here.
Thanks again
Muiris
Hi,
We found that UrlProviders can also be performance issue. These are called everytime you access the .Url property of a published content item.
Dave
Dave,
I notice that Jeroen Breuer uses caching in both his UrlProvider and ContentFinder code (https://www.youtube.com/watch?v=DWjbJiIUQdk)
Perhaps I should try that out.
Alex,
our site will host our main site and 25 smaller sites. I have tried an individual Application Pool for each microsite but I'm not so sure it made much of a difference.
We are not live yet - it's been a while and we're really close.
We have a large number of pages within the Umbraco CMS and a huge amount of media nodes as well, but from what I can see here on the Umbraco forums, there are some pretty big sites out there.
I've tried tidying my contentfinder and getting rid of all the identifiable code, so here goes:
Hi Muiris
I think the problem is that you are using the ContentService to query your content. This will bypass the published content cache stored in memory and will query the database direclty.
Dave
Dave / Alex
I think I might have found one way to speed up the sites.
I've changed my setup in IIS... instead of having a site for each domain (with the appropriate host header), I've tried one site only with multiple host headers.
Yet again, our previous setup allowed the former configuration with no performance issues.
Which is better?
These are not huge sites and so should all run together in harmony.
Thanks
Muiris
Hi Muiris,
One Umbraco installation (even if it hosts multiple sites) should be one IIS website and one application pool. Having multiple IIS sites will have you run in to trouble.
Dave
Thanks Dave,
the other method was working, but when I looked back over my own notes and the blogs and forum posts, everyone was doing it the other way.
Our old CMS allows for the other approach, and when one site goes down, it didn't tend to take the others with it.
I'll change the setup in IIS.
Again, many thanks
Muiris
is working on a reply...