在 ASP.NET 環境下使用 Memcached 快速上手指南

之前一直想研究 Memcached,這幾天花了些時間研究 Memcached Providers 好讓我現有的 ASP.NET 專案能解決多台主機間快取不同步的狀況, 想不到花沒多少時間就上手了,也因此做了一些記錄。

安裝 Memcached (ver 1.4.4) for Win32

1. 下載 memcached 1.4.4 Windows 32-bit binarymemcached Windows 64-bit pre-release

2. 在 C:\Program Files 建立一個 memcached 目錄

3. 將下載的壓縮檔解壓縮至 C:\Program Files\memcached 目錄

4. 開啟命令提示字元

5. 將 memcached 註冊進 Windows 服務

"C:\Program Files\memcached\memcached.exe" -d install

6. 啟動 memcached 服務

"C:\Program Files\memcached\memcached.exe" -d start

 

移除 Memcached (ver 1.4.4) for Win32

1. 開啟命令提示字元

2. 停止 memcached 服務

"C:\Program Files\memcached\memcached.exe" -d stop

3. 將 memcached 服務從 Windows 服務中移除

"C:\Program Files\memcached\memcached.exe" -d uninstall

4. 移除 C:\Program Files\memcached 目錄

 

測試 memcached 是否正常運作

1. 透過 telnet 指令連接到 localhost 的 11211 port, 其中 Port 11211 為 memcached 預設的 Listen Port,如果有開啟防火牆記得要設定才能讓遠端連接。( 預設會 Listen 所有 interface )

telnet localhost 11211

2. 輸入 stats 指令,並按下 Enter 取得目前 memcached 服務的運作狀態,有資料就代表安裝成功了

3. 輸入 quit 指令,並按下 Enter 退出

備註:完整指令請參考 memcached protocol

 

設定 ASP.NET 專案

1. 下載 Memcached Providers 組件 ==>  Memcached Providers 1.2 (.NET 3.5)

2. 將以下組件複製到 ASP.NET 網站的 bin 目錄下,或透過 加入參考(Add Reference) 方式將加入專案

  • Enyim.Caching.dll
  • Enyim.Caching.pdb
  • MemcachedProviders.dll
  • MemcachedProviders.pdb

 

設定 Memcached Cached Provider 並註冊至 web.config

1. 在 <configSections> 區段加入以下定義

<!-- Memcached -->
<section name="cacheProvider"
type="MemcachedProviders.Cache.CacheProviderSection, MemcachedProviders"
allowDefinition="MachineToApplication" restartOnExternalChanges="true"/>

<sectionGroup name="enyim.com">
<section name="memcached"
type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" />
</sectionGroup>

2. 然後在 <appSettings> 之上加入以下定義

<enyim.com>
<memcached>
<servers>
<add address="127.0.0.1" port="11211" />
</servers>
<socketPool minPoolSize="10" maxPoolSize="100"
connectionTimeout="00:00:10" deadTimeout="00:02:00" />
</memcached>
</enyim.com>
<cacheProvider defaultProvider="MemcachedCacheProvider">
<providers>
<add name="MemcachedCacheProvider"
type="MemcachedProviders.Cache.MemcachedCacheProvider, MemcachedProviders"
keySuffix="_MyProjectName_" defaultExpireTime="2000"/>
</providers>
</cacheProvider>

 

開始使用 memcached 的 API

1. 匯入 MemcachedProviders.Cache 命名空間

using MemcachedProviders.Cache;

2. 取得 Cache 項目

object objCache = DistCache.Get(cacheKey);

3. 寫入 Cache 項目

// 寫入快取資料 (預設過期時間)
DistCache.Add(cacheKey, cacheValue);
// 快取 60 秒
DistCache.Add(cacheKey, cacheValue, 60 * 1000);
// 快取至今天結束
DistCache.Add(cacheKey, cacheValue, DateTime.Today.AddDays(1) - DateTime.Now);

4. 移除 Cache 項目

DistCache.Remove(cacheKey);

5. 移除所有 Cache 項目

DistCache.RemoveAll();

 

心得總結 (優點)

  • 架構簡單、容易上手
  • API 與 ASP.NET 快取機制類似,將現有的 HttpRuntime.Cache 替換成 DisCache 也很容易,像我大約只花半天就將一個不小的專案從 ASP.NET 快取轉移至 memcached 快取
  • 安裝部署容易
  • 很容易擴充記憶體快取的總量,增加 memcached 伺服器並修改 web.config 即可!
  • 超高效能,同時支援 TCP 與 UDP 協定
  • 跨平台、跨語言、開放協定、開放原始碼、許多大網站都使用 memcached
  • 內建提供 Session Provider

