You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

747 lines
24 KiB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. namespace Apewer.Source
  9. {
  10. /// <summary></summary>
  11. public class Redis : IDisposable
  12. {
  13. /// <summary>最大长度。</summary>
  14. public const int MaxLength = 1073741824;
  15. private const int BufferSize = 1048576;
  16. #region Instance
  17. string _host = null;
  18. string _pass = null;
  19. int _port = 6379;
  20. int _db = 0;
  21. int _timeout = 1000; // 1000 毫秒。
  22. Socket socket;
  23. BufferedStream buffered;
  24. /// <summary>连接指定主机。</summary>
  25. public Redis(string host, int port = 6379, string password = null)
  26. {
  27. _host = string.IsNullOrEmpty(host) ? "localhost" : host;
  28. _port = (port < 1 || port > 65535) ? 6379 : port;
  29. _pass = password;
  30. _timeout = -1;
  31. }
  32. /// <summary>连接 127.0.0.1 的指定端口。</summary>
  33. public Redis(int port, string host = "127.0.0.1", string password = null) : this(host, port, password) { }
  34. /// <summary>连接 127.0.0.1 的 6379 端口。</summary>
  35. public Redis() : this("127.0.0.1", 6379, null) { }
  36. /// <summary>获取或设置文本。</summary>
  37. public string this[string key]
  38. {
  39. get { return GetText(key); }
  40. set { SetText(key, value); }
  41. }
  42. /// <summary>获取错误信息。</summary>
  43. private Action<string> Error { get; set; }
  44. /// <summary></summary>
  45. ~Redis()
  46. {
  47. Dispose(false);
  48. }
  49. /// <summary></summary>
  50. public void Dispose()
  51. {
  52. Dispose(true);
  53. GC.SuppressFinalize(this);
  54. }
  55. /// <summary></summary>
  56. protected virtual void Dispose(bool disposing)
  57. {
  58. if (disposing)
  59. {
  60. if (socket != null)
  61. {
  62. SendCommand("QUIT");
  63. SendBySuccess();
  64. socket.Close();
  65. socket = null;
  66. }
  67. }
  68. }
  69. /// <summary>连接 Redis 服务。</summary>
  70. public string Connect()
  71. {
  72. try
  73. {
  74. if (socket != null && socket.Connected) return null;
  75. socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  76. socket.NoDelay = true;
  77. socket.SendTimeout = _timeout;
  78. socket.Connect(_host, _port);
  79. if (!socket.Connected)
  80. {
  81. socket.Close();
  82. socket = null;
  83. return "Socket 连接失败。";
  84. }
  85. buffered = new BufferedStream(new NetworkStream(socket), BufferSize);
  86. if (_pass != null) return SendBySuccess("AUTH", _pass);
  87. return null;
  88. }
  89. catch (Exception ex)
  90. {
  91. var error = ex.GetType().Name + ": " + ex.Message;
  92. return error;
  93. }
  94. }
  95. #endregion
  96. #region Static
  97. // byte[] end_data = new byte[] { (byte)'\r', (byte)'\n' };
  98. byte[] CRLF = new byte[] { 13, 10 };
  99. static byte[] ToBytes(string text)
  100. {
  101. if (string.IsNullOrEmpty(text)) return new byte[0];
  102. return Encoding.UTF8.GetBytes(text);
  103. }
  104. static int Length(string text)
  105. {
  106. return ToBytes(text).Length;
  107. }
  108. static string ToText(byte[] bytes)
  109. {
  110. if (bytes != null && bytes.Length > 0)
  111. {
  112. try
  113. {
  114. return Encoding.UTF8.GetString(bytes);
  115. }
  116. catch { }
  117. }
  118. return "";
  119. }
  120. #endregion
  121. #region Common
  122. T OnError<T>(string message, T @return)
  123. {
  124. Error?.Invoke(message);
  125. return @return;
  126. }
  127. object OnError(string message) => OnError<object>(message, null);
  128. string ReadLine()
  129. {
  130. var sb = new StringBuilder();
  131. int c;
  132. while ((c = buffered.ReadByte()) != -1)
  133. {
  134. if (c == '\r') continue;
  135. if (c == '\n') break;
  136. sb.Append((char)c);
  137. }
  138. return sb.ToString();
  139. }
  140. byte[] ReadData()
  141. {
  142. string line = ReadLine();
  143. if (line.Length < 1) return null;
  144. if (line == "$-1") return null;
  145. char flag = line[0];
  146. if (flag == '-') return OnError(line.Substring(1), null as byte[]);
  147. if (flag == '$')
  148. {
  149. int length;
  150. if (!int.TryParse(line.Substring(1), out length)) return null;
  151. var buffer = new byte[length];
  152. int writted = 0;
  153. while (writted < length)
  154. {
  155. int read = buffered.Read(buffer, writted, length - writted);
  156. if (read < 1) return null;
  157. writted += read;
  158. }
  159. if (buffered.ReadByte() != '\r' || buffered.ReadByte() != '\n') return null;
  160. return buffer;
  161. }
  162. return null;
  163. }
  164. bool Send(string head, byte[] data)
  165. {
  166. if (string.IsNullOrEmpty(head)) return false;
  167. if (!string.IsNullOrEmpty(Connect())) return false;
  168. byte[] HeadBytes = ToBytes(head);
  169. try
  170. {
  171. socket.Send(HeadBytes);
  172. if (!head.EndsWith("\r\n")) socket.Send(CRLF);
  173. if (data != null)
  174. {
  175. if (data.Length > 0) socket.Send(data);
  176. socket.Send(CRLF);
  177. }
  178. return true;
  179. }
  180. catch (Exception)
  181. {
  182. // SocketException 已超时。
  183. socket.Close();
  184. socket = null;
  185. return false;
  186. }
  187. }
  188. /// <exception cref="ArgumentException"></exception>
  189. /// <exception cref="ArgumentNullException"></exception>
  190. bool SendCommand(byte[] data, string cmd, params object[] args)
  191. {
  192. if (string.IsNullOrEmpty(cmd)) throw new ArgumentException(nameof(cmd));
  193. var argsLength = (args != null || args is object[]) ? args.Length : 0;
  194. argsLength += 1; // cmd
  195. if (data != null) argsLength += 1; // data
  196. var sb = new StringBuilder();
  197. sb.Append("*");
  198. sb.Append(argsLength.ToString());
  199. sb.Append("\r\n");
  200. sb.Append("$");
  201. sb.Append(cmd.Length);
  202. sb.Append("\r\n");
  203. sb.Append(cmd);
  204. sb.Append("\r\n");
  205. if (args != null)
  206. {
  207. foreach (var arg in args)
  208. {
  209. var line = arg == null ? "" : arg.ToString();
  210. sb.Append("$");
  211. sb.Append(Length(line).ToString());
  212. sb.Append("\r\n");
  213. sb.Append(line);
  214. sb.Append("\r\n");
  215. }
  216. }
  217. if (data != null)
  218. {
  219. sb.Append("$");
  220. sb.Append(data.Length.ToString());
  221. sb.Append("\r\n");
  222. }
  223. return Send(sb.ToString(), data);
  224. }
  225. /// <exception cref="ArgumentException"></exception>
  226. /// <exception cref="ArgumentNullException"></exception>
  227. bool SendCommand(string cmd, params object[] args) => SendCommand(null, cmd, args);
  228. string SendBySuccess(string cmd = null, params object[] args)
  229. {
  230. if (!string.IsNullOrEmpty(cmd))
  231. {
  232. if (!SendCommand(cmd, args)) return "连接 Redis 服务器失败。";
  233. }
  234. // 获取错误信息。
  235. int c = buffered.ReadByte();
  236. if (c == -1) return "没有更多字节。";
  237. string s = ReadLine();
  238. if (c == '-') return s.StartsWith("ERR ") ? s.Substring(4) : s;
  239. return null;
  240. }
  241. long SendByInt(byte[] data, string cmd, params object[] args)
  242. {
  243. if (!SendCommand(data, cmd, args)) return 0;
  244. int c = buffered.ReadByte();
  245. if (c == -1) return 0;
  246. string line = ReadLine();
  247. if (c == '-') return 0;
  248. if (c == ':')
  249. {
  250. if (long.TryParse(line, out long value)) return value;
  251. }
  252. return 0;
  253. }
  254. long SendByInt(string cmd, params object[] args) => SendByInt(null, cmd, args);
  255. string SendByString(string cmd, params object[] args)
  256. {
  257. if (!SendCommand(cmd, args)) return null;
  258. int c = buffered.ReadByte();
  259. if (c == -1) return null;
  260. var line = ReadLine();
  261. if (line.Length > 0)
  262. {
  263. if (line[0] == '-') return OnError(line, line);
  264. if (line[0] == '+') return OnError(line, line);
  265. }
  266. return line;
  267. }
  268. byte[] SendByData(string cmd, params object[] args) => SendCommand(cmd, args) ? ReadData() : null;
  269. /// <summary></summary>
  270. byte[][] SendByDataArray(string cmd, params object[] args)
  271. {
  272. if (!SendCommand(cmd, args)) return null;
  273. int c = buffered.ReadByte();
  274. if (c == -1) return null;
  275. string s = ReadLine();
  276. if (c == '-') return OnError(s, null as byte[][]);
  277. if (c == '*')
  278. {
  279. int count;
  280. if (int.TryParse(s, out count))
  281. {
  282. byte[][] result = new byte[count][];
  283. for (int i = 0; i < count; i++) result[i] = ReadData();
  284. return result;
  285. }
  286. }
  287. return null;
  288. }
  289. /// <summary></summary>
  290. string[] SendByStringArray(string cmd, params object[] args)
  291. {
  292. byte[][] reply = SendByDataArray(cmd, args);
  293. if (reply == null) return null;
  294. string[] keys = new string[reply.Length];
  295. for (int i = 0; i < reply.Length; i++) keys[i] = ToText(reply[i]);
  296. return keys;
  297. }
  298. #endregion
  299. #region DB
  300. /// <summary>获取服务器信息。</summary>
  301. public Dictionary<string, string> Info()
  302. {
  303. byte[] r = SendByData("INFO");
  304. var dict = new Dictionary<string, string>();
  305. foreach (var line in ToText(r).Split('\n'))
  306. {
  307. int p = line.IndexOf(':');
  308. if (p < 0) continue;
  309. dict.Add(line.Substring(0, p), line.Substring(p + 1));
  310. }
  311. return dict;
  312. }
  313. /// <summary>选择数据库,默认为 0。</summary>
  314. /// <returns>错误信息。</returns>
  315. public string SelectDB(int db = 0)
  316. {
  317. _db = db;
  318. return SendBySuccess("SELECT", _db);
  319. }
  320. /// <summary>清空 DB。</summary>
  321. /// <returns>错误信息。</returns>
  322. public string Flush(bool allDB = false)
  323. {
  324. return allDB ? SendBySuccess("FLUSHALL") : SendBySuccess("FLUSHDB");
  325. }
  326. /// <summary>执行同步保存操作,将所有数据的快照以 RDB 文件的形式保存到磁盘上。</summary>
  327. /// <returns>错误信息。</returns>
  328. public string Save(bool background = true)
  329. {
  330. return background ? SendBySuccess("BGSAVE") : SendBySuccess("SAVE");
  331. }
  332. /// <summary>以 UNIX 时间戳格式返回最近一次 Redis 成功将数据保存到磁盘上的时间。</summary>
  333. public DateTime LastSave()
  334. {
  335. const long UnixEpoch = 621355968000000000L;
  336. long t = SendByInt("LASTSAVE");
  337. return new DateTime(UnixEpoch) + TimeSpan.FromSeconds(t);
  338. }
  339. /// <summary>断开所有客户端,并关闭 Redis 服务进程。</summary>
  340. public void Shutdown()
  341. {
  342. SendCommand("SHUTDOWN");
  343. try
  344. {
  345. string s = ReadLine(); // 服务器可能会返回错误。
  346. if (s.Length == 0) return; // throw new ResponseException("Zero length respose");
  347. // throw new ResponseException(s.StartsWith("-ERR ") ? s.Substring(5) : s.Substring(1));
  348. }
  349. catch (IOException)
  350. {
  351. // this is the expected good result
  352. socket.Close();
  353. socket = null;
  354. }
  355. }
  356. #endregion
  357. #region Key
  358. /// <summary>获取 DB 中 Keys 的数量。</summary>
  359. /// <remarks>执行 DBSIZE 命令。</remarks>
  360. public long CountKeys()
  361. {
  362. return SendByInt("DBSIZE");
  363. }
  364. /// <summary>列出 Keys。</summary>
  365. /// <remarks>执行 KEYS 命令。</remarks>
  366. /// <returns>发生错误时返回 NULL 值。</returns>
  367. public string[] ListKeys(string pattern = "*")
  368. {
  369. var p = string.IsNullOrEmpty(pattern) ? "*" : pattern;
  370. return SendByStringArray("KEYS", p);
  371. }
  372. /// <summary>包含指定 Key。</summary>
  373. public bool ContainsKey(string key)
  374. {
  375. if (key == null) return false;
  376. return SendByInt("EXISTS", key) == 1;
  377. }
  378. /// <summary>删除指定 Key。</summary>
  379. public bool Delete(string key)
  380. {
  381. if (key == null) return false; // throw new ArgumentNullException("key");
  382. return SendByInt("DEL", key) == 1;
  383. }
  384. /// <summary>删除指定的多个 Key。不存在的 key 会被忽略。</summary>
  385. /// <returns>删除的 Key 的数量。</returns>
  386. public long Delete(params string[] keys)
  387. {
  388. if (keys == null || keys.Length < 1) return 0;
  389. return SendByInt("DEL", keys);
  390. }
  391. /// <summary>获取 Key 的值类型,正确类型为 none | string | set | list。</summary>
  392. public string TypeOf(string key)
  393. {
  394. if (key == null) return "";
  395. return SendByString("TYPE", key);
  396. }
  397. /// <summary>随机返回一个已有的 Key。</summary>
  398. public string RandomKey()
  399. {
  400. return SendByString("RANDOMKEY");
  401. }
  402. /// <summary>对 Key 重命名。</summary>
  403. /// <remarks>已经存在的 newKey 会被覆盖。</remarks>
  404. public bool Rename(string oldKey, string newKey)
  405. {
  406. // Exceptions
  407. if (string.IsNullOrEmpty(oldKey)) return false;
  408. if (string.IsNullOrEmpty(newKey)) return false;
  409. return SendByString("RENAME", oldKey, newKey)[0] == '+';
  410. }
  411. /// <summary>设置 Key 的过期时间,过期后 Key 会被自动删除。</summary>
  412. /// <remarks>在 Redis 2.1.3 之前的版本中,修改已经设置过生存时间的 Key,将会和删除 Key 有同样的效果。</remarks>
  413. public bool Expire(string key, int seconds)
  414. {
  415. // Exceptions
  416. if (string.IsNullOrEmpty(key)) return false;
  417. if (seconds < 1) return false;
  418. return SendByInt("EXPIRE", key, seconds) == 1L;
  419. }
  420. /// <summary>设置 Key 的过期时间,过期后 Key 会被自动删除。</summary>
  421. public bool ExpireAt(string key, DateTime time)
  422. {
  423. // Exceptions
  424. if (string.IsNullOrEmpty(key)) return false;
  425. var stamp = Convert.ToInt64((time - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds);
  426. return SendByInt("EXPIREAT", key, stamp) == 1L;
  427. }
  428. /// <summary>获取 Key 过期前的剩余时间。</summary>
  429. /// <remarks>秒数。当 Key 不存在时返回 -1 值。</remarks>
  430. public long TimeToLive(string key)
  431. {
  432. // Exceptions
  433. if (string.IsNullOrEmpty(key)) return -1;
  434. return SendByInt("TTL", key);
  435. }
  436. #endregion
  437. #region Bytes 字节数组
  438. /// <summary>获取值。Key 无效时获取 NULL 值。</summary>
  439. public byte[] GetBytes(string key)
  440. {
  441. // Exceptions
  442. if (string.IsNullOrEmpty(key)) return null;
  443. return SendByData("GET", key);
  444. }
  445. /// <summary>获取多个 Key 值。</summary>
  446. /// <returns>参数无效时返回 NULL 值。</returns>
  447. public byte[][] GetMultipleBytes(params string[] keys)
  448. {
  449. // Exceptions
  450. if (keys == null || keys.Length == 0) return null;
  451. return SendByDataArray("MGET", keys);
  452. }
  453. /// <summary>设置字节数组。</summary>
  454. /// <returns>错误信息。</returns>
  455. public string SetBytes(string key, byte[] bytes, bool replace = true)
  456. {
  457. // Exceptions
  458. if (key == null) return "Key 无效。";
  459. if (bytes == null) return "Bytes 无效";
  460. if (bytes.Length > MaxLength) return "Value[] 长度超出限制。";
  461. var cmd = replace ? "SET" : "SETNX";
  462. var success = SendCommand(bytes, cmd, key);
  463. if (!success) return "设置失败。";
  464. return SendBySuccess();
  465. }
  466. /// <summary>设置多个值。</summary>
  467. /// <returns>错误信息。</returns>
  468. public string SetBytes(IEnumerable<KeyValuePair<string, byte[]>> pairs)
  469. {
  470. // Exceptions
  471. if (pairs == null) return "键值对无效。";
  472. var bytes = null as byte[];
  473. var keys = new List<string>();
  474. using (var memory = new MemoryStream())
  475. {
  476. foreach (var pair in pairs)
  477. {
  478. if (string.IsNullOrEmpty(pair.Key)) continue;
  479. if (keys.Contains(pair.Key)) continue;
  480. var keyBytes = ToBytes(pair.Key);
  481. var keyLength = ToBytes("$" + keyBytes.Length.ToString() + "\r\n");
  482. memory.Write(keyLength, 0, keyLength.Length);
  483. memory.Write(keyBytes, 0, keyBytes.Length);
  484. memory.Write(CRLF, 0, CRLF.Length);
  485. var valueBytes = pair.Value ?? new byte[0];
  486. var valueLength = ToBytes("$" + valueBytes.Length + "\r\n");
  487. memory.Write(valueLength, 0, valueLength.Length);
  488. memory.Write(valueBytes, 0, valueBytes.Length);
  489. memory.Write(CRLF, 0, CRLF.Length);
  490. keys.Add(pair.Key);
  491. }
  492. if (keys.Count > 0) bytes = memory.ToArray();
  493. }
  494. if (keys.Count < 1 || bytes == null || bytes.Length < 1) return "无法生成指令。";
  495. var head = "*" + (keys.Count * 2 + 1).ToString() + "\r\n$4\r\nMSET\r\n";
  496. var sent = Send(head, bytes);
  497. if (!sent) return "发送指令失败。";
  498. return SendBySuccess();
  499. }
  500. #endregion
  501. #region Text
  502. /// <summary>获取值。Key 无效时获取 NULL 值。</summary>
  503. public string GetText(string key)
  504. {
  505. // Exceptions
  506. if (key == null) return null;
  507. var bytes = GetBytes(key);
  508. if (bytes == null) return null;
  509. return ToText(bytes);
  510. }
  511. /// <summary>设置 UTF-8 文本。返回错误信息。</summary>
  512. public string SetText(string key, string value, bool replace = true)
  513. {
  514. // Exceptions
  515. if (key == null) return "Key 无效。";
  516. if (value == null) return "Value 无效";
  517. var bytes = value.Length < 1 ? new byte[0] : ToBytes(value);
  518. return SetBytes(key, bytes, replace);
  519. }
  520. /// <summary>设置多个值。返回错误信息。</summary>
  521. public string SetText(IEnumerable<KeyValuePair<string, string>> pairs)
  522. {
  523. // Exceptions
  524. if (pairs == null) return "键值对无效。";
  525. var list = new List<KeyValuePair<string, byte[]>>();
  526. foreach (var pair in pairs)
  527. {
  528. var value = ToBytes(pair.Value);
  529. list.Add(new KeyValuePair<string, byte[]>(pair.Key, value));
  530. }
  531. return SetBytes(list);
  532. }
  533. #endregion
  534. #region Value 数值
  535. /// <summary>增大 Key 中储存的数值,当 Key 不存在,则先初始化为 0,再执行操作。</summary>
  536. /// <remarks>参数 By 为 1 时执行 INCR,否则执行 INCRBY。</remarks>
  537. /// <returns>执行操作后的值。发生错误时返回 0 值。</returns>
  538. public long Increment(string key, long by = 1)
  539. {
  540. // Exceptions
  541. if (string.IsNullOrEmpty(key)) return 0;
  542. if (by == 1) return SendByInt("INCR", key);
  543. else return SendByInt("INCRBY", key, by);
  544. }
  545. /// <summary>减小 Key 中储存的数值,当 Key 不存在,则先初始化为 0,再执行操作。</summary>
  546. /// <remarks>参数 By 为 1 时执行 DECR,否则执行 DECRBY。</remarks>
  547. /// <returns>执行操作后的值。发生错误时返回 0 值。</returns>
  548. public long Decrement(string key, long by = 1)
  549. {
  550. // Exceptions
  551. if (string.IsNullOrEmpty(key)) return 0;
  552. if (by == 1) return SendByInt("DECR", key);
  553. return SendByInt("DECRBY", key, by);
  554. }
  555. #endregion
  556. #region Set 集合。
  557. /// <summary>返回 list, set 或 sorted set 中的元素。</summary>
  558. /// <returns>排序后的元素列表。</returns>
  559. public byte[][] Sort(string key, int skip = 0, int count = 0, bool alpha = false, bool desc = false, string by = null, string get = null)
  560. {
  561. // Exceptions
  562. if (string.IsNullOrEmpty(key)) return null;
  563. var array = new ArrayList();
  564. array.Add(key);
  565. if (skip > 0 || count > 0)
  566. {
  567. array.Add("LIMIT");
  568. array.Add(skip > 0 ? skip : 0);
  569. array.Add(count > 0 ? count : 0);
  570. }
  571. if (alpha) array.Add("ALPHA");
  572. if (desc) array.Add("DESC");
  573. if (!string.IsNullOrEmpty(by))
  574. {
  575. array.Add("BY");
  576. array.Add(by);
  577. }
  578. if (!string.IsNullOrEmpty(get))
  579. {
  580. array.Add("GET");
  581. array.Add(get);
  582. }
  583. var options = array.ToArray();
  584. return SendByDataArray("SORT", options);
  585. }
  586. /// <summary>存储 list, set 或 sorted set 中的元素。</summary>
  587. /// <returns>存储在目的列表中元素个数。</returns>
  588. public long Sort(string key, string store, int skip = 0, int count = 0, bool alpha = false, bool desc = false, string by = null, string get = null)
  589. {
  590. // Exceptions
  591. if (string.IsNullOrEmpty(key)) return -1;
  592. if (string.IsNullOrEmpty(store)) return -1;
  593. var array = new ArrayList();
  594. array.Add(key);
  595. array.Add("STORE");
  596. array.Add(store);
  597. if (skip > 0 || count > 0)
  598. {
  599. array.Add("LIMIT");
  600. array.Add(skip > 0 ? skip : 0);
  601. array.Add(count > 0 ? count : 0);
  602. }
  603. if (alpha) array.Add("ALPHA");
  604. if (desc) array.Add("DESC");
  605. if (!string.IsNullOrEmpty(by))
  606. {
  607. array.Add("BY");
  608. array.Add(by);
  609. }
  610. if (!string.IsNullOrEmpty(get))
  611. {
  612. array.Add("GET");
  613. array.Add(get);
  614. }
  615. var options = array.ToArray();
  616. return SendByInt("SORT", options);
  617. }
  618. #endregion
  619. }
  620. }