├── .gitattributes ├── .gitignore ├── DriveTest ├── App.config ├── BatcherTest.cs ├── DemoModel.cs ├── DriveTest.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── packages.config ├── LICENSE ├── README.md ├── RedisDrive.sln ├── VisualStudio.gitignore ├── Wenli.Drive.Redis ├── App.config ├── Core │ ├── RedisBatcher.cs │ ├── RedisConnection.cs │ ├── SERedisConnection.cs │ ├── SERedisConnectionCache.cs │ ├── SERedisConnectionDefender.cs │ ├── SERedisHelper.cs │ ├── SERedisLock.cs │ ├── SERedisOperation.cs │ ├── SERedisOperationForHash.cs │ ├── SERedisOperationForList.cs │ ├── SERedisOperationForSet.cs │ ├── SERedisOperationForSortedSet.cs │ ├── SERedisOperationForString.cs │ ├── SERedisOperationForSubPush.cs │ └── SESentinelClient.cs ├── Data │ ├── PagedDictionary.cs │ └── PagedList.cs ├── Extends │ └── KeyValueConvert.cs ├── Interface │ ├── IRedisHelper.cs │ └── IRedisOperation.cs ├── RedisConfig.cs ├── RedisConnectType.cs ├── RedisHelper.cs ├── RedisHelperBuilder.cs ├── RedisLocker.cs ├── Tool │ └── SerializeHelper.cs ├── Wenli.Drive.Redis.csproj └── packages.config └── wenli.drive.redis.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | 56 | # StyleCop 57 | StyleCopReport.xml 58 | 59 | # Files built by Visual Studio 60 | *_i.c 61 | *_p.c 62 | *_h.h 63 | *.ilk 64 | *.meta 65 | *.obj 66 | *.iobj 67 | *.pch 68 | *.pdb 69 | *.ipdb 70 | *.pgc 71 | *.pgd 72 | *.rsp 73 | *.sbr 74 | *.tlb 75 | *.tli 76 | *.tlh 77 | *.tmp 78 | *.tmp_proj 79 | *.log 80 | *.vspscc 81 | *.vssscc 82 | .builds 83 | *.pidb 84 | *.svclog 85 | *.scc 86 | 87 | # Chutzpah Test files 88 | _Chutzpah* 89 | 90 | # Visual C++ cache files 91 | ipch/ 92 | *.aps 93 | *.ncb 94 | *.opendb 95 | *.opensdf 96 | *.sdf 97 | *.cachefile 98 | *.VC.db 99 | *.VC.VC.opendb 100 | 101 | # Visual Studio profiler 102 | *.psess 103 | *.vsp 104 | *.vspx 105 | *.sap 106 | 107 | # Visual Studio Trace Files 108 | *.e2e 109 | 110 | # TFS 2012 Local Workspace 111 | $tf/ 112 | 113 | # Guidance Automation Toolkit 114 | *.gpState 115 | 116 | # ReSharper is a .NET coding add-in 117 | _ReSharper*/ 118 | *.[Rr]e[Ss]harper 119 | *.DotSettings.user 120 | 121 | # JustCode is a .NET coding add-in 122 | .JustCode 123 | 124 | # TeamCity is a build add-in 125 | _TeamCity* 126 | 127 | # DotCover is a Code Coverage Tool 128 | *.dotCover 129 | 130 | # AxoCover is a Code Coverage Tool 131 | .axoCover/* 132 | !.axoCover/settings.json 133 | 134 | # Visual Studio code coverage results 135 | *.coverage 136 | *.coveragexml 137 | 138 | # NCrunch 139 | _NCrunch_* 140 | .*crunch*.local.xml 141 | nCrunchTemp_* 142 | 143 | # MightyMoose 144 | *.mm.* 145 | AutoTest.Net/ 146 | 147 | # Web workbench (sass) 148 | .sass-cache/ 149 | 150 | # Installshield output folder 151 | [Ee]xpress/ 152 | 153 | # DocProject is a documentation generator add-in 154 | DocProject/buildhelp/ 155 | DocProject/Help/*.HxT 156 | DocProject/Help/*.HxC 157 | DocProject/Help/*.hhc 158 | DocProject/Help/*.hhk 159 | DocProject/Help/*.hhp 160 | DocProject/Help/Html2 161 | DocProject/Help/html 162 | 163 | # Click-Once directory 164 | publish/ 165 | 166 | # Publish Web Output 167 | *.[Pp]ublish.xml 168 | *.azurePubxml 169 | # Note: Comment the next line if you want to checkin your web deploy settings, 170 | # but database connection strings (with potential passwords) will be unencrypted 171 | *.pubxml 172 | *.publishproj 173 | 174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 175 | # checkin your Azure Web App publish settings, but sensitive information contained 176 | # in these scripts will be unencrypted 177 | PublishScripts/ 178 | 179 | # NuGet Packages 180 | *.nupkg 181 | # The packages folder can be ignored because of Package Restore 182 | **/[Pp]ackages/* 183 | # except build/, which is used as an MSBuild target. 184 | !**/[Pp]ackages/build/ 185 | # Uncomment if necessary however generally it will be regenerated when needed 186 | #!**/[Pp]ackages/repositories.config 187 | # NuGet v3's project.json files produces more ignorable files 188 | *.nuget.props 189 | *.nuget.targets 190 | 191 | # Microsoft Azure Build Output 192 | csx/ 193 | *.build.csdef 194 | 195 | # Microsoft Azure Emulator 196 | ecf/ 197 | rcf/ 198 | 199 | # Windows Store app package directories and files 200 | AppPackages/ 201 | BundleArtifacts/ 202 | Package.StoreAssociation.xml 203 | _pkginfo.txt 204 | *.appx 205 | 206 | # Visual Studio cache files 207 | # files ending in .cache can be ignored 208 | *.[Cc]ache 209 | # but keep track of directories ending in .cache 210 | !*.[Cc]ache/ 211 | 212 | # Others 213 | ClientBin/ 214 | ~$* 215 | *~ 216 | *.dbmdl 217 | *.dbproj.schemaview 218 | *.jfm 219 | *.pfx 220 | *.publishsettings 221 | orleans.codegen.cs 222 | 223 | # Including strong name files can present a security risk 224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 225 | #*.snk 226 | 227 | # Since there are multiple workflows, uncomment next line to ignore bower_components 228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 229 | #bower_components/ 230 | 231 | # RIA/Silverlight projects 232 | Generated_Code/ 233 | 234 | # Backup & report files from converting an old project file 235 | # to a newer Visual Studio version. Backup files are not needed, 236 | # because we have git ;-) 237 | _UpgradeReport_Files/ 238 | Backup*/ 239 | UpgradeLog*.XML 240 | UpgradeLog*.htm 241 | ServiceFabricBackup/ 242 | *.rptproj.bak 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | *.ndf 248 | 249 | # Business Intelligence projects 250 | *.rdl.data 251 | *.bim.layout 252 | *.bim_*.settings 253 | *.rptproj.rsuser 254 | 255 | # Microsoft Fakes 256 | FakesAssemblies/ 257 | 258 | # GhostDoc plugin setting file 259 | *.GhostDoc.xml 260 | 261 | # Node.js Tools for Visual Studio 262 | .ntvs_analysis.dat 263 | node_modules/ 264 | 265 | # Visual Studio 6 build log 266 | *.plg 267 | 268 | # Visual Studio 6 workspace options file 269 | *.opt 270 | 271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 272 | *.vbw 273 | 274 | # Visual Studio LightSwitch build output 275 | **/*.HTMLClient/GeneratedArtifacts 276 | **/*.DesktopClient/GeneratedArtifacts 277 | **/*.DesktopClient/ModelManifest.xml 278 | **/*.Server/GeneratedArtifacts 279 | **/*.Server/ModelManifest.xml 280 | _Pvt_Extensions 281 | 282 | # Paket dependency manager 283 | .paket/paket.exe 284 | paket-files/ 285 | 286 | # FAKE - F# Make 287 | .fake/ 288 | 289 | # JetBrains Rider 290 | .idea/ 291 | *.sln.iml 292 | 293 | # CodeRush 294 | .cr/ 295 | 296 | # Python Tools for Visual Studio (PTVS) 297 | __pycache__/ 298 | *.pyc 299 | 300 | # Cake - Uncomment if you are using it 301 | # tools/** 302 | # !tools/packages.config 303 | 304 | # Tabs Studio 305 | *.tss 306 | 307 | # Telerik's JustMock configuration file 308 | *.jmconfig 309 | 310 | # BizTalk build output 311 | *.btp.cs 312 | *.btm.cs 313 | *.odx.cs 314 | *.xsd.cs 315 | 316 | # OpenCover UI analysis results 317 | OpenCover/ 318 | 319 | # Azure Stream Analytics local run output 320 | ASALocalRun/ 321 | 322 | # MSBuild Binary and Structured Log 323 | *.binlog 324 | 325 | # NVidia Nsight GPU debugger configuration file 326 | *.nvuser 327 | 328 | # MFractors (Xamarin productivity tool) working folder 329 | .mfractor/ 330 | 331 | # Local History for Visual Studio 332 | .localhistory/ -------------------------------------------------------------------------------- /DriveTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /DriveTest/BatcherTest.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:DriveTest 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:DriveTest 6 | *类 名 称:BatcherTest 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/12/3 17:31:05 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/12/3 17:31:05 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using System; 19 | using Wenli.Drive.Redis; 20 | 21 | namespace DriveTest 22 | { 23 | static class BatcherTest 24 | { 25 | static void Test() 26 | { 27 | Console.WriteLine("Wenli.Drive.Redis 批量操作实例"); 28 | 29 | var redisConfig = new RedisConfig() 30 | { 31 | SectionName = "Instance", 32 | Type = RedisConnectType.Instance, 33 | Masters = "127.0.0.1:6379", 34 | Slaves = "127.0.0.1:6380", 35 | Password = "12321", 36 | DefaultDatabase = 0, 37 | BusyRetryWaitMS = 1000 38 | }; 39 | 40 | var redisHelper = RedisHelperBuilder.Build(redisConfig); 41 | 42 | using (var redisBatcher = redisHelper.GetRedisOperation().CreateBatcher()) 43 | { 44 | for (int i = 0; i < 100; i++) 45 | { 46 | redisBatcher.Batch.StringSetAsync($"batch_{i}", $"val_{i}"); 47 | } 48 | } 49 | 50 | Console.Read(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /DriveTest/DemoModel.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2015-2016 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:c91279a5-8753-4ca4-bbdc-d89dfa9dd54d 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:DriveTest 10 | * 命名空间:DriveTest 11 | * 类名称:DemoModel 12 | * 创建时间:2016/12/28 10:20:47 13 | * 创建人:wenli 14 | * 创建说明: 15 | *****************************************************************************************************/ 16 | using System; 17 | 18 | namespace DriveTest 19 | { 20 | public class DemoModel 21 | { 22 | public string ID 23 | { 24 | get; set; 25 | } 26 | public string Name 27 | { 28 | get; set; 29 | } 30 | public int Age 31 | { 32 | get; set; 33 | } 34 | public DateTime Created 35 | { 36 | get; set; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /DriveTest/DriveTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7E8B59D4-683F-4393-864E-C315A79D5A97} 8 | Exe 9 | Properties 10 | DriveTest 11 | DriveTest 12 | v4.6.2 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | x64 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | DriveTest.Program 36 | 37 | 38 | 39 | ..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll 40 | 41 | 42 | ..\packages\Microsoft.Extensions.Configuration.Abstractions.5.0.0\lib\net461\Microsoft.Extensions.Configuration.Abstractions.dll 43 | 44 | 45 | ..\packages\Microsoft.Extensions.Configuration.Binder.5.0.0\lib\net461\Microsoft.Extensions.Configuration.Binder.dll 46 | 47 | 48 | ..\packages\Microsoft.Extensions.Primitives.5.0.0\lib\net461\Microsoft.Extensions.Primitives.dll 49 | 50 | 51 | ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll 52 | 53 | 54 | ..\packages\Pipelines.Sockets.Unofficial.2.2.0\lib\net461\Pipelines.Sockets.Unofficial.dll 55 | 56 | 57 | ..\packages\StackExchange.Redis.2.2.4\lib\net461\StackExchange.Redis.dll 58 | 59 | 60 | 61 | ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll 62 | 63 | 64 | 65 | ..\packages\System.Configuration.ConfigurationManager.5.0.0\lib\net461\System.Configuration.ConfigurationManager.dll 66 | 67 | 68 | 69 | 70 | ..\packages\System.Diagnostics.PerformanceCounter.5.0.0\lib\net461\System.Diagnostics.PerformanceCounter.dll 71 | 72 | 73 | 74 | ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll 75 | 76 | 77 | ..\packages\System.IO.Pipelines.5.0.0\lib\net461\System.IO.Pipelines.dll 78 | 79 | 80 | ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll 81 | 82 | 83 | 84 | 85 | ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll 86 | 87 | 88 | ..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll 89 | 90 | 91 | ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll 92 | 93 | 94 | 95 | ..\packages\System.Security.AccessControl.5.0.0\lib\net461\System.Security.AccessControl.dll 96 | 97 | 98 | ..\packages\System.Security.Permissions.5.0.0\lib\net461\System.Security.Permissions.dll 99 | 100 | 101 | ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll 102 | 103 | 104 | 105 | ..\packages\System.Threading.Channels.5.0.0\lib\net461\System.Threading.Channels.dll 106 | 107 | 108 | ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll 109 | 110 | 111 | 112 | ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | ..\packages\Wenli.Drive.Redis.2.2.8.8\lib\netstandard2.0\Wenli.Drive.Redis.dll 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /DriveTest/Program.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2015-2016 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:c91279a5-8753-4ca4-bbdc-d89dfa9dd54d 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:Program 10 | * 命名空间:DriveTest 11 | * 类名称:DriveTest 12 | * 创建时间:2016/12/28 10:20:47 13 | * 创建人:wenli 14 | * 创建说明: 15 | *****************************************************************************************************/ 16 | using System; 17 | using System.Diagnostics; 18 | using System.Threading; 19 | using System.Threading.Tasks; 20 | using Wenli.Drive.Redis; 21 | 22 | namespace DriveTest 23 | { 24 | class Program 25 | { 26 | static void Main(string[] args) 27 | { 28 | Console.Title = "Wenli.Drive.Redis驱动测试"; 29 | 30 | Console.WriteLine("Wenli.Drive.Redis test"); 31 | 32 | Console.WriteLine("输入s 测试哨兵模式,输入c测试cluster模式,M为连续,l为锁测试,其它为单实例模式"); 33 | 34 | 35 | var redisConfig = new RedisConfig() 36 | { 37 | SectionName = "Instance", 38 | Type = RedisConnectType.Instance, 39 | Masters = "127.0.0.1:6379", 40 | Slaves = "127.0.0.1:6380", 41 | Password = "12321", 42 | DefaultDatabase = 0, 43 | BusyRetryWaitMS = 1000 44 | }; 45 | 46 | 47 | var sentinelConfig = new RedisConfig() 48 | { 49 | SectionName = "Sentinel", 50 | Type = RedisConnectType.Sentinel, 51 | Masters = "127.0.0.1:26379", 52 | Password = "12321", 53 | ServiceName = "mymaster" 54 | }; 55 | 56 | var clusterConfig = new RedisConfig() 57 | { 58 | SectionName= "Cluster", 59 | Type = RedisConnectType.Cluster, 60 | Masters = "127.0.0.1:6380", 61 | Password = "12321" 62 | }; 63 | 64 | while (true) 65 | { 66 | 67 | var c = Console.ReadLine(); 68 | 69 | if (c.ToUpper() == "S") 70 | { 71 | #region sentinel 72 | Console.WriteLine("Wenli.Drive.Redis test 进入哨兵模式---------------------"); 73 | 74 | var redisHelper = RedisHelperBuilder.Build(clusterConfig); 75 | 76 | 77 | #region string 78 | Console.ReadLine(); 79 | Console.WriteLine("string get/set test"); 80 | 81 | redisHelper.GetRedisOperation().StringSet("abcabcabc", "123123"); 82 | Console.WriteLine("写入key:abcabcabc,value:123123"); 83 | 84 | var str = redisHelper.GetRedisOperation().StringGet("abcabcabc"); 85 | Console.WriteLine("查询key:abcabcabc,value:" + str); 86 | 87 | redisHelper.GetRedisOperation().KeyDelete("abcabcabc"); 88 | Console.WriteLine("移除key:abcabcabc"); 89 | #endregion 90 | 91 | #region hashset 92 | Console.ReadLine(); 93 | Console.WriteLine("hashset get/set test"); 94 | var testModel = new DemoModel() 95 | { 96 | ID = Guid.NewGuid().ToString("N"), 97 | Age = 18, 98 | Name = "Kitty", 99 | Created = DateTime.Now 100 | }; 101 | 102 | redisHelper.GetRedisOperation().HashSet(testModel.Name, testModel.ID, testModel); 103 | Console.WriteLine(string.Format("写入hashid:{0},key:{1}", testModel.Name, testModel.ID)); 104 | 105 | testModel = redisHelper.GetRedisOperation().HashGet(testModel.Name, testModel.ID); 106 | Console.WriteLine(string.Format("查询hashid:{0},key:{1}", testModel.Name, testModel.ID)); 107 | 108 | redisHelper.GetRedisOperation().HashDelete(testModel.Name, testModel.ID); 109 | Console.WriteLine("移除hash"); 110 | #endregion 111 | 112 | #region 队列 113 | Console.ReadLine(); 114 | Console.WriteLine("list test"); 115 | 116 | redisHelper.GetRedisOperation().Enqueue("list", "listvalue"); 117 | Console.WriteLine("入队:list,value:listvalue"); 118 | 119 | Console.WriteLine("list.coumt:" + redisHelper.GetRedisOperation().QueueCount("list")); 120 | 121 | Console.WriteLine(string.Format("出队:list,value:{0}", redisHelper.GetRedisOperation().Dnqueue("list"))); 122 | Console.WriteLine("list.coumt:" + redisHelper.GetRedisOperation().QueueCount("list")); 123 | #endregion 124 | 125 | #region sortedset 126 | Console.ReadLine(); 127 | Console.WriteLine("sortedset test"); 128 | Console.WriteLine(string.Format("sortedset add :{0}", redisHelper.GetRedisOperation().SortedSetAdd("sortedset", "sortedset", 0))); 129 | var list = redisHelper.GetRedisOperation().GetSortedSetRangeByRankWithSocres("sortedset", 0, 10000, 1, 9999, true); 130 | Console.WriteLine(string.Format("sortedset getlist :{0}", list)); 131 | Console.WriteLine(string.Format("sortedset remove :{0}", redisHelper.GetRedisOperation().RemoveItemFromSortedSet("sortedset", "sortedset"))); 132 | #endregion 133 | 134 | #region pub/sub 135 | Console.ReadLine(); 136 | Console.WriteLine("sub/pub test"); 137 | 138 | Console.WriteLine("订阅频道:happy"); 139 | 140 | redisHelper.GetRedisOperation().SubscribeWithChannel("happy", (x, y) => 141 | { 142 | Console.WriteLine(string.Format("订阅者收到消息;频道:{0},消息:{1}", x, y)); 143 | }); 144 | 145 | Console.WriteLine("发布频道happy 10 条测试消息"); 146 | for (int i = 1; i <= 10; i++) 147 | { 148 | redisHelper.GetRedisOperation().Publish("happy", "this is a test message" + i); 149 | Thread.Sleep(400); 150 | } 151 | #endregion 152 | Console.ReadLine(); 153 | redisHelper.GetRedisOperation().Unsubscribe("happy"); 154 | 155 | 156 | #endregion 157 | } 158 | else if (c.ToUpper() == "C") 159 | { 160 | #region cluster 161 | Console.WriteLine("Wenli.Drive.Redis test 进入集群模式---------------------"); 162 | 163 | var redisHelper = RedisHelperBuilder.Build(clusterConfig); 164 | 165 | #region string 166 | Console.ReadLine(); 167 | Console.WriteLine("string get/set test"); 168 | 169 | redisHelper.GetRedisOperation().StringSet("abcabcabc", "123123"); 170 | Console.WriteLine("写入key:abcabcabc,value:123123"); 171 | 172 | var str = redisHelper.GetRedisOperation().StringGet("abcabcabc"); 173 | Console.WriteLine("查询key:abcabcabc,value:" + str); 174 | 175 | redisHelper.GetRedisOperation().KeyDelete("abcabcabc"); 176 | Console.WriteLine("移除key:abcabcabc"); 177 | #endregion 178 | 179 | #region hashset 180 | Console.ReadLine(); 181 | Console.WriteLine("hashset get/set test"); 182 | var testModel = new DemoModel() 183 | { 184 | ID = Guid.NewGuid().ToString("N"), 185 | Age = 18, 186 | Name = "Kitty", 187 | Created = DateTime.Now 188 | }; 189 | 190 | redisHelper.GetRedisOperation().HashSet(testModel.Name, testModel.ID, testModel); 191 | Console.WriteLine(string.Format("写入hashid:{0},key:{1}", testModel.Name, testModel.ID)); 192 | 193 | testModel = redisHelper.GetRedisOperation().HashGet(testModel.Name, testModel.ID); 194 | Console.WriteLine(string.Format("查询hashid:{0},key:{1}", testModel.Name, testModel.ID)); 195 | 196 | redisHelper.GetRedisOperation().HashDelete(testModel.Name, testModel.ID); 197 | Console.WriteLine("移除hash"); 198 | #endregion 199 | 200 | #region 队列 201 | Console.ReadLine(); 202 | Console.WriteLine("list test"); 203 | 204 | redisHelper.GetRedisOperation().Enqueue("list", "listvalue"); 205 | Console.WriteLine("入队:list,value:listvalue"); 206 | 207 | Console.WriteLine("list.coumt:" + redisHelper.GetRedisOperation().QueueCount("list")); 208 | 209 | Console.WriteLine(string.Format("出队:list,value:{0}", redisHelper.GetRedisOperation().Dnqueue("list"))); 210 | Console.WriteLine("list.coumt:" + redisHelper.GetRedisOperation().QueueCount("list")); 211 | #endregion 212 | 213 | 214 | #region pub/sub 215 | Console.ReadLine(); 216 | Console.WriteLine("sub/pub test"); 217 | 218 | Console.WriteLine("订阅频道:happy"); 219 | 220 | redisHelper.GetRedisOperation().SubscribeWithChannel("happy", (x, y) => 221 | { 222 | Console.WriteLine(string.Format("订阅者收到消息;频道:{0},消息:{1}", x, y)); 223 | }); 224 | 225 | Console.WriteLine("发布频道happy 10 条测试消息"); 226 | for (int i = 1; i <= 10; i++) 227 | { 228 | redisHelper.GetRedisOperation().Publish("happy", "this is a test message" + i); 229 | Thread.Sleep(400); 230 | } 231 | #endregion 232 | 233 | Console.ReadLine(); 234 | 235 | redisHelper.GetRedisOperation().Unsubscribe("happy"); 236 | #endregion 237 | } 238 | else if (c.ToUpper() == "M") 239 | { 240 | #region default redis 241 | Console.WriteLine("Wenli.Drive.Redis test 进入连续测试模式---------------------"); 242 | 243 | string value = "123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式123123进入连续测试模式"; 244 | 245 | var td1 = new Thread(new ThreadStart(() => 246 | { 247 | var redisHelper = RedisHelperBuilder.Build(sentinelConfig); 248 | 249 | Parallel.For(0, 100000, countIndex => 250 | { 251 | #region string 252 | Console.WriteLine("string get/set test"); 253 | redisHelper.GetRedisOperation().StringSet(countIndex.ToString(), value); 254 | Console.WriteLine("写入key:abcabcabc,value:123123"); 255 | 256 | var str = redisHelper.GetRedisOperation().StringGet(countIndex.ToString()); 257 | Console.WriteLine("查询key:abcabcabc,value:" + str); 258 | 259 | redisHelper.GetRedisOperation().KeyDelete(countIndex.ToString()); 260 | Console.WriteLine("移除key:abcabcabc"); 261 | #endregion 262 | 263 | #region hashset 264 | Console.WriteLine("hashset get/set test"); 265 | var testModel = new DemoModel() 266 | { 267 | ID = Guid.NewGuid().ToString("N"), 268 | Age = 18, 269 | Name = "Kitty", 270 | Created = DateTime.Now 271 | }; 272 | 273 | redisHelper.GetRedisOperation().HashSet(testModel.Name, testModel.ID, testModel); 274 | Console.WriteLine(string.Format("写入hashid:{0},key:{1}", testModel.Name, testModel.ID)); 275 | 276 | testModel = redisHelper.GetRedisOperation().HashGet(testModel.Name, testModel.ID); 277 | Console.WriteLine(string.Format("查询hashid:{0},key:{1}", testModel.Name, testModel.ID)); 278 | 279 | redisHelper.GetRedisOperation().HashDelete(testModel.Name, testModel.ID); 280 | Console.WriteLine("移除hash"); 281 | #endregion 282 | 283 | }); 284 | 285 | })); 286 | var td2 = new Thread(new ThreadStart(() => 287 | { 288 | Parallel.For(0, 100000, countIndex => 289 | { 290 | var redisHelper = RedisHelperBuilder.Build(sentinelConfig); 291 | 292 | #region string 293 | Console.WriteLine("string get/set test"); 294 | redisHelper.GetRedisOperation().StringSet(countIndex.ToString(), value); 295 | Console.WriteLine("写入key:abcabcabc,value:123123"); 296 | 297 | var str = redisHelper.GetRedisOperation().StringGet(countIndex.ToString()); 298 | Console.WriteLine("查询key:abcabcabc,value:" + str); 299 | 300 | redisHelper.GetRedisOperation().KeyDelete(countIndex.ToString()); 301 | Console.WriteLine("移除key:abcabcabc"); 302 | #endregion 303 | 304 | #region hashset 305 | Console.WriteLine("hashset get/set test"); 306 | var testModel = new DemoModel() 307 | { 308 | ID = Guid.NewGuid().ToString("N"), 309 | Age = 18, 310 | Name = "Kitty", 311 | Created = DateTime.Now 312 | }; 313 | 314 | redisHelper.GetRedisOperation().HashSet(testModel.Name, testModel.ID, testModel); 315 | Console.WriteLine(string.Format("写入hashid:{0},key:{1}", testModel.Name, testModel.ID)); 316 | 317 | testModel = redisHelper.GetRedisOperation().HashGet(testModel.Name, testModel.ID); 318 | Console.WriteLine(string.Format("查询hashid:{0},key:{1}", testModel.Name, testModel.ID)); 319 | 320 | redisHelper.GetRedisOperation().HashDelete(testModel.Name, testModel.ID); 321 | Console.WriteLine("移除hash"); 322 | #endregion 323 | 324 | }); 325 | })); 326 | 327 | td1.Start(); 328 | td2.Start(); 329 | 330 | while (td1.IsAlive || td2.IsAlive) 331 | { 332 | Thread.Sleep(50); 333 | } 334 | 335 | Console.WriteLine("Wenli.Drive.Redis test 任务已完成!---------------------"); 336 | #endregion 337 | } 338 | else if (c.ToLower() == "l") 339 | { 340 | var redisHelper = RedisHelperBuilder.Build(redisConfig); 341 | 342 | Stopwatch sw = new Stopwatch(); 343 | 344 | string key = "lock_test"; 345 | 346 | int total = 100000; 347 | 348 | sw.Start(); 349 | 350 | Parallel.For(0, 100000, i => 351 | { 352 | total--; 353 | }); 354 | 355 | Console.WriteLine(string.Format("未加锁 total:{0} time:{1}", total, sw.ElapsedMilliseconds)); 356 | sw.Reset(); 357 | Console.WriteLine("回车锁测试"); 358 | Console.ReadLine(); 359 | sw.Restart(); 360 | 361 | total = 100000; 362 | 363 | int f = 0; 364 | 365 | int val = 1; 366 | 367 | Parallel.For(0, total, i => 368 | { 369 | Stopwatch sw1 = new Stopwatch(); 370 | 371 | sw1.Start(); 372 | 373 | if (redisHelper.GetRedisOperation().Lock(key, 30000, 10)) 374 | { 375 | 376 | if (!int.TryParse(redisHelper.GetRedisOperation().StringGet(key), out val)) 377 | { 378 | val = 1; 379 | } 380 | else 381 | { 382 | val++; 383 | } 384 | 385 | redisHelper.GetRedisOperation().StringSet(key, val.ToString()); 386 | 387 | redisHelper.GetRedisOperation().UnLock(key); 388 | } 389 | else 390 | { 391 | Interlocked.Add(ref f, 1); 392 | Console.Write("f:{0}", f); 393 | } 394 | 395 | sw1.Stop(); 396 | }); 397 | 398 | Console.WriteLine(string.Format("已加锁 total:{0} time:{1} f:{2} StringGet:{3}", total, sw.ElapsedMilliseconds, f, redisHelper.GetRedisOperation().StringGet(key))); 399 | sw.Reset(); 400 | Console.ReadLine(); 401 | redisHelper.GetRedisOperation().KeyDelete(key); 402 | 403 | 404 | 405 | } 406 | else 407 | { 408 | #region default redis 409 | Console.WriteLine("Wenli.Drive.Redis test 进入单实例模式---------------------"); 410 | 411 | var redisHelper = RedisHelperBuilder.Build(redisConfig); 412 | 413 | #region string 414 | Console.ReadLine(); 415 | Console.WriteLine("string get/set test"); 416 | 417 | redisHelper.GetRedisOperation(12).StringSet("abcabcabc", "123123"); 418 | Console.WriteLine("写入key:abcabcabc,value:123123"); 419 | 420 | var str = redisHelper.GetRedisOperation(12).StringGet("abcabcabc"); 421 | Console.WriteLine("查询key:abcabcabc,value:" + str); 422 | 423 | redisHelper.GetRedisOperation(12).KeyDelete("abcabcabc"); 424 | Console.WriteLine("移除key:abcabcabc"); 425 | #endregion 426 | 427 | #region hashset 428 | Console.ReadLine(); 429 | Console.WriteLine("hashset get/set test"); 430 | var testModel = new DemoModel() 431 | { 432 | ID = Guid.NewGuid().ToString("N"), 433 | Age = 18, 434 | Name = "Kitty", 435 | Created = DateTime.Now 436 | }; 437 | 438 | redisHelper.GetRedisOperation().HashSet(testModel.Name, testModel.ID, testModel); 439 | Console.WriteLine(string.Format("写入hashid:{0},key:{1}", testModel.Name, testModel.ID)); 440 | 441 | testModel = redisHelper.GetRedisOperation().HashGet(testModel.Name, testModel.ID); 442 | Console.WriteLine(string.Format("查询hashid:{0},key:{1}", testModel.Name, testModel.ID)); 443 | 444 | redisHelper.GetRedisOperation().HashGetAll(testModel.Name, 1, 1); 445 | 446 | redisHelper.GetRedisOperation().HashDelete(testModel.Name, testModel.ID); 447 | Console.WriteLine("移除hash"); 448 | #endregion 449 | 450 | #region 队列 451 | Console.ReadLine(); 452 | Console.WriteLine("list test"); 453 | 454 | redisHelper.GetRedisOperation().Enqueue("list", "listvalue"); 455 | Console.WriteLine("入队:list,value:listvalue"); 456 | 457 | Console.WriteLine("list.coumt:" + redisHelper.GetRedisOperation().QueueCount("list")); 458 | 459 | Console.WriteLine(string.Format("出队:list,value:{0}", redisHelper.GetRedisOperation().Dnqueue("list"))); 460 | Console.WriteLine("list.coumt:" + redisHelper.GetRedisOperation().QueueCount("list")); 461 | #endregion 462 | 463 | #region pub/sub 464 | Console.ReadLine(); 465 | Console.WriteLine("sub/pub test"); 466 | 467 | Console.WriteLine("订阅频道:happy"); 468 | 469 | redisHelper.GetRedisOperation().SubscribeWithChannel("happy", (x, y) => 470 | { 471 | Console.WriteLine(string.Format("订阅者收到消息;频道:{0},消息:{1}", x, y)); 472 | }); 473 | 474 | Console.WriteLine("发布频道happy 10 条测试消息"); 475 | for (int i = 1; i <= 10; i++) 476 | { 477 | redisHelper.GetRedisOperation().Publish("happy", "this is a test message" + i); 478 | Thread.Sleep(400); 479 | } 480 | #endregion 481 | 482 | Console.ReadLine(); 483 | 484 | redisHelper.GetRedisOperation().Unsubscribe("happy"); 485 | 486 | #endregion 487 | } 488 | } 489 | 490 | Console.ReadLine(); 491 | } 492 | } 493 | } 494 | -------------------------------------------------------------------------------- /DriveTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("DriveTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DriveTest")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("7e8b59d4-683f-4393-864e-c315a79d5a97")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /DriveTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /RedisDrive.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.902 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wenli.Drive.Redis", "Wenli.Drive.Redis\Wenli.Drive.Redis.csproj", "{224E5838-57C6-41E8-B2DE-AF1BEF7F6EC5}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DriveTest", "DriveTest\DriveTest.csproj", "{7E8B59D4-683F-4393-864E-C315A79D5A97}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {224E5838-57C6-41E8-B2DE-AF1BEF7F6EC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {224E5838-57C6-41E8-B2DE-AF1BEF7F6EC5}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {224E5838-57C6-41E8-B2DE-AF1BEF7F6EC5}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {224E5838-57C6-41E8-B2DE-AF1BEF7F6EC5}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {7E8B59D4-683F-4393-864E-C315A79D5A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {7E8B59D4-683F-4393-864E-C315A79D5A97}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {7E8B59D4-683F-4393-864E-C315A79D5A97}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {7E8B59D4-683F-4393-864E-C315A79D5A97}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {D2EC9F5B-3DB0-4E76-BD68-3CF8A589D914} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /VisualStudio.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | 33 | # Visual Studio 2015/2017 cache/options directory 34 | .vs/ 35 | # Uncomment if you have tasks that create the project's static files in wwwroot 36 | #wwwroot/ 37 | 38 | # Visual Studio 2017 auto generated files 39 | Generated\ Files/ 40 | 41 | # MSTest test Results 42 | [Tt]est[Rr]esult*/ 43 | [Bb]uild[Ll]og.* 44 | 45 | # NUnit 46 | *.VisualState.xml 47 | TestResult.xml 48 | nunit-*.xml 49 | 50 | # Build Results of an ATL Project 51 | [Dd]ebugPS/ 52 | [Rr]eleasePS/ 53 | dlldata.c 54 | 55 | # Benchmark Results 56 | BenchmarkDotNet.Artifacts/ 57 | 58 | # .NET Core 59 | project.lock.json 60 | project.fragment.lock.json 61 | artifacts/ 62 | 63 | # StyleCop 64 | StyleCopReport.xml 65 | 66 | # Files built by Visual Studio 67 | *_i.c 68 | *_p.c 69 | *_h.h 70 | *.ilk 71 | *.meta 72 | *.obj 73 | *.iobj 74 | *.pch 75 | *.pdb 76 | *.ipdb 77 | *.pgc 78 | *.pgd 79 | *.rsp 80 | *.sbr 81 | *.tlb 82 | *.tli 83 | *.tlh 84 | *.tmp 85 | *.tmp_proj 86 | *_wpftmp.csproj 87 | *.log 88 | *.vspscc 89 | *.vssscc 90 | .builds 91 | *.pidb 92 | *.svclog 93 | *.scc 94 | 95 | # Chutzpah Test files 96 | _Chutzpah* 97 | 98 | # Visual C++ cache files 99 | ipch/ 100 | *.aps 101 | *.ncb 102 | *.opendb 103 | *.opensdf 104 | *.sdf 105 | *.cachefile 106 | *.VC.db 107 | *.VC.VC.opendb 108 | 109 | # Visual Studio profiler 110 | *.psess 111 | *.vsp 112 | *.vspx 113 | *.sap 114 | 115 | # Visual Studio Trace Files 116 | *.e2e 117 | 118 | # TFS 2012 Local Workspace 119 | $tf/ 120 | 121 | # Guidance Automation Toolkit 122 | *.gpState 123 | 124 | # ReSharper is a .NET coding add-in 125 | _ReSharper*/ 126 | *.[Rr]e[Ss]harper 127 | *.DotSettings.user 128 | 129 | # JustCode is a .NET coding add-in 130 | .JustCode 131 | 132 | # TeamCity is a build add-in 133 | _TeamCity* 134 | 135 | # DotCover is a Code Coverage Tool 136 | *.dotCover 137 | 138 | # AxoCover is a Code Coverage Tool 139 | .axoCover/* 140 | !.axoCover/settings.json 141 | 142 | # Visual Studio code coverage results 143 | *.coverage 144 | *.coveragexml 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # Note: Comment the next line if you want to checkin your web deploy settings, 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 183 | # checkin your Azure Web App publish settings, but sensitive information contained 184 | # in these scripts will be unencrypted 185 | PublishScripts/ 186 | 187 | # NuGet Packages 188 | *.nupkg 189 | # NuGet Symbol Packages 190 | *.snupkg 191 | # The packages folder can be ignored because of Package Restore 192 | **/[Pp]ackages/* 193 | # except build/, which is used as an MSBuild target. 194 | !**/[Pp]ackages/build/ 195 | # Uncomment if necessary however generally it will be regenerated when needed 196 | #!**/[Pp]ackages/repositories.config 197 | # NuGet v3's project.json files produces more ignorable files 198 | *.nuget.props 199 | *.nuget.targets 200 | 201 | # Microsoft Azure Build Output 202 | csx/ 203 | *.build.csdef 204 | 205 | # Microsoft Azure Emulator 206 | ecf/ 207 | rcf/ 208 | 209 | # Windows Store app package directories and files 210 | AppPackages/ 211 | BundleArtifacts/ 212 | Package.StoreAssociation.xml 213 | _pkginfo.txt 214 | *.appx 215 | *.appxbundle 216 | *.appxupload 217 | 218 | # Visual Studio cache files 219 | # files ending in .cache can be ignored 220 | *.[Cc]ache 221 | # but keep track of directories ending in .cache 222 | !?*.[Cc]ache/ 223 | 224 | # Others 225 | ClientBin/ 226 | ~$* 227 | *~ 228 | *.dbmdl 229 | *.dbproj.schemaview 230 | *.jfm 231 | *.pfx 232 | *.publishsettings 233 | orleans.codegen.cs 234 | 235 | # Including strong name files can present a security risk 236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 237 | #*.snk 238 | 239 | # Since there are multiple workflows, uncomment next line to ignore bower_components 240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 241 | #bower_components/ 242 | 243 | # RIA/Silverlight projects 244 | Generated_Code/ 245 | 246 | # Backup & report files from converting an old project file 247 | # to a newer Visual Studio version. Backup files are not needed, 248 | # because we have git ;-) 249 | _UpgradeReport_Files/ 250 | Backup*/ 251 | UpgradeLog*.XML 252 | UpgradeLog*.htm 253 | ServiceFabricBackup/ 254 | *.rptproj.bak 255 | 256 | # SQL Server files 257 | *.mdf 258 | *.ldf 259 | *.ndf 260 | 261 | # Business Intelligence projects 262 | *.rdl.data 263 | *.bim.layout 264 | *.bim_*.settings 265 | *.rptproj.rsuser 266 | *- [Bb]ackup.rdl 267 | *- [Bb]ackup ([0-9]).rdl 268 | *- [Bb]ackup ([0-9][0-9]).rdl 269 | 270 | # Microsoft Fakes 271 | FakesAssemblies/ 272 | 273 | # GhostDoc plugin setting file 274 | *.GhostDoc.xml 275 | 276 | # Node.js Tools for Visual Studio 277 | .ntvs_analysis.dat 278 | node_modules/ 279 | 280 | # Visual Studio 6 build log 281 | *.plg 282 | 283 | # Visual Studio 6 workspace options file 284 | *.opt 285 | 286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 287 | *.vbw 288 | 289 | # Visual Studio LightSwitch build output 290 | **/*.HTMLClient/GeneratedArtifacts 291 | **/*.DesktopClient/GeneratedArtifacts 292 | **/*.DesktopClient/ModelManifest.xml 293 | **/*.Server/GeneratedArtifacts 294 | **/*.Server/ModelManifest.xml 295 | _Pvt_Extensions 296 | 297 | # Paket dependency manager 298 | .paket/paket.exe 299 | paket-files/ 300 | 301 | # FAKE - F# Make 302 | .fake/ 303 | 304 | # CodeRush personal settings 305 | .cr/personal 306 | 307 | # Python Tools for Visual Studio (PTVS) 308 | __pycache__/ 309 | *.pyc 310 | 311 | # Cake - Uncomment if you are using it 312 | # tools/** 313 | # !tools/packages.config 314 | 315 | # Tabs Studio 316 | *.tss 317 | 318 | # Telerik's JustMock configuration file 319 | *.jmconfig 320 | 321 | # BizTalk build output 322 | *.btp.cs 323 | *.btm.cs 324 | *.odx.cs 325 | *.xsd.cs 326 | 327 | # OpenCover UI analysis results 328 | OpenCover/ 329 | 330 | # Azure Stream Analytics local run output 331 | ASALocalRun/ 332 | 333 | # MSBuild Binary and Structured Log 334 | *.binlog 335 | 336 | # NVidia Nsight GPU debugger configuration file 337 | *.nvuser 338 | 339 | # MFractors (Xamarin productivity tool) working folder 340 | .mfractor/ 341 | 342 | # Local History for Visual Studio 343 | .localhistory/ 344 | 345 | # BeatPulse healthcheck temp database 346 | healthchecksdb 347 | 348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 349 | MigrationBackup/ -------------------------------------------------------------------------------- /Wenli.Drive.Redis/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/RedisBatcher.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Im.Data.Redis.Core 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Im.Data.Redis.Core 6 | *类 名 称:RedisBatcher 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/9/17 14:42:04 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/9/17 14:42:04 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using StackExchange.Redis; 19 | using System; 20 | 21 | namespace Wenli.Drive.Redis 22 | { 23 | /// 24 | /// RedisBatcher 25 | /// 26 | public class RedisBatcher : IDisposable 27 | { 28 | IBatch _batch; 29 | 30 | /// 31 | /// RedisBatcher 32 | /// 33 | /// 34 | internal RedisBatcher(IDatabase dataBase) 35 | { 36 | _batch = dataBase.CreateBatch(); 37 | } 38 | 39 | /// 40 | /// 批量 41 | /// 42 | public IBatch Batch 43 | { 44 | get => _batch; 45 | } 46 | 47 | /// 48 | /// Execute 49 | /// 50 | public void Execute() 51 | { 52 | _batch.Execute(); 53 | } 54 | 55 | /// 56 | /// Dispose 57 | /// 58 | public void Dispose() 59 | { 60 | Execute(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/RedisConnection.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Im.Data.Redis.Core 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis 6 | *类 名 称:RedisConnection 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/7/6 16:07:59 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/7/6 16:07:59 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using StackExchange.Redis; 19 | 20 | namespace Wenli.Drive.Redis 21 | { 22 | /// 23 | /// 自定义RedisConnection 24 | /// 25 | public class RedisConnection 26 | { 27 | /// 28 | /// ConnectionMultiplexer 29 | /// 30 | public ConnectionMultiplexer Connection { get; set; } 31 | 32 | /// 33 | /// 修复中 34 | /// 35 | public bool Repairing { get; set; } = false; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisConnection.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2015-2016 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:29c08bfd-59ed-42b7-b2f9-b344225840a3 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:$projectname$ 10 | * 命名空间:Wenli.Drive.Redis.Core 11 | * 类名称:SERedisConnection 12 | * 创建时间:2016/11/8 20:31:02 13 | * 创建人:wenli 14 | * 创建说明: 15 | *****************************************************************************************************/ 16 | 17 | using StackExchange.Redis; 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Linq; 21 | using Wenli.Drive.Redis.Extends; 22 | 23 | namespace Wenli.Drive.Redis.Core 24 | { 25 | /// 26 | /// redis 连接 27 | /// 28 | public class SERedisConnection 29 | { 30 | private readonly int _dbIndex; 31 | 32 | private readonly string _sectionName; 33 | 34 | /// 35 | /// RedisConnection 36 | /// 37 | public RedisConnection RedisConnection { get; private set; } 38 | 39 | /// 40 | /// 连接字符串 41 | /// 42 | public string Configuration { get; set; } 43 | 44 | /// 45 | /// redis 连接 46 | /// 47 | /// 48 | /// 49 | public SERedisConnection(string sectionName, int dbIndex) 50 | { 51 | _sectionName = sectionName; 52 | _dbIndex = dbIndex; 53 | RedisConnection = SERedisConnectionCache.Get(_sectionName); 54 | if (RedisConnection == null || RedisConnection.Connection == null || !RedisConnection.Connection.IsConnected || RedisConnection.Repairing) throw new Exception("Redis 连接未初始化或正在修复中,无法执行此操作,sectionName:" + sectionName); 55 | Configuration = RedisConnection.Connection.Configuration; 56 | } 57 | 58 | /// 59 | /// 获取db 60 | /// 61 | /// 62 | public IDatabase GetDatabase() 63 | { 64 | return RedisConnection.Connection.GetDatabase(_dbIndex); 65 | } 66 | 67 | /// 68 | /// 创建批量处理 69 | /// 70 | /// 71 | public RedisBatcher CreateBatcher() 72 | { 73 | return new RedisBatcher(RedisConnection.Connection.GetDatabase(_dbIndex)); 74 | } 75 | 76 | /// 77 | /// 获取订阅 78 | /// 79 | /// 80 | public ISubscriber GetSubscriber() 81 | { 82 | return RedisConnection.Connection.GetSubscriber(); 83 | } 84 | 85 | /// 86 | /// 尝试修复连接 87 | /// 88 | internal void TryRepairConnection() 89 | { 90 | RedisConnection.Repairing = true; 91 | 92 | RedisConnection = new SERedisConnectionDefender(_sectionName, Configuration).FreeAndConnect(RedisConnection); 93 | 94 | RedisConnection.Repairing = false; 95 | } 96 | 97 | /// 98 | /// 获取某服务器上全部keys 99 | /// 100 | /// 101 | /// 102 | [Obsolete("此方法只用于兼容老数据,且本方法只能查询db0,建议使用sortedset来保存keys")] 103 | public List Keys(string patten = "*") 104 | { 105 | var firstConfigEndPoint = RedisConnection.Connection.GetEndPoints()[0]; 106 | var anyServer = RedisConnection.Connection.GetServer(firstConfigEndPoint); 107 | 108 | var lastResult = new List(); 109 | if (anyServer.ServerType == ServerType.Cluster) 110 | { 111 | // 这个操作比较费时, 使用从来处理 112 | var allMasterNodes = anyServer.ClusterConfiguration.Nodes.Where(n => !n.IsSlave); 113 | 114 | foreach (var masterNode in allMasterNodes) 115 | { 116 | var runCommandServer = masterNode; 117 | if (masterNode.Children.Count > 0) 118 | { 119 | runCommandServer = masterNode.Children.First(); 120 | } 121 | var resultsInOneServer = RedisConnection.Connection.GetServer(runCommandServer.EndPoint).Keys(pattern: patten).Select(b => b.ToString()).ToList(); 122 | lastResult.AddRange(resultsInOneServer); 123 | } 124 | 125 | return lastResult; 126 | } 127 | else 128 | { 129 | return anyServer.Keys(pattern: patten).Select(b => b.ToString()).ToList(); 130 | } 131 | } 132 | 133 | /// 134 | /// 获取某服务器上全部keys(此方法目前只支持cluster模式) 135 | /// 136 | /// 137 | /// 138 | /// 139 | /// 140 | public void Keys(Action> callback, string patten = "*", int size = 10000) 141 | { 142 | if (callback == null) return; 143 | var firstConfigEndPoint = RedisConnection.Connection.GetEndPoints()[0]; 144 | var anyServer = RedisConnection.Connection.GetServer(firstConfigEndPoint); 145 | 146 | if (anyServer.ServerType == ServerType.Cluster) 147 | { 148 | // 这个操作比较费时, 使用从来处理 149 | var allMasterNodes = anyServer.ClusterConfiguration.Nodes.Where(n => !n.IsSlave); 150 | 151 | foreach (var masterNode in allMasterNodes) 152 | { 153 | var runCommandServer = masterNode; 154 | //LogCom.WriteInfoLog($"master node :NodeId [{masterNode.NodeId}] : EndPoint [{masterNode.EndPoint.ToString()}] : ChildrenCount[{masterNode.Children.Count}]"); 155 | 156 | // 驱动自带的 preferslave 不管用,还是运行在了master上,所以自己手动获取slave 157 | var allAliveSlaves = masterNode.Children.Where(r => r.IsConnected).ToList(); 158 | if (allAliveSlaves.Count > 0) 159 | { 160 | runCommandServer = allAliveSlaves.First(); 161 | } 162 | //LogCom.WriteInfoLog($"run keys command on Server :NodeId [{runCommandServer.NodeId}] : EndPoint[{runCommandServer.EndPoint.ToString()}]"); 163 | 164 | var resultsInOneServer = RedisConnection.Connection.GetServer(runCommandServer.EndPoint).Keys(pattern: patten, pageSize: size).Select(b => b.ToString()).ToList(); 165 | callback(resultsInOneServer); 166 | } 167 | } 168 | else 169 | { 170 | callback(anyServer.Keys(pattern: patten, pageSize: size).Select(b => b.ToString()).ToList()); 171 | //yield return anyServer.Keys(pattern: patten).Select(b => b.ToString()).ToList(); 172 | } 173 | } 174 | 175 | /// 176 | /// 获取指定keys 177 | /// 178 | /// 179 | /// 180 | /// 181 | /// 182 | [Obsolete("此方法只用于兼容老数据,且本方法只能查询db0,建议使用sortedset来保存keys")] 183 | public List Keys(int dbIndex = -1, string patten = "*", int count = 20) 184 | { 185 | var result = new List(); 186 | 187 | var endpoint = RedisConnection.Connection.GetEndPoints()[0]; 188 | 189 | var rs = RedisConnection.Connection.GetServer(endpoint); 190 | 191 | var data = rs.Keys(dbIndex, patten, count, CommandFlags.PreferSlave); 192 | 193 | if (data != null && data.Any()) 194 | { 195 | result.AddRange(data.ToList().ConvertTo()); 196 | } 197 | 198 | return result = result.Take(count).ToList(); 199 | } 200 | 201 | 202 | } 203 | } -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisConnectionCache.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis.Core 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis.Core 6 | *类 名 称:SERedisConnectionCache 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/30 9:53:34 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/30 9:53:34 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using StackExchange.Redis; 19 | using System.Collections.Concurrent; 20 | 21 | namespace Wenli.Drive.Redis.Core 22 | { 23 | /// 24 | /// SERedisConnectionCache 25 | /// 26 | internal static class SERedisConnectionCache 27 | { 28 | static ConcurrentDictionary _cache = null; 29 | 30 | 31 | /// 32 | /// SERedisConnectionCache 33 | /// 34 | static SERedisConnectionCache() 35 | { 36 | _cache = new ConcurrentDictionary(); 37 | } 38 | 39 | /// 40 | /// 初始化连接 41 | /// 42 | /// 43 | /// 44 | public static void Init(string sectionName, string connectionStr) 45 | { 46 | var old = Get(sectionName); 47 | 48 | if (old == null) 49 | 50 | new SERedisConnectionDefender(sectionName, connectionStr).FreeAndConnect(); 51 | } 52 | 53 | 54 | #region by section 55 | 56 | 57 | /// 58 | /// 添加或更新连接 59 | /// 60 | /// 61 | /// 62 | public static void Set(string sectionName, RedisConnection cnn) 63 | { 64 | _cache.AddOrUpdate(sectionName, cnn, (k, v) => cnn); 65 | } 66 | 67 | 68 | 69 | /// 70 | /// 是否存在 71 | /// 72 | /// 73 | /// 74 | public static bool Exists(string sectionName) 75 | { 76 | return _cache.ContainsKey(sectionName); 77 | } 78 | 79 | /// 80 | /// 获取连接 81 | /// 82 | /// 83 | /// 84 | public static RedisConnection Get(string sectionName) 85 | { 86 | if (_cache.TryGetValue(sectionName, out RedisConnection cnn)) 87 | { 88 | return cnn; 89 | } 90 | return null; 91 | } 92 | 93 | /// 94 | /// 移除连接 95 | /// 96 | /// 97 | public static void Remove(string sectionName) 98 | { 99 | _cache.TryRemove(sectionName, out RedisConnection cnn); 100 | } 101 | 102 | #endregion 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisConnectionDefender.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis.Core 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis.Core 6 | *类 名 称:SERedisConnectionDefender 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/30 9:53:54 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/30 9:53:54 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using StackExchange.Redis; 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Threading; 22 | 23 | namespace Wenli.Drive.Redis.Core 24 | { 25 | /// 26 | /// 连接维护类 27 | /// 28 | internal class SERedisConnectionDefender 29 | { 30 | string _sectionName = string.Empty; 31 | 32 | string _connectStr = string.Empty; 33 | 34 | /// 35 | /// 连接维护类 36 | /// 37 | /// 38 | /// 39 | public SERedisConnectionDefender(string sectionName, string connectStr) 40 | { 41 | _sectionName = sectionName; 42 | 43 | _connectStr = connectStr; 44 | } 45 | 46 | /// 47 | /// 建立连接并释放旧连接 48 | /// 49 | /// 50 | /// 51 | internal RedisConnection FreeAndConnect(RedisConnection old = null) 52 | { 53 | SERedisConnectionDefenderEx.AutoResetEvent.WaitOne(); 54 | 55 | try 56 | { 57 | if (old != null && old.Connection != null && !old.Repairing) return old; 58 | 59 | #region 延迟修复 60 | 61 | if (SERedisConnectionDefenderEx.Repaired.ContainsKey(_sectionName)) 62 | { 63 | if (SERedisConnectionDefenderEx.Repaired[_sectionName].Created.AddMinutes(1) < DateTime.Now) 64 | { 65 | SERedisConnectionDefenderEx.Repaired.Remove(_sectionName); 66 | } 67 | } 68 | 69 | if (SERedisConnectionDefenderEx.Repaired.ContainsKey(_sectionName)) 70 | { 71 | return old; 72 | } 73 | 74 | #endregion 75 | 76 | //LogCom.WriteInfoLog($"{_sectionName}正在进入连接修复中", _connectStr); 77 | 78 | DisConnect(old); 79 | 80 | return Connect(old); 81 | } 82 | catch (Exception ex) 83 | { 84 | throw new Exception("SERedisConnectionDefender.FreeAndConnect 异常,connectStr:" + _connectStr, ex); 85 | } 86 | finally 87 | { 88 | SERedisConnectionDefenderEx.AutoResetEvent.Set(); 89 | } 90 | } 91 | 92 | /// 93 | /// 建立连接 94 | /// 95 | /// 96 | RedisConnection Connect(RedisConnection old = null) 97 | { 98 | try 99 | { 100 | if (old == null) 101 | { 102 | old = new RedisConnection() { Connection = ConnectionMultiplexer.Connect(_connectStr), Repairing = false }; 103 | } 104 | else 105 | { 106 | old.Connection = ConnectionMultiplexer.Connect(_connectStr); 107 | 108 | old.Repairing = false; 109 | } 110 | 111 | SERedisConnectionDefenderEx.Repaired[_sectionName] = new SectionInfo 112 | { 113 | SectionName = _sectionName, 114 | Created = DateTime.Now 115 | }; 116 | 117 | SERedisConnectionCache.Set(_sectionName, old); 118 | 119 | return old; 120 | } 121 | catch (Exception ex) 122 | { 123 | throw new Exception("SERedisConnectionDefender.Connect 异常,connectStr:" + _connectStr, ex); 124 | } 125 | } 126 | 127 | /// 128 | /// 释放连接 129 | /// 130 | /// 131 | void DisConnect(RedisConnection old = null) 132 | { 133 | if (old == null) return; 134 | 135 | SERedisConnectionCache.Remove(_sectionName); 136 | 137 | old.Connection.Close(); 138 | 139 | old.Connection.Dispose(); 140 | 141 | old.Connection = null; 142 | } 143 | } 144 | 145 | /// 146 | /// SERedisConnectionDefender的辅助类,仅限SERedisConnectionDefender.GetConnection方法中使用 147 | /// 148 | internal static class SERedisConnectionDefenderEx 149 | { 150 | internal static Dictionary Repaired { get; set; } = new Dictionary(); 151 | 152 | internal static AutoResetEvent AutoResetEvent { get; set; } = new AutoResetEvent(true); 153 | } 154 | 155 | /// 156 | /// section配置 157 | /// 158 | class SectionInfo 159 | { 160 | public string SectionName { get; set; } 161 | 162 | public DateTime Created { get; set; } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisHelper.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2016-2020 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:1cc1fa2b-c8c8-4627-bb40-dece98f2b73a 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:Wenli.Drive.Redis 10 | * 命名空间:Wenli.Drive.Redis 11 | * 创建时间:2016/12/28 9:59:30 12 | * 创建人:wenli 13 | * 创建说明: 14 | *****************************************************************************************************/ 15 | using System; 16 | using System.Collections.Concurrent; 17 | using Wenli.Drive.Redis.Interface; 18 | 19 | namespace Wenli.Drive.Redis.Core 20 | { 21 | /// 22 | /// SERedis操作处理类 23 | /// 24 | [Serializable] 25 | public class SERedisHelper : IRedisHelper, IDisposable 26 | { 27 | /// 28 | /// 哨兵监听集合 29 | /// 30 | private static readonly ConcurrentDictionary _SentinelPool = 31 | new ConcurrentDictionary(); 32 | 33 | private static object _LockObj = new object(); 34 | 35 | private int _BusyRetry = 5; 36 | 37 | private int _BusyRetryWaitMS = 200; 38 | 39 | private IRedisOperation _RedisOperation; 40 | 41 | //实例名称 42 | private string _sectionName; 43 | 44 | private static object locker = new object(); 45 | 46 | 47 | /// 48 | /// 释放 49 | /// 50 | public void Dispose() 51 | { 52 | _RedisOperation = null; 53 | } 54 | 55 | /// 56 | /// 初始化池,类似于构造方法 57 | /// 不要重复调用 58 | /// 59 | /// 60 | /// 61 | public void Init(string section) 62 | { 63 | Init(RedisConfig.GetConfig(section)); 64 | } 65 | 66 | /// 67 | /// 初始化 68 | /// 69 | /// 70 | /// 71 | /// 72 | /// 73 | /// 74 | public void Init(string sectionName, RedisConnectType type, string master, string password = "", string serviceName = "") 75 | { 76 | var redisConfig = new RedisConfig() 77 | { 78 | SectionName = sectionName, 79 | Type = type, 80 | Masters = master 81 | }; 82 | if (!string.IsNullOrEmpty(serviceName)) 83 | { 84 | redisConfig.ServiceName = serviceName; 85 | } 86 | if (!string.IsNullOrEmpty(password)) 87 | { 88 | redisConfig.Password = password; 89 | } 90 | this.Init(redisConfig); 91 | } 92 | 93 | 94 | /// 95 | /// redis操作 96 | /// 97 | /// 98 | /// 99 | public IRedisOperation GetRedisOperation(int dbIndex = -1) 100 | { 101 | return new SERedisOperation(_sectionName, dbIndex); 102 | } 103 | 104 | /// 105 | /// 初始化池,类似于构造方法 106 | /// 不要重复调用 107 | /// 108 | /// 109 | public void Init(RedisConfig redisConfig) 110 | { 111 | if (redisConfig == null) 112 | throw new Exception("传入的redisConfig实例不能空"); 113 | _sectionName = redisConfig.SectionName; 114 | _BusyRetry = redisConfig.BusyRetry; 115 | _BusyRetryWaitMS = redisConfig.BusyRetryWaitMS; 116 | if (string.IsNullOrWhiteSpace(_sectionName)) 117 | throw new Exception("redisConfig.SectionName不能为空"); 118 | 119 | lock (locker) 120 | { 121 | if (SERedisConnectionCache.Exists(_sectionName)) 122 | return; 123 | 124 | var configStr = GenerateConnectionString(redisConfig); 125 | 126 | if ((redisConfig.PoolSize < 1) || (redisConfig.PoolSize > 100)) 127 | redisConfig.PoolSize = 1; 128 | 129 | //哨兵特殊处理 130 | if (redisConfig.Type == RedisConnectType.Sentinel) 131 | { 132 | var sentinel = new SESentinelClient(_sectionName, configStr, redisConfig.PoolSize, redisConfig.Password); 133 | 134 | sentinel.OnRedisServerChanged += sentinel_OnRedisServerChanged; 135 | 136 | var operateRedisConnecitonString = sentinel.Start(); 137 | 138 | _SentinelPool.AddOrUpdate(_sectionName + "_" + redisConfig.ServiceName, sentinel, (x, y) => sentinel); 139 | 140 | SERedisConnectionCache.Init(_sectionName, operateRedisConnecitonString); 141 | } 142 | else 143 | { 144 | SERedisConnectionCache.Init(_sectionName, configStr); 145 | } 146 | } 147 | } 148 | 149 | /// 150 | /// 根据redis使用类型来生成相应的连接字符串 151 | /// 152 | /// 153 | /// 154 | private static string GenerateConnectionString(RedisConfig redisConfig) 155 | { 156 | var configStr = string.Empty; 157 | 158 | switch ((RedisConnectType)redisConfig.Type) 159 | { 160 | case RedisConnectType.Instance: 161 | if (!string.IsNullOrWhiteSpace(redisConfig.Slaves)) 162 | configStr = string.Format("{0},{1},defaultDatabase={2}", redisConfig.Masters, redisConfig.Slaves, redisConfig.DefaultDatabase); 163 | else 164 | configStr = string.Format("{0},defaultDatabase={1}", redisConfig.Masters, redisConfig.DefaultDatabase); 165 | if (!string.IsNullOrWhiteSpace(redisConfig.Password)) 166 | configStr += ",password=" + redisConfig.Password; 167 | break; 168 | case RedisConnectType.Sentinel: 169 | //哨兵 170 | configStr = string.Format("{0},defaultDatabase={1},serviceName={2}", redisConfig.Masters, redisConfig.DefaultDatabase, redisConfig.ServiceName); 171 | break; 172 | case RedisConnectType.Cluster: 173 | //集群 174 | configStr = string.Format("{0}", redisConfig.Masters); 175 | if (!string.IsNullOrWhiteSpace(redisConfig.Password)) 176 | configStr += ",password=" + redisConfig.Password; 177 | break; 178 | default: 179 | if (!string.IsNullOrWhiteSpace(redisConfig.Slaves)) 180 | configStr = redisConfig.Masters + "," + redisConfig.Slaves; 181 | else 182 | configStr = redisConfig.Masters; 183 | 184 | if (!string.IsNullOrWhiteSpace(redisConfig.Password)) 185 | configStr += ",password=" + redisConfig.Password; 186 | break; 187 | } 188 | configStr += 189 | string.Format(",allowAdmin={0},connectRetry={1},connectTimeout={2},keepAlive={3},syncTimeout={4},responseTimeout={4},abortConnect=false", redisConfig.AllowAdmin, redisConfig.ConnectRetry, redisConfig.ConnectTimeout, redisConfig.KeepAlive, redisConfig.CommandTimeout); 190 | 191 | if (!string.IsNullOrWhiteSpace(redisConfig.Extention)) 192 | { 193 | configStr += "," + redisConfig.Extention; 194 | } 195 | 196 | return configStr; 197 | } 198 | 199 | 200 | /// 201 | /// 哨兵监测事件 202 | /// 203 | /// 204 | /// 205 | private void sentinel_OnRedisServerChanged(string section, string newConnectionString) 206 | { 207 | SERedisConnectionCache.Init(_sectionName, newConnectionString); 208 | } 209 | } 210 | } -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisLock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Wenli.Drive.Redis.Interface; 4 | 5 | namespace Wenli.Drive.Redis.Core 6 | { 7 | public partial class SERedisOperation : IRedisOperation 8 | { 9 | private static readonly string _prex = "lock_"; 10 | 11 | int _timeout = 30 * 1000; 12 | 13 | string _key = string.Empty; 14 | 15 | string GetKey(string key) 16 | { 17 | return _prex + key; 18 | } 19 | 20 | /// 21 | /// 利用StringSetIfNotExists实现锁 22 | /// 23 | /// 24 | /// 过期时间(毫秒) 25 | /// 26 | /// 27 | public bool Lock(string key, int timeout = 30 * 1000, int rolling = 50) 28 | { 29 | _key = key; 30 | 31 | _timeout = timeout; 32 | 33 | var ts = TimeSpan.FromMilliseconds(_timeout); 34 | 35 | String expiresStr = DateTime.Now.Add(ts).Ticks.ToString(); 36 | 37 | while (_timeout > rolling) 38 | { 39 | if (this.StringSetIfNotExists(GetKey(_key), expiresStr, ts)) 40 | { 41 | return true; 42 | } 43 | timeout -= rolling; 44 | Thread.Sleep(rolling); 45 | } 46 | return false; 47 | } 48 | 49 | /// 50 | /// 移除lock 51 | /// 52 | /// 53 | public void UnLock(string key = "") 54 | { 55 | if (string.IsNullOrEmpty(key)) 56 | { 57 | key = _key; 58 | } 59 | 60 | this.KeyDelete(GetKey(key)); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisOperation.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2016-2020 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:1cc1fa2b-c8c8-4627-bb40-dece98f2b73a 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:Wenli.Drive.Redis 10 | * 命名空间:Wenli.Drive.Redis 11 | * 创建时间:2016/12/28 9:59:30 12 | * 创建人:wenli 13 | * 创建说明: 14 | *****************************************************************************************************/ 15 | using System; 16 | using System.Collections.Generic; 17 | using Wenli.Drive.Redis.Interface; 18 | using Wenli.Drive.Redis.Tool; 19 | 20 | namespace Wenli.Drive.Redis.Core 21 | { 22 | /// 23 | /// SERedis操作类 24 | /// 25 | public partial class SERedisOperation : IRedisOperation 26 | { 27 | /// 28 | /// 倒序排例 29 | /// 30 | private const string DefaultOrder = "descending"; 31 | 32 | private readonly int _dbIndex = -1; 33 | 34 | private readonly string _sectionName; 35 | 36 | SERedisConnection _cnn = null; 37 | 38 | /// 39 | /// SERedis操作类 40 | /// 41 | /// 42 | /// 43 | /// 44 | /// 45 | public SERedisOperation(string sectionName, int dbIndex = -1, int busyRetry = 1000, int busyRetryWaitMS = 200) 46 | { 47 | _sectionName = sectionName; 48 | _dbIndex = dbIndex; 49 | _cnn = new SERedisConnection(_sectionName, _dbIndex); 50 | } 51 | 52 | #region 操作方法重试包装 53 | 54 | private T DoWithRetry(Func func) 55 | { 56 | try 57 | { 58 | if (_cnn.RedisConnection.Repairing) throw new Exception($"SERedisOperation操作失败,sectionName:{_sectionName},_cnn:{SerializeHelper.Serialize(_cnn)}", new Exception("连接正在修复中")); 59 | 60 | return func(); 61 | } 62 | catch (Exception ex) 63 | { 64 | if (ex.Message.IndexOf("SERedisOperation操作失败") > -1) throw ex; 65 | 66 | if (!_cnn.RedisConnection.Repairing) 67 | { 68 | _cnn.TryRepairConnection(); 69 | } 70 | throw new Exception($"SERedisOperation操作失败,sectionName:{_sectionName},_cnn:{SerializeHelper.Serialize(_cnn)}", ex); 71 | } 72 | } 73 | 74 | private void DoWithRetry(Action action) 75 | { 76 | try 77 | { 78 | if (_cnn.RedisConnection.Repairing) throw new Exception($"SERedisOperation操作失败,sectionName:{_sectionName},_cnn:{SerializeHelper.Serialize(_cnn)}", new Exception("连接正在修复中")); 79 | 80 | action(); 81 | } 82 | catch (Exception ex) 83 | { 84 | if (ex.Message.IndexOf("SERedisOperation操作失败") > -1) throw ex; 85 | 86 | if (!_cnn.RedisConnection.Repairing) 87 | { 88 | _cnn.TryRepairConnection(); 89 | } 90 | throw new Exception($"SERedisOperation操作失败,sectionName:{_sectionName},_cnn:{SerializeHelper.Serialize(_cnn)}", ex); 91 | } 92 | } 93 | 94 | #endregion 95 | 96 | /// 97 | /// Ping 98 | /// 99 | /// 100 | public TimeSpan Ping() 101 | { 102 | return DoWithRetry(() => 103 | { 104 | return _cnn.GetDatabase().Ping(); 105 | }); 106 | } 107 | 108 | /// 109 | /// 创建批量处理 110 | /// 111 | /// 112 | public RedisBatcher CreateBatcher() 113 | { 114 | return _cnn.CreateBatcher(); 115 | } 116 | 117 | /// 118 | /// 获取keys 119 | /// 120 | /// 121 | /// 122 | /// 123 | /// 124 | public List Keys(int dbIndex = -1, string patten = "*", int count = 20) 125 | { 126 | return DoWithRetry(() => 127 | { 128 | return _cnn.Keys(dbIndex, patten, count); 129 | }); 130 | } 131 | 132 | } 133 | } -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisOperationForHash.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis 6 | *类 名 称:SERedisOperationForHash 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/3 15:46:25 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/3 15:46:25 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using StackExchange.Redis; 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Linq; 22 | using Wenli.Drive.Redis.Extends; 23 | using Wenli.Drive.Redis.Interface; 24 | using Wenli.Drive.Redis.Tool; 25 | 26 | namespace Wenli.Drive.Redis.Core 27 | { 28 | /// 29 | /// SERedisOperationForHash 30 | /// 31 | public partial class SERedisOperation : IRedisOperation 32 | { 33 | #region Hashes 34 | 35 | /// 36 | /// 检查hash 37 | /// 38 | /// 39 | /// 40 | /// 41 | public bool HashExists(string hashId, string key) 42 | { 43 | return DoWithRetry(() => 44 | { 45 | try 46 | { 47 | return _cnn.GetDatabase().HashExists(hashId, key); 48 | } 49 | catch (Exception ex) 50 | { 51 | throw new Exception(string.Format("HashExists异常,参数:{0},{1},{2},{3},异常信息:{4}", _sectionName, _dbIndex, hashId, key, ex.Message)); 52 | } 53 | }); 54 | } 55 | 56 | /// 57 | /// 设置hash 58 | /// 59 | /// 60 | /// 61 | /// 62 | /// 63 | /// 64 | public bool HashSet(string hashId, string key, T t) where T : class, new() 65 | { 66 | return DoWithRetry(() => 67 | { 68 | return _cnn.GetDatabase().HashSet(hashId, key, SerializeHelper.Serialize(t)); 69 | }); 70 | } 71 | 72 | /// 73 | /// 设置hash 74 | /// 75 | /// 76 | /// 77 | /// 78 | /// 79 | /// 80 | public bool HashSet(string hashId, string key, string val) 81 | { 82 | return DoWithRetry(() => 83 | { 84 | return _cnn.GetDatabase().HashSet(hashId, key, val); 85 | }); 86 | } 87 | 88 | /// 89 | /// 在一个hash中设置一个key - value,仅仅当不存在的时候才设置 90 | /// 91 | /// 92 | /// 93 | /// 94 | /// 95 | /// 96 | public bool HashSetIfNotExists(string hashId, string key, string value) 97 | { 98 | return DoWithRetry(() => 99 | { 100 | return _cnn.GetDatabase().HashSet(hashId, key, value, When.NotExists); 101 | }); 102 | } 103 | 104 | /// 105 | /// 移除hash 106 | /// 107 | /// 108 | /// 109 | /// 110 | public bool HashDelete(string hashId, string key) 111 | { 112 | return DoWithRetry(() => 113 | { 114 | return _cnn.GetDatabase().HashDelete(hashId, key); 115 | }); 116 | } 117 | 118 | /// 119 | /// 批量移除hash 120 | /// 121 | /// 122 | /// 123 | /// 124 | public long HashDelete(string hashId, string[] keys) 125 | { 126 | return DoWithRetry(() => 127 | { 128 | var hkeys = new RedisValue[keys.Length]; 129 | 130 | for (int i = 0; i < keys.Length; i++) 131 | { 132 | hkeys[i] = keys[i]; 133 | } 134 | return _cnn.GetDatabase().HashDelete(hashId, hkeys); 135 | }); 136 | } 137 | 138 | /// 139 | /// hash计数器 140 | /// 141 | /// 142 | /// 143 | /// 144 | /// 145 | public long HashIncrement(string hashId, string key, long value = 1) 146 | { 147 | return DoWithRetry(() => 148 | { 149 | return _cnn.GetDatabase().HashIncrement(hashId, key, value); 150 | }); 151 | } 152 | 153 | /// 154 | /// 获取hash 155 | /// 156 | /// 157 | /// 158 | /// 159 | /// 160 | public T HashGet(string hashId, string key) where T : class, new() 161 | { 162 | return DoWithRetry(() => 163 | { 164 | return _cnn.GetDatabase().HashGet(hashId, key).ConvertTo(); 165 | }); 166 | } 167 | 168 | /// 169 | /// 获取hash 170 | /// 171 | /// 172 | /// 173 | /// 174 | public string HashGet(string hashId, string key) 175 | { 176 | return DoWithRetry(() => 177 | { 178 | return _cnn.GetDatabase().HashGet(hashId, key).ToString(); 179 | }); 180 | } 181 | 182 | /// 183 | /// 获取hash数量 184 | /// 185 | /// 186 | /// 187 | public long GetHashCount(string hashId) 188 | { 189 | return DoWithRetry(() => 190 | { 191 | return _cnn.GetDatabase().HashLength(hashId); 192 | }); 193 | } 194 | 195 | /// 196 | /// 获取某个hashid下面全部hash 197 | /// 198 | /// 199 | /// 200 | public List HashGetAll(string hashId) 201 | { 202 | return DoWithRetry(() => 203 | { 204 | var result = new List(); 205 | var list = _cnn.GetDatabase().HashValues(hashId).ToList(); 206 | if ((list != null) && (list.Count > 0)) 207 | list.ForEach(x => 208 | { 209 | if (x.HasValue) 210 | result.Add(x.ToString()); 211 | }); 212 | return result; 213 | }); 214 | } 215 | 216 | /// 217 | /// 获取某个hashid下面全部hash 218 | /// 219 | /// 220 | /// 221 | /// 222 | public List HashGetAll(string hashId) where T : class, new() 223 | { 224 | return DoWithRetry(() => 225 | { 226 | return _cnn.GetDatabase().HashValues(hashId).ToList().ConvertTo(); 227 | }); 228 | } 229 | 230 | /// 231 | /// 获取全部的hashkey,hashvalue字典 232 | /// 233 | /// 234 | /// 235 | /// 236 | public Dictionary HashGetAllDic(string hashId) where T : class, new() 237 | { 238 | return DoWithRetry(() => 239 | { 240 | return _cnn.GetDatabase().HashGetAll(hashId).ConvertTo(); 241 | }); 242 | } 243 | 244 | /// 245 | /// 获取全部的hashkey,hashvalue字典 246 | /// 247 | /// 248 | /// 249 | public Dictionary HashGetAllDic(string hashId) 250 | { 251 | return DoWithRetry(() => 252 | { 253 | return _cnn.GetDatabase().HashGetAll(hashId).ConvertTo(); 254 | }); 255 | } 256 | 257 | /// 258 | /// 分页某个hashid下面hash 259 | /// 260 | /// 261 | /// 262 | /// 263 | /// 264 | /// 265 | public List HashGetAll(string hashID, int pageIndex, int pageSize) where T : class, new() 266 | { 267 | return DoWithRetry(() => 268 | { 269 | var result = new List(); 270 | pageIndex = pageIndex > 0 ? pageIndex : 1; 271 | pageSize = pageSize > 0 ? pageSize : 20; 272 | var list = 273 | _cnn.GetDatabase().HashValues(hashID).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); 274 | if ((list != null) && (list.Count > 0)) 275 | list.ForEach(value => 276 | { 277 | if (!string.IsNullOrEmpty(value) && value.HasValue) 278 | { 279 | var obj = SerializeHelper.Deserialize(value.ToString()); 280 | if (obj != null) 281 | result.Add(obj); 282 | } 283 | }); 284 | return result; 285 | }); 286 | } 287 | 288 | /// 289 | /// 获取指定的全部hashlist 290 | /// 291 | /// 292 | /// 293 | /// 294 | public List HashGetAll(List hashIds) where T : class, new() 295 | { 296 | return DoWithRetry(() => 297 | { 298 | var result = new List(); 299 | foreach (var hashId in hashIds) 300 | { 301 | var list = HashGetAll(hashId); 302 | if ((list != null) && (list.Count > 0)) 303 | result.AddRange(list); 304 | } 305 | return result; 306 | }); 307 | } 308 | 309 | /// 310 | /// 获取某个hashid下面全部keys 311 | /// 312 | /// 313 | /// 314 | public List GetHashKeys(string hashId) 315 | { 316 | return DoWithRetry(() => 317 | { 318 | return _cnn.GetDatabase().HashKeys(hashId).ConvertTo(); 319 | }); 320 | } 321 | 322 | /// 323 | /// 从指定hashid,keys中获取指定hash集合 324 | /// 325 | /// 326 | /// 327 | /// 328 | /// 329 | public List GetValuesFromHash(string hashId, List keys) where T : class, new() 330 | { 331 | return DoWithRetry(() => 332 | { 333 | var list = new List(); 334 | if ((keys != null) && (keys.Count > 0)) 335 | { 336 | var rv = new RedisValue[keys.Count]; 337 | for (var i = 0; i < keys.Count; i++) 338 | rv[i] = keys[i]; 339 | var vlts = _cnn.GetDatabase().HashGet(hashId, rv).ConvertTo(); 340 | list.AddRange(vlts); 341 | } 342 | return list; 343 | }); 344 | } 345 | 346 | /// 347 | /// 从指定hashid,keys中获取指定hash集合 348 | /// 349 | /// 350 | /// 351 | /// 352 | /// 353 | public Dictionary GetValuesDicFromHash(string hashId, List keys) where T : class, new() 354 | { 355 | return DoWithRetry(() => 356 | { 357 | var dic = new Dictionary(); 358 | if ((keys != null) && (keys.Count > 0)) 359 | { 360 | var rv = new RedisValue[keys.Count]; 361 | for (var i = 0; i < keys.Count; i++) 362 | rv[i] = keys[i]; 363 | var vlts = _cnn.GetDatabase().HashGet(hashId, rv); 364 | for (int i = 0; i < vlts.Length; i++) 365 | if (!string.IsNullOrEmpty(vlts[i])) 366 | { 367 | var obj = SerializeHelper.Deserialize(vlts[i]); 368 | if (obj != null) 369 | dic.Add(rv[i], obj); 370 | } 371 | } 372 | return dic; 373 | }); 374 | } 375 | /// 376 | /// 从指定hashid,keys中获取指定hash集合 377 | /// 378 | /// 379 | /// 380 | /// 381 | public List GetValuesFromHash(string hashId, List keys) 382 | { 383 | return DoWithRetry(() => 384 | { 385 | var list = new List(); 386 | if ((keys != null) && (keys.Count > 0)) 387 | { 388 | var rv = new RedisValue[keys.Count]; 389 | for (var i = 0; i < keys.Count; i++) 390 | rv[i] = keys[i]; 391 | var vlts = _cnn.GetDatabase().HashGet(hashId, rv); 392 | foreach (var val in vlts) 393 | if (!string.IsNullOrEmpty(val) && val.HasValue) 394 | list.Add(val); 395 | } 396 | return list; 397 | }); 398 | } 399 | 400 | #endregion 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisOperationForList.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis.Core 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis.Core 6 | *类 名 称:SERedisOperationForList 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/3 15:50:38 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/3 15:50:38 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using StackExchange.Redis; 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Linq; 22 | using Wenli.Drive.Redis.Interface; 23 | using Wenli.Drive.Redis.Tool; 24 | 25 | namespace Wenli.Drive.Redis.Core 26 | { 27 | /// 28 | /// SERedisOperationForList 29 | /// 30 | public partial class SERedisOperation : IRedisOperation 31 | { 32 | #region Lists 33 | 34 | /// 35 | /// 进队 36 | /// 37 | /// 38 | /// 39 | public void Enqueue(string listId, string value) 40 | { 41 | DoWithRetry(() => 42 | { 43 | _cnn.GetDatabase().ListLeftPush(listId, value); 44 | }); 45 | } 46 | 47 | /// 48 | /// 进队 49 | /// 50 | /// 51 | /// 52 | public long Enqueue(string listId, List values) 53 | { 54 | if (values == null || values.Count == 0) 55 | { 56 | return 0; 57 | } 58 | 59 | return DoWithRetry(() => 60 | { 61 | return _cnn.GetDatabase().ListLeftPush(listId, values.Select(r => (RedisValue)r).ToArray()); 62 | }); 63 | } 64 | 65 | /// 66 | /// 出队 67 | /// 68 | /// 69 | /// 70 | public string Dnqueue(string listId) 71 | { 72 | return DoWithRetry(() => 73 | { 74 | string result = _cnn.GetDatabase().ListRightPop(listId); 75 | 76 | if (!string.IsNullOrEmpty(result)) 77 | { 78 | return result; 79 | } 80 | 81 | result = _cnn.GetDatabase().ListRightPop(listId); 82 | 83 | if (!string.IsNullOrEmpty(result)) 84 | { 85 | return result; 86 | } 87 | 88 | return null; 89 | }); 90 | } 91 | 92 | /// 93 | /// 进队 94 | /// 95 | /// 96 | /// 97 | /// 98 | public void Enqueue(string listId, T t) where T : class, new() 99 | { 100 | DoWithRetry(() => 101 | { 102 | var value = SerializeHelper.Serialize(t); 103 | _cnn.GetDatabase().ListLeftPush(listId, value); 104 | }); 105 | } 106 | 107 | /// 108 | /// 出队 109 | /// 110 | /// 111 | /// 112 | /// 113 | public T Dnqueue(string listId) where T : class, new() 114 | { 115 | return DoWithRetry(() => 116 | { 117 | var json = _cnn.GetDatabase().ListRightPop(listId); 118 | if (json.IsNullOrEmpty) 119 | return default(T); 120 | return SerializeHelper.Deserialize(json.ToString()); 121 | }); 122 | } 123 | 124 | /// 125 | /// 获取队列元素 126 | /// 127 | /// 128 | /// 129 | /// 130 | /// 131 | /// 132 | public List GetList(string listId, long start = 0, long stop = -1) where T : class, new() 133 | { 134 | return DoWithRetry(() => 135 | { 136 | var result = new List(); 137 | var list = _cnn.GetDatabase().ListRange(listId, start, stop).ToList(); 138 | if (list.Count > 0) 139 | list.ForEach(x => 140 | { 141 | if (x.HasValue) 142 | { 143 | var value = SerializeHelper.Deserialize(x); 144 | result.Add(value); 145 | } 146 | }); 147 | return result; 148 | }); 149 | } 150 | 151 | /// 152 | /// 获取队列长度 153 | /// 154 | /// 155 | /// 156 | public long QueueCount(string listId) 157 | { 158 | return DoWithRetry(() => 159 | { 160 | return _cnn.GetDatabase().ListLength(listId); 161 | }); 162 | } 163 | /// 164 | /// 从出队方向入队 165 | /// 166 | /// 167 | /// 168 | public void REnqueue(string listId, string value) 169 | { 170 | DoWithRetry(() => 171 | { 172 | _cnn.GetDatabase().ListRightPush(listId, value); 173 | }); 174 | } 175 | /// 176 | /// 从出队方向入队 177 | /// 178 | /// 179 | /// 180 | /// 181 | public void REnqueue(string listId, T t) where T : class, new() 182 | { 183 | DoWithRetry(() => 184 | { 185 | var value = SerializeHelper.Serialize(t); 186 | _cnn.GetDatabase().ListRightPush(listId, value); 187 | }); 188 | } 189 | 190 | #endregion 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisOperationForSet.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis.Core 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis.Core 6 | *类 名 称:SERedisOperationForSet 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/3 15:48:09 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/3 15:48:09 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using Wenli.Drive.Redis.Extends; 19 | using Wenli.Drive.Redis.Interface; 20 | using System.Collections.Generic; 21 | using System.Linq; 22 | 23 | namespace Wenli.Drive.Redis.Core 24 | { 25 | /// 26 | /// SERedisOperationForSet 27 | /// 28 | public partial class SERedisOperation : IRedisOperation 29 | { 30 | #region Set 31 | 32 | /// 33 | /// 添加一个set 34 | /// 35 | /// 36 | /// 37 | /// 38 | public bool SetAdd(string setId, string val) 39 | { 40 | return DoWithRetry(() => 41 | { 42 | return _cnn.GetDatabase().SetAdd(setId, val); 43 | }); 44 | } 45 | 46 | /// 47 | /// 是否存在于set中 48 | /// 49 | /// 50 | /// 51 | /// 52 | public bool SetContains(string setId, string val) 53 | { 54 | return DoWithRetry(() => 55 | { 56 | return _cnn.GetDatabase().SetContains(setId, val); 57 | }); 58 | } 59 | 60 | /// 61 | /// 获取某个key下面全部的value 62 | /// 63 | /// 64 | /// 65 | public List SetMembers(string setId) 66 | { 67 | return DoWithRetry(() => 68 | { 69 | return _cnn.GetDatabase().SetMembers(setId).ConvertTo(); 70 | }); 71 | } 72 | 73 | /// 74 | /// 移除set 75 | /// 76 | /// 77 | /// 78 | /// 79 | public bool SetRemove(string setId, string val) 80 | { 81 | return DoWithRetry(() => 82 | { 83 | return _cnn.GetDatabase().SetRemove(setId, val); 84 | }); 85 | } 86 | 87 | /// 88 | /// 批量删除set 89 | /// 90 | /// 91 | /// 92 | public void SetsRemove(string setId, string[] vals) 93 | { 94 | DoWithRetry(() => 95 | { 96 | for (var i = 0; i < vals.Length; i++) 97 | _cnn.GetDatabase().SetRemove(setId, vals[i]); 98 | }); 99 | } 100 | 101 | /// 102 | /// 返回指定set长度 103 | /// 104 | /// 105 | /// 106 | public long SetLength(string setId) 107 | { 108 | return DoWithRetry(() => 109 | { 110 | return _cnn.GetDatabase().SetLength(setId); 111 | }); 112 | } 113 | 114 | #endregion 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisOperationForSortedSet.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis.Core 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis.Core 6 | *类 名 称:SERedisOperationForSortedSet 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/3 15:49:14 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/3 15:49:14 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using StackExchange.Redis; 19 | using System.Collections.Generic; 20 | using System.Linq; 21 | using Wenli.Drive.Redis.Data; 22 | using Wenli.Drive.Redis.Extends; 23 | using Wenli.Drive.Redis.Interface; 24 | 25 | namespace Wenli.Drive.Redis.Core 26 | { 27 | /// 28 | /// SERedisOperationForSortedSet 29 | /// 30 | public partial class SERedisOperation : IRedisOperation 31 | { 32 | #region Sorted Sets 33 | 34 | /// 35 | /// 检查SortedSet 36 | /// 37 | /// 38 | /// 39 | /// 40 | public bool SortedSetItemIsExist(string setId, string item) 41 | { 42 | return DoWithRetry(() => 43 | { 44 | var value = GetItemScoreFromSortedSet(setId, item); 45 | if (value != null) 46 | return true; 47 | return false; 48 | }); 49 | } 50 | 51 | /// 52 | /// 添加SortedSet 53 | /// 54 | /// 55 | /// 56 | /// 57 | /// 58 | /// 59 | public bool SortedSetAdd(string setId, string item, double score) 60 | { 61 | return DoWithRetry(() => 62 | { 63 | return _cnn.GetDatabase().SortedSetAdd(setId, item, score); 64 | }); 65 | } 66 | 67 | /// 68 | /// 查询SortedSet集合 69 | /// 70 | /// 71 | /// 72 | /// 73 | /// 74 | /// 75 | public List GetSortedSetRangeByRank(string setId, long fromRank, long toRank, 76 | string order = DefaultOrder) 77 | { 78 | return DoWithRetry(() => 79 | { 80 | return _cnn.GetDatabase() 81 | .SortedSetRangeByRank(setId, fromRank, toRank, 82 | order == Order.Descending.ToString().ToLower() ? Order.Descending : Order.Ascending) 83 | .ToList().ConvertTo(); 84 | }); 85 | } 86 | 87 | /// 88 | /// 根据score查询SortedSet集合 89 | /// 90 | /// 91 | /// 92 | /// 93 | /// 94 | /// 95 | public Dictionary GetSortedSetRangeByRankWithScores(string setId, long fromRank, long toRank, 96 | string order = DefaultOrder) 97 | { 98 | return DoWithRetry(() => 99 | { 100 | return _cnn.GetDatabase() 101 | .SortedSetRangeByRankWithScores(setId, fromRank, toRank, 102 | order == Order.Descending.ToString().ToLower() ? Order.Descending : Order.Ascending) 103 | .ToList().ConvertTo(); 104 | }); 105 | } 106 | 107 | /// 108 | /// 获取SortedSet集合区间 109 | /// 110 | /// 111 | /// 最小score 112 | /// 最大score 113 | /// 114 | /// 115 | /// 116 | /// 117 | public PagedList GetSortedSetRangeByRankWithSocres(string setid, long min, long max, int pageIndex = 1, 118 | int pageSize = 20, bool orderBy = true) 119 | { 120 | return DoWithRetry(() => 121 | { 122 | var count = _cnn.GetDatabase().SortedSetLength(setid); //此处无法正确获取数量 123 | if (count > 0) 124 | { 125 | var list = _cnn.GetDatabase() 126 | .SortedSetRangeByScoreWithScores(setid, min, max, Exclude.Stop, 127 | orderBy ? Order.Ascending : Order.Descending, (pageIndex - 1) * pageSize, pageSize); 128 | if ((list != null) && (list.Length > 0)) 129 | { 130 | var result = new List(); 131 | list.ToList().ForEach(x => 132 | { 133 | if ((x != null) && x.Element.HasValue) 134 | { 135 | var value = x.Element.ToString(); 136 | result.Add(value); 137 | } 138 | }); 139 | return new PagedList 140 | { 141 | PageIndex = pageIndex, 142 | PageSize = pageSize, 143 | Count = count, 144 | List = result 145 | }; 146 | } 147 | } 148 | return new PagedList(); 149 | }); 150 | } 151 | 152 | /// 153 | /// 获取SortedSet集合区间 154 | /// 155 | /// 156 | /// 157 | /// 158 | /// 159 | /// 160 | /// 161 | /// 162 | public PagedList GetSortedSetRangeByRankBySocre(string setid, double minScore, double maxScore, int pageIndex = 1, 163 | int pageSize = 20, bool orderBy = true) 164 | { 165 | return DoWithRetry(() => 166 | { 167 | var count = _cnn.GetDatabase().SortedSetLength(setid, minScore, maxScore); 168 | if (count > 0) 169 | { 170 | var list = _cnn.GetDatabase().SortedSetRangeByScore(setid, minScore, maxScore, Exclude.Stop, orderBy ? Order.Ascending : Order.Descending, (pageIndex - 1) * pageSize, pageSize); 171 | 172 | if (list != null && list.Any()) 173 | { 174 | var result = new List(); 175 | 176 | foreach (var item in list) 177 | { 178 | result.Add(item.ToString()); 179 | } 180 | 181 | return new PagedList 182 | { 183 | PageIndex = pageIndex, 184 | PageSize = pageSize, 185 | Count = count, 186 | List = result 187 | }; 188 | } 189 | } 190 | return new PagedList(); 191 | }); 192 | } 193 | 194 | /// 195 | /// 获取zset 196 | /// 197 | /// 198 | /// 199 | /// 200 | /// 201 | /// 202 | public Dictionary GetSortedSetRangeBySocreWithScore(string setid, double minScore, double maxScore, bool orderBy = true) 203 | { 204 | return DoWithRetry(() => 205 | { 206 | return _cnn.GetDatabase().SortedSetRangeByScoreWithScores(setid, minScore, maxScore, Exclude.Stop, orderBy ? Order.Ascending : Order.Descending).ConvertTo(); 207 | }); 208 | } 209 | 210 | /// 211 | /// 获取zset 212 | /// 213 | /// 214 | /// 215 | /// 216 | /// 217 | /// 218 | /// 219 | /// 220 | public PagedDictionary GetSortedSetRangeBySocreWithScore(string setid, double minScore, double maxScore, int pageIndex, int pageSize, bool orderBy = true) 221 | { 222 | return DoWithRetry(() => 223 | { 224 | var len = _cnn.GetDatabase().SortedSetLength(setid, minScore, maxScore, Exclude.Stop); 225 | return _cnn.GetDatabase().SortedSetRangeByScoreWithScores(setid, minScore, maxScore, Exclude.Stop, orderBy ? Order.Ascending : Order.Descending, skip: (pageIndex - 1) * pageSize, take: pageSize).ConvertTo(pageIndex, pageSize, len); 226 | }); 227 | } 228 | 229 | /// 230 | /// 获取zset 231 | /// 232 | /// 233 | /// 234 | /// 235 | /// 236 | /// 237 | public List GetSortedSetRangeBySocre(string setid, double minScore, double maxScore, bool orderBy = true) 238 | { 239 | return DoWithRetry(() => 240 | { 241 | return _cnn.GetDatabase().SortedSetRangeByScore(setid, minScore, maxScore, Exclude.Stop, orderBy ? Order.Ascending : Order.Descending).ConvertTo(); 242 | }); 243 | } 244 | 245 | 246 | /// 247 | /// 根据值范围获取SortedSet集合 248 | /// 249 | /// 250 | /// 251 | /// 252 | /// 253 | public List GetSortedSetRangeByValue(string setId, long minValue, long maxValue) 254 | { 255 | return DoWithRetry(() => 256 | { 257 | return _cnn.GetDatabase().SortedSetRangeByValue(setId, minValue, maxValue, Exclude.Stop).ConvertTo(); 258 | }); 259 | } 260 | 261 | /// 262 | /// 获取SortedSet长度 263 | /// 264 | /// 265 | /// 266 | public long GetSortedSetLength(string setId) 267 | { 268 | return DoWithRetry(() => 269 | { 270 | return _cnn.GetDatabase().SortedSetLength(setId); 271 | }); 272 | } 273 | 274 | /// 275 | /// 根据值范围儿取SortedSet长度 276 | /// 277 | /// 278 | /// 279 | /// 280 | /// 281 | public long GetSortedSetLength(string setId, double minScore, double maxScore) 282 | { 283 | return DoWithRetry(() => 284 | { 285 | return _cnn.GetDatabase().SortedSetLength(setId, minScore, maxScore, Exclude.Stop); 286 | }); 287 | } 288 | 289 | /// 290 | /// 获取某个SortedSet所在的序号 291 | /// 292 | /// 293 | /// 294 | /// 295 | /// 296 | public long? GetItemRankFromSortedSet(string setId, string item, string order = DefaultOrder) 297 | { 298 | return DoWithRetry(() => 299 | { 300 | return _cnn.GetDatabase() 301 | .SortedSetRank(setId, item, 302 | order == Order.Descending.ToString().ToLower() ? Order.Descending : Order.Ascending); 303 | }); 304 | } 305 | 306 | /// 307 | /// 获取某个SortedSet的score值 308 | /// 309 | /// 310 | /// 311 | /// 312 | public double? GetItemScoreFromSortedSet(string setId, string item) 313 | { 314 | return DoWithRetry(() => 315 | { 316 | return _cnn.GetDatabase().SortedSetScore(setId, item); 317 | }); 318 | } 319 | 320 | /// 321 | /// SortedSet计数器+ 322 | /// 323 | /// 324 | /// 325 | /// 326 | /// 327 | public double SetSortedSetItemIncrement(string setId, string item, double score = 1) 328 | { 329 | return DoWithRetry(() => 330 | { 331 | return _cnn.GetDatabase().SortedSetIncrement(setId, item, score); 332 | }); 333 | } 334 | 335 | /// 336 | /// SortedSet计数器- 337 | /// 338 | /// 339 | /// 340 | /// 341 | /// 342 | public double SortedSetItemDecrement(string setId, string item, double score = 1) 343 | { 344 | return DoWithRetry(() => 345 | { 346 | return _cnn.GetDatabase().SortedSetDecrement(setId, item, score); 347 | }); 348 | } 349 | 350 | /// 351 | /// 称除SortedSet 352 | /// 353 | /// 354 | /// 355 | /// 356 | public bool RemoveItemFromSortedSet(string setId, string item) 357 | { 358 | return DoWithRetry(() => 359 | { 360 | return _cnn.GetDatabase().SortedSetRemove(setId, item); 361 | }); 362 | } 363 | 364 | /// 365 | /// 移除某个序号范围的SortedSet 366 | /// 367 | /// 368 | /// 369 | /// 370 | /// 371 | public long RemoveByRankFromSortedSet(string setId, long fromRank, long toRank) 372 | { 373 | return DoWithRetry(() => 374 | { 375 | return _cnn.GetDatabase().SortedSetRemoveRangeByRank(setId, fromRank, toRank); 376 | }); 377 | } 378 | 379 | /// 380 | /// 移除某个score范围的SortedSet 381 | /// 382 | /// 383 | /// 384 | /// 385 | /// 386 | public long RemoveByScoreFromSortedSet(string setId, double minValue, double maxValue) 387 | { 388 | return DoWithRetry(() => 389 | { 390 | return _cnn.GetDatabase().SortedSetRemoveRangeByScore(setId, minValue, maxValue, Exclude.Stop); 391 | }); 392 | } 393 | 394 | #endregion 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisOperationForString.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis.Core 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis.Core 6 | *类 名 称:SERedisOperationForString 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/3 15:45:03 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/3 15:45:03 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using StackExchange.Redis; 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Threading.Tasks; 22 | using Wenli.Drive.Redis.Interface; 23 | using Wenli.Drive.Redis.Tool; 24 | 25 | namespace Wenli.Drive.Redis.Core 26 | { 27 | /// 28 | /// SERedisOperationForString 29 | /// 30 | public partial class SERedisOperation : IRedisOperation 31 | { 32 | 33 | #region Keys 34 | 35 | /// 36 | /// 是否存在key 37 | /// 38 | /// 39 | /// 40 | public bool KeyExists(string key) 41 | { 42 | return DoWithRetry(() => 43 | { 44 | return _cnn.GetDatabase().KeyExists(key); 45 | }); 46 | } 47 | 48 | /// 49 | /// 设置key过期时间 50 | /// 51 | /// 52 | /// 53 | /// 54 | public bool KeyExpire(string key, DateTime datetime) 55 | { 56 | if (datetime.Kind == DateTimeKind.Unspecified) 57 | { 58 | datetime = DateTime.SpecifyKind(datetime, DateTimeKind.Local); 59 | } 60 | return DoWithRetry(() => 61 | { 62 | return _cnn.GetDatabase().KeyExpire(key, datetime); 63 | }); 64 | } 65 | 66 | /// 67 | /// 设置key过期时间 68 | /// 69 | /// 70 | /// 71 | /// 72 | public bool KeyExpire(string key, int timeout = 0) 73 | { 74 | return DoWithRetry(() => 75 | { 76 | if (timeout > 0) 77 | return _cnn.GetDatabase().KeyExpire(key, DateTime.Now.AddSeconds(timeout)); 78 | return false; 79 | }); 80 | } 81 | 82 | /// 83 | /// 设置key 84 | /// 85 | /// 86 | /// 87 | /// 88 | /// 89 | public bool StringSet(string key, string value, int timeout = 0) 90 | { 91 | return DoWithRetry(() => 92 | { 93 | var bResult = false; 94 | if (timeout > 0) 95 | bResult = _cnn.GetDatabase().StringSet(key, value, new TimeSpan(0, 0, timeout)); 96 | else 97 | bResult = _cnn.GetDatabase().StringSet(key, value); 98 | return bResult; 99 | }); 100 | } 101 | 102 | /// 103 | /// 设置key 104 | /// 105 | /// 106 | /// 107 | /// key的超时时常 108 | /// 109 | public bool StringSet(string key, string value, TimeSpan expire) 110 | { 111 | return DoWithRetry(() => 112 | { 113 | return _cnn.GetDatabase().StringSet(key, value, expire); 114 | }); 115 | } 116 | 117 | /// 118 | /// 设置一个值,仅在不存在的时候设置 119 | /// 120 | /// 121 | /// 122 | /// 123 | public bool StringSetIfNotExists(string key, string value) 124 | { 125 | return DoWithRetry(() => 126 | { 127 | return _cnn.GetDatabase().StringSet(key, value, when: When.NotExists); 128 | }); 129 | } 130 | 131 | /// 132 | /// 设置一个值,仅在不存在的时候设置 133 | /// 134 | /// 135 | /// 136 | /// 超时时间 137 | /// 138 | public bool StringSetIfNotExists(string key, string value, TimeSpan ts) 139 | { 140 | return DoWithRetry(() => 141 | { 142 | return _cnn.GetDatabase().StringSet(key, value, ts, when: When.NotExists); 143 | }); 144 | } 145 | 146 | /// 147 | /// 获取key的同时set该Key 148 | /// 149 | /// 150 | /// 151 | /// 152 | public string StringGetSet(string key, string value) 153 | { 154 | return DoWithRetry(() => 155 | { 156 | return _cnn.GetDatabase().StringGetSet(key, value); 157 | }); 158 | } 159 | 160 | /// 161 | /// 获取全部keys 162 | /// 163 | /// 164 | /// 165 | [Obsolete("此方法只用于兼容老数据,且本方法只能查询db0,建议使用sortedset来保存keys")] 166 | public List StringGetKeys(string patten = "*") 167 | { 168 | return DoWithRetry(() => 169 | { 170 | return _cnn.Keys(patten); 171 | }); 172 | } 173 | 174 | /// 175 | /// 获取全部keys 176 | /// 177 | /// 178 | /// 179 | /// 180 | /// 181 | [Obsolete("此方法只用于兼容老数据,且本方法只能查询db0,建议使用sortedset来保存keys")] 182 | public void StringGetKeys(Action> callback, string patten = "*", int size = 1000) 183 | { 184 | DoWithRetry(() => 185 | { 186 | _cnn.Keys(callback, patten, size); 187 | }); 188 | } 189 | 190 | /// 191 | /// 获取全部keys 192 | /// 193 | /// 194 | /// /// 195 | /// 196 | /// 197 | [Obsolete("此方法只用于兼容老数据,建议使用sortedset来保存keys")] 198 | public List StringGetKeys(int pageSize, int dbIndex = -1, string patten = "*") 199 | { 200 | return DoWithRetry(() => 201 | { 202 | List keys = new List(); 203 | 204 | var result = _cnn.GetDatabase().ScriptEvaluate(LuaScript.Prepare("return redis.call('KEYS', '*')"), CommandFlags.PreferSlave); 205 | 206 | if (!result.IsNull) 207 | { 208 | var list = (RedisResult[])result; 209 | foreach (var item in list) 210 | { 211 | var key = (RedisKey)item; 212 | keys.Add(key.ToString()); 213 | } 214 | } 215 | return keys; 216 | }); 217 | } 218 | 219 | /// 220 | /// 获取key 221 | /// 222 | /// 223 | /// 224 | public string StringGet(string key) 225 | { 226 | return DoWithRetry(() => 227 | { 228 | return _cnn.GetDatabase().StringGet(key); 229 | }); 230 | } 231 | 232 | /// 233 | /// 设置key 234 | /// 235 | /// 236 | /// 237 | /// 238 | /// 239 | /// 240 | public bool StringSet(string key, T t, int timeout = 0) where T : class, new() 241 | { 242 | return DoWithRetry(() => 243 | { 244 | return StringSet(key, SerializeHelper.Serialize(t), timeout); 245 | }); 246 | } 247 | 248 | /// 249 | /// 获取key 250 | /// 251 | /// 252 | /// 253 | /// 254 | public T StringGet(string key) where T : class, new() 255 | { 256 | return DoWithRetry(() => 257 | { 258 | var str = StringGet(key); 259 | if (!string.IsNullOrWhiteSpace(str)) 260 | return SerializeHelper.Deserialize(str); 261 | return default(T); 262 | }); 263 | } 264 | 265 | /// 266 | /// 批量获取 267 | /// 268 | /// 269 | /// 270 | /// 271 | public List BatchStringGet(List keys) where T : class, new() 272 | { 273 | return DoWithRetry(() => 274 | { 275 | var batch = _cnn.GetDatabase().CreateBatch(); 276 | 277 | List> tasks = new List>(); 278 | 279 | foreach (var key in keys) 280 | { 281 | tasks.Add(batch.StringGetAsync(key)); 282 | } 283 | batch.Execute(); 284 | 285 | List result = new List(); 286 | 287 | foreach (var task in tasks) 288 | { 289 | result.Add(SerializeHelper.Deserialize(task.Result)); 290 | } 291 | 292 | return result; 293 | }); 294 | } 295 | 296 | /// 297 | /// 获取kv列表集合 298 | /// 299 | /// 300 | /// 301 | /// 302 | public List GetValues(List keys) where T : class, new() 303 | { 304 | return DoWithRetry(() => 305 | { 306 | return GetValues(keys.ToArray()); 307 | }); 308 | } 309 | 310 | /// 311 | /// 获取kv列表集合 312 | /// 313 | /// 314 | /// 315 | /// 316 | public List GetValues(string[] keys) where T : class, new() 317 | { 318 | return DoWithRetry(() => 319 | { 320 | var list = new List(); 321 | if ((keys != null) && (keys.Length > 0)) 322 | foreach (var key in keys) 323 | { 324 | var item = StringGet(key); 325 | if (item != null) 326 | list.Add(item); 327 | } 328 | return list; 329 | }); 330 | } 331 | 332 | /// 333 | /// 获取kv列表集合 334 | /// 335 | /// 336 | /// 337 | public List GetValues(string[] keys) 338 | { 339 | return DoWithRetry(() => 340 | { 341 | var list = new List(); 342 | if ((keys != null) && (keys.Length > 0)) 343 | foreach (var key in keys) 344 | { 345 | var item = StringGet(key); 346 | if (item != null) 347 | list.Add(item); 348 | } 349 | return list; 350 | }); 351 | } 352 | 353 | /// 354 | /// 移除key 355 | /// 356 | /// 357 | /// 358 | public bool KeyDelete(string key) 359 | { 360 | return DoWithRetry(() => 361 | { 362 | return _cnn.GetDatabase().KeyDelete(key); 363 | }); 364 | } 365 | 366 | /// 367 | /// 批量删除 368 | /// 369 | /// 370 | public void KeysDelete(string[] keys) 371 | { 372 | DoWithRetry(() => 373 | { 374 | for (var i = 0; i < keys.Length; i++) 375 | KeyDelete(keys[i]); 376 | }); 377 | } 378 | 379 | /// 380 | /// 批量删除 381 | /// 382 | /// 383 | public void KeysDelete(List keys) 384 | { 385 | DoWithRetry(() => 386 | { 387 | KeysDelete(keys.ToArray()); 388 | }); 389 | } 390 | 391 | /// 392 | /// 重命名key 393 | /// 394 | /// 395 | /// 396 | /// 397 | public bool KeyRename(string oldKey, string newKey) 398 | { 399 | return DoWithRetry(() => 400 | { 401 | return _cnn.GetDatabase().KeyRename(oldKey, newKey); 402 | }); 403 | } 404 | 405 | /// 406 | /// key计数器 407 | /// 408 | /// 409 | /// 410 | /// 411 | public double StringIncrement(string key, double value) 412 | { 413 | return DoWithRetry(() => 414 | { 415 | return _cnn.GetDatabase().StringIncrement(key, value); 416 | }); 417 | } 418 | 419 | /// 420 | /// key计数器(减去相应的value) 421 | /// 422 | /// 423 | /// 424 | /// 425 | public double StringDecrement(string key, double value) 426 | { 427 | return DoWithRetry(() => 428 | { 429 | return _cnn.GetDatabase().StringDecrement(key, value); 430 | }); 431 | } 432 | 433 | /// 434 | /// 追加value 435 | /// 436 | /// 437 | /// 438 | /// 439 | public long StringAppend(string key, string value) 440 | { 441 | return DoWithRetry(() => 442 | { 443 | return _cnn.GetDatabase().StringAppend(value, value, CommandFlags.None); 444 | }); 445 | } 446 | 447 | #endregion 448 | 449 | } 450 | } 451 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SERedisOperationForSubPush.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis.Core 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis.Core 6 | *类 名 称:SERedisOperationForSubPush 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/3 15:51:48 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/3 15:51:48 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using Wenli.Drive.Redis.Interface; 19 | using StackExchange.Redis; 20 | using System; 21 | 22 | namespace Wenli.Drive.Redis.Core 23 | { 24 | /// 25 | /// SERedisOperationForSubPush 26 | /// 27 | public partial class SERedisOperation : IRedisOperation 28 | { 29 | #region SubPush 30 | 31 | private SERedisConnection subcnn; 32 | 33 | /// 34 | /// 订阅消息 35 | /// 36 | /// 37 | /// 38 | public void Subscribe(string channelPrefix, Action action) 39 | { 40 | DoWithRetry(() => 41 | { 42 | if (subcnn == null) 43 | subcnn = new SERedisConnection(_sectionName, _dbIndex); 44 | var pub = subcnn.GetSubscriber(); 45 | pub.Subscribe(new RedisChannel(channelPrefix, RedisChannel.PatternMode.Auto), action); 46 | }); 47 | } 48 | 49 | /// 50 | /// 订阅消息 51 | /// 52 | /// 53 | /// 54 | public void SubscribeWithChannel(string channelPrefix, Action action) 55 | { 56 | DoWithRetry(() => 57 | { 58 | if (subcnn == null) 59 | subcnn = new SERedisConnection(_sectionName, _dbIndex); 60 | var pub = subcnn.GetSubscriber(); 61 | 62 | var raction = new Action((c, m) => 63 | { 64 | action.Invoke(c, m); 65 | }); 66 | 67 | pub.Subscribe(new RedisChannel(channelPrefix, RedisChannel.PatternMode.Auto), raction); 68 | }); 69 | } 70 | 71 | /// 72 | /// 取消订阅 73 | /// 74 | /// 75 | public void Unsubscribe(string channelPrefix) 76 | { 77 | DoWithRetry(() => 78 | { 79 | if (subcnn == null) 80 | subcnn = new SERedisConnection(_sectionName, _dbIndex); 81 | var pub = subcnn.GetSubscriber(); 82 | pub.Unsubscribe(new RedisChannel(channelPrefix, RedisChannel.PatternMode.Auto)); 83 | }); 84 | } 85 | 86 | /// 87 | /// 发布消息 88 | /// 89 | /// 90 | /// 91 | public void Publish(string channelPrefix, string msg) 92 | { 93 | DoWithRetry(() => 94 | { 95 | var pub = _cnn.GetSubscriber(); 96 | pub.PublishAsync(new RedisChannel(channelPrefix, RedisChannel.PatternMode.Auto), msg); 97 | }); 98 | } 99 | 100 | #endregion 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Core/SESentinelClient.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2016-2020 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:1cc1fa2b-c8c8-4627-bb40-dece98f2b73a 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:Wenli.Drive.Redis 10 | * 命名空间:Wenli.Drive.Redis 11 | * 创建时间:2016/12/28 9:59:30 12 | * 创建人:wenli 13 | * 创建说明: 14 | *****************************************************************************************************/ 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Net; 19 | using StackExchange.Redis; 20 | 21 | namespace Wenli.Drive.Redis.Core 22 | { 23 | /// 24 | /// SE哨兵操作类 25 | /// 26 | public class SESentinelClient 27 | { 28 | /// 29 | /// 主从切换通知事件委托 30 | /// 31 | /// 32 | /// 33 | /// 34 | public delegate void OnRedisServerChangedHander(string section, string newconnectionString); 35 | 36 | private readonly string _Section = string.Empty; 37 | 38 | private ISubscriber _Sentinelsub; 39 | 40 | private string _password; 41 | 42 | /// 43 | /// 初始化哨兵类 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | public SESentinelClient(string section, string connectionStr, int poolSize, string password) 50 | { 51 | if (SentinelConnection != null) 52 | SentinelConnection.Dispose(); 53 | _Section = section; 54 | SentinelConfig = ConfigurationOptions.Parse(connectionStr); 55 | SentinelConfig.TieBreaker = string.Empty; 56 | SentinelConfig.CommandMap = CommandMap.Sentinel; 57 | PoolSize = poolSize; 58 | _password = password; 59 | } 60 | 61 | /// 62 | /// 哨兵配置 63 | /// 64 | public ConfigurationOptions SentinelConfig 65 | { 66 | get; 67 | } 68 | 69 | /// 70 | /// sentinel连接器 71 | /// 72 | public ConnectionMultiplexer SentinelConnection 73 | { 74 | get; private set; 75 | } 76 | 77 | /// 78 | /// 哨兵对应的节点的池大小 79 | /// 80 | public int PoolSize 81 | { 82 | get; 83 | } 84 | 85 | /// 86 | /// 主从切换通知事件 87 | /// 88 | public event OnRedisServerChangedHander OnRedisServerChanged; 89 | 90 | /// 91 | /// 触发主从切换通知事件 92 | /// 93 | /// 94 | /// 95 | protected void RaiseOnRedisServerChanged(string section, string newconnectionString) 96 | { 97 | if (OnRedisServerChanged != null) 98 | OnRedisServerChanged(section, newconnectionString); 99 | } 100 | 101 | /// 102 | /// 获取活动的哨兵服务器 103 | /// 104 | /// 105 | /// 106 | /// 107 | private IServer GetActiveServer(ConnectionMultiplexer conn, EndPoint[] endpoints) 108 | { 109 | foreach (var endpoint in endpoints) 110 | { 111 | var server = conn.GetServer(endpoint); 112 | 113 | if (server.IsConnected) 114 | return server; 115 | } 116 | throw new Exception("找不到可以连接的活动的哨兵服务器"); 117 | } 118 | 119 | /// 120 | /// 中间处理slave endpoint列表 121 | /// 122 | /// 123 | /// 124 | private List SanitizeHostsConfig(KeyValuePair[][] slaves) 125 | { 126 | string ip; 127 | string port; 128 | string flags; 129 | 130 | var servers = new List(); 131 | foreach (var slave in slaves) 132 | { 133 | var dic = slave.ToDictionary(); 134 | 135 | dic.TryGetValue("flags", out flags); 136 | dic.TryGetValue("ip", out ip); 137 | dic.TryGetValue("port", out port); 138 | 139 | if (ip == "127.0.0.1") 140 | ip = (SentinelConfig.EndPoints.First() as IPEndPoint).Address.ToString(); 141 | 142 | if ((ip != null) && (port != null) && !flags.Contains("s_down") && !flags.Contains("o_down")) 143 | servers.Add(string.Format("{0}:{1}", ip, port)); 144 | } 145 | return servers; 146 | } 147 | 148 | /// 149 | /// 根据当前哨兵获取对应redis实例的连接字符串 150 | /// 151 | /// 152 | private string GetConnectionStringFromSentinel() 153 | { 154 | try 155 | { 156 | var activeSentinelServer = GetActiveServer(SentinelConnection, SentinelConnection.GetEndPoints()); 157 | var masterConnectionInfo = activeSentinelServer.SentinelGetMasterAddressByName(SentinelConfig.ServiceName); 158 | var slaveConnectionInfos = 159 | SanitizeHostsConfig(activeSentinelServer.SentinelSlaves(SentinelConfig.ServiceName)); 160 | 161 | var redisConfigs = new ConfigurationOptions 162 | { 163 | AllowAdmin = true, 164 | DefaultDatabase = SentinelConfig.DefaultDatabase, 165 | ConnectRetry = SentinelConfig.ConnectRetry, 166 | ConnectTimeout = SentinelConfig.ConnectTimeout, 167 | KeepAlive = SentinelConfig.KeepAlive, 168 | SyncTimeout = SentinelConfig.SyncTimeout, 169 | AbortOnConnectFail = false 170 | }; 171 | if (!string.IsNullOrEmpty(_password)) 172 | { 173 | redisConfigs.Password = _password; 174 | } 175 | redisConfigs.EndPoints.Add(masterConnectionInfo); 176 | foreach (var slaveInfo in slaveConnectionInfos) 177 | redisConfigs.EndPoints.Add(slaveInfo); 178 | 179 | return redisConfigs.ToString(); 180 | } 181 | catch (Exception ex) 182 | { 183 | throw new Exception(string.Format("SESentinelClient.GetConnectionStringFromSentinel 连接到哨兵服务器({0})失败:{1}", SentinelConfig, ex.Message)); 184 | } 185 | } 186 | 187 | /// 188 | /// 连接到指定的Sentinel,获取 master 和 slave 信息并返回。同时,注册相应的事件用于接收 sentinel 的通知消息 189 | /// 190 | /// 191 | public string Start() 192 | { 193 | var redisConnectionString = string.Empty; 194 | try 195 | { 196 | // redis在哨兵模式下,如果Password不为null 或 "",会使用 AUTH 命令,但这个命令是不可用的。 197 | // 所以,克隆一个配置,并将其 Password信息去除 198 | ConfigurationOptions finalSentinalConfig = SentinelConfig; 199 | if (!string.IsNullOrWhiteSpace(SentinelConfig.Password)) 200 | { 201 | var cloneConfig = SentinelConfig.Clone(); 202 | cloneConfig.Password = ""; 203 | finalSentinalConfig = cloneConfig; 204 | } 205 | 206 | SentinelConnection = ConnectionMultiplexer.Connect(finalSentinalConfig); 207 | 208 | redisConnectionString = GetConnectionStringFromSentinel(); 209 | 210 | _Sentinelsub = SentinelConnection.GetSubscriber(); 211 | 212 | _Sentinelsub.SubscribeAsync("+switch-master", (channle, msg) => 213 | { 214 | redisConnectionString = GetConnectionStringFromSentinel(); 215 | RaiseOnRedisServerChanged(_Section, redisConnectionString); 216 | }); 217 | } 218 | catch (Exception ex) 219 | { 220 | throw new Exception(string.Format("SESentinelClient.Start 连接到哨兵服务器({0})失败:{1}", SentinelConfig, ex.Message)); 221 | } 222 | 223 | return redisConnectionString; 224 | } 225 | } 226 | } -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Data/PagedDictionary.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis.Data 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis.Data 6 | *类 名 称:PagedDictionary 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/30 9:51:39 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/30 9:51:39 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Runtime.Serialization; 21 | 22 | namespace Wenli.Drive.Redis.Data 23 | { 24 | /// 25 | /// 分页信息类 26 | /// 27 | [Serializable] 28 | [DataContract] 29 | public class PagedDictionary 30 | { 31 | /// 32 | /// 分页信息类 33 | /// 34 | public PagedDictionary() 35 | { 36 | PageIndex = 1; 37 | PageSize = 20; 38 | Count = 0; 39 | Dictionary = new Dictionary(); 40 | } 41 | /// 42 | /// 页号 43 | /// 44 | [DataMember] 45 | public int PageIndex { get; set; } 46 | /// 47 | /// 分页条数 48 | /// 49 | [DataMember] 50 | public int PageSize { get; set; } 51 | 52 | /// 53 | /// 数量 54 | /// 55 | [DataMember] 56 | public long Count { get; set; } 57 | 58 | /// 59 | /// 分页数据内容 60 | /// 61 | [DataMember] 62 | public Dictionary Dictionary { get; set; } 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Data/PagedList.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2015-2016 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:8c468370-8519-4057-a58e-36fe1ad3ed8a 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:$projectname$ 10 | * 命名空间:Wenli.Drive.Redis.Data 11 | * 类名称:PagedList 12 | * 创建时间:2016/12/28 10:02:03 13 | * 创建人:wenli 14 | * 创建说明: 15 | *****************************************************************************************************/ 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Runtime.Serialization; 19 | 20 | namespace Wenli.Drive.Redis.Data 21 | { 22 | /// 23 | /// 分页信息类 24 | /// 25 | [Serializable, DataContract] 26 | public class PagedList 27 | { 28 | [DataMember] 29 | public int PageIndex 30 | { 31 | get; set; 32 | } 33 | [DataMember] 34 | public int PageSize 35 | { 36 | get; set; 37 | } 38 | [DataMember] 39 | public long Count 40 | { 41 | get; set; 42 | } 43 | [DataMember] 44 | public List List 45 | { 46 | get; set; 47 | } 48 | 49 | public PagedList() 50 | { 51 | this.PageIndex = 1; 52 | this.PageSize = 20; 53 | this.Count = 0; 54 | this.List = new List(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Extends/KeyValueConvert.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis.Extends 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis.Extends 6 | *类 名 称:KeyValueConvert 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/30 9:50:15 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/30 9:50:15 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using StackExchange.Redis; 19 | using System.Collections.Generic; 20 | using System.Linq; 21 | using Wenli.Drive.Redis.Data; 22 | using Wenli.Drive.Redis.Tool; 23 | 24 | namespace Wenli.Drive.Redis.Extends 25 | { 26 | /// 27 | /// key value 转换 28 | /// 29 | public static class KeyValueConvert 30 | { 31 | /// 32 | /// 转换keys 33 | /// 34 | /// 35 | /// 36 | public static List ConvertTo(this IEnumerable keys) 37 | { 38 | if (keys == null || !keys.Any()) return new List(); 39 | 40 | var result = new List(); 41 | 42 | foreach (var item in keys) 43 | { 44 | result.Add(item.ToString()); 45 | } 46 | 47 | return result; 48 | } 49 | 50 | /// 51 | /// 转换values 52 | /// 53 | /// 54 | /// 55 | public static List ConvertTo(this IEnumerable values) 56 | { 57 | if (values == null || !values.Any()) return new List(); 58 | 59 | var result = new List(); 60 | 61 | foreach (var item in values) 62 | { 63 | result.Add(item.ToString()); 64 | } 65 | 66 | return result; 67 | } 68 | 69 | /// 70 | /// 转换value 71 | /// 72 | /// 73 | /// 74 | /// 75 | public static T ConvertTo(this RedisValue value) 76 | { 77 | if (value.IsNullOrEmpty) return default(T); 78 | 79 | var json = value.ToString(); 80 | 81 | return SerializeHelper.Deserialize(json); 82 | } 83 | 84 | /// 85 | /// 转换values 86 | /// 87 | /// 88 | /// 89 | /// 90 | public static List ConvertTo(this IEnumerable values) 91 | { 92 | if (values == null || !values.Any()) return new List(); 93 | 94 | var result = new List(); 95 | 96 | foreach (var item in values) 97 | { 98 | var json = item.ToString(); 99 | 100 | if (!string.IsNullOrEmpty(json)) 101 | { 102 | result.Add(SerializeHelper.Deserialize(json)); 103 | } 104 | } 105 | 106 | return result; 107 | } 108 | 109 | /// 110 | /// 转换HashEntry 111 | /// 112 | /// 113 | /// 114 | /// 115 | public static Dictionary ConvertTo(this IEnumerable entries) 116 | { 117 | if (entries == null || !entries.Any()) return new Dictionary(); 118 | 119 | var result = new Dictionary(); 120 | 121 | foreach (var entry in entries) 122 | { 123 | var key = entry.Name.ToString(); 124 | 125 | result[key] = entry.Value.ToString(); 126 | } 127 | 128 | return result; 129 | } 130 | 131 | /// 132 | /// 转换HashEntry 133 | /// 134 | /// 135 | /// 136 | /// 137 | public static Dictionary ConvertTo(this IEnumerable entries) 138 | { 139 | if (entries == null || !entries.Any()) return new Dictionary(); 140 | 141 | var result = new Dictionary(); 142 | 143 | foreach (var entry in entries) 144 | { 145 | var key = entry.Name.ToString(); 146 | 147 | result[key] = entry.Value.ConvertTo(); 148 | } 149 | 150 | return result; 151 | } 152 | 153 | /// 154 | /// 转换SortedSetEntry 155 | /// 156 | /// 157 | /// 158 | public static Dictionary ConvertTo(this IEnumerable entries) 159 | { 160 | if (entries == null || !entries.Any()) return new Dictionary(); 161 | 162 | var result = new Dictionary(); 163 | 164 | foreach (var entry in entries) 165 | { 166 | var key = entry.Element.ToString(); 167 | 168 | result[key] = entry.Score; 169 | } 170 | 171 | return result; 172 | } 173 | 174 | /// 175 | /// 转换SortedSetEntry 176 | /// 177 | /// 178 | /// 179 | /// 180 | /// 181 | /// 182 | public static PagedDictionary ConvertTo(this IEnumerable entries, int pageIndex, int pageSize, long len) 183 | { 184 | if (entries == null || !entries.Any()) return new PagedDictionary(); 185 | 186 | var result = new PagedDictionary() { PageIndex = pageIndex, PageSize = pageSize, Count = len }; 187 | 188 | var data = new Dictionary(); 189 | 190 | foreach (var entry in entries) 191 | { 192 | var key = entry.Element.ToString(); 193 | 194 | data[key] = entry.Score; 195 | } 196 | 197 | result.Dictionary = data; 198 | 199 | return result; 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Interface/IRedisHelper.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2016-2020 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:1cc1fa2b-c8c8-4627-bb40-dece98f2b73a 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:Wenli.Drive.Redis 10 | * 命名空间:Wenli.Drive.Redis 11 | * 创建时间:2016/12/28 9:59:30 12 | * 创建人:wenli 13 | * 创建说明: 14 | *****************************************************************************************************/ 15 | namespace Wenli.Drive.Redis.Interface 16 | { 17 | /// 18 | /// redis操作类实现接口 19 | /// 20 | public interface IRedisHelper 21 | { 22 | /// 23 | /// 初始化池 24 | /// 25 | /// 26 | void Init(string section); 27 | 28 | /// 29 | /// 初始化池 30 | /// 31 | /// 32 | void Init(RedisConfig config); 33 | 34 | /// 35 | /// 初始化池 36 | /// 37 | /// 38 | /// 39 | /// 40 | /// 41 | /// 42 | void Init(string sectionName, RedisConnectType type, string master, string password = "", string serviceName = ""); 43 | 44 | /// 45 | /// redis操作 46 | /// 47 | /// 48 | /// 49 | IRedisOperation GetRedisOperation(int dbIndex = -1); 50 | } 51 | } -------------------------------------------------------------------------------- /Wenli.Drive.Redis/RedisConfig.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2016-2020 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:1cc1fa2b-c8c8-4627-bb40-dece98f2b73a 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:Wenli.Drive.Redis 10 | * 命名空间:Wenli.Drive.Redis 11 | * 创建时间:2016/12/28 9:59:30 12 | * 创建人:wenli 13 | * 创建说明: 14 | *****************************************************************************************************/ 15 | 16 | using System.Configuration; 17 | 18 | namespace Wenli.Drive.Redis 19 | { 20 | /// 21 | /// redis配置类 22 | /// 23 | public class RedisConfig : ConfigurationSection 24 | { 25 | /// 26 | /// 当前配置名称 27 | /// 此属性为必须 28 | /// 29 | public string SectionName 30 | { 31 | get; set; 32 | } 33 | 34 | /// 35 | /// 配置类型 36 | /// 37 | [ConfigurationProperty("Type", IsRequired = true)] 38 | public RedisConnectType Type 39 | { 40 | get 41 | { 42 | return (RedisConnectType)base["Type"]; 43 | } 44 | set 45 | { 46 | base["Type"] = value; 47 | } 48 | } 49 | 50 | /// 51 | /// 密码 52 | /// 53 | [ConfigurationProperty("Password", IsRequired = false)] 54 | public string Password 55 | { 56 | get 57 | { 58 | return (string)base["Password"]; 59 | } 60 | set 61 | { 62 | base["Password"] = value; 63 | } 64 | } 65 | 66 | /// 67 | /// 主Redis库,亦可是sentinel服务器地址 68 | /// 69 | [ConfigurationProperty("Masters", IsRequired = true)] 70 | public string Masters 71 | { 72 | get 73 | { 74 | return (string)base["Masters"]; 75 | } 76 | set 77 | { 78 | base["Masters"] = value; 79 | } 80 | } 81 | 82 | /// 83 | /// 从redis库 84 | /// 85 | [ConfigurationProperty("Slaves", IsRequired = false)] 86 | public string Slaves 87 | { 88 | get 89 | { 90 | return (string)base["Slaves"]; 91 | } 92 | set 93 | { 94 | base["Slaves"] = value; 95 | } 96 | } 97 | 98 | /// 99 | /// 哨兵模式下服务名称 100 | /// 101 | [ConfigurationProperty("ServiceName", IsRequired = false, DefaultValue = "mymaster")] 102 | public string ServiceName 103 | { 104 | get 105 | { 106 | return (string)base["ServiceName"]; 107 | } 108 | set 109 | { 110 | base["ServiceName"] = value; 111 | } 112 | } 113 | 114 | /// 115 | /// 非集群模式下可以指定读写db 116 | /// 117 | [ConfigurationProperty("DefaultDatabase", IsRequired = false, DefaultValue = 0)] 118 | public int DefaultDatabase 119 | { 120 | get 121 | { 122 | return (int)base["DefaultDatabase"]; 123 | } 124 | set 125 | { 126 | base["DefaultDatabase"] = value; 127 | } 128 | } 129 | 130 | /// 131 | /// 管理员模式 132 | /// 133 | [ConfigurationProperty("AllowAdmin", IsRequired = false, DefaultValue = true)] 134 | public bool AllowAdmin 135 | { 136 | get 137 | { 138 | return (bool)base["AllowAdmin"]; 139 | } 140 | set 141 | { 142 | base["AllowAdmin"] = value; 143 | } 144 | } 145 | 146 | /// 147 | /// 连接保持(s) 148 | /// 149 | [ConfigurationProperty("KeepAlive", IsRequired = false, DefaultValue = 180)] 150 | public int KeepAlive 151 | { 152 | get 153 | { 154 | return (int)base["KeepAlive"]; 155 | } 156 | set 157 | { 158 | base["KeepAlive"] = value; 159 | } 160 | } 161 | 162 | /// 163 | /// 连接超时(ms) 164 | /// 165 | [ConfigurationProperty("ConnectTimeout", IsRequired = false, DefaultValue = 10 * 1000)] 166 | public int ConnectTimeout 167 | { 168 | get 169 | { 170 | return (int)base["ConnectTimeout"]; 171 | } 172 | set 173 | { 174 | base["ConnectTimeout"] = value; 175 | } 176 | } 177 | 178 | /// 179 | /// 重连次数 180 | /// 181 | [ConfigurationProperty("ConnectRetry", IsRequired = false, DefaultValue = 1)] 182 | public int ConnectRetry 183 | { 184 | get 185 | { 186 | return (int)base["ConnectRetry"]; 187 | } 188 | set 189 | { 190 | base["ConnectRetry"] = value; 191 | } 192 | } 193 | 194 | /// 195 | /// 任务忙重试次数 196 | /// 0-10000之间的整数 197 | /// 198 | [ConfigurationProperty("BusyRetry", IsRequired = false, DefaultValue = 10000)] 199 | public int BusyRetry 200 | { 201 | get 202 | { 203 | return (int)base["BusyRetry"]; 204 | } 205 | set 206 | { 207 | base["BusyRetry"] = value; 208 | } 209 | } 210 | 211 | /// 212 | /// 重试等待时长(ms) 213 | /// 214 | [ConfigurationProperty("BusyRetryWaitMS", IsRequired = false, DefaultValue = 15000)] 215 | public int BusyRetryWaitMS 216 | { 217 | get 218 | { 219 | return (int)base["BusyRetryWaitMS"]; 220 | } 221 | set 222 | { 223 | base["BusyRetryWaitMS"] = value; 224 | } 225 | } 226 | 227 | /// 228 | /// 连接池大小 229 | /// 230 | [ConfigurationProperty("PoolSize", IsRequired = false, DefaultValue = 1)] 231 | public int PoolSize 232 | { 233 | get 234 | { 235 | return (int)base["PoolSize"]; 236 | } 237 | set 238 | { 239 | base["PoolSize"] = value; 240 | } 241 | } 242 | 243 | 244 | /// 245 | /// 命令超时时间 (ms) 246 | /// 247 | [ConfigurationProperty("CommandTimeout", IsRequired = false, DefaultValue = 60000)] 248 | public int CommandTimeout 249 | { 250 | get 251 | { 252 | return (int)base["CommandTimeout"]; 253 | } 254 | set 255 | { 256 | base["CommandTimeout"] = value; 257 | } 258 | } 259 | 260 | /// 261 | /// 扩展 262 | /// 有一些redis因为禁用了某些命令需要添加如下部分 263 | /// $CLIENT=,$CLUSTER=,$CONFIG=,$ECHO=,$INFO=,$PING= 264 | /// 265 | [ConfigurationProperty("Extention", IsRequired = false, DefaultValue = "")] 266 | public string Extention 267 | { 268 | get 269 | { 270 | return (string)base["Extention"]; 271 | } 272 | set 273 | { 274 | base["Extention"] = value; 275 | } 276 | } 277 | 278 | #region 从配置文件中创建redis配置类 279 | 280 | /// 281 | /// 获取默认redis配置类 282 | /// 283 | /// 284 | public static RedisConfig GetConfig() 285 | { 286 | return (RedisConfig)ConfigurationManager.GetSection("RedisConfig"); 287 | } 288 | 289 | /// 290 | /// 获取指定的redis配置类 291 | /// 292 | /// 293 | /// 294 | public static RedisConfig GetConfig(string sectionName) 295 | { 296 | var section = (RedisConfig)ConfigurationManager.GetSection(sectionName); 297 | // 跟默认配置相同的,可以省略 298 | if (section == null) 299 | section = GetConfig(); 300 | if (section == null) 301 | throw new ConfigurationErrorsException("rediscofig节点 " + sectionName + " 未配置."); 302 | section.SectionName = sectionName; 303 | return section; 304 | } 305 | 306 | /// 307 | /// 从指定位置读取配置 308 | /// 309 | /// 310 | /// 311 | /// 312 | public static RedisConfig GetConfig(string fileName, string sectionName) 313 | { 314 | return GetConfig(ConfigurationManager.OpenMappedMachineConfiguration(new ConfigurationFileMap(fileName)), 315 | sectionName); 316 | } 317 | 318 | /// 319 | /// 从指定Configuration中读取配置 320 | /// 321 | /// 322 | /// 323 | /// 324 | public static RedisConfig GetConfig(Configuration config, string sectionName) 325 | { 326 | if (config == null) 327 | throw new ConfigurationErrorsException("传入的配置不能为空"); 328 | var section = (RedisConfig)config.GetSection(sectionName); 329 | if (section == null) 330 | throw new ConfigurationErrorsException("rediscofng节点 " + sectionName + " 未配置."); 331 | section.SectionName = sectionName; 332 | return section; 333 | } 334 | 335 | #endregion 336 | } 337 | } -------------------------------------------------------------------------------- /Wenli.Drive.Redis/RedisConnectType.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis 6 | *类 名 称:RedisConnectType 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/30 9:37:49 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/30 9:37:49 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Text; 21 | 22 | namespace Wenli.Drive.Redis 23 | { 24 | /// 25 | /// redis连接类型 26 | /// 27 | public enum RedisConnectType 28 | { 29 | /// 30 | /// Instance 31 | /// 32 | Instance = 0, 33 | /// 34 | /// Sentinel 35 | /// 36 | Sentinel = 1, 37 | /// 38 | /// Cluster 39 | /// 40 | Cluster = 2 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/RedisHelper.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2016-2020 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:1cc1fa2b-c8c8-4627-bb40-dece98f2b73a 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:Wenli.Drive.Redis 10 | * 命名空间:Wenli.Drive.Redis 11 | * 创建时间:2016/12/28 9:59:30 12 | * 创建人:wenli 13 | * 创建说明: 14 | *****************************************************************************************************/ 15 | using System; 16 | using Wenli.Drive.Redis.Interface; 17 | using Wenli.Drive.Redis.Tool; 18 | 19 | namespace Wenli.Drive.Redis 20 | { 21 | /// 22 | /// redis容器类 23 | /// 此类不要直接new(),需要用RedisHelperBuilder来构造 24 | /// 25 | public class RedisHelper 26 | { 27 | private IRedisHelper _redisHelper; 28 | 29 | /// 30 | /// redis容器类 31 | /// 32 | internal RedisHelper() { } 33 | 34 | /// 35 | /// ioc所需初始化方法 36 | /// 37 | /// 38 | internal void CreateInstance(IRedisHelper redisHelper) 39 | { 40 | _redisHelper = redisHelper.DeepCloneForDynamic(); 41 | } 42 | 43 | /// 44 | /// 初始化 45 | /// 使用RedisHelperBuilder.Build请不要调用此方法 46 | /// 47 | /// 48 | /// 49 | internal void Init(string sectionName) 50 | { 51 | _redisHelper.Init(sectionName); 52 | } 53 | 54 | /// 55 | /// 自定义初始化 56 | /// 57 | /// 58 | /// 59 | /// 60 | /// 61 | internal void Init(string name, string ipPort, string passwords, RedisConnectType type = RedisConnectType.Instance) 62 | { 63 | _redisHelper.Init(name, type, ipPort, passwords); 64 | } 65 | 66 | /// 67 | /// 初始化 68 | /// 69 | /// 70 | /// 71 | /// 72 | /// 73 | /// 74 | /// 75 | /// 76 | /// 77 | internal void Init(string sectionName, RedisConnectType type, string master, string password = "", string serviceName = "", int poolSize = 1, int busyRetry = 10, int busyRetryWaitMS = 1000) 78 | { 79 | _redisHelper.Init(sectionName, type, master, password, serviceName); 80 | } 81 | 82 | /// 83 | /// 初始化 84 | /// 使用RedisHelperBuilder.Build请不要调用此方法 85 | /// 86 | /// 87 | internal void Init(RedisConfig config) 88 | { 89 | _redisHelper.Init(config); 90 | } 91 | 92 | /// 93 | /// redis操作 94 | /// 95 | /// 96 | /// 97 | public IRedisOperation GetRedisOperation(int dbIndex = -1) 98 | { 99 | return _redisHelper.GetRedisOperation(dbIndex); 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /Wenli.Drive.Redis/RedisHelperBuilder.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2016-2020 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:1cc1fa2b-c8c8-4627-bb40-dece98f2b73a 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:Wenli.Drive.Redis 10 | * 命名空间:Wenli.Drive.Redis 11 | * 创建时间:2016/12/28 9:59:30 12 | * 创建人:wenli 13 | * 创建说明: 14 | *****************************************************************************************************/ 15 | using System; 16 | using System.Linq; 17 | using System.Reflection; 18 | using Wenli.Drive.Redis.Interface; 19 | 20 | namespace Wenli.Drive.Redis 21 | { 22 | /// 23 | /// RedisClient容器 24 | /// 25 | public static class RedisHelperBuilder 26 | { 27 | private static readonly string _AssemName; 28 | 29 | private static readonly string _TObjectName; 30 | 31 | private static readonly IRedisHelper _instance; 32 | 33 | /// 34 | /// RedisClient容器 35 | /// 36 | static RedisHelperBuilder() 37 | { 38 | var redisClientConfig = "Wenli.Drive.Redis.Core.SERedisHelper;Wenli.Drive.Redis,Version=2.1.0.5,Culture=neutral,PublicKeyToken=null"; 39 | var appStrArr = redisClientConfig.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries); 40 | _TObjectName = appStrArr[0]; 41 | _AssemName = appStrArr[1]; 42 | 43 | _instance = Assembly.Load(_AssemName).CreateInstance(_TObjectName) as IRedisHelper; 44 | } 45 | 46 | /// 47 | /// 根据指定配置依赖注入产生一个新的实例 48 | /// 49 | /// 50 | /// redis配置实例名称RedisInfo 51 | /// 52 | public static RedisHelper Build(string sectionName) 53 | { 54 | var redisHelper = new RedisHelper(); 55 | redisHelper.CreateInstance(_instance); 56 | redisHelper.Init(sectionName); 57 | return redisHelper; 58 | } 59 | 60 | /// 61 | /// 根据指定配置产生一个新的实例 62 | /// 63 | /// 64 | /// 65 | public static RedisHelper Build(RedisConfig config) 66 | { 67 | var redisHelper = new RedisHelper(); 68 | redisHelper.CreateInstance(_instance); 69 | redisHelper.Init(config); 70 | return redisHelper; 71 | } 72 | 73 | /// 74 | /// 自定义指定配置连接 75 | /// 76 | /// 这个并非是配置节点名,而是逻辑名 77 | /// 78 | /// 79 | /// 80 | /// 81 | public static RedisHelper Build(string name, string ipPort, string passwords, RedisConnectType type = 0) 82 | { 83 | var redisHelper = new RedisHelper(); 84 | redisHelper.CreateInstance(_instance); 85 | redisHelper.Init(name, ipPort, passwords, type); 86 | return redisHelper; 87 | } 88 | 89 | static void Check(params string[] args) 90 | { 91 | if (args == null || !args.Any()) throw new ArgumentNullException("RedisHelperBuilder.Build 必填参数不能空!"); 92 | 93 | foreach (var item in args) 94 | { 95 | if (string.IsNullOrEmpty(item)) 96 | throw new ArgumentNullException("RedisHelperBuilder.Build 必填参数不能空!"); 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /Wenli.Drive.Redis/RedisLocker.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:Wenli.Drive.Redis 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:Wenli.Drive.Redis 6 | *类 名 称:RedisLocker 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2019/10/18 9:38:07 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2019/10/18 9:38:07 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using System; 19 | 20 | namespace Wenli.Drive.Redis 21 | { 22 | /// 23 | /// 分布式锁, 24 | /// using(RedisLocker) 25 | /// 26 | public class RedisLocker : IDisposable 27 | { 28 | RedisHelper _redisHelper = null; 29 | 30 | /// 31 | /// 分布式锁 32 | /// 33 | /// 34 | /// 35 | /// 36 | /// 37 | public RedisLocker(RedisConfig redisConfig, string key, int timeout = 30 * 1000, int rolling = 50) 38 | { 39 | _redisHelper = RedisHelperBuilder.Build(redisConfig); 40 | 41 | if (!_redisHelper.GetRedisOperation().Lock(key, timeout, rolling)) 42 | { 43 | throw new Exception("RedisLocker申请锁已超时!"); 44 | } 45 | } 46 | 47 | /// 48 | /// 分布式锁 49 | /// 50 | /// 51 | /// 52 | /// 53 | /// 54 | /// 55 | /// 56 | /// 57 | public RedisLocker(string serviceName, string ipPort, string passwords, RedisConnectType type, string key, int timeout = 30 * 1000, int rolling = 50) 58 | { 59 | _redisHelper = RedisHelperBuilder.Build(serviceName, ipPort, passwords, type); 60 | 61 | if (!_redisHelper.GetRedisOperation().Lock(key, timeout, rolling)) 62 | { 63 | throw new Exception("RedisLocker申请锁已超时!"); 64 | } 65 | } 66 | 67 | /// 68 | /// dispose 69 | /// 70 | public void Dispose() 71 | { 72 | _redisHelper.GetRedisOperation().UnLock(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Tool/SerializeHelper.cs: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************** 2 | * 本代码版权归@wenli所有,All Rights Reserved (C) 2015-2016 3 | ***************************************************************************************************** 4 | * CLR版本:4.0.30319.42000 5 | * 唯一标识:c52026f9-2b01-46ec-a21d-76ab1d34270f 6 | * 机器名称:WENLI-PC 7 | * 联系人邮箱:wenguoli_520@qq.com 8 | ***************************************************************************************************** 9 | * 项目名称:$projectname$ 10 | * 命名空间:Wenli.Drive.Redis.Tool 11 | * 类名称:SerializeHelper 12 | * 创建时间:2016/12/28 9:56:53 13 | * 创建人:wenli 14 | * 创建说明: 15 | *****************************************************************************************************/ 16 | using System; 17 | using System.IO; 18 | using System.Runtime.Serialization.Formatters.Binary; 19 | using Newtonsoft.Json; 20 | 21 | namespace Wenli.Drive.Redis.Tool 22 | { 23 | /// 24 | /// 序列化类 25 | /// 26 | public static class SerializeHelper 27 | { 28 | /// 29 | /// newton.json序列化,日志参数专用 30 | /// 31 | /// 32 | /// 33 | public static string Serialize(object obj) 34 | { 35 | JsonSerializerSettings jsetting = new JsonSerializerSettings(); 36 | jsetting.ObjectCreationHandling = ObjectCreationHandling.Replace; 37 | jsetting.DateFormatString = "yyyy-MM-dd HH:mm:ss.fff"; 38 | return JsonConvert.SerializeObject(obj, jsetting); 39 | } 40 | 41 | /// 42 | /// newton.json反序列化,日志参数专用 43 | /// 44 | /// 45 | /// 46 | /// 47 | public static T Deserialize(string json) 48 | { 49 | JsonSerializerSettings jsetting = new JsonSerializerSettings(); 50 | jsetting.ObjectCreationHandling = ObjectCreationHandling.Replace; 51 | jsetting.DateFormatString = "yyyy-MM-dd HH:mm:ss.fff"; 52 | return JsonConvert.DeserializeObject(json, jsetting); 53 | } 54 | 55 | /// 56 | /// newton.json反序列化 57 | /// 58 | /// 59 | /// 反序列化的类型 60 | /// 61 | public static object Deserialize(string json, Type type) 62 | { 63 | JsonSerializerSettings settings = new JsonSerializerSettings(); 64 | settings.ObjectCreationHandling = ObjectCreationHandling.Replace; 65 | settings.DateFormatString = "yyyy-MM-dd HH:mm:ss.fff"; 66 | return JsonConvert.DeserializeObject(json, type, settings); 67 | } 68 | 69 | /// 70 | /// 二进制序列化 71 | /// 72 | /// 73 | /// 74 | public static byte[] ByteSerialize(object obj) 75 | { 76 | 77 | using (MemoryStream m = new MemoryStream()) 78 | { 79 | 80 | BinaryFormatter bin = new BinaryFormatter(); 81 | bin.Serialize(m, obj); 82 | 83 | return m.ToArray(); 84 | 85 | } 86 | 87 | } 88 | 89 | /// 90 | /// 二进制反序列化 91 | /// 92 | /// 93 | /// 94 | /// 95 | public static T ByteDeserialize(byte[] buffer) 96 | { 97 | 98 | using (MemoryStream m = new MemoryStream()) 99 | { 100 | 101 | m.Write(buffer, 0, buffer.Length); 102 | m.Position = 0; 103 | 104 | BinaryFormatter bin = new BinaryFormatter(); 105 | 106 | return (T)bin.Deserialize(m); 107 | 108 | } 109 | 110 | } 111 | 112 | /// 113 | /// 深复制当前对象 114 | /// 115 | /// 116 | /// 117 | /// 118 | public static dynamic DeepCloneForDynamic(this T obj) 119 | { 120 | var json = Serialize(obj); 121 | return Deserialize(json, obj.GetType()); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/Wenli.Drive.Redis.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 2.2.8.8 6 | This is a.Net redis integrated driver to support a single instance, cluster, sentinel and other modes of data manipulation 这是一个.net 的redis集成驱动,支持单实例、云集群、哨兵等模式的数据操作 7 | Wenli.Drive.Redis @ yswenli 8 | Wenli.Drive.Redis,Redis,RedisCluster,RedisSentinel 9 | true 10 | true 11 | https://raw.githubusercontent.com/yswenli/RedisDrive/master/LICENSE 12 | This is a.Net redis integrated driver to support a single instance, cluster, sentinel and other modes of data manipulation 这是一个.net 的redis集成驱动,支持单实例、云集群、哨兵等模式的数据操作 13 | https://github.com/yswenli/RedisDrive 14 | http://www.cnblogs.com/yswenli/p/6235765.html 15 | 2.2.8.8 16 | 2.2.8.8 17 | 18 | 19 | 20 | x64 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Wenli.Drive.Redis/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /wenli.drive.redis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/RedisDrive/ed8f81d5bb8d56a11567b8d3f7d360b90ceb1bc6/wenli.drive.redis.png --------------------------------------------------------------------------------