心得總結 (缺點)

  • 無法取得所有快取項目,用 ASP.NET 快取可以透過 Cache.GetEnumerator() 取得所有快取項目 ( 參考: ASP.NET 如何將目前的 Cache 物件全部清空 ),但在 memcached 完全沒辦法,因為 memcached protocol 根本沒有定義這個功能,除非你自行實做。
  • 網路上可下載的 memcached 版本都不支援 高可用性(High Availability; HA) 特性
  • AppFabric Caching (Velocity) 相比功能少很多
  • 缺乏中文資源、文件少、中文社群不積極、有 Bug 不見得有人理 (還好是開源碼可以自己改)

 

相關連結

  

此文章由 will 發表於 2010/1/27 下午 09:09:01

永久連結 | 評論 (9) | 此文章的RSSRSS comment feed |

分類: ASP.NET | ASP.NET MVC | .Net

標籤: , , , ,

收藏:

利用 WebClient 類別模擬 HTTP POST 表單送出的注意事項

我們都知道 WebClient 類別是個簡單易用的東西,不只可以用作 HTTP 用途,連 FTP 都能用,想偷懶時很快就能寫出一些網路資料上傳、下載的程式,像我在寫一些測試程式時經常會使用 WebClient 類別,但大多情況都用來「下載網頁」居多,少有模擬表單上傳資料的情況,但利用 WebClient 類別在「傳送表單資料」時要小心使用,否則遠端接不到資料又很難除錯時哪就麻煩了。

與「上傳資料」有關的方法有以下四種 (不考慮非同步模式的方法)

如果是你,你會選用哪一種呢?你會不會覺得 string 比較友善呢? ^__^

像我們之前就這樣寫,以為可以順利的將資料上傳(錯誤示範):

using (WebClient wc = new WebClient())
{
  try
  {
	  wc.Encoding = Encoding.UTF8;

	  string resultXML = wc.UploadString(Config.PostURL, 
		  String.Format("id={0}&pw={1}", user.ID, user.PW));
  }
  catch (WebException ex)
  {
	  throw new Exception("無法連接遠端伺服器");
  }
}

透過這段程式所送出的 HTTP Request Header 如下:

POST /Home/Echo HTTP/1.1
Host: my.example.com:52215
Content-Length: 13
Connection: Keep-Alive

id=aaa&pw=bbb

雖然一樣是 POST 方法,但是缺少了最重要的 Content-Type 標頭,也就是:

Content-Type: application/x-www-form-urlencoded

以我們常用 ASP.NET MVC 為例,你不傳送標準的 Content-Type 過去,就無法正確抓到所有表單資料,也無法透過 Model Binder 自動繫結到 Action 參數中!

雖然你可以利用 wc.Headers 屬性自行將缺少的 Header 加上去,例如:

wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

但後來我們改用 UploadValues 方法,程式碼範例如下:

using (WebClient wc = new WebClient())
{
  try
  {
      wc.Encoding = Encoding.UTF8;
      
      NameValueCollection nc = new NameValueCollection();
      nc["id"] = "aaa";
      nc["pw"] = "bbb";
      
      byte[] bResult = wc.UploadValues(Config.PostURL, nc);
      
      string resultXML = Encoding.UTF8.GetString(bResult);
  }
  catch (WebException ex)
  {
      throw new Exception("無法連接遠端伺服器");
  }
}

雖然多了幾行 Code,但是其實語法更容易理解,而且也不用自己組 POST 資料中類似 QueryString 的字串,還會自動做 URLEncode 動作,只是最後多一個步驟要將 byte[] 轉回 string 而已,這段程式最後會送出的 HTTP Request Header 如下:

POST /Home/Echo HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: my.example.com:52215
Content-Length: 13
Connection: Keep-Alive

id=aaa&pw=bbb

但是,使用 WebClient 類別其實有許多缺點,所以我文章一開頭就說過「想偷懶時」,主要的缺點有:

  • 無法指定 Timeout,所以當網路連不上時,網頁或程式會整個停在那裡很久!
  • 不適合用來下載大量的檔案,高負載的網站也不適合這樣用,即便你用非同步的方式撰寫,也會讓 WebClient 因為佔據過多 Threads 而導致效能降低,壹台電腦的 Threads 數是有限制的。

對於較正式的場合,還是建議改用 HttpWebRequest 類別處理。

相關連結

  

此文章由 will 發表於 2010/1/23 下午 01:34:45

永久連結 | 評論 (5) | 此文章的RSSRSS comment feed |

分類: Web | .Net | ASP.NET MVC | ASP.NET

標籤: , , ,

收藏:

整理一大堆 ASP.NET MVC 速查表(Cheat Sheets)

