寫程序超過10年的企業內部管理系統,今年開始總算是徹底走向web網絡編程了,但是由於以前的一些老的思想還沒徹底拋棄,導致寫程序的思維有時候還有一些老舊。下面從一些思維的轉變開始談起。
實際情況:
1:當公司的網站訪問量達到每天幾十萬IP時,網站服務器的壓力就非常大,一個非常簡單的程序,相鄰的2個sql語句,在服務器繁忙時,可能會過3-5分鍾才能運行完畢,甚至更長時間。服務器的硬件配置也已經足夠高了,這時候幾乎無法靠平常的數據庫的讀寫,數據庫的優化來提高程序的性能的。
2:硬盤的轉速是有限的,當數據庫量已經很大時,數據庫讀取數據也耗費很多時間。而且加硬盤相對比加內存條更復雜一些。
3:當數據庫的索引優化,分區優化都已經用完了,數據庫的結構也不能隨便修改時,靠數據庫優化的就遇到了瓶頸了。
4:現在內存都比較便宜,服務器上把能插內存條的地方都可以插滿了,但是系統往往不會用掉所有的內存,內存空間還是可以有富足。
5:雖然也可以用很多第3方組件來達到優化的目的,但是需要有學習成本,有采購成本,再有后期的維護成本,服務器的性能同樣也是增加壓力。
6:目前服務器的壓力已經快崩潰了,也比較難提升性能時,再有比較復雜的權限計算,每刷新一個頁面時,還判斷10次8次以上操作權限項目,需要更多的I/O時,很可能系統就真的徹底崩潰了。
7:當然我們可以在另外購買服務器,把程序的壓力進行分擔,但是我們假設不購買硬件了,數據庫也必須需要用同一個,從同一個服務器上的數據庫需要讀取數據。
在上面的程序環境下,就是老頑固也需要轉變思維了。
1:老頑固都比較難轉變思想:
因為事實擺在眼前,就是老頑固也必須接納緩存的做法了,雖然緩存有時候很折磨人,但是不靠緩存已經很難解決問題了。雖然以前有很多人給我過這樣的建議,都沒放在心上。
2:程序的及時性思維的轉變:
以前寫程序都強調,數據設置發生了變化程序能馬上顯示出來效果,例如修改了某個人的權限設置后,馬上就生效了。其實有時候沒必要那么馬上生效。有必要時刷新一下緩存,若沒必要用戶下次登錄時就生效了,頂多若有問題用戶再登錄一次就可以了,權限設置又不是每時每刻都在設置的,很多時候設置好了,半年一年都不用設置,沒必要過分強調實時性。
其實程序員都有過度設計的問題,用戶權限方面,我也的確是想的有些過度了,其實稍微放寬一下,也能滿足正常的日常使用的,頂多加個刷新緩存的功能,若有必要馬上見效就馬上刷洗一下緩存就可以了。
3:在不提高,就倍很多年輕人徹底超越了:
奔35了,體力腦力都明顯大幅下降,明顯感覺到身邊的年輕人又聰明又能干,這時候自己再不提高,很容易就徹底走下坡路了。雖然難起領頭羊的作用,但是至少不要被大家徹底甩在后面去了。
4:馬上動手改進程序:
有了想法了就需要馬上動手,架構良好的程序都經得起重構才對,所以一直認為自己的程序架構是非常良好的,那就應該能經得起修改才對,架構好的程序應該不是全盤推倒從來,而是小修改幾個函數就應該能達到內存緩存的目的。
5:新系統要上線要靠譜的測試確認:
程序更新上去后,前后至少要測試1周,各種功能都穩定,數據都正確才能正式投入實際實用。
接着就是程序修改的部分:
其實總共就寫了300行不到的代碼,系統的本質的改造就完成了。
1:用戶能訪問的模塊菜單,用戶擁有的操作權限項,改進為泛型。
protected List<BaseModuleEntity> ModuleList
protected List<BasePermissionItemEntity> PermissionItemList
2:當用戶需要判斷權限時,一次性把權限讀取到Cache緩存中。
3:權限判斷函數改進為從內存Cache緩存進行判斷。
4:用戶退出時,把相應的內存緩存清除掉,減輕內存的壓力。
5:寫個刷新緩存的功能,有需要時,對所有的緩存進行實時的刷新。
有時候代碼也就300行不到還有一大堆是注釋,有一大堆是沒用的,還有一大堆是重復的,真正有價值的代碼可能不超過50行,但是里面有蠻多故事,有故事的代碼更有生命力,有故事的代碼就更有賣點,有故事的代碼經常更經得起考驗,歡迎大家拍磚,大家一起學習提高,在交流中不斷修正代碼,不斷提高自己,不斷改進錯誤,一天比一天強大。
2 // All Rights Reserved , Copyright (C) 2012 , Hairihan TECH, Ltd .
3 // -----------------------------------------------------------------
4
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using System.Web;
9
10 using DotNet.Business;
11
12 /// <remarks>
13 /// BasePage
14 /// 基礎網頁類
15 ///
16 /// 修改紀錄
17 ///
18 /// 版本:1.0 2012.11.10 JiRiGaLa 整理代碼。
19 ///
20 /// 版本:1.0
21 /// <author>
22 /// <name> JiRiGaLa </name>
23 /// <date> 2012.11.10 </date>
24 /// </author>
25 /// </remarks>
26 public partial class BasePage : System.Web.UI.Page
27 {
28 /// <summary>
29 /// 用戶鎖
30 /// </summary>
31 public static readonly object UserLock = new object();
32
33 #region 常用操作權限項定義
34
35 /// <summary>
36 /// 訪問權限
37 /// </summary>
38 protected bool permissionAccess = true;
39
40 /// <summary>
41 /// 新增權限
42 /// </summary>
43 protected bool permissionAdd = true;
44
45 /// <summary>
46 /// 編輯權限
47 /// </summary>
48 protected bool permissionEdit = true;
49
50 /// <summary>
51 /// 刪除權限
52 /// </summary>
53 protected bool permissionDelete = true;
54
55 /// <summary>
56 /// 查詢權限
57 /// </summary>
58 protected bool permissionSearch = true;
59
60 /// <summary>
61 /// 管理權限
62 /// </summary>
63 protected bool permissionAdmin = false;
64
65 /// <summary>
66 /// 導出權限
67 /// </summary>
68 protected bool permissionExport = true;
69
70 /// <summary>
71 /// 導入權限
72 /// </summary>
73 protected bool permissionImport = true;
74
75 /// <summary>
76 /// 打印權限
77 /// </summary>
78 protected bool permissionPrint = true;
79
80 #endregion
81
82 // 用戶是否在某個角色里(按編號,按名稱的)
83
84 #region public bool UserIsInRole(string roleCode)
85 /// <summary>
86 /// 用戶是否在某個角色里
87 /// </summary>
88 /// <param name="roleCode"> 角色編號 </param>
89 /// <returns> 是否在某個角色里 </returns>
90 public bool UserIsInRole( string roleCode)
91 {
92 BaseUserManager userManager = new BaseUserManager( this.UserCenterDbHelper, userInfo);
93 return userManager.IsInRoleByCode( this.UserInfo.Id, roleCode);
94 }
95 #endregion
96
97 // 用戶操作權限常用判斷函數
98
99 #region public void Authorized(string permissionItemCode, string accessDenyUrl = null) 是否有相應權限,同時若沒權限會重新定位到某個頁面
100 /// <summary>
101 /// 是否有相應權限,同時若沒權限會重新定位到某個頁面
102 /// </summary>
103 /// <param name="permissionItemCode"> 權限編號 </param>
104 /// <param name="accessDenyUrl"> 訪問被阻止的url </param>
105 public void Authorized( string permissionItemCode, string accessDenyUrl = null)
106 {
107 // 若沒有相應的權限,那就跳轉到沒有權限的頁面里
108 if (!Utilities.UserIsLogOn() || !IsAuthorized(permissionItemCode))
109 {
110 if (! string.IsNullOrEmpty(accessDenyUrl))
111 {
112 HttpContext.Current.Response.Redirect(accessDenyUrl);
113 }
114 else
115 {
116 HttpContext.Current.Response.Redirect(Utilities.AccessDenyPage + " ?PermissionItemCode= " + permissionItemCode);
117 }
118 }
119 }
120 #endregion
121
122 #region public bool IsAuthorized(string permissionItemCode, string permissionItemName = null) 是否有相應的權限
123 /// <summary>
124 /// 是否有相應的權限
125 /// </summary>
126 /// <param name="permissionItemCode"> 權限編號 </param>
127 /// <returns> 是否有權限 </returns>
128 public bool IsAuthorized( string permissionItemCode, string permissionItemName = null)
129 {
130 return IsAuthorized( this.UserInfo.Id, permissionItemCode, permissionItemName);
131 }
132
133 public bool IsAuthorized( string userId, string permissionItemCode, string permissionItemName = null)
134 {
135 // 是否從服務器緩存讀取用戶權限
136 bool fromCache = true;
137 if (fromCache)
138 {
139 // 這里也可以優化一下,沒必要遍歷所有的操作權限列表
140 int count = this.PermissionItemList.Count(entity => ! string.IsNullOrEmpty(entity.Code) && entity.Code.Equals(permissionItemCode, StringComparison.OrdinalIgnoreCase));
141 return count > 0;
142 }
143 // 實時從數據庫讀取操作權限的設置方法
144 DotNetService dotNetService = new DotNetService();
145 return dotNetService.PermissionService.IsAuthorizedByUser( this.UserInfo, userId, permissionItemCode, permissionItemName);
146 }
147 #endregion
148
149 #region protected void GetPermissionItemList() 獲用戶擁有的操作權限列表
150 /// <summary>
151 /// 獲用戶擁有的操作權限列表
152 /// </summary>
153 protected void GetPermissionItemList()
154 {
155 // 這里是控制用戶並發的,減少框架等重復讀取數據庫的效率問題
156 lock (BasePage.UserLock)
157 {
158 string cacheKey = " P " + this.UserInfo.Id;
159 if (HttpContext.Current.Session == null || Cache[cacheKey] == null)
160 {
161 // 這個是默認的系統表名稱
162 DotNetService dotNetService = new DotNetService();
163 PermissionItemList = dotNetService.PermissionService.GetPermissionItemListByUser( this.UserInfo, this.UserInfo.Id);
164 }
165 }
166 }
167 #endregion
168
169 #region protected List<BasePermissionItemEntity> PermissionItemList 獲用戶擁有的操作權限列表
170 /// <summary>
171 /// 獲用戶擁有的操作權限列表
172 /// </summary>
173 protected List<BasePermissionItemEntity> PermissionItemList
174 {
175 get
176 {
177 lock (BasePage.UserLock)
178 {
179 // 這里進行了操作權限優化,出錯問題
180 this.GetPermissionItemList();
181 }
182 string cacheKey = " P " + this.UserInfo.Id;
183 return Cache[cacheKey] as List<BasePermissionItemEntity>;
184 }
185 set
186 {
187 string cacheKey = " P " + this.UserInfo.Id;
188 Cache[cacheKey] = value;
189 }
190 }
191 #endregion
192
193 // 用戶模塊菜單權限判斷常用函數
194
195 #region public void ModuleAuthorized(string moduleCode, string accessDenyUrl = null) 是否有相應的模塊權限,同時若沒權限會重新定位到某個頁面
196 /// <summary>
197 /// 是否有相應的模塊權限,同時若沒權限會重新定位到某個頁面
198 /// </summary>
199 /// <param name="moduleCode"> 模塊編號 </param>
200 /// <param name="accessDenyUrl"> 訪問被阻止的url </param>
201 public void ModuleAuthorized( string moduleCode, string accessDenyUrl = null)
202 {
203 // 若沒有相應的權限,那就跳轉到沒有權限的頁面里
204 if (!Utilities.UserIsLogOn() || !IsModuleAuthorized(moduleCode))
205 {
206 if (! string.IsNullOrEmpty(accessDenyUrl))
207 {
208 HttpContext.Current.Response.Redirect(accessDenyUrl);
209 }
210 else
211 {
212 HttpContext.Current.Response.Redirect(Utilities.AccessDenyPage + " ?ModuleCode= " + moduleCode);
213 }
214 }
215 }
216 #endregion
217
218 #region public bool IsModuleAuthorized(string moduleCode) 是否有相應的模塊權限
219 /// <summary>
220 /// 是否有相應的模塊權限
221 /// </summary>
222 /// <param name="moduleCode"> 模塊編號 </param>
223 /// <returns> 是否有權限 </returns>
224 public bool IsModuleAuthorized( string moduleCode)
225 {
226 if ( this.UserInfo.IsAdministrator)
227 {
228 return true;
229 }
230 // 這里也可以優化一下,沒必要遍歷所有的模塊列表
231 int count = this.ModuleList.Count(entity => ! string.IsNullOrEmpty(entity.Code) && entity.Code.Equals(moduleCode, StringComparison.OrdinalIgnoreCase));
232 return count > 0;
233 }
234 #endregion
235
236 #region protected void GetModuleList() 獲用戶有訪問權限的模塊列表
237 /// <summary>
238 /// 獲用戶有訪問權限的模塊列表
239 /// </summary>
240 protected void GetModuleList()
241 {
242 // 這里是控制用戶並發的,減少框架等重復讀取數據庫的效率問題
243 lock (BasePage.UserLock)
244 {
245 string cacheKey = " M " + this.UserInfo.Id;
246 if (HttpContext.Current.Session == null || Cache[cacheKey] == null)
247 {
248 // 這個是默認的系統表名稱
249 DotNetService dotNetService = new DotNetService();
250 ModuleList = dotNetService.PermissionService.GetModuleListByUser( this.UserInfo, this.UserInfo.Id);
251 }
252 }
253 }
254 #endregion
255
256 #region protected List<BaseModuleEntity> ModuleList 獲用戶有訪問權限的模塊列表
257 /// <summary>
258 /// 獲用戶有訪問權限的模塊列表
259 /// </summary>
260 protected List<BaseModuleEntity> ModuleList
261 {
262 get
263 {
264 lock (BasePage.UserLock)
265 {
266 // 這里進行了菜單優化,出錯問題
267 this.GetModuleList();
268 }
269 string cacheKey = " M " + this.UserInfo.Id;
270 // return Utilities.GetFromSession("UserModuleList") as List<BaseModuleEntity>;
271 return Cache[cacheKey] as List<BaseModuleEntity>;
272 }
273 set
274 {
275 string cacheKey = " M " + this.UserInfo.Id;
276 Cache[cacheKey] = value;
277 // Utilities.AddSession("UserModuleList", value);
278 }
279 }
280 #endregion
281 }