最近文章:高可用数据采集平台(如何玩转3门语言php+.net+aauto)、高并发数据采集的架构应用(Redis的应用)
吐槽:本人也是非常讨厌拿来主义的,有些培训每个细节都提到过,主管还找我要实际案例,而不是去安排合适的人去做这件事情,有点过于拿来主义了,有点担心。
好消息的是:高并发数据采集的架构应用(Redis的应用)团队已经实现了,不过有部分代码还是我写的,值得喝彩下,说明团队的能力还是不错的。
最近有时间,我也是用.net完成高可用数据采集平台(如何玩转3门语言php+.net+aauto)服务的编码工作。
正文开始
要想快速学习某一件事情,我们就得必须做好规划。
首先我们先来分析下要实现该项目需要哪些方面的技术,我们从哪里开始分析呢,当然有系统分析师帮你做啦!
所需的技术
1. Microsoft Studio 2010 IDE以及.Net Framework基础知识的了解
2. 如何利用C#创建服务,服务安装以及卸载
3. SQLite的基础应用以及C#如何使用SQLite
4. C#定时器的应用
5. C#的多线程应用以及线程池的应用
6. 操作系统信号量,互斥同步锁概念以及C#如何使用信号量、互斥同步锁
7. C#文件操作,ini配置文件读取技术
8. C# WEB API的调用,以及异步处理知识和C# TASK的了解应用
9. 数据传输解析技术,以及C#如何解析JSON
10. 其它常用应用,比如DataSet、DataTable、DataRow、Dictionary、List、KeyValuePair、String等
这些点看起来好恐怖,要想做好应用这些只是小小的一部分,但相对于菜鸟或者学生们就等于天书了,但是我只有一个星期的事情。
只要我们有目的去做事,任何事都不会有阻碍,废话少说,等下闲我啰嗦了。
1. Microsoft Studio 2010 IDE以及.Net Framework基础知识的了解
这点我竟然列为了重点,有些人有可能对这点不重视,但是我相信很多有经验的人都会同我一样非常重视这点。
Microsoft Studio 2010 IDE都不用说了,这个大家都是专家,我为什么会选择博客园写文章,是因为我在上学的时候看了一本博客园出版的书,讲的是设计模式的应用,觉得非常的不错,当时博客园是.Net技术平台的佼佼者。
Microsoft Studio 2010 IDE最主要的一点,就是选择框架,选择项目适合的框架。
.Net Framework 主要也是注意下为什么有那么多版本,了解下他们互相是否有关系,比如从2.0升到4.0是否能够平稳的过渡。
2. 如何利用C#创建服务,服务安装以及卸载
我也是看了一篇博客而学来的,非常不错,我也把地址贴出来:http://www.cnblogs.com/aierong/archive/2012/05/28/2521409.html
我也是主要用了(a) 利用.net框架类ServiceBase,步骤参考上面博客的地址:安装的时候注意下选择好对应版本的installutil。
代码贴出来
public partial class MainService : ServiceBase { readonly System.Timers.Timer _timer_request; readonly System.Timers.Timer _timer_upload; WorkProcess work = new WorkProcess(10); public MainService() { InitializeComponent(); //install db table TaskModel.install(); _timer_upload = _timer_request = new System.Timers.Timer(5000) { AutoReset = true, Enabled = true }; _timer_request.Elapsed += delegate(object sender, ElapsedEventArgs e) { Logger.log("Start Request Data From Api"); try { TaskResponse response = TaskFactory.createFromApi(); TaskModel.save(response); } catch (Exception ex) { Logger.error(ex.Message); } Logger.log("End Request Data From Api"); }; _timer_upload.Elapsed += delegate(object sender, ElapsedEventArgs e) { Logger.log("Start Upload Data To Api"); try { if (!work.wait()) { work.doing(); } } catch (Exception ex) { Logger.error(ex.Message); } Logger.log("End Upload Data To Api"); }; } protected override void OnStart(string[] args) { _timer_request.Enabled = true; _timer_upload.Enabled = true; } protected override void OnStop() { _timer_request.Enabled = false; _timer_upload.Enabled = false; } }
3. SQLite的基础应用以及C#如何使用SQLite
也是一样,有博客就是好,推荐代码直接拷贝一份过来。http://www.cnblogs.com/OnlyVersion/p/3746086.html
我也贴个相对精简的吧。
class DB { /// <summary> /// 获得连接对象 /// </summary> /// <returns>SQLiteConnection</returns> public static SQLiteConnection GetSQLiteConnection(){ string str = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var con = new SQLiteConnection("Data Source=" + str + @"\" + "DataBase" + @"\" + "InfoServiceDb.db"); return con; } /// <summary> /// 准备操作命令参数 /// </summary> /// <param name="cmd">SQLiteCommand</param> /// <param name="conn">SQLiteConnection</param> /// <param name="cmdText">Sql命令文本</param> /// <param name="data">参数数组</param> private static void PrepareCommand(SQLiteCommand cmd, SQLiteConnection conn, string cmdText, Dictionary<String, String> data) { if (conn.State != ConnectionState.Open) conn.Open(); cmd.Parameters.Clear(); cmd.Connection = conn; cmd.CommandText = cmdText; cmd.CommandType = CommandType.Text; cmd.CommandTimeout = 30; if (data != null && data.Count >= 1) { foreach (KeyValuePair<String, String> val in data) { cmd.Parameters.AddWithValue(val.Key, val.Value); } } } /// <summary> /// 查询,返回DataSet /// </summary> /// <param name="cmdText">Sql命令文本</param> /// <param name="data">参数数组</param> /// <returns>DataSet</returns> public static DataSet ExecuteDataset(string cmdText, Dictionary<string, string> data) { var ds = new DataSet(); using (SQLiteConnection connection = GetSQLiteConnection()) { var command = new SQLiteCommand(); PrepareCommand(command, connection, cmdText, data); var da = new SQLiteDataAdapter(command); da.Fill(ds); } return ds; } /// <summary> /// 查询,返回DataTable /// </summary> /// <param name="cmdText">Sql命令文本</param> /// <param name="data">参数数组</param> /// <returns>DataTable</returns> public static DataTable ExecuteDataTable(string cmdText, Dictionary<string, string> data) { var dt = new DataTable(); using (SQLiteConnection connection = GetSQLiteConnection()) { var command = new SQLiteCommand(); PrepareCommand(command, connection, cmdText, data); SQLiteDataReader reader = command.ExecuteReader(); dt.Load(reader); } return dt; } /// <summary> /// 返回一行数据 /// </summary> /// <param name="cmdText">Sql命令文本</param> /// <param name="data">参数数组</param> /// <returns>DataRow</returns> public static DataRow ExecuteDataRow(string cmdText, Dictionary<string, string> data) { DataSet ds = ExecuteDataset(cmdText, data); if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0) return ds.Tables[0].Rows[0]; return null; } /// <summary> /// 执行数据库操作 /// </summary> /// <param name="cmdText">Sql命令文本</param> /// <param name="data">传入的参数</param> /// <returns>返回受影响的行数</returns> public static int ExecuteNonQuery(string cmdText, Dictionary<string, string> data) { using (SQLiteConnection connection = GetSQLiteConnection()) { var command = new SQLiteCommand(); PrepareCommand(command, connection, cmdText, data); return command.ExecuteNonQuery(); } } /// <summary> /// 返回SqlDataReader对象 /// </summary> /// <param name="cmdText">Sql命令文本</param> /// <param name="data">传入的参数</param> /// <returns>SQLiteDataReader</returns> public static SQLiteDataReader ExecuteReader(string cmdText, Dictionary<string, string> data) { var command = new SQLiteCommand(); SQLiteConnection connection = GetSQLiteConnection(); try { PrepareCommand(command, connection, cmdText, data); SQLiteDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection); return reader; } catch { connection.Close(); command.Dispose(); throw; } } /// <summary> /// 返回结果集中的第一行第一列,忽略其他行或列 /// </summary> /// <param name="cmdText">Sql命令文本</param> /// <param name="data">传入的参数</param> /// <returns>object</returns> public static object ExecuteScalar(string cmdText, Dictionary<string, string> data) { using (SQLiteConnection connection = GetSQLiteConnection()) { var cmd = new SQLiteCommand(); PrepareCommand(cmd, connection, cmdText, data); return cmd.ExecuteScalar(); } }}
4. C#定时器的应用
参考1.创建服务的代码
主要两个定时器:
1. 用于请求服务端要待处理事项
2. 用于上传已处理事项的数据到服务器。
5. C#的多线程应用以及线程池的应用
多线程和线程池的概念这儿略过,本次应用只使用了.Net Framework中提供ThreadPool
ThreadPool:http://www.cnblogs.com/xugang/archive/2010/04/20/1716042.html ,这篇博客很好的介绍了基础知识。
我也贴一下,本次应用的代码:
public class WorkProcess { public static int iCount = 0; public static int iMaxCount = 0; public WorkProcess(int MaxCount) { iMaxCount = MaxCount; } //线程池里的线程将调用Beta()方法 public void execute(Object state) { Interlocked.Increment(ref iCount); try { TaskState taskState = (TaskState)state; string lockedStr = string.Format("task.locked.{0}.{1}", taskState.taskId, taskState.uploadId); lock (lockedStr) { TaskUpload.work(taskState); } int iX = 10000; Thread.Sleep(iX); } catch (Exception e) { Logger.error(e.Message); } finally { Interlocked.Decrement(ref iCount); } } public bool wait() { int threadNumber = 0; //获取当前线程数 threadNumber = Interlocked.Exchange(ref iCount, iCount); Logger.log(string.Format("Thread Num:{0}",threadNumber)); if (threadNumber <= iMaxCount) { return false; } return true; } public void doing() { string sql = "SELECT task.id as id,task_upload.id as uploadId FROM task LEFT JOIN task_upload ON task.id=task_upload.task_id WHERE status=:status AND upload_status=:upload_status LIMIT 10"; Dictionary<string, string> data = new Dictionary<string, string>(); data.Add(":status", "DONE"); data.Add(":upload_status", "WAIT"); DataTable dt = DB.ExecuteDataTable(sql, data); foreach (DataRow row in dt.Rows) { ThreadPool.QueueUserWorkItem(new WaitCallback(this.execute), new TaskState((int)row["id"], (int)row["uploadId"])); } } }
6. 操作系统信号量,互斥同步锁概念以及C#如何使用信号量、互斥同步锁
本次应用使用了信号量,主要作用是防止无限制的往线程池里面PUSH任务,适当的等待下任务的处理进度。
7. C#文件操作,ini配置文件读取技术
本次应用主要使用文件存储一些必要的调试信息以及错误信息。
public class Logger { private static readonly string ErrorFileName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"\" +"logs"+@"\"+ "error.txt"; private static readonly string LogFileName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"\" + "logs" + @"\" + "log.txt"; public static void log(string msg) { StreamWriter sw = File.AppendText(LogFileName); sw.WriteLine(string.Format("{0}:{1}",DateTime.Now,msg)); sw.Flush(); sw.Close(); } public static void error(string msg) { StreamWriter sw = File.AppendText(ErrorFileName); sw.WriteLine(string.Format("{0}:{1}", DateTime.Now, msg)); sw.Flush(); sw.Close(); } }
Ini配置读取是为了更好的成为一个独立的构件,源码来源博客园的其他作者。
/// <summary> /// IniFiles的类 /// </summary> public class IniFiles { public string FileName; //INI文件名 //声明读写INI文件的API函数 [DllImport("kernel32")] private static extern bool WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32")] private static extern int GetPrivateProfileString(string section, string key, string def, byte[] retVal, int size, string filePath); public static IniFiles config(){ string configFileName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)+@"\config.ini"; return new IniFiles(configFileName); } //类的构造函数,传递INI文件名 public IniFiles(string AFileName) { // 判断文件是否存在 FileInfo fileInfo = new FileInfo(AFileName); //Todo:搞清枚举的用法 if ((!fileInfo.Exists)) { //|| (FileAttributes.Directory in fileInfo.Attributes)) //文件不存在,建立文件 System.IO.StreamWriter sw = new System.IO.StreamWriter(AFileName, false, System.Text.Encoding.Default); try { sw.Write("#表格配置档案"); sw.Close(); } catch { throw (new ApplicationException("Ini文件不存在")); } } //必须是完全路径,不能是相对路径 FileName = fileInfo.FullName; } //写INI文件 public void WriteString(string Section, string Ident, string Value) { if (!WritePrivateProfileString(Section, Ident, Value, FileName)) { throw (new ApplicationException("写Ini文件出错")); } } //读取INI文件指定 public string ReadString(string Section, string Ident, string Default) { Byte[] Buffer = new Byte[65535]; int bufLen = GetPrivateProfileString(Section, Ident, Default, Buffer, Buffer.GetUpperBound(0), FileName); //必须设定0(系统默认的代码页)的编码方式,否则无法支持中文 string s = Encoding.GetEncoding(0).GetString(Buffer); s = s.Substring(0, bufLen); return s.Trim(); } //读整数 public int ReadInteger(string Section, string Ident, int Default) { string intStr = ReadString(Section, Ident, Convert.ToString(Default)); try { return Convert.ToInt32(intStr); } catch (Exception ex) { Console.WriteLine(ex.Message); return Default; } } //写整数 public void WriteInteger(string Section, string Ident, int Value) { WriteString(Section, Ident, Value.ToString()); } //读布尔 public bool ReadBool(string Section, string Ident, bool Default) { try { return Convert.ToBoolean(ReadString(Section, Ident, Convert.ToString(Default))); } catch (Exception ex) { Console.WriteLine(ex.Message); return Default; } } //写Bool public void WriteBool(string Section, string Ident, bool Value) { WriteString(Section, Ident, Convert.ToString(Value)); } //从Ini文件中,将指定的Section名称中的所有Ident添加到列表中 public void ReadSection(string Section, StringCollection Idents) { Byte[] Buffer = new Byte[16384]; //Idents.Clear(); int bufLen = GetPrivateProfileString(Section, null, null, Buffer, Buffer.GetUpperBound(0), FileName); //对Section进行解析 GetStringsFromBuffer(Buffer, bufLen, Idents); } private void GetStringsFromBuffer(Byte[] Buffer, int bufLen, StringCollection Strings) { Strings.Clear(); if (bufLen != 0) { int start = 0; for (int i = 0; i < bufLen; i++) { if ((Buffer[i] == 0) && ((i - start) > 0)) { String s = Encoding.GetEncoding(0).GetString(Buffer, start, i - start); Strings.Add(s); start = i + 1; } } } } //从Ini文件中,读取所有的Sections的名称 public void ReadSections(StringCollection SectionList) { //Note:必须得用Bytes来实现,StringBuilder只能取到第一个Section byte[] Buffer = new byte[65535]; int bufLen = 0; bufLen = GetPrivateProfileString(null, null, null, Buffer, Buffer.GetUpperBound(0), FileName); GetStringsFromBuffer(Buffer, bufLen, SectionList); } //读取指定的Section的所有Value到列表中 public void ReadSectionValues(string Section, NameValueCollection Values) { StringCollection KeyList = new StringCollection(); ReadSection(Section, KeyList); Values.Clear(); foreach (string key in KeyList) { Values.Add(key, ReadString(Section, key, "")); } } ////读取指定的Section的所有Value到列表中, //public void ReadSectionValues(string Section, NameValueCollection Values,char splitString) //{ string sectionValue; // string[] sectionValueSplit; // StringCollection KeyList = new StringCollection(); // ReadSection(Section, KeyList); // Values.Clear(); // foreach (string key in KeyList) // { // sectionValue=ReadString(Section, key, ""); // sectionValueSplit=sectionValue.Split(splitString); // Values.Add(key, sectionValueSplit[0].ToString(),sectionValueSplit[1].ToString()); // } //} //清除某个Section public void EraseSection(string Section) { if (!WritePrivateProfileString(Section, null, null, FileName)) { throw (new ApplicationException("无法清除Ini文件中的Section")); } } //删除某个Section下的键 public void DeleteKey(string Section, string Ident) { WritePrivateProfileString(Section, Ident, null, FileName); } //Note:对于Win9X,来说需要实现UpdateFile方法将缓冲中的数据写入文件 //在Win NT, 2000和XP上,都是直接写文件,没有缓冲,所以,无须实现UpdateFile //执行完对Ini文件的修改之后,应该调用本方法更新缓冲区。 public void UpdateFile() { WritePrivateProfileString(null, null, null, FileName); } //检查某个Section下的某个键值是否存在 public bool ValueExists(string Section, string Ident) { StringCollection Idents = new StringCollection(); ReadSection(Section, Idents); return Idents.IndexOf(Ident) > -1; } //确保资源的释放 ~IniFiles() { UpdateFile(); } }
我们也是主要学习下使用方式就好了。
8. C# WEB API的调用,以及异步处理知识和C# TASK的了解应用
C# WEB API的调用也是参考了博客园的知识:http://www.cnblogs.com/r01cn/archive/2012/11/20/2779011.html
主要也是使用了HttpClient。
代码如下:
public static TaskResponse createFromApi() { HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json") ); string readUri = IniFiles.config().ReadString("uri","read",""); HttpResponseMessage response = client.GetAsync(readUri).Result; if (response.IsSuccessStatusCode) { // Parse the response body. Blocking! var taskString = response.Content.ReadAsStringAsync().Result; StringReader sr = new StringReader(taskString.ToString()); JsonSerializer serializer = new JsonSerializer(); TaskResponse taskResponse = (TaskResponse)serializer.Deserialize(new JsonTextReader(sr),typeof(TaskResponse)); Logger.log(string.Format("nick:{0},Type:{1}", taskResponse.nick, taskResponse.type)); return taskResponse; } else { Logger.error(string.Format("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase)); throw new Exception(string.Format("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase)); } } public static bool upload(string jsonData) { HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json") ); string uploadUri = IniFiles.config().ReadString("uri", "upload", ""); List<KeyValuePair<String, String>> paramList = new List<KeyValuePair<String, String>>(); paramList.Add(new KeyValuePair<string, string>("data",jsonData)); var response = client.PostAsync(uploadUri, new FormUrlEncodedContent(paramList)).Result; if (response.IsSuccessStatusCode) { var responseString = response.Content.ReadAsStringAsync().Result; Logger.log(responseString.ToString()); return true; } else { Logger.error(string.Format("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase)); return false; } }
9. 数据传输解析技术,以及C#如何解析JSON
数据传输技术已经经历过很多时代,不过现在的时代用json还是相当比较普及的,比如现在的手机行业的普及,JSON的传输方式再一次被推向浪的高潮。
C# json解析,我使用了第三方包Newtonsoft.Json,原因很简单,我觉得易用,哪个好用我就选哪个。
使用方法参考 8.C# WEB API的调用,以及异步处理知识和C# TASK的了解应用
10. 其它常用应用,比如DataSet、DataTable、DataRow、Dictionary、List、KeyValuePair、String等
因为这些是基础,多使用,摸清他们的关系,基本讲求到会用就行了,当然性能优化的另说了,你要纠结的话那就去纠结吧。
总结:
要想成功做好一件事情,是要有目的的去做去学,像我这样能把所有的都列清楚,一步一步的走吧,相信你离成功将会不远了。
- 1楼拿笔小心
- 方法名,类名,第一个字母有大有小的,真有趣