Home >Backend Development >C#.Net Tutorial >.Net Core distributed mail system
This article shares a distributed mail system built by NetCore, which mainly uses NetCore's Api and Console application. Since this system belongs The company is here so I can only share the design drawings and some classes or methods that are not designed for business;
Why is it the first time in the company Using NetCore for development, aren’t some netcore APIs not comprehensive enough? Do you dare to try them? I'm afraid someone will ask me this. I can only tell you that NetCore has now released version 2.0. Many common packages of Framwork are already available. Moreover, its main focus is the MVC mode, which can efficiently develop systems and also has Many Core Nuget packages are supported, and it has reached the point where you can almost use it with confidence. So what if some things are not supported? You can use Interface to connect it from other places. A nice solution. In order to make this excellent language C# widely used, we are working hard silently.
AspNetCore - MVC Practical Series Directory
.NetCoreUpload Several examples of multiple files
Open source a cross-platform service plug-in - TaskCore.MainForm
NET Core-Study Notes
##Asp.NetCore1.1 version no longer has the project .json, in this way to generate a cross-platform package
Feature link - Distributed email system design diagram Distributed Mail System DescriptionIn fact, as you can see from the above picture, I mainly use the Api+ service model, which is also a default combination often used by Internet companies now; Use the api to accept requests to insertto-be-sent emailsqueueand storage, and then deploy multiple NetCore cross-platform services (services here refer to: console applications)To do distributed processing operations, Cross-platform serviceThe main operations are:
. Notification of email sending status (if the sub-business needs to be notified, then the business party needs to be notified of the status of email sending)
. Notification failure processing (automatically bound Send an email to the designated responsible person)
. Fill the queue (if the data in the outgoing email queue or notification queue is incomplete, the queue data needs to be repaired)
Uniform verification entrance of Api interfaceHere I use the simplest way,InheritanceController encapsulates a parent BaseController to allow the Controller of each API to perform authentication in a unified manner; Let’s take a look at the verification code of overriding public override void OnActionExecuting(ActionExecutingContext context):
1 public override void OnActionExecuting(ActionExecutingContext context) 2 { 3 base.OnActionExecuting(context); 4 5 var moResponse = new MoBaseRp(); 6 try 7 { 8 9 #region 安全性验证 10 11 var key = "request"; 12 if (!context.ActionArguments.ContainsKey(key)) { moResponse.Msg = "请求方式不正确"; return; } 13 var request = context.ActionArguments[key]; 14 var baseRq = request as MoBaseRq; 15 //暂时不验证登录账号密码 16 if (string.IsNullOrWhiteSpace(baseRq.UserName) || string.IsNullOrWhiteSpace(baseRq.UserPwd)) { moResponse.Msg = "登录账号或密码不能为空"; return; } 17 else if (baseRq.AccId <= 0) { moResponse.Msg = "发送者Id无效"; return; } 18 else if (string.IsNullOrWhiteSpace(baseRq.FuncName)) { moResponse.Msg = "业务方法名不正确"; return; } 19 20 //token验证 21 var strToken = PublicClass._Md5($"{baseRq.UserName}{baseRq.AccId}", ""); 22 if (!strToken.Equals(baseRq.Token, StringComparison.OrdinalIgnoreCase)) { moResponse.Msg = "Token验证失败"; return; } 23 24 //验证发送者Id 25 if (string.IsNullOrWhiteSpace(baseRq.Ip)) 26 { 27 var account = _db.EmailAccount.SingleOrDefault(b => b.Id == baseRq.AccId); 28 if (account == null) { moResponse.Msg = "发送者Id无效。"; return; } 29 else 30 { 31 if (account.Status != (int)EnumHelper.EmStatus.启用) 32 { 33 moResponse.Msg = "发送者Id已禁用"; return; 34 } 35 36 //验证ip 37 var ipArr = account.AllowIps.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); 38 //当前请求的Ip 39 var nowIp = this.GetUserIp(); 40 baseRq.Ip = nowIp; 41 //默认*为所有ip , 匹配ip 42 if (!ipArr.Any(b => b.Equals("*")) && !ipArr.Any(b => b.Equals(nowIp))) 43 { 44 moResponse.Msg = "请求IP为授权"; return; 45 } 46 } 47 } 48 else 49 { 50 var account = _db.EmailAccount.SingleOrDefault(b => b.Id == baseRq.AccId && b.AllowIps.Any(bb => bb.Equals(baseRq.Ip))); 51 if (account == null) { moResponse.Msg = "发送者未授权"; return; } 52 else if (account.Status != (int)EnumHelper.EmStatus.启用) 53 { 54 moResponse.Msg = "发送者Id已禁用"; return; 55 } 56 } 57 58 //内容非空,格式验证 59 if (!context.ModelState.IsValid) 60 { 61 var values = context.ModelState.Values.Where(b => b.Errors.Count > 0); 62 if (values.Count() > 0) 63 { 64 moResponse.Msg = values.First().Errors.First().ErrorMessage; 65 return; 66 } 67 } 68 69 #endregion 70 71 moResponse.Status = 1; 72 } 73 catch (Exception ex) 74 { 75 moResponse.Msg = "O No请求信息错误"; 76 } 77 finally 78 { 79 if (moResponse.Status == 0) { context.Result = Json(moResponse); } 80 } 81 }Mail request parent entity:
1 /// <summary> 2 /// 邮件请求父类 3 /// </summary> 4 public class MoBaseRq 5 { 6 7 public string UserName { get; set; } 8 9 public string UserPwd { get; set; } 10 11 /// <summary> 12 /// 验证token(Md5(账号+配置发送者账号信息的Id+Ip)) 必填 13 /// </summary> 14 public string Token { get; set; } 15 16 /// <summary> 17 /// 配置发送者账号信息的Id 必填 18 /// </summary> 19 public int AccId { get; set; } 20 21 /// <summary> 22 /// 业务方法名称 23 /// </summary> 24 public string FuncName { get; set; } 25 26 /// <summary> 27 /// 请求者Ip,如果客户端没赋值,默认服务端获取 28 /// </summary> 29 public string Ip { get; set; } 30 31 }
此邮件系统使用到了第三方包,这也能够看出有很多朋友正为开源,便利,NetCore的推广努力着;
首先看看MailKit(邮件发送)包,通过安装下载命令: Install-Package MailKit 能够下载最新包,然后你不需要做太花哨的分装,只需要正对于邮件发送的服务器,端口,账号,密码做一些设置基本就行了,如果可以您可以直接使用我的代码:
1 /// 631fb227578dfffda61e1fa4d04b7d25 2 /// 发送邮件 3 /// 039f3e95db2a684c7b74365531eb6044 4 /// 99b66721bb716c6eb6248858e6afabfd8bb7487ae6a16a43571bc14c7fcf93c2 5 /// 0650a652733f77474d453f9eb9a48d388bb7487ae6a16a43571bc14c7fcf93c2 6 /// 1c385df0e091b792f8bbe6e914c8e6778bb7487ae6a16a43571bc14c7fcf93c2 7 /// 77f0c99f65057d8cbdf7a816a81405a98bb7487ae6a16a43571bc14c7fcf93c2 8 /// 1e8c6117c440c6fca484f2c6d869a8108bb7487ae6a16a43571bc14c7fcf93c2 9 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 10 public static bool _SendEmail( 11 Dictionary1c31b2bed3c3ee28739bab1dbc9f292f dicToEmail, 12 string title, string content, 13 string name = "爱留图网", string fromEmail = "841202396@qq.com", 14 string host = "smtp.qq.com", int port = 587, 15 string userName = "841202396@qq.com", string userPwd = "123123") 16 { 17 var isOk = false; 18 try 19 { 20 if (string.IsNullOrWhiteSpace(title) || string.IsNullOrWhiteSpace(content)) { return isOk; } 21 22 //设置基本信息 23 var message = new MimeMessage(); 24 message.From.Add(new MailboxAddress(name, fromEmail)); 25 foreach (var item in dicToEmail.Keys) 26 { 27 message.To.Add(new MailboxAddress(item, dicToEmail[item])); 28 } 29 message.Subject = title; 30 message.Body = new TextPart("html") 31 { 32 Text = content 33 }; 34 35 //链接发送 36 using (var client = new SmtpClient()) 37 { 38 // For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS) 39 client.ServerCertificateValidationCallback = (s, c, h, e) => true; 40 41 //采用qq邮箱服务器发送邮件 42 client.Connect(host, port, false); 43 44 // Note: since we don't have an OAuth2 token, disable 45 // the XOAUTH2 authentication mechanism. 46 client.AuthenticationMechanisms.Remove("XOAUTH2"); 47 48 //qq邮箱,密码(安全设置短信获取后的密码) ufiaszkkulbabejh 49 client.Authenticate(userName, userPwd); 50 51 client.Send(message); 52 client.Disconnect(true); 53 } 54 isOk = true; 55 } 56 catch (Exception ex) 57 { 58 59 } 60 return isOk; 61 }
Redis方面的操作包StackExchange.Redis,现在NetCore支持很多数据库驱动(例如:Sqlserver,mysql,postgressql,db2等)这么用可以参考下这篇文章AspNetCore - MVC实战系列(一)之Sqlserver表映射实体模型,不仅如此还支持很多缓存服务(如:Memorycach,Redis),这里讲到的就是Redis,我利用Redis的list的队列特性来做分布式任务存储,尽管目前我用到的只有一个主Redis服务还没有业务场景需要用到主从复制等功能;这里分享的代码是基于StackExchange.Redis基础上封装对于string,list的操作:
1 public class StackRedis : IDisposable 2 { 3 #region 配置属性 基于 StackExchange.Redis 封装 4 //连接串 (注:IP:端口,属性=,属性=) 5 public string _ConnectionString = "127.0.0.1:6377,password=shenniubuxing3"; 6 //操作的库(注:默认0库) 7 public int _Db = 0; 8 #endregion 9 10 #region 管理器对象 11 12 /// 631fb227578dfffda61e1fa4d04b7d25 13 /// 获取redis操作类对象 14 /// 039f3e95db2a684c7b74365531eb6044 15 private static StackRedis _StackRedis; 16 private static object _locker_StackRedis = new object(); 17 public static StackRedis Current 18 { 19 get 20 { 21 if (_StackRedis == null) 22 { 23 lock (_locker_StackRedis) 24 { 25 _StackRedis = _StackRedis ?? new StackRedis(); 26 return _StackRedis; 27 } 28 } 29 30 return _StackRedis; 31 } 32 } 33 34 /// 631fb227578dfffda61e1fa4d04b7d25 35 /// 获取并发链接管理器对象 36 /// 039f3e95db2a684c7b74365531eb6044 37 private static ConnectionMultiplexer _redis; 38 private static object _locker = new object(); 39 public ConnectionMultiplexer Manager 40 { 41 get 42 { 43 if (_redis == null) 44 { 45 lock (_locker) 46 { 47 _redis = _redis ?? GetManager(this._ConnectionString); 48 return _redis; 49 } 50 } 51 52 return _redis; 53 } 54 } 55 56 /// 631fb227578dfffda61e1fa4d04b7d25 57 /// 获取链接管理器 58 /// 039f3e95db2a684c7b74365531eb6044 59 /// 907b64d6f55b544ebfbd0f523985dc218bb7487ae6a16a43571bc14c7fcf93c2 60 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 61 public ConnectionMultiplexer GetManager(string connectionString) 62 { 63 return ConnectionMultiplexer.Connect(connectionString); 64 } 65 66 /// 631fb227578dfffda61e1fa4d04b7d25 67 /// 获取操作数据库对象 68 /// 039f3e95db2a684c7b74365531eb6044 69 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 70 public IDatabase GetDb() 71 { 72 return Manager.GetDatabase(_Db); 73 } 74 #endregion 75 76 #region 操作方法 77 78 #region string 操作 79 80 /// 631fb227578dfffda61e1fa4d04b7d25 81 /// 根据Key移除 82 /// 039f3e95db2a684c7b74365531eb6044 83 /// 4b9508bee1b4e18a78c442a485ff2a938bb7487ae6a16a43571bc14c7fcf93c2 84 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 85 public async Task9eac9cfd9e022188a134e2cbc39820d5 Remove(string key) 86 { 87 var db = this.GetDb(); 88 89 return await db.KeyDeleteAsync(key); 90 } 91 92 /// 631fb227578dfffda61e1fa4d04b7d25 93 /// 根据key获取string结果 94 /// 039f3e95db2a684c7b74365531eb6044 95 /// 4b9508bee1b4e18a78c442a485ff2a938bb7487ae6a16a43571bc14c7fcf93c2 96 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 97 public async Task98c455a79ddfebb79781bff588e7b37e Get(string key) 98 { 99 var db = this.GetDb(); 100 return await db.StringGetAsync(key); 101 } 102 103 /// 631fb227578dfffda61e1fa4d04b7d25 104 /// 根据key获取string中的对象 105 /// 039f3e95db2a684c7b74365531eb6044 106 /// d65706098b2250eee2f48a758c9680c88ed7e392d75469626ca6a252f320a704 107 /// 4b9508bee1b4e18a78c442a485ff2a938bb7487ae6a16a43571bc14c7fcf93c2 108 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 109 public async Task8742468051c85b06f0a0af9e3e506b5c Get8742468051c85b06f0a0af9e3e506b5c(string key) 110 { 111 var t = default(T); 112 try 113 { 114 var _str = await this.Get(key); 115 if (string.IsNullOrWhiteSpace(_str)) { return t; } 116 117 t = JsonConvert.DeserializeObject8742468051c85b06f0a0af9e3e506b5c(_str); 118 } 119 catch (Exception ex) { } 120 return t; 121 } 122 123 /// 631fb227578dfffda61e1fa4d04b7d25 124 /// 存储string数据 125 /// 039f3e95db2a684c7b74365531eb6044 126 /// 4b9508bee1b4e18a78c442a485ff2a938bb7487ae6a16a43571bc14c7fcf93c2 127 /// baf7518bd06a20614696879534a64e8e8bb7487ae6a16a43571bc14c7fcf93c2 128 /// 52d2b40b4cb5f9a554e9da01ad3fff218bb7487ae6a16a43571bc14c7fcf93c2 129 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 130 public async Task9eac9cfd9e022188a134e2cbc39820d5 Set(string key, string value, int expireMinutes = 0) 131 { 132 var db = this.GetDb(); 133 if (expireMinutes > 0) 134 { 135 return db.StringSet(key, value, TimeSpan.FromMinutes(expireMinutes)); 136 } 137 return await db.StringSetAsync(key, value); 138 } 139 140 /// 631fb227578dfffda61e1fa4d04b7d25 141 /// 存储对象数据到string 142 /// 039f3e95db2a684c7b74365531eb6044 143 /// d65706098b2250eee2f48a758c9680c88ed7e392d75469626ca6a252f320a704 144 /// 4b9508bee1b4e18a78c442a485ff2a938bb7487ae6a16a43571bc14c7fcf93c2 145 /// baf7518bd06a20614696879534a64e8e8bb7487ae6a16a43571bc14c7fcf93c2 146 /// 52d2b40b4cb5f9a554e9da01ad3fff218bb7487ae6a16a43571bc14c7fcf93c2 147 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 148 public async Task9eac9cfd9e022188a134e2cbc39820d5 Set8742468051c85b06f0a0af9e3e506b5c(string key, T value, int expireMinutes = 0) 149 { 150 try 151 { 152 var jsonOption = new JsonSerializerSettings() 153 { 154 ReferenceLoopHandling = ReferenceLoopHandling.Ignore 155 }; 156 var _str = JsonConvert.SerializeObject(value, jsonOption); 157 if (string.IsNullOrWhiteSpace(_str)) { return false; } 158 159 return await this.Set(key, _str, expireMinutes); 160 } 161 catch (Exception ex) { } 162 return false; 163 } 164 #endregion 165 166 #region List操作(注:可以当做队列使用) 167 168 /// 631fb227578dfffda61e1fa4d04b7d25 169 /// list长度 170 /// 039f3e95db2a684c7b74365531eb6044 171 /// d65706098b2250eee2f48a758c9680c88ed7e392d75469626ca6a252f320a704 172 /// 4b9508bee1b4e18a78c442a485ff2a938bb7487ae6a16a43571bc14c7fcf93c2 173 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 174 public async Task4db15037c2d45d75b28ec2b6a696f099 GetListLen8742468051c85b06f0a0af9e3e506b5c(string key) 175 { 176 try 177 { 178 var db = this.GetDb(); 179 return await db.ListLengthAsync(key); 180 } 181 catch (Exception ex) { } 182 return 0; 183 } 184 185 /// 631fb227578dfffda61e1fa4d04b7d25 186 /// 获取队列出口数据并移除 187 /// 039f3e95db2a684c7b74365531eb6044 188 /// d65706098b2250eee2f48a758c9680c88ed7e392d75469626ca6a252f320a704 189 /// 4b9508bee1b4e18a78c442a485ff2a938bb7487ae6a16a43571bc14c7fcf93c2 190 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 191 public async Task8742468051c85b06f0a0af9e3e506b5c GetListAndPop8742468051c85b06f0a0af9e3e506b5c(string key) 192 { 193 var t = default(T); 194 try 195 { 196 var db = this.GetDb(); 197 var _str = await db.ListRightPopAsync(key); 198 if (string.IsNullOrWhiteSpace(_str)) { return t; } 199 t = JsonConvert.DeserializeObject8742468051c85b06f0a0af9e3e506b5c(_str); 200 } 201 catch (Exception ex) { } 202 return t; 203 } 204 205 /// 631fb227578dfffda61e1fa4d04b7d25 206 /// 集合对象添加到list左边 207 /// 039f3e95db2a684c7b74365531eb6044 208 /// d65706098b2250eee2f48a758c9680c88ed7e392d75469626ca6a252f320a704 209 /// 4b9508bee1b4e18a78c442a485ff2a938bb7487ae6a16a43571bc14c7fcf93c2 210 /// 3c7bf1722eecc8ca7bc30043734f11978bb7487ae6a16a43571bc14c7fcf93c2 211 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 212 public async Task4db15037c2d45d75b28ec2b6a696f099 SetLists8742468051c85b06f0a0af9e3e506b5c(string key, List8742468051c85b06f0a0af9e3e506b5c values) 213 { 214 var result = 0L; 215 try 216 { 217 var jsonOption = new JsonSerializerSettings() 218 { 219 ReferenceLoopHandling = ReferenceLoopHandling.Ignore 220 }; 221 var db = this.GetDb(); 222 foreach (var item in values) 223 { 224 var _str = JsonConvert.SerializeObject(item, jsonOption); 225 result += await db.ListLeftPushAsync(key, _str); 226 } 227 return result; 228 } 229 catch (Exception ex) { } 230 return result; 231 } 232 233 /// 631fb227578dfffda61e1fa4d04b7d25 234 /// 单个对象添加到list左边 235 /// 039f3e95db2a684c7b74365531eb6044 236 /// d65706098b2250eee2f48a758c9680c88ed7e392d75469626ca6a252f320a704 237 /// 4b9508bee1b4e18a78c442a485ff2a938bb7487ae6a16a43571bc14c7fcf93c2 238 /// baf7518bd06a20614696879534a64e8e8bb7487ae6a16a43571bc14c7fcf93c2 239 /// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3 240 public async Task4db15037c2d45d75b28ec2b6a696f099 SetList8742468051c85b06f0a0af9e3e506b5c(string key, T value) 241 { 242 var result = 0L; 243 try 244 { 245 result = await this.SetLists(key, new List8742468051c85b06f0a0af9e3e506b5c { value }); 246 } 247 catch (Exception ex) { } 248 return result; 249 } 250 251 252 #endregion 253 254 #region 额外扩展 255 256 /// 631fb227578dfffda61e1fa4d04b7d25 257 /// 手动回收管理器对象 258 /// 039f3e95db2a684c7b74365531eb6044 259 public void Dispose() 260 { 261 this.Dispose(_redis); 262 } 263 264 public void Dispose(ConnectionMultiplexer con) 265 { 266 if (con != null) 267 { 268 con.Close(); 269 con.Dispose(); 270 } 271 } 272 273 #endregion 274 275 #endregion 276 }
用到Redis的那些操作就添加哪些就行了,也不用太花哨能用就行;
这小节的内容最重要,由于之前有相关的文章,这里就不用再赘述了,来这里看看:Asp.NetCore1.1版本没了project.json,这样来生成跨平台包
The above is the detailed content of .Net Core distributed mail system. For more information, please follow other related articles on the PHP Chinese website!