我知道有些地方沒說到位,但願大神們提出來,我會吸收教訓,你們共同進步!javascript
三種方法:(1)數據庫(2)頁面靜態化(3)Redis、Memcachedcss
概述:redis是一種nosql數據庫,他的數據是保存在內存中,同時redis能夠定時把內存數據同步到磁盤,便可以將數據持久化,而且他比memcached支持更多的數據結構(string,list列表[隊列和棧],set[集合],sorted set[有序集合] hash(hash表))html
注:關於Redis的安裝網上有不少文章,講的要比個人好,建議你們去看那些大神的文章,這裏我只簡單介紹一下。前端
若是你看到有6379 在監聽,說明ok(默認的端口號時候:6379)java
(1)到官網上下載安裝包 redis-stable.tar.gz https://redis.io/,官網只提供Linux版本,沒有Windows版本的,只要Windows版本的都是微軟移植過來的,並且官方推薦使用Linux版本。程序員
(2)使用WinSCP把下載的安裝包,放到Ubuntu中對應的目錄中。ajax
若是在登陸的過程當中有彈窗,不要慌,點擊是便可。登陸成以後的界面:正則表達式
使用Linux的指令(mkdir src)建立一個目錄,來放Redis的安裝包:redis
因爲以前測試,已經建了src目錄,因此在這裏咱們能夠直接把安裝包,拖過來便可。sql
(3)解壓
進入到src目錄,執行 tar -zxvf redis-stable.tar.gz 解壓,解壓的過程就不截圖了,解壓後的結果爲:
(4)編譯源代碼
進入到redis-stable目錄中,再執行make
(5)使用ls指令,能夠看到該目錄下全部的文件:
該目錄下用一個src的目錄,使用cd src進入到該目錄,再使用 ls指令
將 redis-benchmark(壓力測試工具)、redis-check-aof(檢查.aof文件完整性的工具)、redis-check-dump(檢查數據文件完整性的工具)、redis-sentinel(監控集羣運行狀態)、redis-server(服務端)、redis-cli(客戶端),還有一個文件 redis.conf 也拷貝到 myredis該文件在src的上級目錄
拷貝到你的工做目錄myredis 中:cp redis-* /home/gz/myredis/
進入到myredis目錄中,發現有多餘的文件,而後再使用:
是否是乾淨多了。
(6)啓動Redis
進入到myredis目錄中,使用 ./redis-serve redis.conf來啓動服務
若是咱們的6379端口被監聽,說明咱們的服務已經成功啓動了。
注意:
默認是前端啓動,佔用你的控制檯,咱們修改 redis.conf 文件爲後臺進行,將 daemonize no 修改爲yes。
(7)C#鏈接Redis簡單測試一下:
在這裏回答一下@Partialsky的問題:用StackExchange.Redis ,而不是ServiceStack.Redis,由於StackExchange.Redis依賴組件少,並且操做更接近原生的redis操做,ServiceStack封裝的太厲害,並且以前收費,反正最好仍是用StackExchange.Redis。
step1:使用VS2017新建一個控制檯程序
step2: Install-Package StackExchange.Redis
step3:編寫代碼:
1 using StackExchange.Redis; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace LinuxRedis 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 using (ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("192.168.0.109:6379")) 15 { 16 17 IDatabase db = redis.GetDatabase(); 18 db.StringSet("guozheng", "hahaha"); 19 20 var age = db.StringGet("guozheng"); 21 Console.WriteLine(age); 22 } 23 24 25 26 27 28 Console.ReadKey(); 29 } 30 } 31 }
測試結果:
再到Linux上看看輸入是否存入到Redis中:
啓動服務:
./redis-server redis.conf
鏈接到redis
./redis-cli -h ip地址 -p 端口
數據也成功存入到redis中了。好了,C#如何簡單操做Redis就講到這裏。若是你們對如何安裝Ubuntu和Linux的操做指令不太清楚,能夠先看看其餘園友的文章,有時間根據你們的反應,再去寫篇關於Linux的文章。
前言:Redis中存儲的數據都爲字符串格式的。下面來分別介紹Redis中經常使用的數據結構。
太簡單了,略過。
概述: 什麼是list ,list是一種數據結構,能夠當作隊列和棧來使用。
當你從左邊添加數據,再從左邊取數據,就模擬出棧;當你從右邊添加數據,再從左邊取數據,就模擬出隊列。所以Redis真的很強大,看到棧和隊列這樣的數據結構,你難道就不激動嗎?這樣的數據結構太TM好了,能幫咱們處理不少棘手的問題。這裏我先賣個關子,下面會介紹我在項目中是如何使用Redis解決棘手的問題。
和list結構差很少,這裏再也不囉嗦。
下面就是操做set的一些命令。
圖中的"user:100"就至關於key,而它所指向的相似於表結構的數據就是value,這樣的數據結構有利於存儲對象數據。也是很是經常使用的方法。
強烈推薦: Redis經常使用命令文檔:http://redisdoc.com/ 文檔上有詳細的操做案例和高級用法。
注意:
redis指令不區分大小寫,可是出於規範考慮,應該使用大寫
redis中存放的鍵是區分大小寫的.
3.1C#中如何使用Redis來解決郵箱激活的實效性。
首先思考個問題:爲何要進行郵件激活?激活碼該存到哪裏?(你們先思考,我不直接說,這樣經過下面的例子你會體會的更深。)
緣由:用戶在註冊的時候,雖然正則表達式能檢查郵箱的格式是否正確,可是正則檢查不了郵箱是否可用,因而讓用戶進行激活,就能避免用戶填寫一個不可用的郵箱。
傳統方法的代碼實現:
1)數據庫表的設計:
在用戶註冊的表中添加一個字段:IsActive用來判斷激活的狀態。
該表用來存放激活碼。
代碼實現:
BLL層代碼:
1 /// <summary> 2 /// 2016-08-30 3 /// 註冊的時候看看是否已經存在該用戶 4 /// </summary> 5 /// <param name="username"></param> 6 /// <returns></returns> 7 public T_Users GetByUserName(string username) 8 { 9 T_UsersDAL userDal = new T_UsersDAL(); 10 return userDal.GetByUserName(username); 11 }
1 /// <summary> 2 /// 註冊的時候看看郵箱是否已經被註冊 3 /// </summary> 4 /// <returns></returns> 5 public bool CheckEmailOnReg(string email) 6 { 7 T_UsersDAL userDal = new T_UsersDAL(); 8 T_Users user= userDal.CheckEmailOnReg(email); 9 return user == null; 10 }
DAL層代碼:
1 /// <summary> 2 /// 註冊時候看看是否已經存在該用戶名 3 /// </summary> 4 /// <param name="username"></param> 5 /// <returns></returns> 6 public T_Users GetByUserName(string username) 7 { 8 string sql = "select * from T_Users where UserName=@UserName"; 9 DataTable dt = SqlHelper.ExecuteQuery(sql, new SqlParameter("@UserName", username)); 10 T_Users userInfo = null; 11 if (dt.Rows.Count > 0) 12 { 13 foreach (DataRow dr in dt.Rows) 14 { 15 userInfo = RowToUserInfoByDataRow(dr); 16 } 17 } 18 return userInfo; 19 }
1 /// <summary> 2 /// 註冊的時候檢查用戶的郵箱是否被註冊 3 /// </summary> 4 /// <param name="email"></param> 5 /// <returns></returns> 6 public T_Users CheckEmailOnReg(string email) 7 { 8 string sql = "select * from T_Users where Email=@Email "; 9 DataTable dt = SqlHelper.ExecuteQuery(sql, new SqlParameter("@Email",email)); 10 11 T_Users userInfo = null; 12 if (dt.Rows.Count>0) 13 { 14 foreach (DataRow dr in dt.Rows) 15 { 16 userInfo=RowToUserInfoByDataRow(dr); 17 } 18 } 19 return userInfo; 20 }
UI層代碼:
1 <!--#include file="/html/head.html"--> 2 <title>註冊</title> 3 <!--#include file="/html/linkscript.html"--> 4 <script type="text/javascript"> 5 function checkPasswordLevel(value) { 6 if (!value) { 7 return 1; 8 } 9 if (value.length < 6) { 10 return 1; 11 } 12 if (value.length == 6 && (/[0-9]/.test(value) || /[a-z]/.test(value))) { 13 return 1; 14 } 15 16 if (value.length >= 6 && /[0-9]/.test(value) && /[a-z]/.test(value) && /(?=[\x21-\x7e]+)[^A-Za-z0-9]/.test(value)) { 17 return 3; 18 } 19 return 2; 20 } 21 $(function () { 22 $("#btnReg").click(function () { 23 var username = $("#username").val(); 24 var password = $("#password").val(); 25 var password2 = $("#password2").val(); 26 var email = $("#email").val(); 27 var phone = $("#PhoneNum").val(); 28 var qq = $("#qq").val(); 29 var school = $("#school").val(); 30 31 var validCode = $("#validCode").val(); 32 //todo:非空驗證。JQuery EasyUI 33 if (phone == "") { 34 $("#phoneMsg").text("手機號不能爲空!"); 35 return; 36 } 37 else { 38 var reg = "^1(3[0-9]|4[57]|5[0-35-9]|7[01678]|8[0-9])\\d{8}$"; 39 if (!reg.test(phone)) { 40 $("#phoneMsg").text("手機號不合法!"); 41 return; 42 } 43 } 44 if (qq=="") { 45 $("#qqMsg").text("QQ號不能爲空!"); 46 return; 47 } 48 if (school=="") { 49 $("#schoolMsg").text("學校不能爲空!"); 50 } 51 if (validCode == "") { 52 $("#validateCodeMsg").text("驗證碼不能爲空!"); 53 return; 54 } 55 if (password == "") { 56 $("#userPasswordMsg").text("密碼不能爲空!"); 57 return; 58 } 59 if (password != password2) { 60 $("#pwdError").text("兩次輸入的密碼不一致!"); 61 return; 62 } 63 64 $.ajax({ 65 url: "UserController.ashx", type: "post", 66 dataType: "json", 67 data: { action: "registerSubmit", username: username, password: password, email: email, validCode: validCode, phone: phone, qq: qq, school: school }, 68 success: function (data) { 69 if (data.status == "ok") { 70 alert("註冊成功"); 71 window.location.href = "index.shtml"; 72 } 73 else { 74 alert("註冊失敗:" + data.msg); 75 //只有這句話刷新驗證碼是不安全的,須要後臺也刷新驗證碼 76 $("#imgValidCode").attr("src", "UserController.ashx?action=createValideCode&id=" + new Date()); 77 78 79 } 80 }, 81 error: function () { 82 alert("註冊請求失敗"); 83 } 84 }); 85 86 }); 87 88 $("#password").keyup(function () { 89 90 var level = checkPasswordLevel($("#password").val()); 91 switch (level) { 92 case 1: { 93 $("#td1").css("backgroundColor", "#FF8000"); 94 $("#td2").css("backgroundColor", ""); 95 $("#td3").css("backgroundColor", ""); 96 } 97 break; 98 case 2: { 99 $("#td1").css("backgroundColor", ""); 100 $("#td2").css("backgroundColor", "#FF4000"); 101 $("#td3").css("backgroundColor", ""); 102 } 103 break; 104 case 3: { 105 $("#td1").css("backgroundColor", ""); 106 $("#td2").css("backgroundColor", ""); 107 $("#td3").css("backgroundColor", "#5CB85C"); 108 } 109 break; 110 } 111 }) 112 $("#password2").blur(function () { 113 var password = $("#password").val(); 114 var password2 = $("#password2").val(); 115 if (password != password2) { 116 $("#pwdError").text("兩次輸入的密碼不一致!"); 117 return; 118 } 119 else { 120 $("#pwdError").text(""); 121 } 122 }); 123 //todo:焦點離開email的時候,編寫正則表達式檢查email地址是否正確 124 //todo:前臺用戶的註冊:郵件發送激活碼,一個郵件只能註冊一個帳號。 125 $("#email").blur(function () { 126 var email = $(this).val(); 127 if (email == "") { 128 $("#userEmailMsg").text("郵箱不能爲空!"); 129 return; 130 } 131 else { 132 var re = /^\w+@[a-z0-9]+(\.[a-z]+){1,3}$/; 133 if (re.test(email)) { 134 $.ajax({ 135 url: "UserController.ashx", type: "post", dataType: "json", 136 data: { action: "checkEmail", email: email }, 137 success: function (data) { 138 if (data.status == "ok") { 139 $("#userEmailMsg").text(data.msg); 140 return; 141 } 142 else if (data.status == "error") { 143 $("#userEmailMsg").text(data.msg); 144 return; 145 } 146 }, 147 error: function () { 148 $("#userEmailMsg").text("檢查郵箱是否可用失敗"); 149 return; 150 } 151 }) 152 } 153 else { 154 $("#userEmailMsg").text("郵箱的格式不正確!"); 155 return; 156 } 157 } 158 159 }); 160 $("#username").keyup(function () { 161 $("#userNameMsg").text(""); 162 }); 163 $("#email").keyup(function () { 164 $("#userEmailMsg").text(""); 165 }); 166 //檢查用戶名是否可用。 167 $("#username").blur(function () { 168 var username = $("#username").val(); 169 if (username == "") { 170 $("#userNameMsg").text("用戶名不能爲空!"); 171 return; 172 } 173 $.ajax({ 174 url: "UserController.ashx", type: "post", dataType: "json", 175 data: { action: "checkUserName", username: username }, 176 success: function (data) { 177 if (data.status == "ok") { 178 $("#userNameMsg").text("此用戶名可用"); 179 } 180 else { 181 $("#userNameMsg").text("此用戶名不可用,請換用其餘用戶名"); 182 } 183 }, 184 error: function () { 185 $("#userNameMsg").text("檢查用戶名是否可用失敗"); 186 } 187 }); 188 }); 189 }); 190 </script> 191 <style type="text/css"> 192 #table td 193 { 194 width: 70px; 195 height: 12px; 196 background-color: lightgray; 197 border: 1px solid #D0D0D0; 198 color: #BBBBBB; 199 line-height: 9px; 200 color: white; 201 font-size: 12px; 202 font-family: 微軟雅黑; 203 } 204 </style> 205 <!--#include file="/html/headend.html"--> 206 <!--#include file="/html/navbar.html"--> 207 <main id="post-page" class="container mainContent" role="main"> 208 <table> 209 <tr><td><label for="username">用戶名:</label></td><td><input type="text" id="username" /><span id="userNameMsg"></span></td></tr> 210 <tr> 211 <td><label for="password">輸入密碼:</label></td> 212 <td> 213 <input type="password" id="password" /><span id="userPasswordMsg"></span> 214 <table id="table" border="0" cellpadding="0" cellspacing="1" style="display: inline-table;"> 215 <tr> 216 <td id="td1" style="height: 12px; text-align: center;">弱</td> 217 <td id="td2" style="height: 12px; text-align: center;">中</td> 218 <td id="td3" style="height: 12px; text-align: center;">強</td> 219 </tr> 220 </table> 221 </td> 222 223 </tr> 224 <tr><td><label for="password2">再次輸入密碼:</label></td><td><input type="password" id="password2" /><label id="pwdError"></label></td></tr> 225 <tr><td><label>郵箱:</label></td><td><input type="text" id="email" /><span id="userEmailMsg"></span></td></tr> 226 <tr><td><label>手機號:</label></td><td><input type="text" id="PhoneNum" /><span id="phoneMsg"></span></td></tr> 227 <tr><td><label>QQ:</label></td><td><input type="text" id="qq" /><span id="qqMsg"></span></td></tr> 228 <tr><td><label>學校:</label></td><td><input type="text" id="school"/><span id="schoolMsg"></span></td></tr> 229 230 <tr><td><label>驗證碼:</label></td><td><input type="text" id="validCode" /><img src="UserController.ashx?action=createValideCode" id="imgValidCode" /><span id="validateCodeMsg"></span></td></tr> 231 <tr><td><input type="button" id="btnReg" value="註冊" /></td><td></td></tr> 232 </table> 233 </main> 234 <!--#include file="/html/foot.html"-->
通常處理程序:
/// <summary> /// 用戶註冊 /// </summary> /// <param name="context"></param> public void registerSubmit(HttpContext context) { //獲取請求報文中從瀏覽器傳過來的數據 string username = context.Request["username"]; string password = context.Request["password"]; string email = context.Request["email"]; string phone = context.Request["phone"]; string qq = context.Request["qq"]; string school = context.Request["school"]; string validCode = context.Request["validCode"]; //注意:經過js進行數據合法性校驗,只是爲了用戶用起來方便而已,在服務器中校驗才能保證數據的安全。 if (string.IsNullOrWhiteSpace(phone)) { AjaxHelper.WriteJson(context.Response, "error", "手機號不能爲空!"); return; } if (string.IsNullOrWhiteSpace(qq)) { AjaxHelper.WriteJson(context.Response,"error","QQ號不能爲空!"); return; } if (string.IsNullOrWhiteSpace(school)) { AjaxHelper.WriteJson(context.Response,"error","學校不能爲空!"); return; } if (string.IsNullOrWhiteSpace(username)) { AjaxHelper.WriteJson(context.Response,"error","用戶名不能爲空!"); return; } if (string.IsNullOrWhiteSpace(password)) { AjaxHelper.WriteJson(context.Response,"error","密碼不能爲空!"); return; } if (string.IsNullOrWhiteSpace(email)) { AjaxHelper.WriteJson(context.Response,"error","郵箱不能爲空!"); return; } if (string.IsNullOrWhiteSpace(validCode)) { AjaxHelper.WriteJson(context.Response,"error","驗證碼不能爲空!"); return; } if (validCode!=CommonHelper.GetValidCode(context)) { AjaxHelper.WriteJson(context.Response,"error","驗證碼錯誤"); CommonHelper.ResetValidCode(context); return; } T_UsersBLL userBll = new T_UsersBLL(); if (!userBll.CheckUserNameOnReg(username)) { AjaxHelper.WriteJson(context.Response,"error","當前用戶不可用"); return; } if (!userBll.CheckEmailOnReg(email)) { AjaxHelper.WriteJson(context.Response,"error","該郵箱已被註冊!"); return; } //插入數據庫(T_Users) long userId = userBll.AddNewUser(username, password, email,phone,qq,school); //激活碼 Random rand = new Random(); string activeCode = rand.Next(10000,99999).ToString(); //方案一:把激活碼存入到數據庫(T_UserActiveCodes) T_UserActiveCodes userActiveCode = new T_UserActiveCodes(); userActiveCode.UserName = username; userActiveCode.RegDateTime = DateTime.Now; userActiveCode.ActiveCode = activeCode; //插入到激活碼數據表中 new T_UserActiveCodesBLL().Add(userActiveCode); //郵件連接和正文 string activeUrl = "http://localhost:22585/UserController.ashx?action=active&username=" + context.Server.UrlEncode(username) + "&activeCode=" + activeCode; string emailBody = "尊敬的" + username + "您好,請點擊下面的連接激活您的帳戶" + "<a href='" + activeUrl + "'>點擊此連接激活您的帳號</a>,若是連接打不開,則把下面的地址複製到瀏覽器中進行激活:" + activeUrl; //發送郵件 FrontHelper.SendEmail(email,"請激活您的***帳號",emailBody); /* * 測試了網易和qq郵箱,能發是能發可是,對所發的郵件標題和內容是有限制的,不能發很容就能識別出來是垃圾郵件的郵件,標題和正文要正式點,負責不會接收到。 * 在生產環境中:沒法使用16三、qq等這種免費郵箱發送大量的郵件。 * Edm專用服務器,掏錢就ok。 SendCloud、Comm100、yiye */
郵件發送代碼:
1 public static void SendEmail(string toEmail, string subject, string body) 2 { 3 string smtpServer = ConfigurationManager.AppSettings["SmtpServer"]; 4 string smtpFrom = ConfigurationManager.AppSettings["SmtpFrom"]; 5 string smtpUserName = ConfigurationManager.AppSettings["SmtpUserName"]; 6 string smtpPassword = ConfigurationManager.AppSettings["SmtpPassword"]; 7 8 MailMessage mailObj = new MailMessage(); 9 mailObj.IsBodyHtml = true; 10 //from:abc@qq.com 11 mailObj.From = new MailAddress(smtpFrom); //發送人郵箱地址 12 mailObj.To.Add(toEmail); //收件人郵箱地址 13 mailObj.Subject = subject; //主題 14 mailObj.Body = body; //正文 15 SmtpClient smtp = new SmtpClient();//經過.Net內置的SmtpClient類和郵件服務器進行通信,發送郵件。 16 //是和發郵件方的smtp通信,由發郵件方的郵件服務器和收郵件方的郵件服務器通信進行郵件的轉接。 17 smtp.Host = smtpServer; //smtp服務器名稱 18 smtp.UseDefaultCredentials = true; 19 smtp.Credentials = new NetworkCredential(smtpUserName, smtpPassword); //發送人的登陸名和密碼 20 smtp.Send(mailObj); 21 } 22 23 關於郵箱的帳號和密碼最好配置到配置文件中。爲了安全。
好好思考一下這樣寫的缺陷在哪?不只有缺陷並且還有安全問題,有哪些安全問題?若是用戶量大的話這樣設計是否合理?會對什麼有壓力?若是不合理該如何優化?
首先咱們來分析一下:
上面的方法是在用戶表的基礎上再增長一個字段,用來存激活碼。這樣合理嗎?
因爲激活碼只用一次,因此在用戶表的基礎上再增長一個字段會麻煩一下,以前的功能會有影響。那到底該怎麼解決比較好?
這時候Redis的好處就很是明顯了,key-value數據庫,而且還能設置數據的有效時間,很好的解決了上面遇到的問題,只須要改動上面不多的一部分代碼就能夠實現想要的功能。
代碼以下:
1 //方案二:把激活碼存入的Redis中(最佳) 2 //Redis代替數據庫保存UserName和激活碼的字典結構 3 using (var client = RedisManager.ClientManager.GetClient()) 4 { 5 client.Set<string>(ACTIVECODE_PREFIX + username, activeCode, DateTime.Now.AddMinutes(30)); 6 }
若是到這裏真的就OK了嗎?你們能夠想一想爲何我要添加下面的這段代碼:
1 //把註冊用戶信息,放入消息隊列。便於另一個程序來獲取消息隊列數據,發送郵件 2 using (var client = RedisManager.ClientManager.GetClient()) 3 { 4 string info = username + "|" + email; 5 client.EnqueueItemOnList("NewRegUsers", info); 6 }
今天看看,以爲這篇博客仍是有好多沒有寫的,後續我會從新寫這篇博客,整理下今年全部項目中使用redis的案例和本身後續學習過程的總結。