最近整理了一堆與 ASP.NET MVC 有關的速查表(Cheat Sheets),非常的實用,特別整理成一篇文章,對於 ASP.NET MVC 一知半解的人可以透過速查表快速瞭解全貌,並針對不熟悉的部分進一步研讀相關文章。另外我在 2010/2/25 (四) PM 1:30 在微軟 7A/7B 有場『ASP.NET MVC 開發實務經驗分享』的研討會 [ 報名連結 ] ,有興趣的人可以來聽看看,聽說已經超過 200 人報名了,欲報從速!

Getting Started with ASP.NET MVC 1.0 [ DZone Refcardz ]

Getting Started with ASP.NET MVC 1.0 [ DZone Refcardz ]

The ASP.NET MVC Request Handling Pipeline [ redgate ]

The ASP.NET MVC Request Handling Pipeline [ redgate ]

ASP.NET MVC 101 Sheet [ Forerunner-G34 ]

ASP.NET MVC 101 Sheet [ Forerunner-G34 ]

ASP.NET MVC – View / Proven Practices / Framework / Controller – Cheat Sheets [ BB&D DRP ]

ASP.NET MVC – View / Proven Practices / Framework / Controller – Cheat Sheets [ BB&D DRP ] 

 

相關連結

  

此文章由 will 發表於 2010/1/13 上午 11:54:53

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: ASP.NET MVC

標籤: ,

收藏:

ASP.NET MVC 開發心得分享 (15):全球化和當地語系化

之前幾篇文章有講過 ASP.NET MVC 事實上共用許多 ASP.NET 原本就有的東西,這當然連 ASP.NET 全球化和當地語系化的支援也包含在內,在建置多國語言網站時我們通常會利用 App_GlobalResources 儲存全域資源檔,與頁面有關的本地資源檔會放在每一個 *.aspx 所在目錄下的 App_LocalResources 目錄下,這樣的架構在 ASP.NET MVC 之中也一樣可以利用,只是需要一些小技巧才能方便在 View 中使用,今天會分享一個 HtmlHelper 幫助我們在 View 中取用資源檔的資料。

首先我要感謝 Matt Hawley 寫了一個超好用的 HtmlHelper 幫助大家在 ASP.NET MVC 中讀取資源檔,我們可以來看看程式碼,一共分成三個部分:

1. 自訂 WebFormViewEngine 與 WebFormView 讓在 View 中可以取得 View 的實際所在路徑

LocalizationWebFormView.cs

using System.IO;
using System.Web.Mvc;

namespace MvcApplication
{
    public class LocalizationWebFormView : WebFormView
    {
        internal const string ViewPathKey = "__ViewPath__";

        public LocalizationWebFormView(string viewPath) : base(viewPath)
        {
        }

        public LocalizationWebFormView(string viewPath, string masterPath) : base(viewPath, masterPath)
        {
        }

        public override void Render(ViewContext viewContext, TextWriter writer)
        {
            // there seems to be a bug with RenderPartial tainting the page's view data
            // so we should capture the current view path, and revert back after rendering
            string originalViewPath = (string) viewContext.ViewData[ViewPathKey];
            
            viewContext.ViewData[ViewPathKey] = ViewPath;
            base.Render(viewContext, writer);
            
            viewContext.ViewData[ViewPathKey] = originalViewPath;
        }
    }
}

LocalizationWebFormViewEngine.cs

using System.Web.Mvc;

namespace MvcApplication
{
    public class LocalizationWebFormViewEngine : WebFormViewEngine
    {
        protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
        {
            return new LocalizationWebFormView(viewPath, masterPath);
        }

        protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
        {
            return new LocalizationWebFormView(partialPath, null);
        }
    }
}

2. 建立一個 Resource HtmlHelper ,方便在 View 中直接以 Html Helper 的方式取用資源檔

ResourceExtensions.cs

using System.Globalization;
using System.Web;
using System.Web.Compilation;
using System.Web.Mvc;

namespace MvcApplication
{
    public static class ResourceExtensions
    {
        public static string Resource(this Controller controller, string expression, params object[] args)
        {
            ResourceExpressionFields fields = GetResourceFields(expression, "~/");
            return GetGlobalResource(fields, args);
        }

        public static string Resource(this HtmlHelper htmlHelper, string expression, params object[] args)
        {
            string path = (string)htmlHelper.ViewData[LocalizationWebFormView.ViewPathKey];
            if (string.IsNullOrEmpty(path))
                path = "~/";

            ResourceExpressionFields fields = GetResourceFields(expression, path);
            if (!string.IsNullOrEmpty(fields.ClassKey))
                return GetGlobalResource(fields, args);

            return GetLocalResource(path, fields, args);
        }

        static string GetLocalResource(string path, ResourceExpressionFields fields, object[] args)
        {
            return string.Format((string)HttpContext.GetLocalResourceObject(path, fields.ResourceKey, CultureInfo.CurrentUICulture), args);
        }

