├── .gitignore ├── .idea ├── compiler.xml ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── libraries │ ├── Maven__aopalliance_aopalliance_1_0.xml │ ├── Maven__ch_qos_logback_logback_classic_1_0_13.xml │ ├── Maven__ch_qos_logback_logback_core_1_0_13.xml │ ├── Maven__com_google_guava_guava_19_0.xml │ ├── Maven__commons_logging_commons_logging_1_2.xml │ ├── Maven__javax_servlet_servlet_api_2_5.xml │ ├── Maven__junit_junit_4_8_2.xml │ ├── Maven__org_apache_commons_commons_pool2_2_4_2.xml │ ├── Maven__org_aspectj_aspectjweaver_1_8_6.xml │ ├── Maven__org_slf4j_jcl_over_slf4j_1_7_25.xml │ ├── Maven__org_slf4j_slf4j_api_1_7_7.xml │ ├── Maven__org_springframework_data_spring_data_commons_1_13_6_RELEASE.xml │ ├── Maven__org_springframework_data_spring_data_keyvalue_1_2_6_RELEASE.xml │ ├── Maven__org_springframework_data_spring_data_redis_1_8_6_RELEASE.xml │ ├── Maven__org_springframework_spring_aop_4_2_1_RELEASE.xml │ ├── Maven__org_springframework_spring_aspects_4_2_1_RELEASE.xml │ ├── Maven__org_springframework_spring_beans_4_2_1_RELEASE.xml │ ├── Maven__org_springframework_spring_context_4_2_1_RELEASE.xml │ ├── Maven__org_springframework_spring_context_support_4_2_1_RELEASE.xml │ ├── Maven__org_springframework_spring_core_4_2_1_RELEASE.xml │ ├── Maven__org_springframework_spring_expression_4_2_1_RELEASE.xml │ ├── Maven__org_springframework_spring_oxm_4_3_10_RELEASE.xml │ ├── Maven__org_springframework_spring_tx_4_3_10_RELEASE.xml │ ├── Maven__org_springframework_spring_web_4_2_1_RELEASE.xml │ ├── Maven__org_springframework_spring_webmvc_4_2_1_RELEASE.xml │ └── Maven__redis_clients_jedis_2_9_0.xml ├── misc.xml ├── modules.xml ├── smartfox_info.xml ├── uiDesigner.xml ├── vcs.xml └── workspace.xml ├── LICENSE ├── README.md ├── distributed-learn.iml ├── pom.xml ├── src ├── main │ ├── java │ │ ├── Hash │ │ │ ├── Analysis.java │ │ │ ├── GetHashCode.java │ │ │ ├── IPAddressGenerate.java │ │ │ ├── LoadBalancer.java │ │ │ ├── SortedMapWithVirtualNode.java │ │ │ └── SortedMapWithoutVirtualNode.java │ │ ├── Idenpotemce │ │ │ └── Idempotence.java │ │ ├── bloomFilter │ │ │ ├── BloomFilterCachePenetration.java │ │ │ ├── redisBloomFilter │ │ │ │ ├── BloomFilter.java │ │ │ │ ├── BloomFilterBuilder.java │ │ │ │ └── BloomFilterImpl.java │ │ │ └── simpleBloomFilter │ │ │ │ ├── BloomFilter.java │ │ │ │ ├── Hash.java │ │ │ │ └── SimpleBloomFilter.java │ │ ├── limit │ │ │ ├── countLimit │ │ │ │ └── RedisLimit.java │ │ │ └── tokenBucketLimit │ │ │ │ ├── BucketToken.java │ │ │ │ └── RedisLimitToken.java │ │ ├── lock │ │ │ └── RedisLock.java │ │ └── util │ │ │ └── ScriptUtil.java │ └── resources │ │ ├── limit.lua │ │ ├── limitToken.lua │ │ └── lock.lua └── test │ └── java │ ├── BloomFilter │ ├── BloomFilterCachePenetrationTest.java │ ├── GuavaBloomFilterTest.java │ └── SimpleBloomFilterTest.java │ ├── Hash │ ├── FNVIHASHTest.java │ ├── SortedMapWithVirtualNodeTest.java │ ├── SortedMapWithoutVirtualNodeTest.java │ └── StringHashCodeTest.java │ ├── Idenpotemce │ └── IdempotenceTest.java │ ├── limit │ ├── RedisLimitTest.java │ └── RedisLimitTokenTest.java │ └── lock │ └── RedisLockTest.java └── target └── classes ├── limit.lua ├── limitToken.lua └── lock.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 94 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__aopalliance_aopalliance_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__ch_qos_logback_logback_classic_1_0_13.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__ch_qos_logback_logback_core_1_0_13.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_google_guava_guava_19_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__commons_logging_commons_logging_1_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__javax_servlet_servlet_api_2_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__junit_junit_4_8_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_apache_commons_commons_pool2_2_4_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_aspectj_aspectjweaver_1_8_6.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_slf4j_jcl_over_slf4j_1_7_25.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_7.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_data_spring_data_commons_1_13_6_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_data_spring_data_keyvalue_1_2_6_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_data_spring_data_redis_1_8_6_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_aop_4_2_1_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_aspects_4_2_1_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_beans_4_2_1_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_context_4_2_1_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_context_support_4_2_1_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_core_4_2_1_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_expression_4_2_1_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_oxm_4_3_10_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_tx_4_3_10_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_web_4_2_1_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_webmvc_4_2_1_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__redis_clients_jedis_2_9_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/smartfox_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 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 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 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 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | 168 | 169 | 170 | 171 | 172 | true 173 | DEFINITION_ORDER 174 | 175 | 176 | 181 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 347 | 348 | 349 | 350 | 356 | 357 | 358 | 359 | 362 | 363 | 375 | 376 | 377 | 378 | 381 | 382 | 394 | 395 | 396 | 397 | 400 | 401 | 413 | 414 | 415 | 416 | 419 | 420 | 432 | 433 | 434 | 435 | 438 | 439 | 451 | 452 | 453 | 465 | 466 | 467 | 468 | 482 | 483 | 484 | 485 | 486 | 487 | 497 | 498 | 504 | 505 | 506 | 507 | 525 | 531 | 532 | 533 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 1553673990344 564 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 675 | 676 | 678 | 679 | 680 | 681 | 682 | jar://$MAVEN_REPOSITORY$/redis/clients/jedis/2.9.0/jedis-2.9.0.jar!/redis/clients/jedis/Jedis.class 683 | 1016 684 | 685 | 687 | 689 | 690 | 692 | 693 | 694 | 695 | 696 | 697 | hashes 698 | JAVA 699 | CODE_FRAGMENT 700 | 701 | 702 | map.get(nodes[i]) 703 | JAVA 704 | CODE_FRAGMENT 705 | 706 | 707 | map 708 | JAVA 709 | CODE_FRAGMENT 710 | 711 | 712 | 8973/10000.0 713 | JAVA 714 | CODE_FRAGMENT 715 | 716 | 717 | stayRequestCount 718 | JAVA 719 | CODE_FRAGMENT 720 | 721 | 722 | stayRequestCount / 10000L 723 | JAVA 724 | CODE_FRAGMENT 725 | 726 | 727 | 1L - stayRequestCount / 10000L 728 | JAVA 729 | CODE_FRAGMENT 730 | 731 | 732 | treeMapHash 733 | JAVA 734 | CODE_FRAGMENT 735 | 736 | 737 | script 738 | JAVA 739 | CODE_FRAGMENT 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1.8 1112 | 1113 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1.8 1124 | 1125 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # distributed-learn 2 | 3 | ## 分布式学习 4 | 5 | - 基于 Redis 的分布式锁 6 | - 基于 Redis + Lua 的计数限流 7 | - 基于 Redis + Lua 的令牌桶限流 8 | - 一致性 Hash 算法 Java 实现(不带虚拟节点 + 虚拟节点) 9 | - 基于乘法 Hash 的布隆过滤器 Java 实现 10 | - 基于 Redis BitMap 的布隆过滤器 Java 实现 11 | - 基于布隆过滤器的缓存穿透解决方案 12 | - 基于 Redis 分布式锁保证服务幂等性 13 | 14 | ## 相关博客 15 | 16 | - [分布式锁]() 17 | - [浅析限流算法]() 18 | - [一致性 Hash 算法 Java 实现]() 19 | - [浅析布隆过滤器]() 20 | - [如何保证服务的幂等性]() 21 | -------------------------------------------------------------------------------- /distributed-learn.iml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.daydreamdev 8 | daydreamdev 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 1.0.13 13 | 1.7.7 14 | 4.2.1.RELEASE 15 | 16 | 17 | 18 | 19 | redis.clients 20 | jedis 21 | 2.9.0 22 | 23 | 24 | org.springframework.data 25 | spring-data-redis 26 | 1.8.6.RELEASE 27 | 28 | 29 | junit 30 | junit 31 | 4.8.2 32 | test 33 | 34 | 35 | 36 | com.google.guava 37 | guava 38 | 19.0 39 | 40 | 41 | 42 | 43 | 44 | org.slf4j 45 | slf4j-api 46 | ${slf4j.version} 47 | 48 | 49 | 50 | 51 | ch.qos.logback 52 | logback-core 53 | ${logback.version} 54 | 55 | 56 | ch.qos.logback 57 | logback-classic 58 | ${logback.version} 59 | 60 | 61 | 62 | 63 | org.springframework 64 | spring-aop 65 | ${spring.version} 66 | 67 | 68 | org.springframework 69 | spring-aspects 70 | ${spring.version} 71 | 72 | 73 | org.springframework 74 | spring-beans 75 | ${spring.version} 76 | 77 | 78 | org.springframework 79 | spring-context 80 | ${spring.version} 81 | 82 | 83 | org.springframework 84 | spring-context-support 85 | ${spring.version} 86 | 87 | 88 | org.springframework 89 | spring-core 90 | ${spring.version} 91 | 92 | 93 | org.springframework 94 | spring-web 95 | ${spring.version} 96 | 97 | 98 | org.springframework 99 | spring-webmvc 100 | ${spring.version} 101 | 102 | 103 | 104 | javax.servlet 105 | servlet-api 106 | 2.5 107 | provided 108 | 109 | 110 | 111 | 112 | 113 | 114 | src/main/java 115 | 116 | 117 | org.apache.maven.plugins 118 | maven-compiler-plugin 119 | 3.1 120 | 121 | 1.8 122 | 1.8 123 | 124 | 125 | 126 | 127 | org.jacoco 128 | jacoco-maven-plugin 129 | 0.7.7.201606060606 130 | 131 | 132 | 133 | prepare-agent 134 | 135 | 136 | 137 | report 138 | test 139 | 140 | report 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-source-plugin 150 | 2.2.1 151 | 152 | 153 | package 154 | 155 | jar-no-fork 156 | 157 | 158 | 159 | 160 | 161 | 162 | org.apache.maven.plugins 163 | maven-javadoc-plugin 164 | 2.9.1 165 | 166 | 167 | package 168 | 169 | jar 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | release 183 | 184 | 185 | 186 | 187 | org.apache.maven.plugins 188 | maven-source-plugin 189 | 2.2.1 190 | 191 | 192 | package 193 | 194 | jar-no-fork 195 | 196 | 197 | 198 | 199 | 200 | 201 | org.apache.maven.plugins 202 | maven-javadoc-plugin 203 | 2.9.1 204 | 205 | 206 | package 207 | 208 | jar 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /src/main/java/Hash/Analysis.java: -------------------------------------------------------------------------------- 1 | package Hash; 2 | 3 | import java.util.Collection; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * @auther G.Fukang 11 | * @date 4/4 16:59 12 | */ 13 | public class Analysis { 14 | 15 | // <节点,服务器> 16 | public double analysis(HashMap map) { 17 | Iterator> iterator = map.entrySet().iterator(); 18 | // 计数 map 19 | ConcurrentHashMap countMap = new ConcurrentHashMap<>(); 20 | while (iterator.hasNext()) { 21 | Map.Entry entry = iterator.next(); 22 | String server = entry.getValue(); 23 | if (countMap.containsKey(server)) { 24 | countMap.put(server, countMap.get(server) + 1); 25 | } else { 26 | countMap.put(server, 1); 27 | } 28 | } 29 | Collection values = countMap.values(); 30 | Iterator val = values.iterator(); 31 | int count = 0; 32 | int[] res = new int[values.size()]; 33 | while (val.hasNext()) { 34 | res[count] = val.next(); 35 | } 36 | return variance(res); 37 | } 38 | 39 | // 求方差 40 | private static double variance(int[] arr) { 41 | int m = arr.length; 42 | double sum = 0; 43 | for (int i = 0; i < m; i++) { 44 | sum += arr[i]; 45 | } 46 | double dAve = sum / m; 47 | double dVar = 0; 48 | for (int i = 0; i < m; i++) { 49 | dVar += (arr[i] - dAve) * (arr[i] - dAve); 50 | } 51 | return dVar / m; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/Hash/GetHashCode.java: -------------------------------------------------------------------------------- 1 | package Hash; 2 | 3 | /** 4 | * 使用 FNVI_32_HASH 算法 5 | * 6 | * @auther G.Fukang 7 | * @date 4/4 9:53 8 | */ 9 | public class GetHashCode { 10 | 11 | private static final long FNV_32_INIT = 2166136261L; 12 | private static final int FNV_32_PRIME = 16777619; 13 | 14 | public int getHashCode(String origin) { 15 | 16 | final int p = FNV_32_PRIME; 17 | int hash = (int)FNV_32_INIT; 18 | for (int i = 0; i < origin.length(); i++) { 19 | hash = (hash ^ origin.charAt(i)) * p; 20 | } 21 | hash += hash << 13; 22 | hash ^= hash >> 7; 23 | hash += hash << 3; 24 | hash ^= hash >> 17; 25 | hash += hash << 5; 26 | hash = Math.abs(hash); 27 | 28 | return hash; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/Hash/IPAddressGenerate.java: -------------------------------------------------------------------------------- 1 | package Hash; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * @auther G.Fukang 7 | * @date 4/4 16:28 8 | */ 9 | public class IPAddressGenerate { 10 | 11 | public String[] getIPAddress(int num) { 12 | String[] res = new String[num]; 13 | Random random = new Random(); 14 | for (int i = 0; i < num; i++) { 15 | res[i] = String.valueOf(random.nextInt(256)) + "." + String.valueOf(random.nextInt(256)) + "." 16 | + String.valueOf(random.nextInt(256)) + "." + String.valueOf(random.nextInt(256)) + ":" 17 | + String.valueOf(random.nextInt(9999)); 18 | } 19 | return res; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/Hash/LoadBalancer.java: -------------------------------------------------------------------------------- 1 | package Hash; 2 | 3 | 4 | import java.util.HashMap; 5 | 6 | /** 7 | * @auther G.Fukang 8 | * @date 4/4 9:50 9 | */ 10 | public interface LoadBalancer { 11 | 12 | // 添加服务器节点 13 | public void addServerNode(String serverNodeName); 14 | 15 | // 删除服务器节点 16 | public void delServerNode(String serverNodeName); 17 | 18 | // 选择服务器节点 19 | public String selectServerNode(String requestURL); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/Hash/SortedMapWithVirtualNode.java: -------------------------------------------------------------------------------- 1 | package Hash; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.*; 7 | 8 | /** 9 | * @auther G.Fukang 10 | * @date 4/4 15:56 11 | */ 12 | public class SortedMapWithVirtualNode implements LoadBalancer { 13 | 14 | private static Logger logger = LoggerFactory.getLogger(SortedMapWithVirtualNode.class); 15 | 16 | private TreeMap treeMapHash; 17 | 18 | @Override 19 | public void addServerNode(String serverNodeName) { 20 | int hash = new GetHashCode().getHashCode(serverNodeName); 21 | treeMapHash.put(hash, serverNodeName); 22 | // logger.info("服务器节点:{} 上线", serverNodeName); 23 | } 24 | 25 | @Override 26 | public void delServerNode(String serverNodeName) { 27 | int hash = new GetHashCode().getHashCode(serverNodeName); 28 | treeMapHash.remove(hash); 29 | logger.info("服务器节点:{} 下线", serverNodeName); 30 | } 31 | 32 | @Override 33 | public String selectServerNode(String requestURL) { 34 | int hash = new GetHashCode().getHashCode(requestURL); 35 | // 向右寻找第一个 key 36 | Map.Entry subEntry = treeMapHash.ceilingEntry(hash); 37 | // 设置成一个环,如果超过尾部,则取第一个点 38 | subEntry = subEntry == null ? treeMapHash.firstEntry() : subEntry; 39 | String VNNode = subEntry.getValue(); 40 | // logger.info("虚拟节点:" + "[" + requestURL + "] -> " + VNNode); 41 | return VNNode.substring(0, VNNode.indexOf("&&")); 42 | } 43 | 44 | // 构建 Hash 环 45 | public SortedMap buildHash(TreeMap treeMap) { 46 | this.treeMapHash = treeMap; 47 | return treeMapHash; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/Hash/SortedMapWithoutVirtualNode.java: -------------------------------------------------------------------------------- 1 | package Hash; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.*; 7 | 8 | /** 9 | * @auther G.Fukang 10 | * @date 4/4 10:26 11 | */ 12 | public class SortedMapWithoutVirtualNode implements LoadBalancer { 13 | 14 | private static Logger logger = LoggerFactory.getLogger(SortedMapWithoutVirtualNode.class); 15 | 16 | private TreeMap treeMapHash; 17 | 18 | @Override 19 | public void addServerNode(String serverNodeName) { 20 | int hash = new GetHashCode().getHashCode(serverNodeName); 21 | treeMapHash.put(hash, serverNodeName); 22 | } 23 | 24 | @Override 25 | public void delServerNode(String serverNodeName) { 26 | int hash = new GetHashCode().getHashCode(serverNodeName); 27 | treeMapHash.remove(hash); 28 | logger.info("服务器节点:{} 下线", serverNodeName); 29 | } 30 | 31 | @Override 32 | public String selectServerNode(String requestURL) { 33 | int hash = new GetHashCode().getHashCode(requestURL); 34 | // 向右寻找第一个 key 35 | Map.Entry subEntry= treeMapHash.ceilingEntry(hash); 36 | // 设置成一个环,如果超过尾部,则取第一个点 37 | subEntry = subEntry == null ? treeMapHash.firstEntry() : subEntry; 38 | return subEntry.getValue(); 39 | } 40 | 41 | // 构建 Hash 环 42 | public SortedMap buildHash(TreeMap treeMap) { 43 | this.treeMapHash = treeMap; 44 | return treeMapHash; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/Idenpotemce/Idempotence.java: -------------------------------------------------------------------------------- 1 | package Idenpotemce; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.data.redis.connection.RedisConnection; 6 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 7 | import redis.clients.jedis.Jedis; 8 | 9 | /** 10 | * 保证幂等性 11 | * 12 | * @auther G.Fukang 13 | * @date 4/1 20:16 14 | */ 15 | public class Idempotence { 16 | 17 | private static Logger logger = LoggerFactory.getLogger(Idempotence.class); 18 | 19 | // 前缀 20 | private String idempotencePrfix; 21 | 22 | private JedisConnectionFactory jedisConnectionFactory; 23 | 24 | // lua 25 | private String script; 26 | 27 | private Idempotence(Builder builder) { 28 | this.jedisConnectionFactory = builder.jedisConnectionFactory; 29 | this.idempotencePrfix = builder.idemPrefix; 30 | } 31 | 32 | /** 33 | * 获取 Redis 连接 34 | * 35 | * @author G.Fukang 36 | * @date: 3/31 22:20 37 | */ 38 | private Object getConnection() { 39 | Object connection; 40 | RedisConnection redisConnection = jedisConnectionFactory.getConnection(); 41 | connection = redisConnection.getNativeConnection(); 42 | return connection; 43 | } 44 | 45 | public boolean insertRequestId(String requestId, int expireTime) { 46 | // 向 Redis 中插入 requestId 47 | Object connection = getConnection(); 48 | Long setnxResult = ((Jedis) connection).setnx(idempotencePrfix + requestId, String.valueOf(expireTime)); 49 | if (setnxResult != null && setnxResult.intValue() == 1) { 50 | return true; 51 | } else { 52 | return false; 53 | } 54 | } 55 | 56 | public boolean delRequestId(String requestId) { 57 | Object connection = getConnection(); 58 | Long delResult = ((Jedis) connection).del(idempotencePrfix + requestId); 59 | if (delResult.equals(1L)) { 60 | return true; 61 | } else { 62 | return false; 63 | } 64 | } 65 | 66 | public static class Builder { 67 | 68 | private static final String DEFAULT_PREFIX = "idem_"; 69 | 70 | private JedisConnectionFactory jedisConnectionFactory = null; 71 | 72 | private String idemPrefix = DEFAULT_PREFIX; 73 | 74 | public Builder(JedisConnectionFactory jedisConnectionFactory) { 75 | this.jedisConnectionFactory = jedisConnectionFactory; 76 | } 77 | 78 | public Builder idempotencePrefix(String idemPrefix) { 79 | this.idemPrefix = idemPrefix; 80 | return this; 81 | } 82 | 83 | public Idempotence build() { 84 | return new Idempotence(this); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/bloomFilter/BloomFilterCachePenetration.java: -------------------------------------------------------------------------------- 1 | package bloomFilter; 2 | 3 | import com.google.common.hash.BloomFilter; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.data.redis.connection.RedisConnection; 7 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 8 | import org.springframework.util.StringUtils; 9 | import redis.clients.jedis.Jedis; 10 | 11 | import java.util.HashMap; 12 | 13 | /** 14 | * @auther G.Fukang 15 | * @date 4/3 20:12 16 | */ 17 | public class BloomFilterCachePenetration { 18 | 19 | private static Logger logger = LoggerFactory.getLogger(BloomFilterCachePenetration.class); 20 | 21 | private JedisConnectionFactory jedisConnectionFactory; 22 | 23 | private BloomFilter bloomFilter; 24 | 25 | // 模拟数据库 26 | private HashMap mapDB; 27 | 28 | 29 | public BloomFilterCachePenetration(Builder builder) { 30 | this.jedisConnectionFactory = builder.jedisConnectionFactory; 31 | this.bloomFilter = builder.bloomFilter; 32 | this.mapDB = builder.mapDB; 33 | } 34 | 35 | public String getByKey(String key) { 36 | // 通过 key 在 Redis 中查找 value 37 | String value = get(key); 38 | if (StringUtils.isEmpty(value)) { 39 | logger.info("Redis 没命中 {}", key); 40 | // 在 BloomFilter 中查找 41 | if (bloomFilter.mightContain(key)) { 42 | logger.info("BloomFilter 命中 {}", key); 43 | // 命中 value 为 null 44 | return value; 45 | } else { 46 | // 没命中,到数据库中查询 47 | if (mapDB.containsKey(key)) { 48 | logger.info("更新 Key {} 到 Redis", key); 49 | String valDB = mapDB.get(key); 50 | set(key, valDB); 51 | return valDB; 52 | } else { 53 | logger.info("更新 Key {} 到 BloomFilter", key); 54 | bloomFilter.put(key); 55 | return value; 56 | } 57 | } 58 | } else { 59 | logger.info("Redis 命中 {}", key); 60 | return value; 61 | } 62 | } 63 | 64 | /** 65 | * 获取 Redis 连接 66 | * 67 | * @author G.Fukang 68 | * @date: 3/31 22:20 69 | */ 70 | private Object getConnection() { 71 | Object connection; 72 | RedisConnection redisConnection = jedisConnectionFactory.getConnection(); 73 | connection = redisConnection.getNativeConnection(); 74 | return connection; 75 | } 76 | 77 | /** 78 | * Redis 插入数据 79 | * 80 | * @author G.Fukang 81 | * @date: 4/3 20:28 82 | */ 83 | private String set(String key, String value) { 84 | String result = null; 85 | Object connection = getConnection(); 86 | result = ((Jedis) connection).set(key, value); 87 | return result; 88 | } 89 | 90 | /** 91 | * Redis 92 | * 93 | * @author G.Fukang 94 | * @date: 4/3 20:37 95 | */ 96 | public String get(String key) { 97 | String result = null; 98 | Object connection = getConnection(); 99 | result = ((Jedis) connection).get(key); 100 | return result; 101 | } 102 | 103 | 104 | public static class Builder { 105 | 106 | private JedisConnectionFactory jedisConnectionFactory = null; 107 | private BloomFilter bloomFilter = null; 108 | private HashMap mapDB; 109 | 110 | public Builder(JedisConnectionFactory jedisConnectionFactory, BloomFilter bloomFilter, HashMap mapDB) { 111 | this.jedisConnectionFactory = jedisConnectionFactory; 112 | this.bloomFilter = bloomFilter; 113 | this.mapDB = mapDB; 114 | } 115 | 116 | public BloomFilterCachePenetration build() { 117 | return new BloomFilterCachePenetration(this); 118 | } 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/bloomFilter/redisBloomFilter/BloomFilter.java: -------------------------------------------------------------------------------- 1 | package bloomFilter.redisBloomFilter; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | /** 7 | * 布隆过滤器接口 8 | * @auther G.Fukang 9 | * @date 5/10 18:47 10 | */ 11 | public interface BloomFilter { 12 | 13 | /** 14 | * 是否添加成功 15 | */ 16 | boolean add(T object); 17 | 18 | /** 19 | * 批量添加 20 | */ 21 | Map batchAdd(List objectList); 22 | 23 | /** 24 | * 是否包含 25 | */ 26 | boolean contains(T object); 27 | 28 | /** 29 | * 预期插入的数量 30 | */ 31 | long getExpectedInsertions(); 32 | 33 | /** 34 | * 预期错误率 35 | */ 36 | double getFalseProbability(); 37 | 38 | /** 39 | * 布隆过滤器总长度 40 | */ 41 | long getSize(); 42 | 43 | /** 44 | * Hash 函数迭代次数 45 | */ 46 | int getHashIterations(); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/bloomFilter/redisBloomFilter/BloomFilterBuilder.java: -------------------------------------------------------------------------------- 1 | package bloomFilter.redisBloomFilter; 2 | 3 | import com.google.common.hash.HashFunction; 4 | import com.google.common.hash.Hashing; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import redis.clients.jedis.JedisPool; 8 | 9 | 10 | /** 11 | * @auther G.Fukang 12 | * @date 5/10 19:13 13 | */ 14 | public class BloomFilterBuilder { 15 | 16 | private Logger logger = LoggerFactory.getLogger(BloomFilterBuilder.class); 17 | 18 | private static final long MAX_SIZE = Integer.MAX_VALUE * 100L; 19 | 20 | private JedisPool jedisPool; 21 | 22 | // 布隆过滤器名(位图的 key) 23 | private String name; 24 | 25 | // 位图长度 26 | private long totalSize; 27 | 28 | // hash 函数循环次数 29 | private int hashIterations; 30 | 31 | // 预期插入条数 32 | private long expectedInsertions; 33 | 34 | // 预期错误率概率 35 | private double falseProbability; 36 | 37 | // Hash 函数 38 | private HashFunction hashFunction = Hashing.murmur3_128(); 39 | 40 | // 是否完成 41 | private boolean done = false; 42 | 43 | public BloomFilterBuilder(JedisPool jedisPool, String name, long expectedInsertions, double falseProbability) { 44 | this.jedisPool = jedisPool; 45 | this.name = name; 46 | this.expectedInsertions = expectedInsertions; 47 | this.falseProbability = falseProbability; 48 | } 49 | 50 | public BloomFilterBuilder setHashFunction(HashFunction hashFunction) { 51 | this.hashFunction = hashFunction; 52 | return this; 53 | } 54 | 55 | public JedisPool getJedisPool() { 56 | return jedisPool; 57 | } 58 | 59 | public String getName() { 60 | return name; 61 | } 62 | 63 | public long getTotalSize() { 64 | return totalSize; 65 | } 66 | 67 | public int getHashIterations() { 68 | return hashIterations; 69 | } 70 | 71 | public long getExpectedInsertions() { 72 | return expectedInsertions; 73 | } 74 | 75 | public double getFalseProbability() { 76 | return falseProbability; 77 | } 78 | 79 | public HashFunction getHashFunction() { 80 | return hashFunction; 81 | } 82 | 83 | public BloomFilterImpl builder() { 84 | checkBloomFilterParam(); 85 | return new BloomFilterImpl(this); 86 | } 87 | 88 | /** 89 | * 检查布隆过滤器参数 90 | */ 91 | private void checkBloomFilterParam() { 92 | if (done) { 93 | return; 94 | } 95 | 96 | if (name == null || "".equals(name.trim())) { 97 | throw new IllegalArgumentException("Bloom filter name is empty"); 98 | } 99 | 100 | if (expectedInsertions < 0 || expectedInsertions > MAX_SIZE) { 101 | throw new IllegalArgumentException("Bloom filter expectedInsertions can't be greater than " + MAX_SIZE + " or smaller than 0"); 102 | } 103 | 104 | if (falseProbability > 1) { 105 | throw new IllegalArgumentException("Bloom filter false probability cant't be greater than 1"); 106 | } 107 | 108 | if (falseProbability < 0) { 109 | throw new IllegalArgumentException("Bloom filter false probability cant't be negative"); 110 | } 111 | 112 | // 计算布隆过滤器位图的长度 113 | totalSize = optimalNumOfBits(); 114 | logger.info("{} optimalNumOfBits is {}", name, totalSize); 115 | if (totalSize == 0) { 116 | throw new IllegalArgumentException("Bloom filter calculated totalSize is " + totalSize); 117 | } 118 | 119 | if (totalSize > MAX_SIZE) { 120 | throw new IllegalArgumentException("Bloom filter totalSize can't be greater than " + MAX_SIZE + 121 | "But calculated totalSize is " + totalSize); 122 | } 123 | 124 | // Hash 函数迭代次数 125 | hashIterations = optimalNumOfHashFunctions(); 126 | logger.info("{} hashIterations is {}", name, hashFunction); 127 | 128 | done = true; 129 | } 130 | 131 | // 根据预期插入条数和概率计算布隆过滤器位图长度 132 | private long optimalNumOfBits() { 133 | if (falseProbability == 0) { 134 | falseProbability = Double.MIN_VALUE; 135 | } 136 | return (long) (-expectedInsertions * Math.log(falseProbability) / (Math.log(2) * Math.log(2))); 137 | } 138 | 139 | // 根据布隆过滤器长度与预期插入长度对比,计算 Hash 函数个数 140 | private int optimalNumOfHashFunctions() { 141 | return Math.max(1, (int) Math.round((double) totalSize / expectedInsertions * Math.log(2))); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/bloomFilter/redisBloomFilter/BloomFilterImpl.java: -------------------------------------------------------------------------------- 1 | package bloomFilter.redisBloomFilter; 2 | 3 | import com.google.common.hash.HashFunction; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import redis.clients.jedis.Jedis; 7 | import redis.clients.jedis.JedisPool; 8 | 9 | import java.util.Collections; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * @auther G.Fukang 16 | * @date 5/10 18:55 17 | */ 18 | public class BloomFilterImpl implements BloomFilter{ 19 | 20 | private Logger logger = LoggerFactory.getLogger(BloomFilterImpl.class); 21 | 22 | private BloomFilterBuilder config; 23 | 24 | public BloomFilterImpl(BloomFilterBuilder bloomFilterBuilder) { 25 | this.config = bloomFilterBuilder; 26 | } 27 | 28 | @Override 29 | public boolean add(T object) { 30 | if (object == null) { 31 | return false; 32 | } 33 | // 偏移量列表 34 | List offsetList = hash(object); 35 | if (offsetList == null || offsetList.isEmpty()) { 36 | return false; 37 | } 38 | for (Integer offset : offsetList) { 39 | Jedis jedis = null; 40 | try { 41 | jedis = getJedisPool().getResource(); 42 | // 每个 Hash 函数得到一个位置,然后将按照位置设置 bit 43 | jedis.setbit(getName(), offset, true); 44 | } catch (Exception e) { 45 | logger.error(e.getMessage(), e); 46 | return false; 47 | } finally { 48 | if (jedis != null) { 49 | jedis.close(); 50 | } 51 | } 52 | } 53 | return true; 54 | } 55 | 56 | @Override 57 | public Map batchAdd(List objectList) { 58 | if (objectList == null || objectList.isEmpty()) { 59 | return Collections.emptyMap(); 60 | } 61 | Map resultMap = new HashMap<>(); 62 | for (T object : objectList) { 63 | boolean result = add(object); 64 | resultMap.put(object, result); 65 | } 66 | return resultMap; 67 | } 68 | 69 | @Override 70 | public boolean contains(T object) { 71 | if (object == null) { 72 | return false; 73 | } 74 | // 偏移量列表 75 | List offsetList = hash(object); 76 | if (offsetList == null || offsetList.isEmpty()) { 77 | return false; 78 | } 79 | for (int offset : offsetList) { 80 | Jedis jedis = null; 81 | try { 82 | jedis = getJedisPool().getResource(); 83 | boolean result = jedis.getbit(getName(), offset); 84 | if (!result) { 85 | return false; 86 | } 87 | } catch (Exception e) { 88 | logger.error(e.getMessage(), e); 89 | } finally { 90 | if (jedis != null) { 91 | jedis.close(); 92 | } 93 | } 94 | } 95 | return true; 96 | } 97 | 98 | public BloomFilterBuilder getConfig() { 99 | return config; 100 | } 101 | 102 | @Override 103 | public long getExpectedInsertions() { 104 | return getConfig().getExpectedInsertions(); 105 | } 106 | 107 | @Override 108 | public double getFalseProbability() { 109 | return getConfig().getFalseProbability(); 110 | } 111 | 112 | @Override 113 | public long getSize() { 114 | return getConfig().getTotalSize(); 115 | } 116 | 117 | @Override 118 | public int getHashIterations() { 119 | return getConfig().getHashIterations(); 120 | } 121 | 122 | public String getName() { 123 | return getConfig().getName(); 124 | } 125 | 126 | public JedisPool getJedisPool() { 127 | return getConfig().getJedisPool(); 128 | } 129 | 130 | public HashFunction getHashFuction() { 131 | return getConfig().getHashFunction(); 132 | } 133 | 134 | 135 | public List hash(Object object) { 136 | byte[] bytes = object.toString().getBytes(); 137 | getHashFuction().hashBytes(bytes, (int)getSize(), getConfig().getHashIterations()); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/bloomFilter/simpleBloomFilter/BloomFilter.java: -------------------------------------------------------------------------------- 1 | package bloomFilter.simpleBloomFilter; 2 | 3 | /** 4 | * @auther G.Fukang 5 | * @date 4/5 15:58 6 | */ 7 | public interface BloomFilter { 8 | // 添加元素 9 | public void put(String key); 10 | // 判断元素是否存在 11 | public boolean mightContain(String key); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/bloomFilter/simpleBloomFilter/Hash.java: -------------------------------------------------------------------------------- 1 | package bloomFilter.simpleBloomFilter; 2 | 3 | /** 4 | * 乘法 Hash 5 | * 6 | * @auther G.Fukang 7 | * @date 4/5 15:20 8 | */ 9 | public class Hash { 10 | // bit 数组容量 11 | private int capacity; 12 | // 哈希因子 13 | private int seed; 14 | 15 | public Hash(int capacity, int seed) { 16 | this.capacity = capacity; 17 | this.seed = seed; 18 | } 19 | 20 | // 乘法 Hash 算法 21 | public int Hash(String key) { 22 | int res = 0; 23 | int length = key.length(); 24 | 25 | for (int i = 0; i < length; i++) { 26 | res = seed * res + key.charAt(i); 27 | } 28 | // 将结果限定在 capacity 内 29 | return (capacity - 1) & res; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/bloomFilter/simpleBloomFilter/SimpleBloomFilter.java: -------------------------------------------------------------------------------- 1 | package bloomFilter.simpleBloomFilter; 2 | 3 | import java.util.BitSet; 4 | 5 | /** 6 | * @auther G.Fukang 7 | * @date 4/5 15:18 8 | */ 9 | public class SimpleBloomFilter implements BloomFilter { 10 | 11 | // bit 数组的长度 12 | private static final int DEFAULT_SIZE = 2 << 24; 13 | // Hash 算法中所用的乘数 14 | private static final int[] seeds = new int[]{3, 5, 7, 11, 13, 17, 19, 23}; 15 | // bit 数组 16 | private static BitSet bitSet = new BitSet(DEFAULT_SIZE); 17 | // Hash 函数 18 | private static Hash[] hashes = new Hash[seeds.length]; 19 | 20 | @Override 21 | public void put(String key) { 22 | if (key != null) { 23 | // 将 key Hash 为 8 个或者多个整数,然后在这些整数的 bit 上变为 1 24 | for (Hash hash : hashes) { 25 | bitSet.set(hash.Hash(key), true); 26 | } 27 | } 28 | } 29 | 30 | @Override 31 | public boolean mightContain(String key) { 32 | if (key == null) { 33 | return false; 34 | } 35 | boolean res = true; 36 | for (Hash hash : hashes) { 37 | res = res && bitSet.get(hash.Hash(key)); 38 | if (!res) { 39 | return res; 40 | } 41 | } 42 | return res; 43 | } 44 | 45 | public Hash[] build() { 46 | Hash[] res = new Hash[seeds.length]; 47 | for (int i = 0; i < seeds.length; i++) { 48 | res[i] = new Hash(DEFAULT_SIZE, seeds[i]); 49 | } 50 | hashes = res; 51 | return hashes; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/limit/countLimit/RedisLimit.java: -------------------------------------------------------------------------------- 1 | package limit.countLimit; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.data.redis.connection.RedisConnection; 6 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 7 | import redis.clients.jedis.Jedis; 8 | import util.ScriptUtil; 9 | 10 | import java.util.Collections; 11 | 12 | /** 13 | * @auther G.Fukang 14 | * @date 3/27 16:50 15 | */ 16 | public class RedisLimit { 17 | 18 | private static Logger logger = LoggerFactory.getLogger(RedisLimit.class); 19 | 20 | 21 | private JedisConnectionFactory jedisConnectionFactory; 22 | private int limit = 200; 23 | 24 | private static final int FAIL_CODE = 0; 25 | 26 | /** 27 | * lua script 28 | */ 29 | private String script; 30 | 31 | private RedisLimit(Builder builder) { 32 | this.limit = builder.limit; 33 | this.jedisConnectionFactory = builder.jedisConnectionFactory; 34 | buildScript(); 35 | } 36 | 37 | 38 | /** 39 | * limit traffic 40 | * 41 | * @return if true 42 | */ 43 | public boolean limit() { 44 | 45 | //get connection 46 | Object connection = getConnection(); 47 | 48 | Object result = limitRequest(connection); 49 | 50 | if (FAIL_CODE != (Long) result) { 51 | return true; 52 | } else { 53 | return false; 54 | } 55 | } 56 | 57 | private Object limitRequest(Object connection) { 58 | Object result = null; 59 | String key = String.valueOf(System.currentTimeMillis() / 1000); 60 | result = ((Jedis) connection).eval(script, Collections.singletonList(key), Collections.singletonList(String.valueOf(limit))); 61 | ((Jedis) connection).close(); 62 | return result; 63 | } 64 | 65 | /** 66 | * get Redis connection 67 | * 68 | * @return 69 | */ 70 | private Object getConnection() { 71 | Object connection; 72 | RedisConnection redisConnection = jedisConnectionFactory.getConnection(); 73 | connection = redisConnection.getNativeConnection(); 74 | return connection; 75 | } 76 | 77 | /** 78 | * read lua script 79 | */ 80 | private void buildScript() { 81 | script = ScriptUtil.getScript("limit.lua"); 82 | } 83 | 84 | 85 | /** 86 | * the builder 87 | */ 88 | public static class Builder { 89 | private JedisConnectionFactory jedisConnectionFactory = null; 90 | 91 | private int limit = 200; 92 | 93 | 94 | public Builder(JedisConnectionFactory jedisConnectionFactory) { 95 | this.jedisConnectionFactory = jedisConnectionFactory; 96 | } 97 | 98 | public Builder limit(int limit) { 99 | this.limit = limit; 100 | return this; 101 | } 102 | 103 | public RedisLimit build() { 104 | return new RedisLimit(this); 105 | } 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/limit/tokenBucketLimit/BucketToken.java: -------------------------------------------------------------------------------- 1 | package limit.tokenBucketLimit; 2 | 3 | /** 4 | * @auther G.Fukang 5 | * @date 3/31 19:36 6 | */ 7 | public interface BucketToken { 8 | 9 | // 令牌桶唯一标识 10 | String bucketToken = "BUCKET_TOKEN"; 11 | 12 | // 上次请求的时间 13 | String LAST_QUEST_TIME = "LAST_QUEST_TIME"; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/limit/tokenBucketLimit/RedisLimitToken.java: -------------------------------------------------------------------------------- 1 | package limit.tokenBucketLimit; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.data.redis.connection.RedisConnection; 6 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 7 | import redis.clients.jedis.Jedis; 8 | import util.ScriptUtil; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | /** 14 | * @auther G.Fukang 15 | * @date 3/27 16:50 16 | */ 17 | public class RedisLimitToken { 18 | 19 | private static Logger logger = LoggerFactory.getLogger(RedisLimitToken.class); 20 | 21 | // 令牌桶容量 22 | private int limit = 0; 23 | // 令牌桶的速率 n/s 24 | private int rate = 1; 25 | // 请求令牌的数量 26 | private int permits= 1; 27 | 28 | private JedisConnectionFactory jedisConnectionFactory; 29 | 30 | private static final int FAIL_CODE = 0; 31 | 32 | /** 33 | * lua script 34 | */ 35 | private String script; 36 | 37 | private RedisLimitToken(Builder builder) { 38 | this.limit = builder.limit; 39 | this.rate = builder.rate; 40 | this.permits = builder.permits; 41 | this.jedisConnectionFactory = builder.jedisConnectionFactory; 42 | buildScript(); 43 | } 44 | 45 | 46 | /** 47 | * limit traffic 48 | * 49 | * @return if true 50 | */ 51 | public boolean limit() { 52 | 53 | //get connection 54 | Object connection = getConnection(); 55 | 56 | Object result = limitRequest(connection); 57 | 58 | if (FAIL_CODE != (Long) result) { 59 | return true; 60 | } else { 61 | return false; 62 | } 63 | } 64 | 65 | private Object limitRequest(Object connection) { 66 | Object result = null; 67 | List keys = Arrays.asList(BucketToken.bucketToken, BucketToken.LAST_QUEST_TIME); 68 | List argvs = Arrays.asList(String.valueOf(limit), String.valueOf(permits), String.valueOf(rate), 69 | String.valueOf(System.currentTimeMillis() / 1000)); 70 | // win 下不支持多个参数 71 | // 令牌桶标识、上次请求时间、令牌桶容量、请求令牌的数量、令牌流入速率、当前时间 72 | result = ((Jedis) connection).eval(script, keys, argvs); 73 | ((Jedis) connection).close(); 74 | return result; 75 | } 76 | 77 | /** 78 | * get Redis connection 79 | * 80 | * @return 81 | */ 82 | private Object getConnection() { 83 | Object connection; 84 | RedisConnection redisConnection = jedisConnectionFactory.getConnection(); 85 | connection = redisConnection.getNativeConnection(); 86 | return connection; 87 | } 88 | 89 | /** 90 | * read lua script 91 | */ 92 | private void buildScript() { 93 | script = ScriptUtil.getScript("limitToken.lua"); 94 | } 95 | 96 | 97 | /** 98 | * the builder 99 | */ 100 | public static class Builder { 101 | private JedisConnectionFactory jedisConnectionFactory = null; 102 | 103 | private int limit = 200; 104 | 105 | private int rate = 1; 106 | 107 | private int permits = 1; 108 | 109 | 110 | public Builder(JedisConnectionFactory jedisConnectionFactory) { 111 | this.jedisConnectionFactory = jedisConnectionFactory; 112 | } 113 | 114 | public Builder limit(int limit, int rate, int permits) { 115 | this.limit = limit; 116 | this.rate = rate; 117 | this.permits = permits; 118 | return this; 119 | } 120 | 121 | public RedisLimitToken build() { 122 | return new RedisLimitToken(this); 123 | } 124 | 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/lock/RedisLock.java: -------------------------------------------------------------------------------- 1 | package lock; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.data.redis.connection.RedisConnection; 6 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 7 | import redis.clients.jedis.Jedis; 8 | import util.ScriptUtil; 9 | 10 | import java.util.Collections; 11 | 12 | 13 | /** 14 | * @auther G.Fukang 15 | * @date 3/31 21:52 16 | */ 17 | public class RedisLock { 18 | 19 | private static Logger logger = LoggerFactory.getLogger(RedisLock.class); 20 | 21 | private static final String LOCK_MSG = "OK"; 22 | 23 | private static final Long UNLOCK_MSG = 1L; 24 | 25 | private static final String SET_IF_NOT_EXIST = "NX"; 26 | private static final String SET_WITH_EXPIRE_TIME = "PX"; 27 | 28 | // 前缀 29 | private String lockPrefix; 30 | 31 | private int sleepTime; 32 | 33 | private JedisConnectionFactory jedisConnectionFactory; 34 | 35 | // 等待时间 36 | private static final int Time = 1000; 37 | 38 | // lua 39 | private String script; 40 | 41 | private RedisLock(Builder builder) { 42 | this.jedisConnectionFactory = builder.jedisConnectionFactory; 43 | this.lockPrefix = builder.lockPrefix; 44 | this.sleepTime = builder.sleepTime; 45 | 46 | buildScript(); 47 | } 48 | 49 | /** 50 | * 获取 Redis 连接 51 | * 52 | * @author G.Fukang 53 | * @date: 3/31 22:20 54 | */ 55 | private Object getConnection() { 56 | Object connection; 57 | RedisConnection redisConnection = jedisConnectionFactory.getConnection(); 58 | connection = redisConnection.getNativeConnection(); 59 | return connection; 60 | } 61 | 62 | /** 63 | * 加锁 64 | * 非阻塞锁 + 过期时间 65 | * 66 | * @author G.Fukang 67 | * @date: 3/31 22:21 68 | */ 69 | public boolean tryLock(String key, String request, int expireTime) { 70 | Object connection = getConnection(); 71 | String result; 72 | result = ((Jedis) connection).set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); 73 | ((Jedis) connection).close(); 74 | 75 | if (LOCK_MSG.equals(result)) { 76 | return true; 77 | } else { 78 | return false; 79 | } 80 | } 81 | 82 | /** 83 | * 解锁 84 | * 85 | * @author G.Fukang 86 | * @date: 3/31 22:35 87 | */ 88 | public boolean unlock(String key, String request) { 89 | Object connection = getConnection(); 90 | 91 | Object result = null; 92 | 93 | result = ((Jedis) connection).eval(script, Collections.singletonList(lockPrefix + key), Collections.singletonList(request)); 94 | ((Jedis) connection).close(); 95 | 96 | if (UNLOCK_MSG.equals(result)) { 97 | return true; 98 | } else { 99 | return false; 100 | } 101 | } 102 | 103 | /** 104 | * Redis 指令实现加锁 105 | * 106 | * @author G.Fukang 107 | * @date: 3/31 23:06 108 | */ 109 | public boolean tryLockWithRedis(String key, String request, int expireTime) { 110 | Object connection = getConnection(); 111 | Long setnxResult = ((Jedis) connection).setnx(lockPrefix + key + request, String.valueOf(System.currentTimeMillis() + expireTime)); 112 | if (setnxResult != null && setnxResult.intValue() == 1) { 113 | return true; 114 | } else { 115 | // 未获取到锁,继续判断时间戳,看是否可以重置锁 116 | String lockValueStr = ((Jedis) connection).get(lockPrefix + key + request); 117 | if (lockValueStr != null && System.currentTimeMillis() > Long.parseLong(lockValueStr)) { 118 | String getSetResult = ((Jedis) connection).getSet(lockPrefix + key + request, String.valueOf(System.currentTimeMillis() + expireTime)); 119 | // 锁过期了,可以获取锁 120 | if (getSetResult == null || (getSetResult != null && lockValueStr.equals(getSetResult))) { 121 | return true; 122 | } else { 123 | return false; 124 | } 125 | } else { 126 | // 旧的锁还没有失效 127 | return false; 128 | } 129 | } 130 | } 131 | 132 | /** 133 | * Redis 指令实现解锁 134 | * 135 | * @author G.Fukang 136 | * @date: 3/31 23:21 137 | */ 138 | public boolean tryUnlockWithRedis(String key, String request) { 139 | Object connection = getConnection(); 140 | Long delResult = ((Jedis) connection).del(lockPrefix + key + request); 141 | 142 | if (UNLOCK_MSG.equals(delResult)) { 143 | return true; 144 | } else { 145 | return false; 146 | } 147 | } 148 | 149 | 150 | /** 151 | * 读取 lua 文件 152 | * 153 | * @author G.Fukang 154 | * @date: 3/31 22:19 155 | */ 156 | private void buildScript() { 157 | script = ScriptUtil.getScript("lock.lua"); 158 | } 159 | 160 | /** 161 | * 构建器 builder 162 | * 163 | * @author G.Fukang 164 | * @date: 3/31 22:20 165 | */ 166 | public static class Builder { 167 | private static final String DEFAULT_LOCK_PREFIX = "lock_"; 168 | 169 | // 默认等待时间 170 | private static final int DEFAULT_SLEEP_TIME = 100; 171 | 172 | private JedisConnectionFactory jedisConnectionFactory = null; 173 | 174 | private String lockPrefix = DEFAULT_LOCK_PREFIX; 175 | private int sleepTime = DEFAULT_SLEEP_TIME; 176 | 177 | public Builder(JedisConnectionFactory jedisConnectionFactory) { 178 | this.jedisConnectionFactory = jedisConnectionFactory; 179 | } 180 | 181 | public Builder lockPrefix(String lockPrefix) { 182 | this.lockPrefix = lockPrefix; 183 | return this; 184 | } 185 | 186 | public Builder sleepTime(int sleepTime) { 187 | this.sleepTime = sleepTime; 188 | return this; 189 | } 190 | 191 | public RedisLock builder() { 192 | return new RedisLock(this); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/main/java/util/ScriptUtil.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.Arrays; 8 | 9 | /** 10 | * @auther G.Fukang 11 | * @date 3/27 16:50 12 | */ 13 | public class ScriptUtil { 14 | 15 | // 解析 lua 16 | public static String getScript(String path) { 17 | StringBuilder stringBuilder = new StringBuilder(); 18 | 19 | InputStream inputStream = ScriptUtil.class.getClassLoader().getResourceAsStream(path); 20 | 21 | try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { 22 | 23 | String str; 24 | 25 | while ((str = bufferedReader.readLine()) != null) { 26 | stringBuilder.append(str).append(System.lineSeparator()); 27 | } 28 | } catch (IOException e) { 29 | System.out.println(Arrays.toString(e.getStackTrace())); 30 | } 31 | 32 | return stringBuilder.toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/limit.lua: -------------------------------------------------------------------------------- 1 | -- 实现原理 2 | -- 每次请求都将当前时间,精确到秒作为 key 放入 Redis 中,超时时间设置为 2s, Redis 将该 key 的值进行自增 3 | -- 当达到阈值时返回错误,表示请求被限流 4 | -- 写入 Redis 的操作用 Lua 脚本来完成,利用 Redis 的单线程机制可以保证每个 Redis 请求的原子性 5 | 6 | -- 资源唯一标志位 7 | local key = KEYS[1] 8 | -- 限流大小 9 | local limit = tonumber(ARGV[1]) 10 | 11 | -- 获取当前流量大小 12 | local currentLimit = tonumber(redis.call('get', key) or "0") 13 | 14 | if currentLimit + 1 > limit then 15 | -- 达到限流大小 返回 16 | return 0; 17 | else 18 | -- 没有达到阈值 value + 1 19 | redis.call("INCRBY", key, 1) 20 | -- 设置过期时间 21 | redis.call("EXPIRE", key, 2) 22 | return currentLimit + 1 23 | end -------------------------------------------------------------------------------- /src/main/resources/limitToken.lua: -------------------------------------------------------------------------------- 1 | -- 令牌桶限流 2 | 3 | -- 令牌的唯一标识 4 | local bucketKey = KEYS[1] 5 | -- 上次请求的时间 6 | local last_mill_request_key = KEYS[2] 7 | -- 令牌桶的容量 8 | local limit = tonumber(ARGV[1]) 9 | -- 请求令牌的数量 10 | local permits = tonumber(ARGV[2]) 11 | -- 令牌流入的速率 12 | local rate = tonumber(ARGV[3]) 13 | -- 当前时间 14 | local curr_mill_time = tonumber(ARGV[4]) 15 | 16 | -- 添加令牌 17 | 18 | -- 获取当前令牌的数量 19 | local current_limit = tonumber(redis.call('get', bucketKey) or "0") 20 | -- 获取上次请求的时间 21 | local last_mill_request_time = tonumber(redis.call('get', last_mill_request_key) or "0") 22 | -- 计算向桶里添加令牌的数量 23 | if last_mill_request_time == 0 then 24 | -- 令牌桶初始化 25 | -- 更新上次请求时间 26 | redis.call("HSET", last_mill_request_key, curr_mill_time) 27 | return 0 28 | else 29 | local add_token_num = math.floor((curr_mill_time - last_mill_request_time) * rate) 30 | end 31 | 32 | -- 更新令牌的数量 33 | if current_limit + add_token_num > limit then 34 | current_limit = limit 35 | else 36 | current_limit = current_limit + add_token_num 37 | end 38 | redis.pcall("HSET",bucketKey, current_limit) 39 | -- 设置过期时间 40 | redis.call("EXPIRE", bucketKey, 2) 41 | 42 | -- 限流判断 43 | 44 | if current_limit - permits < 1 then 45 | -- 达到限流大小 46 | return 0 47 | else 48 | -- 没有达到限流大小 49 | current_limit = current_limit - permits 50 | redis.pcall("HSET", bucketKey, current_limit) 51 | -- 设置过期时间 52 | redis.call("EXPIRE", bucketKey, 2) 53 | -- 更新上次请求的时间 54 | redis.call("HSET", last_mill_request_key, curr_mill_time) 55 | end 56 | 57 | -------------------------------------------------------------------------------- /src/main/resources/lock.lua: -------------------------------------------------------------------------------- 1 | if redis.call('get', KEYS[1]) == ARGV[1] then 2 | return redis.call('del', KEYS[1]) 3 | else 4 | return 0 5 | end -------------------------------------------------------------------------------- /src/test/java/BloomFilter/BloomFilterCachePenetrationTest.java: -------------------------------------------------------------------------------- 1 | package BloomFilter; 2 | 3 | import bloomFilter.BloomFilterCachePenetration; 4 | import com.google.common.base.Charsets; 5 | import com.google.common.hash.BloomFilter; 6 | import com.google.common.hash.Funnel; 7 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 8 | import org.junit.Before; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 12 | import redis.clients.jedis.Jedis; 13 | import redis.clients.jedis.JedisPoolConfig; 14 | 15 | import java.util.HashMap; 16 | import java.util.concurrent.*; 17 | 18 | /** 19 | * @auther G.Fukang 20 | * @date 4/3 21:03 21 | */ 22 | public class BloomFilterCachePenetrationTest { 23 | 24 | private static final Logger LOGGER = LoggerFactory.getLogger(BloomFilterCachePenetrationTest.class); 25 | 26 | private static ExecutorService executorServicePool; 27 | 28 | private static final int capacity = 1000; 29 | private BloomFilter bloomFilter = BloomFilter 30 | .create((Funnel) (s, primitiveSink) -> primitiveSink.putString(s, Charsets.UTF_8), capacity); 31 | 32 | private HashMap mapDB = new HashMap<>(); 33 | 34 | private static BloomFilterCachePenetration cache; 35 | 36 | private void init() { 37 | 38 | JedisPoolConfig config = new JedisPoolConfig(); 39 | config.setMaxIdle(10); 40 | config.setMaxTotal(300); 41 | config.setMaxWaitMillis(10000); 42 | config.setTestOnBorrow(true); 43 | config.setTestOnReturn(true); 44 | 45 | JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(config); 46 | jedisConnectionFactory.setHostName("47.106.218.181"); 47 | jedisConnectionFactory.setPort(6379); 48 | jedisConnectionFactory.setPassword(""); 49 | jedisConnectionFactory.setTimeout(100000); 50 | jedisConnectionFactory.afterPropertiesSet(); 51 | 52 | // 设置 Redis 初始状态 53 | Jedis connection = (Jedis) jedisConnectionFactory.getConnection().getNativeConnection(); 54 | // 清空 Redis 55 | for (int i = 70; i < 90; i++) { 56 | connection.del(String.valueOf(i)); 57 | } 58 | LOGGER.info("Redis 清理完成"); 59 | for (int i = 70; i < 80; i++) { 60 | connection.set(String.valueOf(i), String.valueOf(i) + "A"); 61 | } 62 | // 设置 DB 63 | for (int i = 70; i < 90; i++) { 64 | this.mapDB.put(String.valueOf(i), String.valueOf(i) + "A"); 65 | } 66 | // 设置 BloomFilter 67 | for (int i = 90; i < 95; i++) { 68 | this.bloomFilter.put(String.valueOf(i)); 69 | } 70 | 71 | cache = new BloomFilterCachePenetration.Builder(jedisConnectionFactory, bloomFilter, mapDB).build(); 72 | } 73 | 74 | public static void initThread() { 75 | ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() 76 | .setNameFormat("current-thread-%d").build(); 77 | executorServicePool = new ThreadPoolExecutor(350, 350, 0L, TimeUnit.MILLISECONDS, 78 | new ArrayBlockingQueue(200), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); 79 | } 80 | 81 | @Before 82 | public void setBefore() { 83 | init(); 84 | } 85 | 86 | public static void main(String[] args) throws InterruptedException { 87 | 88 | BloomFilterCachePenetrationTest cacheTest = new BloomFilterCachePenetrationTest(); 89 | cacheTest.init(); 90 | initThread(); 91 | 92 | for (int i = 70; i < 100; i++) { 93 | executorServicePool.execute(new Worker(i, String.valueOf(i))); 94 | } 95 | 96 | for (int i = 90; i < 100; i++) { 97 | executorServicePool.execute(new Worker(i, String.valueOf(i))); 98 | } 99 | 100 | executorServicePool.shutdown(); 101 | while (!executorServicePool.awaitTermination(1, TimeUnit.SECONDS)) { 102 | LOGGER.info("worker running"); 103 | } 104 | LOGGER.info("worker over"); 105 | } 106 | 107 | private static class Worker implements Runnable { 108 | private int index; 109 | private String key; 110 | 111 | public Worker(int index, String key) { 112 | this.index = index; 113 | this.key = key; 114 | } 115 | 116 | @Override 117 | public void run() { 118 | String result = cache.getByKey(key); 119 | // LOGGER.info("Value 值为:" + result); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/java/BloomFilter/GuavaBloomFilterTest.java: -------------------------------------------------------------------------------- 1 | package BloomFilter; 2 | 3 | import com.google.common.hash.BloomFilter; 4 | import com.google.common.hash.Funnels; 5 | 6 | /** 7 | * @auther G.Fukang 8 | * @date 4/3 14:30 9 | */ 10 | public class GuavaBloomFilterTest { 11 | 12 | private static final int capacity = 10000000; 13 | 14 | private static final int key = 9999998; 15 | 16 | private static BloomFilter bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity); 17 | 18 | static { 19 | for (int i = 0; i < capacity; i++) { 20 | bloomFilter.put(i); 21 | } 22 | } 23 | 24 | public static void main(String[] args) { 25 | long startTime = System.nanoTime(); 26 | 27 | if (bloomFilter.mightContain(key)) { 28 | System.out.println( " key : " + key + " 包含在布隆过滤器中 "); 29 | } 30 | 31 | long endTime = System.nanoTime(); 32 | 33 | System.out.println("消耗时间: " + (endTime - startTime) + " 微秒"); 34 | 35 | // 错误率判断 36 | double errNums = 0; 37 | for (int i = capacity + 1000000; i < capacity + 2000000; i++) { 38 | if (bloomFilter.mightContain(i)) { 39 | ++errNums; 40 | } 41 | } 42 | 43 | System.out.println("错误率: " + (errNums/1000000)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/BloomFilter/SimpleBloomFilterTest.java: -------------------------------------------------------------------------------- 1 | package BloomFilter; 2 | 3 | import bloomFilter.simpleBloomFilter.SimpleBloomFilter; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * @auther G.Fukang 9 | * @date 4/5 16:51 10 | */ 11 | public class SimpleBloomFilterTest { 12 | 13 | private static final Logger LOGGER = LoggerFactory.getLogger(SimpleBloomFilterTest.class); 14 | 15 | private static SimpleBloomFilter bloomFilter = new SimpleBloomFilter(); 16 | 17 | static { 18 | bloomFilter.build(); 19 | 20 | for (int i = 0; i < 2000000; i++) { 21 | bloomFilter.put(String.valueOf(i)); 22 | } 23 | } 24 | 25 | public static void main(String[] args) { 26 | 27 | String testKey = "9999"; 28 | 29 | long startTime = System.nanoTime(); 30 | 31 | if (bloomFilter.mightContain(testKey)) { 32 | LOGGER.info(" key : " + testKey + " 包含在布隆过滤器中 "); 33 | } 34 | 35 | long endTime = System.nanoTime(); 36 | 37 | LOGGER.info("消耗时间: " + (endTime - startTime) + " 微秒"); 38 | 39 | double errNum = 0; 40 | for (int i = 2000000; i < 4000000; i++) { 41 | if (bloomFilter.mightContain(String.valueOf(i))) { 42 | ++errNum; 43 | } 44 | } 45 | 46 | LOGGER.info("误差:" + (errNum / 2000000) * 100 + "%"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/Hash/FNVIHASHTest.java: -------------------------------------------------------------------------------- 1 | package Hash; 2 | 3 | /** 4 | * @auther G.Fukang 5 | * @date 4/4 12:57 6 | */ 7 | public class FNVIHASHTest { 8 | 9 | public static void main(String[] args) { 10 | 11 | GetHashCode hashCode = new GetHashCode(); 12 | 13 | System.out.println("192.168.0.0:111 的哈希值:" + hashCode.getHashCode("192.168.0.0:111")); 14 | System.out.println("192.168.0.1:111 的哈希值:" + hashCode.getHashCode("192.168.0.1:111")); 15 | System.out.println("192.168.0.2:111 的哈希值:" + hashCode.getHashCode("192.168.0.2:111")); 16 | System.out.println("192.168.0.3:111 的哈希值:" + hashCode.getHashCode("192.168.0.3:111")); 17 | System.out.println("192.168.0.4:111 的哈希值:" + hashCode.getHashCode("192.168.0.4:111")); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/Hash/SortedMapWithVirtualNodeTest.java: -------------------------------------------------------------------------------- 1 | package Hash; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.*; 7 | 8 | /** 9 | * @auther G.Fukang 10 | * @date 4/4 15:59 11 | */ 12 | public class SortedMapWithVirtualNodeTest { 13 | 14 | private static Logger logger = LoggerFactory.getLogger(SortedMapWithVirtualNodeTest.class); 15 | 16 | private static SortedMapWithVirtualNode sortedMapWithVirtualNode = new SortedMapWithVirtualNode(); 17 | 18 | // Hash 环 19 | private static SortedMap treeMapHash; 20 | // 服务器总数 21 | private static final int SERVERS_NUM = 100; 22 | // 每台服务器需要设置的虚拟节点数 23 | private static final int VIRTUAL_NODES = 10; 24 | 25 | // 待加入 Hash 环的服务器列表 26 | private static ArrayList serverList = new ArrayList<>(); 27 | 28 | private static void init() { 29 | // 构造服务器数据 30 | for (int i = 0; i < SERVERS_NUM; i++) { 31 | String s = new StringBuilder().append("192.168.0.").append(String.valueOf(i)).toString(); 32 | serverList.add(s); 33 | } 34 | // 构建 Hash 环 35 | treeMapHash = sortedMapWithVirtualNode.buildHash(new TreeMap()); 36 | // 将服务器的虚拟节点添加到 Hash 环中 37 | for (String s : serverList) { 38 | for (int i = 0; i < VIRTUAL_NODES; i++) { 39 | String VNNode = s + "&&VN" + String.valueOf(i); 40 | sortedMapWithVirtualNode.addServerNode(VNNode); 41 | } 42 | } 43 | } 44 | 45 | public static void main(String[] args) { 46 | init(); 47 | 48 | // <节点,服务器> 49 | HashMap map = new HashMap<>(); 50 | 51 | // 请求节点 52 | String[] nodes = new IPAddressGenerate().getIPAddress(10000); 53 | // <节点,服务器> 54 | for (int i = 0; i < nodes.length; i++) { 55 | // 选择服务器 56 | String serverIP = sortedMapWithVirtualNode.selectServerNode(nodes[i]); 57 | // 记录服务器信息 58 | map.put(nodes[i], serverIP); 59 | // logger.info("[" + nodes[i] + "] -> " + serverIP); 60 | } 61 | 62 | logger.info("虚拟节点,初始方差: " + new Analysis().analysis(map)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/Hash/SortedMapWithoutVirtualNodeTest.java: -------------------------------------------------------------------------------- 1 | package Hash; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.*; 7 | 8 | /** 9 | * @auther G.Fukang 10 | * @date 4/4 10:43 11 | */ 12 | public class SortedMapWithoutVirtualNodeTest { 13 | 14 | private static Logger logger = LoggerFactory.getLogger(SortedMapWithoutVirtualNodeTest.class); 15 | 16 | private static SortedMapWithoutVirtualNode sortedMapWithoutVirtualNode = new SortedMapWithoutVirtualNode(); 17 | 18 | // Hash 环 19 | private static SortedMap treeMapHash; 20 | // 服务器总数 21 | private static final int SERVERS_NUM = 100; 22 | 23 | // 待加入 Hash 环的服务器列表 24 | private static ArrayList servers = new ArrayList<>(); 25 | 26 | private static void init() { 27 | // 构造服务器数据 28 | for (int i = 0; i < SERVERS_NUM; i++) { 29 | StringBuilder stringBuilder = new StringBuilder(); 30 | servers.add(stringBuilder.append("192.168.0.").append(String.valueOf(i)).toString()); 31 | } 32 | // 构建 Hash 环 33 | treeMapHash = sortedMapWithoutVirtualNode.buildHash(new TreeMap()); 34 | // 将服务器添加到 Hash 环中 35 | for (int i = 0; i < SERVERS_NUM; i++) { 36 | sortedMapWithoutVirtualNode.addServerNode(servers.get(i)); 37 | } 38 | } 39 | 40 | public static void main(String[] args) { 41 | 42 | init(); 43 | 44 | // 请求节点 45 | String[] nodes = new IPAddressGenerate().getIPAddress(10000); 46 | // <节点,服务器> 47 | HashMap map = new HashMap<>(); 48 | for (int i = 0; i < nodes.length; i++) { 49 | // 选择服务器 50 | String serverIP = sortedMapWithoutVirtualNode.selectServerNode(nodes[i]); 51 | // 记录服务器信息 52 | map.put(nodes[i], serverIP); 53 | // logger.info("[" + nodes[i] + "] -> " + serverIP); 54 | } 55 | 56 | logger.info("初始方差: " + new Analysis().analysis(map)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/Hash/StringHashCodeTest.java: -------------------------------------------------------------------------------- 1 | package Hash; 2 | 3 | /** 4 | * @auther G.Fukang 5 | * @date 4/4 9:41 6 | */ 7 | public class StringHashCodeTest { 8 | 9 | public static void main(String[] args) { 10 | System.out.println("192.168.0.0:111 的哈希值:" + Math.abs("192.168.0.0:111".hashCode())); 11 | System.out.println("192.168.0.1:111 的哈希值:" + Math.abs("192.168.0.1:111".hashCode())); 12 | System.out.println("192.168.0.2:111 的哈希值:" + Math.abs("192.168.0.2:111".hashCode())); 13 | System.out.println("192.168.0.3:111 的哈希值:" + Math.abs("192.168.0.3:111".hashCode())); 14 | System.out.println("192.168.0.4:111 的哈希值:" + Math.abs("192.168.0.4:111".hashCode())); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/Idenpotemce/IdempotenceTest.java: -------------------------------------------------------------------------------- 1 | package Idenpotemce; 2 | 3 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 4 | import org.junit.Before; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 8 | import redis.clients.jedis.JedisPoolConfig; 9 | 10 | import java.util.concurrent.*; 11 | 12 | /** 13 | * @auther G.Fukang 14 | * @date 4/1 20:41 15 | */ 16 | public class IdempotenceTest { 17 | 18 | private static Logger logger = LoggerFactory.getLogger(IdempotenceTest.class); 19 | private static ExecutorService executorServicePool; 20 | 21 | 22 | private static Idempotence idempotence; 23 | 24 | 25 | public static void main(String[] args) throws InterruptedException { 26 | IdempotenceTest idempotenceTest = new IdempotenceTest(); 27 | idempotenceTest.init(); 28 | initThread(); 29 | 30 | for (int i = 0; i < 100; i++) { 31 | executorServicePool.execute(new Worker(i)); 32 | } 33 | 34 | executorServicePool.shutdown(); 35 | while (!executorServicePool.awaitTermination(1, TimeUnit.SECONDS)) { 36 | logger.info("worker running"); 37 | } 38 | logger.info("worker over"); 39 | 40 | } 41 | 42 | @Before 43 | public void setBefore() { 44 | init(); 45 | } 46 | 47 | private void init() { 48 | 49 | JedisPoolConfig config = new JedisPoolConfig(); 50 | config.setMaxIdle(10); 51 | config.setMaxTotal(300); 52 | config.setMaxWaitMillis(10000); 53 | config.setTestOnBorrow(true); 54 | config.setTestOnReturn(true); 55 | 56 | JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(config); 57 | jedisConnectionFactory.setHostName("47.106.218.181"); 58 | jedisConnectionFactory.setPort(6379); 59 | jedisConnectionFactory.setPassword(""); 60 | jedisConnectionFactory.setTimeout(100000); 61 | jedisConnectionFactory.afterPropertiesSet(); 62 | 63 | idempotence = new Idempotence.Builder(jedisConnectionFactory).idempotencePrefix("idem_").build(); 64 | } 65 | 66 | public static void initThread() { 67 | ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() 68 | .setNameFormat("current-thread-%d").build(); 69 | executorServicePool = new ThreadPoolExecutor(350, 350, 0L, TimeUnit.MILLISECONDS, 70 | new ArrayBlockingQueue(200), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); 71 | 72 | } 73 | 74 | 75 | private static class Worker implements Runnable { 76 | 77 | private int index; 78 | 79 | public Worker(int index) { 80 | this.index = index; 81 | } 82 | 83 | @Override 84 | public void run() { 85 | boolean requestIdem = idempotence.insertRequestId("request001", 1000); 86 | if (requestIdem) { 87 | logger.info("***** 请求执行中 {} *****", index); 88 | boolean requestDel = idempotence.delRequestId("request001"); 89 | if (requestDel) { 90 | logger.info("请求执行结束 ****"); 91 | } else { 92 | logger.info("请求未能结束"); 93 | } 94 | } else { 95 | logger.info("请求 {} 重复提交", index); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/limit/RedisLimitTest.java: -------------------------------------------------------------------------------- 1 | package limit; 2 | 3 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 4 | import limit.countLimit.RedisLimit; 5 | import org.junit.Before; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 9 | import redis.clients.jedis.JedisPoolConfig; 10 | 11 | import java.util.concurrent.*; 12 | 13 | 14 | /** 15 | * @auther G.Fukang 16 | * @date 3/31 10:26 17 | */ 18 | public class RedisLimitTest { 19 | 20 | private static Logger logger = LoggerFactory.getLogger(RedisLimitTest.class); 21 | private static ExecutorService executorServicePool; 22 | 23 | 24 | private static RedisLimit redisLimit; 25 | 26 | 27 | public static void main(String[] args) throws InterruptedException { 28 | RedisLimitTest redisLimitTest = new RedisLimitTest(); 29 | redisLimitTest.init(); 30 | initThread(); 31 | 32 | for (int i = 0; i < 250; i++) { 33 | executorServicePool.execute(new Worker(i)); 34 | } 35 | 36 | executorServicePool.shutdown(); 37 | while (!executorServicePool.awaitTermination(1, TimeUnit.SECONDS)) { 38 | logger.info("worker running"); 39 | } 40 | logger.info("worker over"); 41 | 42 | } 43 | 44 | @Before 45 | public void setBefore() { 46 | init(); 47 | } 48 | 49 | private void init() { 50 | 51 | JedisPoolConfig config = new JedisPoolConfig(); 52 | config.setMaxIdle(10); 53 | config.setMaxTotal(300); 54 | config.setMaxWaitMillis(10000); 55 | config.setTestOnBorrow(true); 56 | config.setTestOnReturn(true); 57 | 58 | JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(config); 59 | jedisConnectionFactory.setHostName("47.106.218.181"); 60 | jedisConnectionFactory.setPort(6379); 61 | jedisConnectionFactory.setPassword(""); 62 | jedisConnectionFactory.setTimeout(100000); 63 | jedisConnectionFactory.afterPropertiesSet(); 64 | 65 | redisLimit = new RedisLimit.Builder(jedisConnectionFactory).limit(5).build(); 66 | } 67 | 68 | public static void initThread() { 69 | ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() 70 | .setNameFormat("current-thread-%d").build(); 71 | executorServicePool = new ThreadPoolExecutor(350, 350, 0L, TimeUnit.MILLISECONDS, 72 | new ArrayBlockingQueue(200), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); 73 | 74 | } 75 | 76 | 77 | private static class Worker implements Runnable { 78 | 79 | private int index; 80 | 81 | public Worker(int index) { 82 | this.index = index; 83 | } 84 | 85 | @Override 86 | public void run() { 87 | boolean limit = redisLimit.limit(); 88 | if (!limit) { 89 | logger.info("请求 {} 被限流了", index); 90 | } else { 91 | logger.info("***** 请求 {} 没有被限流 *****", index); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/limit/RedisLimitTokenTest.java: -------------------------------------------------------------------------------- 1 | package limit; 2 | 3 | import limit.tokenBucketLimit.RedisLimitToken; 4 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 5 | import org.junit.Before; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 9 | import redis.clients.jedis.JedisPoolConfig; 10 | 11 | import java.util.concurrent.*; 12 | 13 | 14 | /** 15 | * @auther G.Fukang 16 | * @date 3/31 10:26 17 | */ 18 | public class RedisLimitTokenTest { 19 | 20 | private static Logger logger = LoggerFactory.getLogger(RedisLimitTokenTest.class); 21 | 22 | private static final String initTime = String.valueOf(System.currentTimeMillis() / 1000); 23 | 24 | private static ExecutorService executorServicePool; 25 | 26 | 27 | private static RedisLimitToken redisLimitToken; 28 | 29 | 30 | public static void main(String[] args) throws InterruptedException { 31 | RedisLimitTokenTest redisLimitTest = new RedisLimitTokenTest(); 32 | redisLimitTest.init(); 33 | initThread(); 34 | 35 | for (int i = 0; i < 250; i++) { 36 | executorServicePool.execute(new Worker(i)); 37 | } 38 | 39 | executorServicePool.shutdown(); 40 | while (!executorServicePool.awaitTermination(1, TimeUnit.SECONDS)) { 41 | logger.info("worker running"); 42 | } 43 | logger.info("worker over"); 44 | 45 | } 46 | 47 | @Before 48 | public void setBefore() { 49 | init(); 50 | } 51 | 52 | private void init() { 53 | 54 | JedisPoolConfig config = new JedisPoolConfig(); 55 | config.setMaxIdle(10); 56 | config.setMaxTotal(300); 57 | config.setMaxWaitMillis(10000); 58 | config.setTestOnBorrow(true); 59 | config.setTestOnReturn(true); 60 | 61 | JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(config); 62 | jedisConnectionFactory.setHostName("47.106.218.181"); 63 | jedisConnectionFactory.setPort(6379); 64 | jedisConnectionFactory.setPassword(""); 65 | jedisConnectionFactory.setTimeout(100000); 66 | jedisConnectionFactory.afterPropertiesSet(); 67 | 68 | redisLimitToken = new RedisLimitToken.Builder(jedisConnectionFactory).limit(100, 5, 1).build(); 69 | } 70 | 71 | public static void initThread() { 72 | ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() 73 | .setNameFormat("current-thread-%d").build(); 74 | executorServicePool = new ThreadPoolExecutor(350, 350, 0L, TimeUnit.MILLISECONDS, 75 | new ArrayBlockingQueue(200), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); 76 | 77 | } 78 | 79 | 80 | private static class Worker implements Runnable { 81 | 82 | private int index; 83 | 84 | public Worker(int index) { 85 | this.index = index; 86 | } 87 | 88 | @Override 89 | public void run() { 90 | boolean limit = redisLimitToken.limit(); 91 | if (!limit) { 92 | logger.info("请求 {} 被限流了", index); 93 | } else { 94 | logger.info("***** 请求 {} 没有被限流 *****", index); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/lock/RedisLockTest.java: -------------------------------------------------------------------------------- 1 | package lock; 2 | 3 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 4 | import javafx.concurrent.Worker; 5 | import org.junit.Before; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 9 | import redis.clients.jedis.JedisPoolConfig; 10 | 11 | import java.util.concurrent.*; 12 | 13 | /** 14 | * @auther G.Fukang 15 | * @date 3/31 22:38 16 | */ 17 | public class RedisLockTest { 18 | 19 | private static Logger logger = LoggerFactory.getLogger(RedisLockTest.class); 20 | private static ExecutorService executorServicePool; 21 | 22 | 23 | private static RedisLock redisLock; 24 | 25 | 26 | public static void main(String[] args) throws InterruptedException { 27 | RedisLockTest redisLockTest = new RedisLockTest(); 28 | redisLockTest.init(); 29 | initThread(); 30 | 31 | for (int i = 0; i < 250; i++) { 32 | executorServicePool.execute(new Worker(i)); 33 | } 34 | 35 | executorServicePool.shutdown(); 36 | while (!executorServicePool.awaitTermination(1, TimeUnit.SECONDS)) { 37 | logger.info("worker running"); 38 | } 39 | logger.info("worker over"); 40 | 41 | } 42 | 43 | @Before 44 | public void setBefore() { 45 | init(); 46 | } 47 | 48 | private void init() { 49 | 50 | JedisPoolConfig config = new JedisPoolConfig(); 51 | config.setMaxIdle(10); 52 | config.setMaxTotal(300); 53 | config.setMaxWaitMillis(10000); 54 | config.setTestOnBorrow(true); 55 | config.setTestOnReturn(true); 56 | 57 | JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(config); 58 | jedisConnectionFactory.setHostName("47.106.218.181"); 59 | jedisConnectionFactory.setPort(6379); 60 | jedisConnectionFactory.setPassword(""); 61 | jedisConnectionFactory.setTimeout(100000); 62 | jedisConnectionFactory.afterPropertiesSet(); 63 | 64 | redisLock = new RedisLock.Builder(jedisConnectionFactory).lockPrefix("lock_").sleepTime(100).builder(); 65 | } 66 | 67 | public static void initThread() { 68 | ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() 69 | .setNameFormat("current-thread-%d").build(); 70 | executorServicePool = new ThreadPoolExecutor(350, 350, 0L, TimeUnit.MILLISECONDS, 71 | new ArrayBlockingQueue(200), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); 72 | } 73 | 74 | private static class Worker implements Runnable { 75 | 76 | private int index; 77 | 78 | public Worker(int index) { 79 | this.index = index; 80 | } 81 | 82 | @Override 83 | public void run() { 84 | boolean tryLock = redisLock.tryLock("test", "12345", 1000); 85 | if (tryLock) { 86 | logger.info("加锁成功 ***** "); 87 | boolean tryUnLock = redisLock.unlock("test", "12345"); 88 | if (tryUnLock) { 89 | logger.info("解锁成功 ****"); 90 | } else { 91 | logger.info("解锁失败"); 92 | } 93 | } else { 94 | logger.info("加锁失败"); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /target/classes/limit.lua: -------------------------------------------------------------------------------- 1 | -- 实现原理 2 | -- 每次请求都将当前时间,精确到秒作为 key 放入 Redis 中,超时时间设置为 2s, Redis 将该 key 的值进行自增 3 | -- 当达到阈值时返回错误,表示请求被限流 4 | -- 写入 Redis 的操作用 Lua 脚本来完成,利用 Redis 的单线程机制可以保证每个 Redis 请求的原子性 5 | 6 | -- 资源唯一标志位 7 | local key = KEYS[1] 8 | -- 限流大小 9 | local limit = tonumber(ARGV[1]) 10 | 11 | -- 获取当前流量大小 12 | local currentLimit = tonumber(redis.call('get', key) or "0") 13 | 14 | if currentLimit + 1 > limit then 15 | -- 达到限流大小 返回 16 | return 0; 17 | else 18 | -- 没有达到阈值 value + 1 19 | redis.call("INCRBY", key, 1) 20 | -- 设置过期时间 21 | redis.call("EXPIRE", key, 2) 22 | return currentLimit + 1 23 | end -------------------------------------------------------------------------------- /target/classes/limitToken.lua: -------------------------------------------------------------------------------- 1 | -- 令牌桶限流 2 | 3 | -- 令牌的唯一标识 4 | local bucketKey = KEYS[1] 5 | -- 上次请求的时间 6 | local last_mill_request_key = KEYS[2] 7 | -- 令牌桶的容量 8 | local limit = tonumber(ARGV[1]) 9 | -- 请求令牌的数量 10 | local permits = tonumber(ARGV[2]) 11 | -- 令牌流入的速率 12 | local rate = tonumber(ARGV[3]) 13 | -- 当前时间 14 | local curr_mill_time = tonumber(ARGV[4]) 15 | 16 | -- 添加令牌 17 | 18 | -- 获取当前令牌的数量 19 | local current_limit = tonumber(redis.call('get', bucketKey) or "0") 20 | -- 获取上次请求的时间 21 | local last_mill_request_time = tonumber(redis.call('get', last_mill_request_key) or "0") 22 | -- 计算向桶里添加令牌的数量 23 | if last_mill_request_time == 0 then 24 | -- 令牌桶初始化 25 | -- 更新上次请求时间 26 | redis.call("HSET", last_mill_request_key, curr_mill_time) 27 | return 0 28 | else 29 | local add_token_num = math.floor((curr_mill_time - last_mill_request_time) * rate) 30 | end 31 | 32 | -- 更新令牌的数量 33 | if current_limit + add_token_num > limit then 34 | current_limit = limit 35 | else 36 | current_limit = current_limit + add_token_num 37 | end 38 | redis.pcall("HSET",bucketKey, current_limit) 39 | -- 设置过期时间 40 | redis.call("EXPIRE", bucketKey, 2) 41 | 42 | -- 限流判断 43 | 44 | if current_limit - permits < 1 then 45 | -- 达到限流大小 46 | return 0 47 | else 48 | -- 没有达到限流大小 49 | current_limit = current_limit - permits 50 | redis.pcall("HSET", bucketKey, current_limit) 51 | -- 设置过期时间 52 | redis.call("EXPIRE", bucketKey, 2) 53 | -- 更新上次请求的时间 54 | redis.call("HSET", last_mill_request_key, curr_mill_time) 55 | end 56 | 57 | -------------------------------------------------------------------------------- /target/classes/lock.lua: -------------------------------------------------------------------------------- 1 | if redis.call('get', KEYS[1]) == ARGV[1] then 2 | return redis.call('del', KEYS[1]) 3 | else 4 | return 0 5 | end --------------------------------------------------------------------------------