        static string GetGlobalResource(ResourceExpressionFields fields, object[] args)
        {
            return string.Format((string)HttpContext.GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture), args);
        }

        static ResourceExpressionFields GetResourceFields(string expression, string virtualPath)
        {
            var context = new ExpressionBuilderContext(virtualPath);
            var builder = new ResourceExpressionBuilder();
            return (ResourceExpressionFields)builder.ParseExpression(expression, typeof(string), context);
        }
    }
}

3. 修改 Global.asax.cs 將預設的 WebFormViewEngine 換成 LocalizationWebFormViewEngine

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new LocalizationWebFormViewEngine());
}

這樣就大功告成了!接著我們看看如何在 View 中運用這個 Html Helper,如下圖示是我寫的一個範例:

專案內容包括:

  1. 由上述新增的三個檔案
  2. 修改過的 Global.asax.cs
  3. 新增的 App_GlobalResources 目錄與一個 Resource.resx 資源檔
  4. 在 Views\Home 下新增一個 App_LocalResources 目錄與三個本地資源檔 (Index.aspx.resx , Index.aspx.zh-TW.resx , Index.aspx.en-US.resx )

Views\Home\Index.aspx 中取用資源的範例如下:

其中有三點必須注意:

  1. 由於 ASP.NET MVC 的 Html Helper 透過擴充方法(Extension Method),所以必須要載入命名空間才能取用自訂的擴充方法,如果不希望每個 View 都匯入命名空間可以參考我的另一篇文章:ASP.NET 如何預設匯入指定的命名空間(Namespace)
  2. 讀取「全域資源檔」的格式與之前在寫 ASP.NET 的時候一樣:
    資源檔的檔名 + 逗號 + 資源名稱
  3. 讀取「本地資源檔」的時候可以直接輸入「資源名稱」就好

若想要測試不同語系的資料,可以參考下圖或我另外兩篇文章:

 

 

本文範例可以從這裡下載:
ASP.NET MVC Globalization and Localization Sample.zip (314.66 kb)

 

相關連結

  

此文章由 will 發表於 2010/1/5 下午 01:14:22

永久連結 | 評論 (3) | 此文章的RSSRSS comment feed |

分類: ASP.NET MVC

標籤: , ,

收藏:

ASP.NET MVC 開發心得分享 (14):實做 HTTP 301 永久轉向

最近有人問我 ASP.NET MVC 如何實做 HTTP 301 永久轉向 (HTTP 301 Moved Permanently),因為內建的 RedirectResult 或 RedirectActionResult 或 RedirectToRouteResult 的轉向動作都是用 HTTP 302 做轉向,但事實上這個問題與 ASP.NET MVC 無關,因為這部分的解決方法跟 ASP.NET Web Form 一模一樣。

由於 ASP.NET 核心中 HttpResponse 類別HttpResponse.Redirect 方法預設就是以 HTTP 302 做轉向,ASP.NET MVC 只是繼續沿用這個方法轉向而已,即便在 ASP.NET Web Form 開發架構下要利用 HTTP 301 轉向一樣要用相同的開發技巧,只要兩行就能夠完成:

Response.StatusCode = 301;
Response.AppendHeader("Location", "/Home/About");

在 ASP.NET MVC 的 Controller 裡由於轉向之後並不需要直接回應 View,所以針對 Action 的回傳值可以有兩種寫法:

1. 使用 Implicit Action Result 將 Action 的回傳型別改成 void

public void Redirect1()
{
Response.StatusCode = 301;
Response.AppendHeader("Location", Url.Action("About"));
}

2. 使用內建的 EmptyResult 回應

public ActionResult Redirect2()
{
Response.StatusCode = 301;
Response.AppendHeader("Location", Url.Action("About"));

return new EmptyResult();
}

當然,如上範例中對於轉向網址的產生我們一律透過 Url Helper 幫我們計算出實際要轉向的網址,這是在 ASP.NET MVC 中的標準作法,因為 ASP.NET 3.5 的 Routing 機制可以讓你輕鬆的定義千變萬化的網址格式。

注意:對於這種不需要回傳 View 的情況,千萬不要利用 Response.End() 中斷 ASP.NET 的執行,因為每當執行 Response.End() 方法都會引發一次 ThreadAbortException 例外狀況,過於頻繁的發生例外狀況會導致系統效能降低、消耗系統資源,對於這種需要轉向而不需要回應 View 的情況,最好利用 EmptyResult 來回應較為恰當。

相關連結

  

此文章由 will 發表於 2010/1/4 上午 01:52:33

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: ASP.NET | ASP.NET MVC

標籤: ,

收藏: