├── .gitignore ├── LICENSE ├── README.md ├── nz-client ├── cmd │ ├── nodesyn.sh │ └── nodesynEnv.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── anduo │ │ │ └── filesync │ │ │ ├── SyncMain.java │ │ │ ├── cli │ │ │ └── SyncCommand.java │ │ │ ├── codec │ │ │ ├── FileMsgDecoder.java │ │ │ ├── FileMsgEncoder.java │ │ │ └── StringCodec.java │ │ │ ├── common │ │ │ └── Constants.java │ │ │ ├── core │ │ │ ├── FileMsgSendInitializer.java │ │ │ ├── FileMsgSender.java │ │ │ ├── FileMsgServer.java │ │ │ ├── FileRecvInitializer.java │ │ │ └── SynServer.java │ │ │ ├── handler │ │ │ ├── FileMsgRecvHandler.java │ │ │ └── FileMsgSendHandler.java │ │ │ ├── msg │ │ │ ├── FileMsg.java │ │ │ └── MsgType.java │ │ │ ├── util │ │ │ ├── CleanUpUtil.java │ │ │ ├── NetUtil.java │ │ │ ├── PropertiesLoader.java │ │ │ └── Threads.java │ │ │ └── zk │ │ │ ├── ChangedEvent.java │ │ │ ├── ConnectionState.java │ │ │ ├── ConnectionStateListener.java │ │ │ ├── NodeListener.java │ │ │ ├── ZKClient.java │ │ │ ├── ZKClientCache.java │ │ │ └── ZKClientImpl.java │ └── resources │ │ ├── application.properties │ │ ├── applicationContext.xml │ │ ├── local-commoncfg.properties │ │ ├── local-nodes.properties │ │ ├── log4j.properties │ │ └── name.properties │ └── test │ ├── java │ └── com │ │ └── anduo │ │ └── log4j │ │ ├── LogTest.java │ │ └── SampleAnt.java │ └── resources │ └── log4j.properties ├── nz-server ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── anduo │ │ └── nz │ │ ├── client │ │ └── FileUploadClient.java │ │ ├── common │ │ ├── ConfigFile.java │ │ └── Constants.java │ │ ├── entity │ │ └── EchoFile.java │ │ ├── netty │ │ ├── EchoClient.java │ │ ├── InitializerPipeline.java │ │ ├── NettyProtoServer.java │ │ ├── codec │ │ │ ├── MessageDecoder.java │ │ │ └── MessageEncoder.java │ │ └── handler │ │ │ ├── EchoClientHandler.java │ │ │ └── EchoServerHandler.java │ │ ├── server │ │ ├── HttpStaticFileServer.java │ │ ├── HttpStaticFileServerHandler.java │ │ └── HttpStaticFileServerInitializer.java │ │ └── zk │ │ ├── ClientDict.java │ │ ├── ClientListener.java │ │ ├── ClientNodes.java │ │ ├── CommonNodes.java │ │ └── ZKUtil.java │ └── test │ └── java │ └── com │ └── anduo │ └── nz │ └── netty │ ├── DiscardServer.java │ ├── DiscardServerHandler.java │ ├── HelloWorldClient.java │ ├── HelloWorldServer.java │ ├── TimeClient.java │ ├── TimeClientHandler.java │ └── TimeServerHandler.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | #CUSTOMS 4 | 5 | pid 6 | 7 | Logs/ 8 | Data/ 9 | 10 | 11 | 12 | ### JetBrains template 13 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion 14 | 15 | *.iml 16 | 17 | 18 | 19 | ## Directory-based project format: 20 | .idea/ 21 | # if you remove the above rule, at least ignore the following: 22 | 23 | # User-specific stuff: 24 | # .idea/workspace.xml 25 | # .idea/tasks.xml 26 | # .idea/dictionaries 27 | 28 | # Sensitive or high-churn files: 29 | # .idea/dataSources.ids 30 | # .idea/dataSources.xml 31 | # .idea/sqlDataSources.xml 32 | # .idea/dynamic.xml 33 | # .idea/uiDesigner.xml 34 | 35 | # Gradle: 36 | # .idea/gradle.xml 37 | # .idea/libraries 38 | 39 | # Mongo Explorer plugin: 40 | # .idea/mongoSettings.xml 41 | 42 | ## File-based project format: 43 | *.ipr 44 | *.iws 45 | 46 | ## Plugin-specific files: 47 | 48 | # IntelliJ 49 | /out/ 50 | 51 | # mpeltonen/sbt-idea plugin 52 | .idea_modules/ 53 | 54 | # JIRA plugin 55 | atlassian-ide-plugin.xml 56 | 57 | # Crashlytics plugin (for Android Studio and IntelliJ) 58 | com_crashlytics_export_strings.xml 59 | crashlytics.properties 60 | crashlytics-build.properties 61 | 62 | 63 | ### OSX template 64 | .DS_Store 65 | .AppleDouble 66 | .LSOverride 67 | 68 | # Icon must end with two \r 69 | Icon 70 | 71 | # Thumbnails 72 | ._* 73 | 74 | # Files that might appear in the root of a volume 75 | .DocumentRevisions-V100 76 | .fseventsd 77 | .Spotlight-V100 78 | .TemporaryItems 79 | .Trashes 80 | .VolumeIcon.icns 81 | 82 | # Directories potentially created on remote AFP share 83 | .AppleDB 84 | .AppleDesktop 85 | Network Trash Folder 86 | Temporary Items 87 | .apdisk 88 | 89 | 90 | ### Java template 91 | *.class 92 | 93 | # Mobile Tools for Java (J2ME) 94 | .mtj.tmp/ 95 | 96 | # Package Files # 97 | *.jar 98 | *.war 99 | *.ear 100 | 101 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 102 | hs_err_pid* 103 | 104 | 105 | ### Maven template 106 | target/ 107 | pom.xml.tag 108 | pom.xml.releaseBackup 109 | pom.xml.versionsBackup 110 | pom.xml.next 111 | release.properties 112 | dependency-reduced-pom.xml 113 | buildNumber.properties 114 | .mvn/timing.properties 115 | 116 | 117 | -------------------------------------------------------------------------------- /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 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # filesync 2 | zk + netty 实现集群节点文件同步服务 3 | -------------------------------------------------------------------------------- /nz-client/cmd/nodesyn.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # This script cleans up old transaction logs and snapshots 5 | # 6 | 7 | # 8 | # If this scripted is run out of /usr/bin or some other system bin directory 9 | # it should be linked to and not copied. Things like java jar files are found 10 | # relative to the canonical path of this script. 11 | # 12 | 13 | # use POSTIX interface, symlink is followed automatically 14 | ZOOBIN="${BASH_SOURCE-$0}" 15 | ZOOBIN="$(dirname "${ZOOBIN}")" 16 | ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" 17 | 18 | if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then 19 | . "$ZOOBINDIR"/../libexec/zkEnv.sh 20 | else 21 | . "$ZOOBINDIR"/zkEnv.sh 22 | fi 23 | 24 | "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ 25 | -cp "$CLASSPATH" $CLIENT_JVMFLAGS $JVMFLAGS \ 26 | org.apache.zookeeper.ZooKeeperMain "$@" 27 | -------------------------------------------------------------------------------- /nz-client/cmd/nodesynEnv.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script should be sourced into other zookeeper 4 | # scripts to setup the env variables 5 | 6 | # We use ZOOCFGDIR if defined, 7 | # otherwise we use /etc/zookeeper 8 | # or the conf directory that is 9 | # a sibling of this script's directory 10 | 11 | ZOOBINDIR="${ZOOBINDIR:-/usr/bin}" 12 | ZOOKEEPER_PREFIX="${ZOOBINDIR}/.." 13 | 14 | if [ "x$ZOOCFGDIR" = "x" ] 15 | then 16 | if [ -e "${ZOOKEEPER_PREFIX}/conf" ]; then 17 | ZOOCFGDIR="$ZOOBINDIR/../conf" 18 | else 19 | ZOOCFGDIR="$ZOOBINDIR/../etc/zookeeper" 20 | fi 21 | fi 22 | 23 | if [ -f "${ZOOCFGDIR}/zookeeper-env.sh" ]; then 24 | . "${ZOOCFGDIR}/zookeeper-env.sh" 25 | fi 26 | 27 | if [ "x$ZOOCFG" = "x" ] 28 | then 29 | ZOOCFG="zoo.cfg" 30 | fi 31 | 32 | ZOOCFG="$ZOOCFGDIR/$ZOOCFG" 33 | 34 | if [ -f "$ZOOCFGDIR/java.env" ] 35 | then 36 | . "$ZOOCFGDIR/java.env" 37 | fi 38 | 39 | if [ "x${ZOO_LOG_DIR}" = "x" ] 40 | then 41 | ZOO_LOG_DIR="." 42 | fi 43 | 44 | if [ "x${ZOO_LOG4J_PROP}" = "x" ] 45 | then 46 | ZOO_LOG4J_PROP="INFO,CONSOLE" 47 | fi 48 | 49 | if [ "$JAVA_HOME" != "" ]; then 50 | JAVA="$JAVA_HOME/bin/java" 51 | else 52 | JAVA=java 53 | fi 54 | 55 | #add the zoocfg dir to classpath 56 | CLASSPATH="$ZOOCFGDIR:$CLASSPATH" 57 | 58 | for i in "$ZOOBINDIR"/../src/java/lib/*.jar 59 | do 60 | CLASSPATH="$i:$CLASSPATH" 61 | done 62 | 63 | #make it work in the binary package 64 | #(use array for LIBPATH to account for spaces within wildcard expansion) 65 | if [ -e "${ZOOKEEPER_PREFIX}"/share/zookeeper/zookeeper-*.jar ]; then 66 | LIBPATH=("${ZOOKEEPER_PREFIX}"/share/zookeeper/*.jar) 67 | else 68 | #release tarball format 69 | for i in "$ZOOBINDIR"/../zookeeper-*.jar 70 | do 71 | CLASSPATH="$i:$CLASSPATH" 72 | done 73 | LIBPATH=("${ZOOBINDIR}"/../lib/*.jar) 74 | fi 75 | 76 | for i in "${LIBPATH[@]}" 77 | do 78 | CLASSPATH="$i:$CLASSPATH" 79 | done 80 | 81 | #make it work for developers 82 | for d in "$ZOOBINDIR"/../build/lib/*.jar 83 | do 84 | CLASSPATH="$d:$CLASSPATH" 85 | done 86 | 87 | #make it work for developers 88 | CLASSPATH="$ZOOBINDIR/../build/classes:$CLASSPATH" 89 | 90 | case "`uname`" in 91 | CYGWIN*) cygwin=true ;; 92 | *) cygwin=false ;; 93 | esac 94 | 95 | if $cygwin 96 | then 97 | CLASSPATH=`cygpath -wp "$CLASSPATH"` 98 | fi 99 | 100 | #echo "CLASSPATH=$CLASSPATH" -------------------------------------------------------------------------------- /nz-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | nz 7 | com.anduo 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | com.anduo.nz 13 | client 14 | jar 15 | 16 | 17 | 1.8 18 | UTF-8 19 | 4.1.1.RELEASE 20 | 2.5 21 | 2.3.2 22 | 2.1 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.springframework 40 | spring-beans 41 | 4.1.6.RELEASE 42 | 43 | 44 | 45 | org.springframework 46 | spring-core 47 | ${org.springframework.version} 48 | 49 | 50 | 51 | org.springframework 52 | spring-context 53 | ${org.springframework.version} 54 | 55 | 56 | 57 | org.springframework 58 | spring-context-support 59 | ${org.springframework.version} 60 | 61 | 62 | 63 | net.coobird 64 | thumbnailator 65 | 66 | 67 | 68 | com.alibaba 69 | fastjson 70 | 71 | 72 | 73 | io.netty 74 | netty-all 75 | 76 | 77 | 78 | 79 | com.github.sgroschupf 80 | zkclient 81 | 0.1 82 | 83 | 84 | org.apache.zookeeper 85 | zookeeper 86 | 87 | 88 | jline 89 | jline 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | org.apache.curator 112 | curator-recipes 113 | 2.8.0 114 | 115 | 116 | 117 | org.apache.commons 118 | commons-lang3 119 | 120 | 121 | 122 | commons-collections 123 | commons-collections 124 | 3.2.2 125 | 126 | 127 | 128 | com.google.guava 129 | guava 130 | 131 | 132 | 133 | log4j 134 | log4j 135 | 136 | 137 | 138 | org.slf4j 139 | slf4j-log4j12 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | commons-io 150 | commons-io 151 | 2.4 152 | 153 | 154 | 155 | args4j 156 | args4j 157 | 2.32 158 | 159 | 160 | 161 | com.fasterxml.jackson.core 162 | jackson-databind 163 | 2.5.4 164 | 165 | 166 | 167 | org.apache.httpcomponents 168 | httpasyncclient 169 | 4.0 170 | 171 | 172 | 173 | 174 | 175 | 176 | nodesyn 177 | 178 | 179 | org.apache.maven.plugins 180 | maven-resources-plugin 181 | ${maven-resources-plugin.version} 182 | 183 | ${project.build.sourceEncoding} 184 | 185 | 186 | 187 | org.apache.maven.plugins 188 | maven-compiler-plugin 189 | ${maven-compiler-plugin.version} 190 | 191 | ${java.version} 192 | ${java.version} 193 | ${project.build.sourceEncoding} 194 | 195 | 196 | 197 | maven-source-plugin 198 | ${maven-source-plugin.version} 199 | 200 | true 201 | 202 | 203 | 204 | compile 205 | 206 | jar 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/SyncMain.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync; 4 | 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.context.support.AbstractXmlApplicationContext; 9 | import org.springframework.context.support.ClassPathXmlApplicationContext; 10 | 11 | import java.io.File; 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.lang.management.ManagementFactory; 15 | 16 | /** 17 | * ━━━━━━神兽出没━━━━━━ 18 | *    ┏┓   ┏┓ 19 | *   ┏┛┻━━━┛┻┓ 20 | *   ┃       ┃ 21 | *   ┃   ━   ┃ 22 | *   ┃ ┳┛ ┗┳ ┃ 23 | *   ┃       ┃ 24 | *   ┃   ┻   ┃ 25 | *   ┃       ┃ 26 | *   ┗━┓   ┏━┛ 27 | *     ┃   ┃神兽保佑, 永无BUG! 28 | *     ┃   ┃Code is far away from bug with the animal protecting 29 | *     ┃   ┗━━━┓ 30 | *     ┃       ┣┓ 31 | *     ┃       ┏┛ 32 | *     ┗┓┓┏━┳┓┏┛ 33 | *      ┃┫┫ ┃┫┫ 34 | *      ┗┻┛ ┗┻┛ 35 | * ━━━━━━感觉萌萌哒━━━━━━ 36 | * Summary: 程序入口 37 | * Author : anduo@qq.com 38 | * Version: 1.0 39 | * Date : 15/7/3 40 | * time : 22:24 41 | */ 42 | public class SyncMain { 43 | 44 | private static final Logger LOGGER = LoggerFactory.getLogger(SyncMain.class); 45 | 46 | public static final Object forWait = new Object(); 47 | 48 | public static void main(String[] args) 49 | throws InterruptedException, IOException { 50 | // 获取jvm名 51 | String vm = ManagementFactory.getRuntimeMXBean().getName(); 52 | if (StringUtils.isBlank(vm)) { 53 | LOGGER.error("can't get pid"); 54 | return; 55 | } 56 | FileOutputStream out = null; 57 | try { 58 | // 将jvm进程id保存到pid文件 59 | File pid = new File("pid"); 60 | if (pid.exists()) { 61 | pid.delete(); 62 | //LOGGER.error("the pid file is exist at {}", pid.getAbsolutePath()); 63 | //return; 64 | } 65 | out = new FileOutputStream(pid); 66 | out.write(vm.split("@")[0].getBytes()); 67 | out.flush(); 68 | } finally { 69 | if (out != null) { 70 | out.close(); 71 | } 72 | } 73 | 74 | LOGGER.info("SyncMain main starting at {}", vm); 75 | AbstractXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); 76 | context.start(); 77 | synchronized (forWait) { 78 | forWait.wait(); 79 | } 80 | context.close(); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/cli/SyncCommand.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.cli; 4 | 5 | /** 6 | * ━━━━━━神兽出没━━━━━━ 7 | *    ┏┓   ┏┓ 8 | *   ┏┛┻━━━┛┻┓ 9 | *   ┃       ┃ 10 | *   ┃   ━   ┃ 11 | *   ┃ ┳┛ ┗┳ ┃ 12 | *   ┃       ┃ 13 | *   ┃   ┻   ┃ 14 | *   ┃       ┃ 15 | *   ┗━┓   ┏━┛ 16 | *     ┃   ┃神兽保佑, 永无BUG! 17 | *     ┃   ┃Code is far away from bug with the animal protecting 18 | *     ┃   ┗━━━┓ 19 | *     ┃       ┣┓ 20 | *     ┃       ┏┛ 21 | *     ┗┓┓┏━┳┓┏┛ 22 | *      ┃┫┫ ┃┫┫ 23 | *      ┗┻┛ ┗┻┛ 24 | * ━━━━━━感觉萌萌哒━━━━━━ 25 | * Summary: 命令行工具 26 | * Author : anduo@qq.com 27 | * Version: 1.0 28 | * Date : 15/7/5 29 | * time : 19:39 30 | */ 31 | public class SyncCommand {} 32 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/codec/FileMsgDecoder.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.codec; 4 | 5 | import com.anduo.filesync.msg.FileMsg; 6 | import com.anduo.filesync.msg.MsgType; 7 | import io.netty.buffer.ByteBuf; 8 | import io.netty.buffer.ByteBufUtil; 9 | import io.netty.channel.ChannelHandlerContext; 10 | import io.netty.handler.codec.ByteToMessageDecoder; 11 | import io.netty.util.CharsetUtil; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * ━━━━━━神兽出没━━━━━━ 19 | *    ┏┓   ┏┓ 20 | *   ┏┛┻━━━┛┻┓ 21 | *   ┃       ┃ 22 | *   ┃   ━   ┃ 23 | *   ┃ ┳┛ ┗┳ ┃ 24 | *   ┃       ┃ 25 | *   ┃   ┻   ┃ 26 | *   ┃       ┃ 27 | *   ┗━┓   ┏━┛ 28 | *     ┃   ┃神兽保佑, 永无BUG! 29 | *     ┃   ┃Code is far away from bug with the animal protecting 30 | *     ┃   ┗━━━┓ 31 | *     ┃       ┣┓ 32 | *     ┃       ┏┛ 33 | *     ┗┓┓┏━┳┓┏┛ 34 | *      ┃┫┫ ┃┫┫ 35 | *      ┗┻┛ ┗┻┛ 36 | * ━━━━━━感觉萌萌哒━━━━━━ 37 | * Summary: 解码器 38 | * Author : anduo@qq.com 39 | * Version: 1.0 40 | * Date : 15/7/5 41 | * time : 18:05 42 | */ 43 | public class FileMsgDecoder extends ByteToMessageDecoder { 44 | /** 45 | * 日志组件. 46 | */ 47 | private static final Logger LOGGER = LoggerFactory.getLogger(FileMsgDecoder.class); 48 | 49 | public FileMsgDecoder() { 50 | } 51 | 52 | @Override 53 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) 54 | throws Exception { 55 | // 防止不发报文就关闭连接出现的错误 56 | if (!in.isReadable()) { 57 | return; 58 | } 59 | LOGGER.info(String.format("[%s]收到的的报文:[%s]", ctx.channel().remoteAddress(), ByteBufUtil.hexDump(in))); 60 | 61 | in.markReaderIndex();//标记初始位置 62 | if (in.readableBytes() < 3) { 63 | return; 64 | } 65 | byte msgType = in.readByte(); 66 | short dataLength = in.readShort(); 67 | if (in.readableBytes() < dataLength) { 68 | in.resetReaderIndex();//恢复到标记位 69 | return; 70 | } 71 | if (MsgType.HEADER == msgType) { 72 | //文件头 73 | // 1、文件名 74 | FileMsg msg = getMsgHeader(in); 75 | out.add(msg); 76 | } else if (MsgType.BODY == msgType) { 77 | //数据段 78 | } else if (MsgType.TAILER == msgType) { 79 | //数据尾端 80 | } else if (MsgType.COMMAND == msgType) { 81 | // 命令工具 82 | } 83 | 84 | } 85 | 86 | private FileMsg getMsgHeader(ByteBuf in) { 87 | long sumCountPackage = in.readLong(); 88 | short fileNameLength = in.readShort(); 89 | byte[] fileNameData = new byte[fileNameLength]; 90 | in.readBytes(fileNameData); 91 | String filename = new String(fileNameData, CharsetUtil.UTF_8); 92 | 93 | // 2、数据源node节点名称 94 | short srcNodeLength = in.readShort(); 95 | byte[] srcNodeData = new byte[srcNodeLength]; 96 | in.readBytes(srcNodeData); 97 | String srcNode = new String(srcNodeData, CharsetUtil.UTF_8); 98 | 99 | //3、数据文件md5值 100 | short fileMd5Length = in.readShort(); 101 | byte[] fileMd5Data = new byte[fileMd5Length]; 102 | in.readBytes(fileMd5Data); 103 | String fileMd5 = new String(fileMd5Data, CharsetUtil.UTF_8); 104 | 105 | FileMsg msg = new FileMsg(); 106 | msg.setSumCountPackage(sumCountPackage); 107 | msg.setFileName(filename); 108 | msg.setSrcNode(srcNode); 109 | msg.setFileMd5(fileMd5); 110 | return msg; 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/codec/FileMsgEncoder.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.codec; 4 | 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.buffer.ByteBufUtil; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.MessageToByteEncoder; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * ━━━━━━神兽出没━━━━━━ 15 | *    ┏┓   ┏┓ 16 | *   ┏┛┻━━━┛┻┓ 17 | *   ┃       ┃ 18 | *   ┃   ━   ┃ 19 | *   ┃ ┳┛ ┗┳ ┃ 20 | *   ┃       ┃ 21 | *   ┃   ┻   ┃ 22 | *   ┃       ┃ 23 | *   ┗━┓   ┏━┛ 24 | *     ┃   ┃神兽保佑, 永无BUG! 25 | *     ┃   ┃Code is far away from bug with the animal protecting 26 | *     ┃   ┗━━━┓ 27 | *     ┃       ┣┓ 28 | *     ┃       ┏┛ 29 | *     ┗┓┓┏━┳┓┏┛ 30 | *      ┃┫┫ ┃┫┫ 31 | *      ┗┻┛ ┗┻┛ 32 | * ━━━━━━感觉萌萌哒━━━━━━ 33 | * Summary: 编码器 34 | * Author : anduo@qq.com 35 | * Version: 1.0 36 | * Date : 15/7/5 37 | * time : 18:08 38 | */ 39 | public class FileMsgEncoder extends MessageToByteEncoder { 40 | /** 41 | * 日志组件. 42 | */ 43 | private static final Logger LOGGER = LoggerFactory.getLogger(FileMsgEncoder.class); 44 | 45 | public FileMsgEncoder() {} 46 | 47 | /** 48 | * 编码. 49 | * 50 | * @param ctx Netty上下文 51 | * @param msg 信息实体 52 | * @param out 缓冲区 53 | *

54 | * 方法添加日期 :2014-10-11
55 | * 创建者:刘源 56 | */ 57 | @Override 58 | protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) { 59 | LOGGER.info(String.format("[%s]发送出的报文:[%s]", 60 | ctx.channel().remoteAddress(), 61 | ByteBufUtil.hexDump((ByteBuf) msg))); 62 | out.writeBytes((byte[]) msg); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/codec/StringCodec.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.codec; 4 | 5 | import io.netty.channel.ChannelDuplexHandler; 6 | import io.netty.channel.ChannelHandler; 7 | import io.netty.channel.CombinedChannelDuplexHandler; 8 | import io.netty.handler.codec.string.StringDecoder; 9 | import io.netty.handler.codec.string.StringEncoder; 10 | import io.netty.util.CharsetUtil; 11 | 12 | import java.nio.charset.Charset; 13 | 14 | /** 15 | * ━━━━━━神兽出没━━━━━━ 16 | *    ┏┓   ┏┓ 17 | *   ┏┛┻━━━┛┻┓ 18 | *   ┃       ┃ 19 | *   ┃   ━   ┃ 20 | *   ┃ ┳┛ ┗┳ ┃ 21 | *   ┃       ┃ 22 | *   ┃   ┻   ┃ 23 | *   ┃       ┃ 24 | *   ┗━┓   ┏━┛ 25 | *     ┃   ┃神兽保佑, 永无BUG! 26 | *     ┃   ┃Code is far away from bug with the animal protecting 27 | *     ┃   ┗━━━┓ 28 | *     ┃       ┣┓ 29 | *     ┃       ┏┛ 30 | *     ┗┓┓┏━┳┓┏┛ 31 | *      ┃┫┫ ┃┫┫ 32 | *      ┗┻┛ ┗┻┛ 33 | * ━━━━━━感觉萌萌哒━━━━━━ 34 | * Summary: TODO 描述信息 35 | * Author : anduo@qq.com 36 | * Version: 1.0 37 | * Date : 15/7/5 38 | * time : 17:06 39 | */ 40 | public enum StringCodec { 41 | 42 | UTF8(new StrCodec(CharsetUtil.UTF_8)); 43 | 44 | private final ChannelDuplexHandler c; 45 | 46 | StringCodec(ChannelDuplexHandler c) { 47 | this.c = c; 48 | } 49 | 50 | public ChannelDuplexHandler getCodec() { 51 | return c; 52 | } 53 | 54 | @ChannelHandler.Sharable 55 | private static class StrCodec extends CombinedChannelDuplexHandler { 56 | private StrCodec(Charset charset) { 57 | super(new StringDecoder(charset), new StringEncoder(charset)); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/common/Constants.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.common; 4 | 5 | /** 6 | * ━━━━━━神兽出没━━━━━━ 7 | *    ┏┓   ┏┓ 8 | *   ┏┛┻━━━┛┻┓ 9 | *   ┃       ┃ 10 | *   ┃   ━   ┃ 11 | *   ┃ ┳┛ ┗┳ ┃ 12 | *   ┃       ┃ 13 | *   ┃   ┻   ┃ 14 | *   ┃       ┃ 15 | *   ┗━┓   ┏━┛ 16 | *     ┃   ┃神兽保佑, 永无BUG! 17 | *     ┃   ┃Code is far away from bug with the animal protecting 18 | *     ┃   ┗━━━┓ 19 | *     ┃       ┣┓ 20 | *     ┃       ┏┛ 21 | *     ┗┓┓┏━┳┓┏┛ 22 | *      ┃┫┫ ┃┫┫ 23 | *      ┗┻┛ ┗┻┛ 24 | * ━━━━━━感觉萌萌哒━━━━━━ 25 | * Summary: 常量 26 | * Author : anduo@qq.com 27 | * Version: 1.0 28 | * Date : 15/7/2 29 | * time : 22:32 30 | */ 31 | public class Constants { 32 | 33 | /** 34 | * 配置文件路径 35 | */ 36 | private static final String NAME_CONFIG_PATH = "name.properties"; 37 | private static final String INIT_CONFIG_PATH = "local-nodes.properties"; 38 | /** 39 | * NODE节点相关配置路径 40 | */ 41 | public static final String CONFIG_NODES = "/myapp/config/nodes"; 42 | /** 43 | * 公用配置信息路径 44 | */ 45 | public static final String CONFIG_COMMON = "/myapp/config/commoncfg"; 46 | /** 47 | * 节点注册路径 48 | */ 49 | public static final String NODE_ROOT = "/myapp/nodes"; 50 | /** 51 | * zookeeper服务器地址的properties参数名,在properties文件中设置 52 | */ 53 | public static final String ZOOKEEPER_SERVERS_PRO = "zk.servers"; 54 | /** 55 | * 所有配置所在zookeeper的根节点的 的properties参数名,在properties文件中设置 56 | */ 57 | public static final String ZOOKEEPER_CONFIG_ROOT_PATH_PRO = "zk.config.root.path"; 58 | /** 59 | * 项目配置数据的根节点 60 | */ 61 | public static final String CONFIG_ROOT_PATH = "/myapp/config"; 62 | 63 | public static final String DEFAULT_DATA_FILE_PATH = "Data"; 64 | public static final int RANDOM_FILE_COUNT = 10; 65 | } 66 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/core/FileMsgSendInitializer.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.core; 4 | 5 | import com.anduo.filesync.codec.FileMsgDecoder; 6 | import com.anduo.filesync.codec.FileMsgEncoder; 7 | import com.anduo.filesync.handler.FileMsgSendHandler; 8 | import io.netty.channel.ChannelInitializer; 9 | import io.netty.channel.ChannelPipeline; 10 | import io.netty.channel.socket.SocketChannel; 11 | import io.netty.handler.stream.ChunkedWriteHandler; 12 | import io.netty.handler.timeout.IdleStateHandler; 13 | 14 | /** 15 | * ━━━━━━神兽出没━━━━━━ 16 | *    ┏┓   ┏┓ 17 | *   ┏┛┻━━━┛┻┓ 18 | *   ┃       ┃ 19 | *   ┃   ━   ┃ 20 | *   ┃ ┳┛ ┗┳ ┃ 21 | *   ┃       ┃ 22 | *   ┃   ┻   ┃ 23 | *   ┃       ┃ 24 | *   ┗━┓   ┏━┛ 25 | *     ┃   ┃神兽保佑, 永无BUG! 26 | *     ┃   ┃Code is far away from bug with the animal protecting 27 | *     ┃   ┗━━━┓ 28 | *     ┃       ┣┓ 29 | *     ┃       ┏┛ 30 | *     ┗┓┓┏━┳┓┏┛ 31 | *      ┃┫┫ ┃┫┫ 32 | *      ┗┻┛ ┗┻┛ 33 | * ━━━━━━感觉萌萌哒━━━━━━ 34 | * Summary: FileServerInitializer 35 | * Author : anduo@qq.com 36 | * Version: 1.0 37 | * Date : 15/7/5 38 | * time : 16:56 39 | */ 40 | public class FileMsgSendInitializer extends ChannelInitializer { 41 | 42 | @Override 43 | public void initChannel(SocketChannel ch) 44 | throws Exception { 45 | // Create a default pipeline implementation. 46 | ChannelPipeline pipeline = ch.pipeline(); 47 | pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); 48 | pipeline.addLast("encod", new FileMsgEncoder());//编码 49 | pipeline.addLast("idle", new IdleStateHandler(3, 0, 0));//心跳 50 | pipeline.addLast("handle", new FileMsgSendHandler());//send file msg 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/core/FileMsgSender.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.core; 4 | 5 | import com.anduo.filesync.msg.FileMsg; 6 | import com.anduo.filesync.zk.ZKClient; 7 | import com.anduo.filesync.zk.ZKClientCache; 8 | import com.anduo.nodesyn.Constants; 9 | import com.anduo.nodesyn.util.NetUtil; 10 | import io.netty.bootstrap.Bootstrap; 11 | import io.netty.buffer.PooledByteBufAllocator; 12 | import io.netty.channel.AdaptiveRecvByteBufAllocator; 13 | import io.netty.channel.Channel; 14 | import io.netty.channel.ChannelOption; 15 | import io.netty.channel.EventLoopGroup; 16 | import io.netty.channel.nio.NioEventLoopGroup; 17 | import io.netty.channel.socket.nio.NioSocketChannel; 18 | import org.apache.curator.utils.ZKPaths; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | /** 23 | * ━━━━━━神兽出没━━━━━━ 24 | *    ┏┓   ┏┓ 25 | *   ┏┛┻━━━┛┻┓ 26 | *   ┃       ┃ 27 | *   ┃   ━   ┃ 28 | *   ┃ ┳┛ ┗┳ ┃ 29 | *   ┃       ┃ 30 | *   ┃   ┻   ┃ 31 | *   ┃       ┃ 32 | *   ┗━┓   ┏━┛ 33 | *     ┃   ┃神兽保佑, 永无BUG! 34 | *     ┃   ┃Code is far away from bug with the animal protecting 35 | *     ┃   ┗━━━┓ 36 | *     ┃       ┣┓ 37 | *     ┃       ┏┛ 38 | *     ┗┓┓┏━┳┓┏┛ 39 | *      ┃┫┫ ┃┫┫ 40 | *      ┗┻┛ ┗┻┛ 41 | * ━━━━━━感觉萌萌哒━━━━━━ 42 | * Summary: 客户端负责连接服务端发送数据报文到服务端 43 | * Author : anduo@qq.com 44 | * Version: 1.0 45 | * Date : 15/7/5 46 | * time : 17:37 47 | */ 48 | public class FileMsgSender { 49 | private final static Logger LOGGER = LoggerFactory.getLogger(FileMsgSender.class); 50 | 51 | private final String zkNode; 52 | 53 | private final ZKClient zkClient; 54 | 55 | private volatile Channel channel; 56 | 57 | protected final Bootstrap bootstrap; 58 | 59 | private volatile boolean closed = false; 60 | 61 | private static final EventLoopGroup GROUP = new NioEventLoopGroup(1); 62 | 63 | public FileMsgSender(String zkAddrs, String zkNode) { 64 | this.zkNode = zkNode; 65 | this.zkClient = ZKClientCache.get(zkAddrs); 66 | this.bootstrap = new Bootstrap(); 67 | bootstrap.group(GROUP); 68 | bootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT); 69 | bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 70 | bootstrap.option(ChannelOption.TCP_NODELAY, true); 71 | bootstrap.option(ChannelOption.SO_KEEPALIVE, true); 72 | bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000); 73 | bootstrap.channel(NioSocketChannel.class); 74 | bootstrap.handler(new FileMsgSendInitializer()); 75 | 76 | } 77 | 78 | /** 79 | * 传输文件 80 | * @param msg 81 | */ 82 | public void send(FileMsg msg) { 83 | if (isActive()) { 84 | LOGGER.error("sender is not active"); 85 | return; 86 | } 87 | LOGGER.info("send msg to server"); 88 | channel.writeAndFlush(msg); 89 | } 90 | 91 | public boolean isActive() { 92 | return channel != null && !closed && channel.isActive(); 93 | } 94 | 95 | public void close() { 96 | this.closed = true; 97 | if (this.channel != null) { 98 | this.channel.close(); 99 | } 100 | } 101 | 102 | public void destroy() 103 | throws Exception { 104 | close(); 105 | bootstrap.group().shutdownGracefully(); 106 | } 107 | 108 | public void connect() 109 | throws Exception { 110 | String clientAddr = zkClient.getPathValue(ZKPaths.makePath(Constants.NODE_ROOT, zkNode)); 111 | bootstrap.connect(NetUtil.createSocketAddress(clientAddr)); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/core/FileMsgServer.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.core; 4 | 5 | import com.anduo.filesync.common.Constants; 6 | import com.anduo.filesync.util.NetUtil; 7 | import com.anduo.filesync.zk.ChangedEvent; 8 | import com.anduo.filesync.zk.ZKClient; 9 | import com.anduo.filesync.zk.ZKClientCache; 10 | import com.google.common.collect.ImmutableSet; 11 | import com.google.common.collect.Maps; 12 | import com.google.common.collect.Sets; 13 | import io.netty.bootstrap.ServerBootstrap; 14 | import io.netty.channel.nio.NioEventLoopGroup; 15 | import io.netty.channel.socket.nio.NioServerSocketChannel; 16 | import io.netty.handler.logging.LogLevel; 17 | import io.netty.handler.logging.LoggingHandler; 18 | import org.apache.commons.lang3.RandomUtils; 19 | import org.apache.curator.utils.ZKPaths; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import java.io.File; 24 | import java.io.FileWriter; 25 | import java.io.IOException; 26 | import java.io.PrintWriter; 27 | import java.util.Map; 28 | import java.util.Set; 29 | import java.util.concurrent.ExecutorService; 30 | import java.util.concurrent.Executors; 31 | 32 | /** 33 | * ━━━━━━神兽出没━━━━━━ 34 | *    ┏┓   ┏┓ 35 | *   ┏┛┻━━━┛┻┓ 36 | *   ┃       ┃ 37 | *   ┃   ━   ┃ 38 | *   ┃ ┳┛ ┗┳ ┃ 39 | *   ┃       ┃ 40 | *   ┃   ┻   ┃ 41 | *   ┃       ┃ 42 | *   ┗━┓   ┏━┛ 43 | *     ┃   ┃神兽保佑, 永无BUG! 44 | *     ┃   ┃Code is far away from bug with the animal protecting 45 | *     ┃   ┗━━━┓ 46 | *     ┃       ┣┓ 47 | *     ┃       ┏┛ 48 | *     ┗┓┓┏━┳┓┏┛ 49 | *      ┃┫┫ ┃┫┫ 50 | *      ┗┻┛ ┗┻┛ 51 | * ━━━━━━感觉萌萌哒━━━━━━ 52 | * Summary: 服务端负责启动服务并且监听node节点的server,如果节点的id大于自己的id就建立一个client,并传输文件 53 | * Author : anduo@qq.com 54 | * Version: 1.0 55 | * Date : 15/7/5 56 | * time : 16:59 57 | */ 58 | public class FileMsgServer { 59 | 60 | private static final Logger LOGGER = LoggerFactory.getLogger(FileMsgServer.class); 61 | static final int PORT = Integer.parseInt(System.getProperty("port", "1843")); 62 | private final ServerBootstrap bootstrap; 63 | private final String serverName; 64 | //NettyServer绑定端口号 65 | private final int bindPort; 66 | private final String zkAddrs; 67 | private final ZKClient zkClient; 68 | //当前服务所注册的zk节点名 69 | private volatile String zkNode; 70 | private Set lastNodes = Sets.newHashSet(); 71 | Map clientMap = Maps.newConcurrentMap(); 72 | 73 | //数据发送并发控制 74 | private final ExecutorService senderThreadPool; 75 | 76 | public FileMsgServer(String zkAddrs, String serverName, int bindPort) { 77 | this.zkAddrs = zkAddrs; 78 | this.zkClient = ZKClientCache.get(zkAddrs); 79 | this.serverName = serverName; 80 | this.bindPort = bindPort; 81 | this.senderThreadPool = Executors 82 | .newFixedThreadPool(5, runnable -> new Thread(runnable, serverName + "-send-thread")); 83 | NioEventLoopGroup boss = new NioEventLoopGroup(1); 84 | NioEventLoopGroup worker = new NioEventLoopGroup(); 85 | this.bootstrap = new ServerBootstrap().group(boss, worker); 86 | this.bootstrap.channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.DEBUG)); 87 | this.bootstrap.childHandler(new FileRecvInitializer()); 88 | } 89 | 90 | public void start() 91 | throws InterruptedException { 92 | bootstrap.bind(bindPort).addListener(future -> register()).addListener(future -> initLocalFiles()) 93 | .addListener(future -> addNodeWatchListenner()).sync().channel().closeFuture().sync(); 94 | } 95 | 96 | //注册节点到zk 97 | private void register() { 98 | try { 99 | if (!zkClient.checkExist(Constants.NODE_ROOT)) { 100 | zkClient.addPersistentNode(Constants.NODE_ROOT); 101 | } 102 | String serverAddr = NetUtil.getLocalAddress().getHostAddress() + ":" + bindPort; 103 | String node = ZKPaths.makePath(Constants.NODE_ROOT, serverName); 104 | if (zkClient.checkExist(node)) { 105 | try { 106 | zkClient.deletePath(node); 107 | } catch (Exception e) { 108 | LOGGER.error("删除节点{}失败", node); 109 | } 110 | } 111 | //注册节点,并将节点的服务地址写入节点数据 112 | zkClient.addEphemeralNodeData(node, serverAddr); 113 | if (LOGGER.isDebugEnabled()) { 114 | String pathValue = zkClient.getPathValue(node); 115 | LOGGER.debug("ZK _node {} , value {}", node, pathValue); 116 | } 117 | this.zkNode = node; 118 | this.lastNodes.add(serverName); 119 | LOGGER.info("ZK 注册成功, _node {}", node); 120 | } catch (Exception e) { 121 | LOGGER.error("注册服务失败", e); 122 | } 123 | } 124 | 125 | private void initLocalFiles() 126 | throws IOException { 127 | LOGGER.info("{}开始生成随机文件", serverName); 128 | //随机在data目录下生成100个随机文本文件,内容长度随机控制在10K到10M,内容最后都为You are Best! 这些是待发送文件 129 | String dir = Constants.DEFAULT_DATA_FILE_PATH + "/source/" + serverName; 130 | 131 | ExecutorService threadPool = Executors.newFixedThreadPool(5); 132 | for (int i = 0; i < Constants.RANDOM_FILE_COUNT; i++) { 133 | final String fileName = dir + "/data-" + i + ".txt"; 134 | createRandomFile(fileName); 135 | Runnable task = () -> { 136 | try { 137 | createRandomFile(fileName); 138 | } catch (IOException e) { 139 | e.printStackTrace(); 140 | } 141 | }; 142 | threadPool.submit(task); 143 | } 144 | threadPool.shutdown(); 145 | LOGGER.info("{}生成随机文件结束,路径{}", serverName, dir); 146 | } 147 | 148 | private void createRandomFile(String filePath) 149 | throws IOException { 150 | String lastStr = "You are Best!"; 151 | //自己补全字母和数字,这个字符数是作为随机取值的源 152 | String str = "012345678vasdjhklsadfqwiurewopt"; 153 | File distFile = new File(filePath); 154 | if (!distFile.getParentFile().exists()) { distFile.getParentFile().mkdirs(); } 155 | PrintWriter pw = new PrintWriter(new FileWriter(filePath)); 156 | int len = str.length(); 157 | int size = RandomUtils.nextInt(1, 2); 158 | //每次写入10K,写入1024次就是 10M 159 | for (int i = 0; i < size; i++) { 160 | StringBuilder s = new StringBuilder(); 161 | for (int j = 0; j < 10240; j++) { 162 | s.append(str.charAt((int) (Math.random() * len))); 163 | } 164 | pw.println(s.toString()); 165 | } 166 | 167 | pw.write(lastStr); 168 | pw.close(); 169 | } 170 | 171 | /*** 172 | * 监听节点 173 | */ 174 | private void addNodeWatchListenner() { 175 | try { 176 | zkClient.listenChildrenPath(Constants.NODE_ROOT, (sender, event) -> { 177 | ImmutableSet diffNodeServers = getDiffNodes(); 178 | if (event.getType().equals(ChangedEvent.Type.CHILD_ADDED)) { 179 | LOGGER.info("{}监听到有新节点添加进来了!", serverName); 180 | sendFileMsgs(diffNodeServers); 181 | } 182 | }, true); 183 | } catch (Exception e) { 184 | LOGGER.error("{}添加监听器失败", serverName); 185 | } 186 | } 187 | 188 | private void sendFileMsgs(ImmutableSet diffNodes) 189 | throws Exception { 190 | for (String _node : diffNodes) { 191 | try { 192 | int currentNodeId = Integer.parseInt(serverName.split("-")[1]); 193 | int diffNodeId = Integer.parseInt(_node.split("-")[1]); 194 | if (currentNodeId > diffNodeId) { 195 | LOGGER.info("当前节点{}大于新添加的节点{},尝试连接!", serverName, _node); 196 | FileMsgSender sender = new FileMsgSender(zkAddrs, _node); 197 | } 198 | } catch (Exception e) { 199 | LOGGER.error("发送数据失败!"); 200 | } 201 | } 202 | } 203 | 204 | private ImmutableSet getDiffNodes() 205 | throws Exception { 206 | Set currentNodes = Sets.newHashSet(zkClient.getChildren(Constants.NODE_ROOT)); 207 | ImmutableSet diffNodes = Sets.difference(currentNodes, lastNodes).immutableCopy(); 208 | lastNodes = currentNodes; 209 | return diffNodes; 210 | } 211 | 212 | public void destroy() 213 | throws Exception { 214 | if (zkNode != null) { 215 | zkClient.deletePath(zkNode); 216 | } 217 | Set clientNodes = clientMap.keySet(); 218 | for (String clientNode : clientNodes) { 219 | clientMap.get(clientNode).destroy(); 220 | clientMap.remove(clientNode); 221 | } 222 | senderThreadPool.shutdown(); 223 | bootstrap.group().shutdownGracefully(); 224 | bootstrap.childGroup().shutdownGracefully(); 225 | } 226 | 227 | public static void main(String[] args) 228 | throws InterruptedException { 229 | LOGGER.info("Server set up on port: {}", PORT); 230 | new FileMsgServer("127.0.0.1:2181", "server-1", 8411).start(); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/core/FileRecvInitializer.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.core; 4 | 5 | import com.anduo.filesync.codec.FileMsgDecoder; 6 | import com.anduo.filesync.handler.FileMsgRecvHandler; 7 | import io.netty.channel.ChannelInitializer; 8 | import io.netty.channel.ChannelPipeline; 9 | import io.netty.channel.socket.SocketChannel; 10 | import io.netty.handler.stream.ChunkedWriteHandler; 11 | import io.netty.handler.timeout.IdleStateHandler; 12 | 13 | /** 14 | * ━━━━━━神兽出没━━━━━━ 15 | *    ┏┓   ┏┓ 16 | *   ┏┛┻━━━┛┻┓ 17 | *   ┃       ┃ 18 | *   ┃   ━   ┃ 19 | *   ┃ ┳┛ ┗┳ ┃ 20 | *   ┃       ┃ 21 | *   ┃   ┻   ┃ 22 | *   ┃       ┃ 23 | *   ┗━┓   ┏━┛ 24 | *     ┃   ┃神兽保佑, 永无BUG! 25 | *     ┃   ┃Code is far away from bug with the animal protecting 26 | *     ┃   ┗━━━┓ 27 | *     ┃       ┣┓ 28 | *     ┃       ┏┛ 29 | *     ┗┓┓┏━┳┓┏┛ 30 | *      ┃┫┫ ┃┫┫ 31 | *      ┗┻┛ ┗┻┛ 32 | * ━━━━━━感觉萌萌哒━━━━━━ 33 | * Summary: FileServerInitializer 34 | * Author : anduo@qq.com 35 | * Version: 1.0 36 | * Date : 15/7/5 37 | * time : 16:56 38 | */ 39 | public class FileRecvInitializer extends ChannelInitializer { 40 | 41 | @Override 42 | public void initChannel(SocketChannel ch) 43 | throws Exception { 44 | // Create a default pipeline implementation. 45 | ChannelPipeline pipeline = ch.pipeline(); 46 | pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); 47 | pipeline.addLast("codec", new FileMsgDecoder());//解码 48 | pipeline.addLast("idle", new IdleStateHandler(3, 0, 0));//心跳 49 | pipeline.addLast("handler", new FileMsgRecvHandler());//recive file msg 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/core/SynServer.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.core; 4 | 5 | import com.anduo.filesync.common.Constants; 6 | import com.anduo.filesync.util.Threads; 7 | import com.anduo.filesync.zk.ZKClient; 8 | import com.anduo.filesync.zk.ZKClientCache; 9 | import com.google.common.base.Splitter; 10 | import com.google.common.collect.Maps; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import org.springframework.beans.factory.DisposableBean; 15 | import org.springframework.beans.factory.InitializingBean; 16 | import org.springframework.beans.factory.annotation.Value; 17 | import org.springframework.stereotype.Component; 18 | 19 | import java.util.Map; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | * ━━━━━━神兽出没━━━━━━ 24 | *    ┏┓   ┏┓ 25 | *   ┏┛┻━━━┛┻┓ 26 | *   ┃       ┃ 27 | *   ┃   ━   ┃ 28 | *   ┃ ┳┛ ┗┳ ┃ 29 | *   ┃       ┃ 30 | *   ┃   ┻   ┃ 31 | *   ┃       ┃ 32 | *   ┗━┓   ┏━┛ 33 | *     ┃   ┃神兽保佑, 永无BUG! 34 | *     ┃   ┃Code is far away from bug with the animal protecting 35 | *     ┃   ┗━━━┓ 36 | *     ┃       ┣┓ 37 | *     ┃       ┏┛ 38 | *     ┗┓┓┏━┳┓┏┛ 39 | *      ┃┫┫ ┃┫┫ 40 | *      ┗┻┛ ┗┻┛ 41 | * ━━━━━━感觉萌萌哒━━━━━━ 42 | * Summary: 服务启动器 43 | * Author : anduo@qq.com 44 | * Version: 1.0 45 | * Date : 15/7/4 46 | * time : 01:47 47 | */ 48 | @Component 49 | public class SynServer implements DisposableBean, InitializingBean { 50 | 51 | private static final Logger LOGGER = LoggerFactory.getLogger(SynServer.class); 52 | 53 | @Value("${zk.config.nodes.path}") 54 | private String zkCfgNodesPath; 55 | @Value("${zk.config.commoncfg.path}") 56 | private String zkCfgCommoncfgPath; 57 | @Value("${zk.servers}") 58 | private String zkAddrs; 59 | private Map serverNodeMap = Maps.newConcurrentMap(); 60 | 61 | /** 62 | * 容器销毁的时候做相关线程和进程的清理工作 63 | * 64 | * @throws Exception 65 | */ 66 | @Override 67 | public void destroy() 68 | throws Exception { 69 | for (String server : serverNodeMap.keySet()) { 70 | serverNodeMap.get(server).destroy(); 71 | } 72 | ZKClientCache.get(zkAddrs).close(); 73 | } 74 | 75 | /** 76 | * 当spring容器加载完毕之后执行以下动作 77 | * @throws Exception 78 | */ 79 | @Override 80 | public void afterPropertiesSet() 81 | throws Exception { 82 | //开始启动服务 83 | //1、从zk nodes配置文件的路径拿到相关配置信息 84 | Map serversCfg = getNodesCfg(); 85 | if (serversCfg == null) { return; } 86 | for (String serverNode : serversCfg.keySet()) { 87 | Integer bindPort = Integer.parseInt(serversCfg.get(serverNode)); 88 | 89 | FileMsgServer server = new FileMsgServer(zkAddrs, serverNode, bindPort); 90 | server.start(); 91 | serverNodeMap.put(serverNode, server); 92 | //暂停10s再启动下一台机器 93 | Threads.sleep(1, TimeUnit.SECONDS); 94 | } 95 | //强行下线测试 96 | serverNodeMap.get("Leader-3").destroy(); 97 | serverNodeMap.remove("Leader-3"); 98 | } 99 | 100 | private Map getNodesCfg() 101 | throws Exception { 102 | ZKClient zkClient = ZKClientCache.get(zkAddrs); 103 | if (!zkClient.checkExist(zkCfgNodesPath)) { 104 | return null; 105 | } 106 | LOGGER.info("开始启动服务"); 107 | String pathValue = zkClient.getPathValue(zkCfgNodesPath); 108 | if (StringUtils.isBlank(pathValue)) { 109 | return null; 110 | } 111 | //删除之前的节点注册信息 112 | if (zkClient.checkExist(Constants.NODE_ROOT)) { 113 | zkClient.deletAllPath(Constants.NODE_ROOT); 114 | } 115 | return Splitter.on(",").withKeyValueSeparator("=").split(StringUtils.remove(pathValue, " ")); 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/handler/FileMsgRecvHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.handler; 4 | 5 | import com.anduo.filesync.msg.FileMsg; 6 | import com.anduo.filesync.util.CleanUpUtil; 7 | import io.netty.channel.*; 8 | import io.netty.handler.timeout.IdleState; 9 | import io.netty.handler.timeout.IdleStateEvent; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.net.InetAddress; 14 | 15 | /** 16 | * ━━━━━━神兽出没━━━━━━ 17 | *    ┏┓   ┏┓ 18 | *   ┏┛┻━━━┛┻┓ 19 | *   ┃       ┃ 20 | *   ┃   ━   ┃ 21 | *   ┃ ┳┛ ┗┳ ┃ 22 | *   ┃       ┃ 23 | *   ┃   ┻   ┃ 24 | *   ┃       ┃ 25 | *   ┗━┓   ┏━┛ 26 | *     ┃   ┃神兽保佑, 永无BUG! 27 | *     ┃   ┃Code is far away from bug with the animal protecting 28 | *     ┃   ┗━━━┓ 29 | *     ┃       ┣┓ 30 | *     ┃       ┏┛ 31 | *     ┗┓┓┏━┳┓┏┛ 32 | *      ┃┫┫ ┃┫┫ 33 | *      ┗┻┛ ┗┻┛ 34 | * ━━━━━━感觉萌萌哒━━━━━━ 35 | * Summary: 文件接收 36 | * Author : anduo@qq.com 37 | * Version: 1.0 38 | * Date : 15/7/5 39 | * time : 16:55 40 | */ 41 | public class FileMsgRecvHandler extends SimpleChannelInboundHandler { 42 | 43 | private static final Logger LOGGER = LoggerFactory.getLogger(FileMsgRecvHandler.class); 44 | 45 | @Override 46 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) 47 | throws Exception { 48 | if (evt instanceof IdleStateEvent) { 49 | IdleStateEvent state = (IdleStateEvent) evt; 50 | LOGGER.info("Closing timeout connection : {}", ctx.channel().remoteAddress()); 51 | if (state.state() == IdleState.READER_IDLE) { 52 | CleanUpUtil.closeOnFlush(ctx.channel()); 53 | } 54 | } 55 | super.userEventTriggered(ctx, evt); 56 | } 57 | 58 | /** 59 | * 收到文件消息 60 | * 61 | * @param ctx 62 | * @param msg 63 | * @throws Exception 64 | */ 65 | @Override 66 | protected void channelRead0(ChannelHandlerContext ctx, FileMsg msg) 67 | throws Exception { 68 | try { 69 | 70 | } catch (Exception e) { 71 | CleanUpUtil.closeOnFlush(ctx.channel()); 72 | CleanUpUtil.closeOnFlush(ctx.channel().parent()); 73 | } 74 | } 75 | 76 | /*** 77 | * 覆盖 channelActive 方法 在channel被启用的时候触发 (在建立连接的时候) 78 | * channelActive 和 channelInActive 在后面的内容中讲述,这里先不做详细的描述 79 | * 80 | * @param ctx 81 | * @throws Exception 82 | */ 83 | @Override 84 | public void channelActive(ChannelHandlerContext ctx) 85 | throws Exception { 86 | LOGGER.info("RamoteAddress : " + ctx.channel().remoteAddress() + " active !"); 87 | ctx.writeAndFlush("Welcome to " + InetAddress.getLocalHost().getHostName() + " service!\n"); 88 | super.channelActive(ctx); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/handler/FileMsgSendHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.handler; 4 | 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.*; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * ━━━━━━神兽出没━━━━━━ 12 | *    ┏┓   ┏┓ 13 | *   ┏┛┻━━━┛┻┓ 14 | *   ┃       ┃ 15 | *   ┃   ━   ┃ 16 | *   ┃ ┳┛ ┗┳ ┃ 17 | *   ┃       ┃ 18 | *   ┃   ┻   ┃ 19 | *   ┃       ┃ 20 | *   ┗━┓   ┏━┛ 21 | *     ┃   ┃神兽保佑, 永无BUG! 22 | *     ┃   ┃Code is far away from bug with the animal protecting 23 | *     ┃   ┗━━━┓ 24 | *     ┃       ┣┓ 25 | *     ┃       ┏┛ 26 | *     ┗┓┓┏━┳┓┏┛ 27 | *      ┃┫┫ ┃┫┫ 28 | *      ┗┻┛ ┗┻┛ 29 | * ━━━━━━感觉萌萌哒━━━━━━ 30 | * Summary: 文件接收方,处理器 31 | * Author : anduo@qq.com 32 | * Version: 1.0 33 | * Date : 15/7/5 34 | * time : 17:15 35 | */ 36 | public class FileMsgSendHandler extends ChannelOutboundHandlerAdapter { 37 | 38 | private static final Logger LOGGER = LoggerFactory.getLogger(FileMsgSendHandler.class); 39 | 40 | 41 | @Override 42 | public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) 43 | throws Exception { 44 | LOGGER.info("发送文件啦"); 45 | ByteBuf buf = ctx.alloc().buffer(); 46 | ctx.writeAndFlush(buf, promise); 47 | } 48 | 49 | @Override 50 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 51 | cause.printStackTrace(); 52 | ctx.close(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/msg/FileMsg.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.msg; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * ━━━━━━神兽出没━━━━━━ 9 | *    ┏┓   ┏┓ 10 | *   ┏┛┻━━━┛┻┓ 11 | *   ┃       ┃ 12 | *   ┃   ━   ┃ 13 | *   ┃ ┳┛ ┗┳ ┃ 14 | *   ┃       ┃ 15 | *   ┃   ┻   ┃ 16 | *   ┃       ┃ 17 | *   ┗━┓   ┏━┛ 18 | *     ┃   ┃神兽保佑, 永无BUG! 19 | *     ┃   ┃Code is far away from bug with the animal protecting 20 | *     ┃   ┗━━━┓ 21 | *     ┃       ┣┓ 22 | *     ┃       ┏┛ 23 | *     ┗┓┓┏━┳┓┏┛ 24 | *      ┃┫┫ ┃┫┫ 25 | *      ┗┻┛ ┗┻┛ 26 | * ━━━━━━感觉萌萌哒━━━━━━ 27 | * Summary: 文件传输对象 28 | * Author : anduo@qq.com 29 | * Version: 1.0 30 | * Date : 15/7/5 31 | * time : 17:20 32 | */ 33 | public class FileMsg implements Serializable { 34 | private static final long serialVersionUID = 8953150675564212795L; 35 | /** 36 | * 总包数. 37 | */ 38 | private long sumCountPackage; 39 | /** 40 | * 当前包数. 41 | */ 42 | private long countPackage; 43 | /** 44 | * 文件名 45 | */ 46 | private String fileName; 47 | /** 48 | * 文件来源数据节点 49 | */ 50 | private String srcNode; 51 | /** 52 | * 文件md5值,防止文件在网路传输过程中对包 53 | */ 54 | private String fileMd5;// 55 | /** 56 | * 文件内容字节数组 57 | */ 58 | private byte[] bytes;// 59 | 60 | /** 61 | * @return the sumCountPackage 62 | */ 63 | public long getSumCountPackage() { 64 | return sumCountPackage; 65 | } 66 | 67 | /** 68 | * @param sumCountPackage the sumCountPackage to set 69 | */ 70 | public void setSumCountPackage(long sumCountPackage) { 71 | this.sumCountPackage = sumCountPackage; 72 | } 73 | 74 | /** 75 | * @return the countPackage 76 | */ 77 | public long getCountPackage() { 78 | return countPackage; 79 | } 80 | 81 | public void setCountPackage(long countPackage) { 82 | this.countPackage = countPackage; 83 | } 84 | 85 | public byte[] getBytes() { 86 | return bytes; 87 | } 88 | 89 | public void setBytes(byte[] bytes) { 90 | this.bytes = bytes; 91 | } 92 | 93 | public String getFileName() { 94 | return fileName; 95 | } 96 | 97 | public void setFileName(String fileName) { 98 | this.fileName = fileName; 99 | } 100 | 101 | public String getSrcNode() { 102 | return srcNode; 103 | } 104 | 105 | public void setSrcNode(String srcNode) { 106 | this.srcNode = srcNode; 107 | } 108 | 109 | public String getFileMd5() { 110 | return fileMd5; 111 | } 112 | 113 | public void setFileMd5(String fileMd5) { 114 | this.fileMd5 = fileMd5; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/msg/MsgType.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 meituan 2 | // All rights reserved 3 | package com.anduo.filesync.msg; 4 | 5 | /** 6 | * ━━━━━━神兽出没━━━━━━ 7 | *    ┏┓   ┏┓ 8 | *   ┏┛┻━━━┛┻┓ 9 | *   ┃       ┃ 10 | *   ┃   ━   ┃ 11 | *   ┃ ┳┛ ┗┳ ┃ 12 | *   ┃       ┃ 13 | *   ┃   ┻   ┃ 14 | *   ┃       ┃ 15 | *   ┗━┓   ┏━┛ 16 | *     ┃   ┃神兽保佑, 永无BUG! 17 | *     ┃   ┃Code is far away from bug with the animal protecting 18 | *     ┃   ┗━━━┓ 19 | *     ┃       ┣┓ 20 | *     ┃       ┏┛ 21 | *     ┗┓┓┏━┳┓┏┛ 22 | *      ┃┫┫ ┃┫┫ 23 | *      ┗┻┛ ┗┻┛ 24 | * ━━━━━━感觉萌萌哒━━━━━━ 25 | * Summary: msg的类型,目的是为了在传输过程中对文件进行相应的业务处理 26 | * Author : anduo@meituan.com 27 | * Version: 1.0 28 | * Date : 15/7/5 29 | * time : 22:34 30 | */ 31 | public class MsgType { 32 | public static final byte HEADER = 0x01; 33 | public static final byte BODY = 0x02; 34 | public static final byte TAILER = 0x03; 35 | public static final byte COMMAND = 0x04; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/util/CleanUpUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.util; 4 | 5 | import com.anduo.filesync.core.FileMsgServer; 6 | import io.netty.buffer.Unpooled; 7 | import io.netty.channel.Channel; 8 | import io.netty.channel.ChannelFutureListener; 9 | 10 | import java.io.Closeable; 11 | import java.io.File; 12 | import java.io.IOException; 13 | 14 | /** 15 | * ━━━━━━神兽出没━━━━━━ 16 | *    ┏┓   ┏┓ 17 | *   ┏┛┻━━━┛┻┓ 18 | *   ┃       ┃ 19 | *   ┃   ━   ┃ 20 | *   ┃ ┳┛ ┗┳ ┃ 21 | *   ┃       ┃ 22 | *   ┃   ┻   ┃ 23 | *   ┃       ┃ 24 | *   ┗━┓   ┏━┛ 25 | *     ┃   ┃神兽保佑, 永无BUG! 26 | *     ┃   ┃Code is far away from bug with the animal protecting 27 | *     ┃   ┗━━━┓ 28 | *     ┃       ┣┓ 29 | *     ┃       ┏┛ 30 | *     ┗┓┓┏━┳┓┏┛ 31 | *      ┃┫┫ ┃┫┫ 32 | *      ┗┻┛ ┗┻┛ 33 | * ━━━━━━感觉萌萌哒━━━━━━ 34 | * Summary: TODO 描述信息 35 | * Author : anduo@qq.com 36 | * Version: 1.0 37 | * Date : 15/7/5 38 | * time : 17:00 39 | */ 40 | public class CleanUpUtil { 41 | 42 | public static void closeOnFlush(Channel ch) { 43 | if (ch.isActive()) { 44 | ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); 45 | } 46 | } 47 | 48 | public static void closeQuietly(Closeable c) { 49 | try { 50 | if (c != null) 51 | c.close(); 52 | } catch (IOException ignored) { 53 | } 54 | } 55 | 56 | public static boolean deleteFile(String fileName) { 57 | File file = new File(FileMsgServer.POLICY_FILE); 58 | return file.exists() && file.delete(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/util/NetUtil.java: -------------------------------------------------------------------------------- 1 | package com.anduo.filesync.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.InetAddress; 7 | import java.net.InetSocketAddress; 8 | import java.net.NetworkInterface; 9 | import java.net.SocketAddress; 10 | import java.util.Enumeration; 11 | import java.util.regex.Pattern; 12 | 13 | /** 14 | * Date: 12/1/14 15 | * Time: 7:18 PM 16 | */ 17 | public class NetUtil { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(NetUtil.class); 20 | 21 | private static volatile InetAddress LOCAL_ADDRESS = null; 22 | 23 | public static InetAddress getLocalAddress() { 24 | if (LOCAL_ADDRESS != null) { return LOCAL_ADDRESS; } 25 | InetAddress localAddress = getLocalAddress0(); 26 | LOCAL_ADDRESS = localAddress; 27 | return localAddress; 28 | } 29 | 30 | private static InetAddress getLocalAddress0() { 31 | InetAddress localAddress = null; 32 | try { 33 | localAddress = InetAddress.getLocalHost(); 34 | if (isValidAddress(localAddress)) { 35 | return localAddress; 36 | } 37 | } catch (Throwable e) { 38 | LOGGER.warn("Failed to retriving ip address, " + e.getMessage(), e); 39 | } 40 | try { 41 | Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); 42 | if (interfaces != null) { 43 | while (interfaces.hasMoreElements()) { 44 | try { 45 | NetworkInterface network = interfaces.nextElement(); 46 | Enumeration addresses = network.getInetAddresses(); 47 | if (addresses != null) { 48 | while (addresses.hasMoreElements()) { 49 | try { 50 | InetAddress address = addresses.nextElement(); 51 | if (isValidAddress(address)) { 52 | return address; 53 | } 54 | } catch (Throwable e) { 55 | LOGGER.warn("Failed to retriving ip address, " + e.getMessage(), e); 56 | } 57 | } 58 | } 59 | } catch (Throwable e) { 60 | LOGGER.warn("Failed to retriving ip address, " + e.getMessage(), e); 61 | } 62 | } 63 | } 64 | } catch (Throwable e) { 65 | LOGGER.warn("Failed to retriving ip address, " + e.getMessage(), e); 66 | } 67 | LOGGER.error("Could not get local host ip address, will use 127.0.0.1 instead."); 68 | return localAddress; 69 | } 70 | 71 | private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$"); 72 | 73 | private static boolean isValidAddress(InetAddress address) { 74 | if (address == null || address.isLoopbackAddress()) { return false; } 75 | String name = address.getHostAddress(); 76 | return (name != null && !ANYHOST.equals(name) && !LOCALHOST.equals(name) && IP_PATTERN.matcher(name).matches()); 77 | } 78 | 79 | public static final String LOCALHOST = "127.0.0.1"; 80 | 81 | public static final String ANYHOST = "0.0.0.0"; 82 | 83 | public static SocketAddress createSocketAddress(String server) { 84 | String[] serverSplit = server.split(":"); 85 | String hostname = serverSplit[0]; 86 | int port = Integer.parseInt(serverSplit[1]); 87 | return new InetSocketAddress(hostname, port); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/util/PropertiesLoader.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.util; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | import java.util.Properties; 13 | 14 | /** 15 | * ━━━━━━神兽出没━━━━━━ 16 | *    ┏┓   ┏┓ 17 | *   ┏┛┻━━━┛┻┓ 18 | *   ┃       ┃ 19 | *   ┃   ━   ┃ 20 | *   ┃ ┳┛ ┗┳ ┃ 21 | *   ┃       ┃ 22 | *   ┃   ┻   ┃ 23 | *   ┃       ┃ 24 | *   ┗━┓   ┏━┛ 25 | *     ┃   ┃神兽保佑, 永无BUG! 26 | *     ┃   ┃Code is far away from bug with the animal protecting 27 | *     ┃   ┗━━━┓ 28 | *     ┃       ┣┓ 29 | *     ┃       ┏┛ 30 | *     ┗┓┓┏━┳┓┏┛ 31 | *      ┃┫┫ ┃┫┫ 32 | *      ┗┻┛ ┗┻┛ 33 | * ━━━━━━感觉萌萌哒━━━━━━ 34 | * Summary: TODO 描述信息 35 | * Author : anduo@qq.com 36 | * Version: 1.0 37 | * Date : 15/7/4 38 | * time : 00:50 39 | */ 40 | public class PropertiesLoader { 41 | 42 | private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesLoader.class); 43 | 44 | public static Properties load(String fileName) { 45 | return load(fileName, null); 46 | } 47 | 48 | public static Properties load(String fileName, Properties parent) { 49 | 50 | InputStream is = PropertiesLoader.class.getClassLoader().getResourceAsStream(fileName); 51 | if (is == null) { return null; } 52 | 53 | try { 54 | BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); 55 | Properties prop = new Properties(parent); 56 | prop.load(reader); 57 | return prop; 58 | } catch (IOException e) { 59 | throw new RuntimeException(e); 60 | } finally { 61 | try { 62 | if (is != null) { is.close(); } 63 | } catch (IOException e) { 64 | LOGGER.warn("资源关闭时出错"); 65 | } 66 | } 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/util/Threads.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.filesync.util; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | /** 8 | * ━━━━━━神兽出没━━━━━━ 9 | *    ┏┓   ┏┓ 10 | *   ┏┛┻━━━┛┻┓ 11 | *   ┃       ┃ 12 | *   ┃   ━   ┃ 13 | *   ┃ ┳┛ ┗┳ ┃ 14 | *   ┃       ┃ 15 | *   ┃   ┻   ┃ 16 | *   ┃       ┃ 17 | *   ┗━┓   ┏━┛ 18 | *     ┃   ┃神兽保佑, 永无BUG! 19 | *     ┃   ┃Code is far away from bug with the animal protecting 20 | *     ┃   ┗━━━┓ 21 | *     ┃       ┣┓ 22 | *     ┃       ┏┛ 23 | *     ┗┓┓┏━┳┓┏┛ 24 | *      ┃┫┫ ┃┫┫ 25 | *      ┗┻┛ ┗┻┛ 26 | * ━━━━━━感觉萌萌哒━━━━━━ 27 | * Summary: 线程工具类 28 | * Author : anduo@qq.com 29 | * Version: 1.0 30 | * Date : 15/7/4 31 | * time : 02:56 32 | */ 33 | public class Threads { 34 | 35 | /** 36 | * sleep等待,单位为毫秒,忽略InterruptedException. 37 | */ 38 | public static void sleep(long millis) { 39 | try { 40 | Thread.sleep(millis); 41 | } catch (InterruptedException e) { 42 | // Ignore. 43 | return; 44 | } 45 | } 46 | 47 | /** 48 | * sleep等待,忽略InterruptedException. 49 | */ 50 | public static void sleep(long duration, TimeUnit unit) { 51 | try { 52 | Thread.sleep(unit.toMillis(duration)); 53 | } catch (InterruptedException e) { 54 | // Ignore. 55 | return; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/zk/ChangedEvent.java: -------------------------------------------------------------------------------- 1 | package com.anduo.filesync.zk; 2 | 3 | /** 4 | * 5 | */ 6 | public class ChangedEvent { 7 | public enum Type { 8 | CHILD_ADDED, 9 | CHILD_UPDATED, 10 | CHILD_REMOVED 11 | } 12 | 13 | private String path; 14 | private Type type; 15 | 16 | public ChangedEvent(String path, Type type) { 17 | this.path = path; 18 | this.type = type; 19 | } 20 | 21 | public String getPath() { 22 | return this.path; 23 | } 24 | 25 | public Type getType() { 26 | return type; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/zk/ConnectionState.java: -------------------------------------------------------------------------------- 1 | package com.anduo.filesync.zk; 2 | 3 | /** 4 | * 5 | */ 6 | public enum ConnectionState { 7 | CONNECTED, SUSPENDED, RECONNECTED, LOST, READ_ONLY 8 | } 9 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/zk/ConnectionStateListener.java: -------------------------------------------------------------------------------- 1 | package com.anduo.filesync.zk; 2 | 3 | /** 4 | * 5 | */ 6 | public interface ConnectionStateListener { 7 | void stateChanged(ZKClient sender, ConnectionState state); 8 | } 9 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/zk/NodeListener.java: -------------------------------------------------------------------------------- 1 | package com.anduo.filesync.zk; 2 | 3 | /** 4 | * 5 | */ 6 | public interface NodeListener { 7 | void nodeChanged(ZKClient sender, ChangedEvent event) throws Exception; 8 | } 9 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/zk/ZKClient.java: -------------------------------------------------------------------------------- 1 | package com.anduo.filesync.zk; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 7 | */ 8 | public interface ZKClient { 9 | 10 | /** 11 | * 使用分布式锁执行任务 12 | * 13 | * @param path 14 | * @param getLockTimeout 获取锁超时时间(单位ms) 15 | * @param task 16 | * @auth anduo 2015年5月8日 17 | */ 18 | public void distributeLock(String path, int getLockTimeout, Runnable task); 19 | 20 | /** 21 | * 获取给定路径的子节点 22 | * 23 | * @param path 24 | * @return 25 | * @throws Exception 26 | */ 27 | List getChildren(String path) 28 | throws Exception; 29 | 30 | /** 31 | * 监听给定路径子节点变化,并且获取子节点集合 32 | * 33 | * @param parent 父路径 34 | * @param listener 监听器 35 | * @param sync 该方法是否同步获取子节点,如果为true, 36 | * 则方法返回的时候一定获得最新子节点集合 37 | * @return 38 | * @throws Exception 39 | */ 40 | List listenChildrenPath(final String parent, final NodeListener listener, final boolean sync) 41 | throws Exception; 42 | 43 | /** 44 | * 在给定路径下添加临时子节点 45 | * 46 | * @param parent 47 | * @param node 48 | * @return 49 | * @throws Exception 50 | */ 51 | String addEphemeralNode(String parent, String node) 52 | throws Exception; 53 | 54 | /** 55 | * 添加临时节点 56 | * 57 | * @param path 临时节点全路径 58 | * @return 59 | * @throws Exception 60 | */ 61 | String addEphemeralNode(String path) 62 | throws Exception; 63 | 64 | /** 65 | * 添加永久节点 66 | * 67 | * @param path 永久节点全路径 68 | * @throws Exception 69 | */ 70 | void addPersistentNode(String path) 71 | throws Exception; 72 | 73 | /** 74 | * 是否已经连接上 75 | * 76 | * @return 77 | */ 78 | boolean isConnected(); 79 | 80 | /** 81 | * 注册连接状态监听器 82 | * 83 | * @param listener 84 | */ 85 | void addConnectionChangeListenter(final ConnectionStateListener listener); 86 | 87 | /** 88 | * 删除给定路径 89 | * 90 | * @param path 91 | * @throws Exception 92 | */ 93 | void deletePath(String path) 94 | throws Exception; 95 | 96 | //递归删除所有节点 97 | void deletAllPath(String path) 98 | throws Exception; 99 | 100 | /** 101 | * 判断给定路径是否存在 102 | * 103 | * @param path 104 | * @return 105 | */ 106 | boolean checkExist(String path); 107 | 108 | /** 109 | * 添加临时节点数据 110 | * @param path 111 | * @param data 112 | * @throws Exception 113 | */ 114 | void addEphemeralNodeData(String path, String data) 115 | throws Exception; 116 | 117 | /** 118 | * 119 | * @param path 120 | * @param data 121 | */ 122 | void createPath(String path, String data); 123 | 124 | public void updatePathValue(String path, String data); 125 | 126 | String getPathValue(String path) 127 | throws Exception; 128 | 129 | /** 130 | * 关闭 131 | */ 132 | void close(); 133 | } 134 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/zk/ZKClientCache.java: -------------------------------------------------------------------------------- 1 | package com.anduo.filesync.zk; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * 11 | */ 12 | public class ZKClientCache { 13 | 14 | private static final Logger LOGGER = LoggerFactory.getLogger(ZKClientCache.class); 15 | 16 | private static final Map CACHE = new HashMap(); 17 | 18 | public synchronized static ZKClient get(String address) { 19 | LOGGER.info("Get zkclient for {}", address); 20 | ZKClientImpl client = CACHE.get(address); 21 | if (client == null) { 22 | CACHE.put(address, new ZKClientImpl(address)); 23 | } 24 | client = CACHE.get(address); 25 | client.incrementReference(); 26 | return client; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /nz-client/src/main/java/com/anduo/filesync/zk/ZKClientImpl.java: -------------------------------------------------------------------------------- 1 | package com.anduo.filesync.zk; 2 | 3 | import com.google.common.util.concurrent.MoreExecutors; 4 | import org.apache.curator.RetryPolicy; 5 | import org.apache.curator.framework.CuratorFramework; 6 | import org.apache.curator.framework.CuratorFrameworkFactory; 7 | import org.apache.curator.framework.recipes.cache.ChildData; 8 | import org.apache.curator.framework.recipes.cache.PathChildrenCache; 9 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; 10 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; 11 | import org.apache.curator.framework.recipes.locks.InterProcessMutex; 12 | import org.apache.curator.retry.ExponentialBackoffRetry; 13 | import org.apache.curator.utils.DebugUtils; 14 | import org.apache.curator.utils.ThreadUtils; 15 | import org.apache.curator.utils.ZKPaths; 16 | import org.apache.zookeeper.CreateMode; 17 | import org.apache.zookeeper.KeeperException; 18 | import org.apache.zookeeper.data.Stat; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | import javax.annotation.PreDestroy; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | import java.util.concurrent.CountDownLatch; 26 | import java.util.concurrent.ExecutorService; 27 | import java.util.concurrent.Executors; 28 | import java.util.concurrent.TimeUnit; 29 | import java.util.concurrent.atomic.AtomicInteger; 30 | import java.util.concurrent.locks.Lock; 31 | import java.util.concurrent.locks.ReentrantLock; 32 | 33 | /** 34 | * 35 | */ 36 | class ZKClientImpl implements ZKClient { 37 | 38 | private static final Logger LOGGER = LoggerFactory.getLogger(ZKClientImpl.class); 39 | 40 | public static final int MAX_RETRIES = 3000; 41 | public static final int BASE_SLEEP_TIMEMS = 3000; 42 | 43 | private final CuratorFramework client; 44 | private final ExecutorService EVENT_THREAD_POOL = Executors 45 | .newFixedThreadPool(1, ThreadUtils.newThreadFactory("PathChildrenCache")); 46 | private final ExecutorService SAME_EXECUTOR = MoreExecutors.sameThreadExecutor(); 47 | private final AtomicInteger REFERENCE_COUNT = new AtomicInteger(0); 48 | 49 | private Lock _lock = new ReentrantLock(true); 50 | 51 | public ZKClientImpl(String adds) { 52 | System.setProperty(DebugUtils.PROPERTY_DONT_LOG_CONNECTION_ISSUES, "false"); 53 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(BASE_SLEEP_TIMEMS, MAX_RETRIES); 54 | this.client = CuratorFrameworkFactory.builder().connectString(adds).retryPolicy(retryPolicy) 55 | .connectionTimeoutMs(5000).build(); 56 | waitUntilZkStart(); 57 | 58 | } 59 | 60 | private void waitUntilZkStart() { 61 | CountDownLatch latch = new CountDownLatch(1); 62 | addConnectionChangeListenter(new ConnectionWatcher(latch)); 63 | client.start(); 64 | try { 65 | latch.await(); 66 | } catch (InterruptedException e) { 67 | LOGGER.error("start zk latch.await() error", e); 68 | Thread.currentThread().interrupt(); 69 | } 70 | } 71 | 72 | /** 73 | * 使用分布式锁执行任务 74 | * 75 | * @param path 76 | * @param getLockTimeout 获取锁超时时间(单位ms) 77 | * @param task 78 | * @auth anduo 2015年5月8日 79 | */ 80 | public void distributeLock(String path, int getLockTimeout, Runnable task) { 81 | InterProcessMutex lock = new InterProcessMutex(client, path); 82 | try { 83 | LOGGER.debug("尝试获取锁。。。"); 84 | if (lock.acquire(getLockTimeout, TimeUnit.MILLISECONDS)) { 85 | try { 86 | LOGGER.debug("获得锁,开始执行任务。。。"); 87 | task.run(); 88 | } finally { 89 | lock.release(); 90 | LOGGER.debug("释放锁,path:" + path); 91 | } 92 | } else { 93 | LOGGER.info("任务执行失败,在时间:" + getLockTimeout + "ms内,未获得分布式锁!"); 94 | } 95 | } catch (Exception e) { 96 | LOGGER.error("执行分布式锁任务异常。", e); 97 | } 98 | 99 | } 100 | 101 | @Override 102 | public List getChildren(String path) 103 | throws Exception { 104 | return client.getChildren().forPath(path); 105 | } 106 | 107 | @Override 108 | public List listenChildrenPath(final String parent, final NodeListener listener, final boolean sync) 109 | throws Exception { 110 | PathChildrenCache cache = new PathChildrenCache(client, parent, false, false, EVENT_THREAD_POOL); 111 | cache.getListenable().addListener(new PathChildrenCacheListener() { 112 | @Override 113 | public void childEvent(CuratorFramework c, PathChildrenCacheEvent e) 114 | throws Exception { 115 | if (e.getData() == null) { return; } 116 | switch (e.getType()) { 117 | case CHILD_ADDED: 118 | listener.nodeChanged(ZKClientImpl.this, 119 | new ChangedEvent(e.getData().getPath(), ChangedEvent.Type.CHILD_ADDED)); 120 | break; 121 | case CHILD_REMOVED: 122 | listener.nodeChanged(ZKClientImpl.this, 123 | new ChangedEvent(e.getData().getPath(), ChangedEvent.Type.CHILD_REMOVED)); 124 | break; 125 | case CHILD_UPDATED: 126 | listener.nodeChanged(ZKClientImpl.this, 127 | new ChangedEvent(e.getData().getPath(), ChangedEvent.Type.CHILD_UPDATED)); 128 | break; 129 | } 130 | } 131 | }, SAME_EXECUTOR); 132 | PathChildrenCache.StartMode mode = sync ? PathChildrenCache.StartMode.BUILD_INITIAL_CACHE : PathChildrenCache.StartMode.NORMAL; 133 | cache.start(mode); 134 | List children = cache.getCurrentData(); 135 | List result = new ArrayList(); 136 | for (ChildData child : children) { 137 | result.add(child.getPath()); 138 | } 139 | return result; 140 | } 141 | 142 | @Override 143 | public String addEphemeralNode(String parent, String node) 144 | throws Exception { 145 | return addEphemeralNode(ZKPaths.makePath(parent, node)); 146 | } 147 | 148 | @Override 149 | public String addEphemeralNode(String path) 150 | throws Exception { 151 | return client.create().withMode(CreateMode.EPHEMERAL).forPath(path); 152 | } 153 | 154 | @Override 155 | public void addPersistentNode(String path) 156 | throws Exception { 157 | try { 158 | client.newNamespaceAwareEnsurePath(path).ensure(client.getZookeeperClient()); 159 | client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path); 160 | } catch (KeeperException.NodeExistsException e) { 161 | LOGGER.warn("Node already exists: {}", path); 162 | } catch (Exception e) { 163 | throw new Exception("addPersistentNode error", e); 164 | } 165 | } 166 | 167 | @Override 168 | public void addEphemeralNodeData(String path, String data) 169 | throws Exception { 170 | try { 171 | client.newNamespaceAwareEnsurePath(path).ensure(client.getZookeeperClient()); 172 | client.create().withMode(CreateMode.EPHEMERAL).forPath(path, data.getBytes()); 173 | } catch (KeeperException.NodeExistsException e) { 174 | LOGGER.warn("Node already exists: {}", path); 175 | } catch (Exception e) { 176 | throw new Exception("addEphemeralNodeData error", e); 177 | } 178 | } 179 | 180 | /** 181 | * 创建 节点 182 | * 183 | * @param path 184 | * @param data 185 | * @auth anduo 2015年5月8日 186 | */ 187 | @Override 188 | public void createPath(String path, String data) { 189 | try { 190 | client.newNamespaceAwareEnsurePath(path).ensure(client.getZookeeperClient()); 191 | client.setData().forPath(path, data.getBytes()); 192 | } catch (Exception ex) { 193 | LOGGER.error("创建节点异常,path:" + path + " , data:" + data, ex); 194 | } 195 | } 196 | 197 | /** 198 | * 设置节点值 199 | * 200 | * @param path 201 | * @param data 202 | * @auth anduo 2015年5月8日 203 | */ 204 | @Override 205 | public void updatePathValue(String path, String data) { 206 | try { 207 | LOGGER.debug("设置结点值,path:" + path + ",data:" + data); 208 | this.client.setData().forPath(path, data.getBytes("UTF-8")); 209 | } catch (Exception e) { 210 | LOGGER.error("设置zookeeper节点值异常,path:" + path + ",data" + data, e); 211 | } 212 | } 213 | 214 | /** 215 | * 获取节点值 216 | * 217 | * @param path 218 | * @return 219 | * @throws Exception 220 | * @auth anduo 2015年5月7日 221 | */ 222 | @Override 223 | public String getPathValue(String path) 224 | throws Exception { 225 | if (!checkExist(path)) { 226 | throw new RuntimeException("Path " + path + " does not exists."); 227 | } 228 | return new String(client.getData().forPath(path), "UTF-8"); 229 | } 230 | 231 | @PreDestroy 232 | @Override 233 | public void close() { 234 | LOGGER.info("Call close of ZKClient, reference count is: {}", REFERENCE_COUNT.get()); 235 | if (REFERENCE_COUNT.decrementAndGet() == 0) { 236 | client.close(); 237 | LOGGER.info("ZKClient is closed"); 238 | } 239 | } 240 | 241 | @Override 242 | public boolean isConnected() { 243 | return client.getZookeeperClient().isConnected(); 244 | } 245 | 246 | /** 247 | * @param listener 248 | */ 249 | @Override 250 | public void addConnectionChangeListenter(final ConnectionStateListener listener) { 251 | if (listener != null) { 252 | client.getConnectionStateListenable() 253 | .addListener((sender, state) -> listener.stateChanged(ZKClientImpl.this, convertTo(state))); 254 | } 255 | } 256 | 257 | private ConnectionState convertTo(org.apache.curator.framework.state.ConnectionState state) { 258 | switch (state) { 259 | case CONNECTED: 260 | return ConnectionState.CONNECTED; 261 | case SUSPENDED: 262 | return ConnectionState.SUSPENDED; 263 | case RECONNECTED: 264 | return ConnectionState.RECONNECTED; 265 | case LOST: 266 | return ConnectionState.LOST; 267 | default: 268 | return ConnectionState.READ_ONLY; 269 | } 270 | } 271 | 272 | /** 273 | * 删除一个znode,如果该znode下面有子节点则会抛异常 274 | * 275 | * @param path 276 | * @throws Exception 277 | */ 278 | @Override 279 | public void deletePath(String path) 280 | throws Exception { 281 | client.delete().forPath(path); 282 | } 283 | 284 | @Override 285 | public void deletAllPath(String path) 286 | throws Exception { 287 | List children = client.getChildren().forPath(path); 288 | if (children == null || children.size() == 0) { 289 | deletePath(path); 290 | return; 291 | } 292 | for (String child : children) { 293 | deletAllPath(ZKPaths.makePath(path, child)); 294 | } 295 | 296 | } 297 | 298 | private class ConnectionWatcher implements ConnectionStateListener { 299 | CountDownLatch latch; 300 | 301 | ConnectionWatcher(CountDownLatch latch) { 302 | this.latch = latch; 303 | } 304 | 305 | @Override 306 | public void stateChanged(ZKClient client, ConnectionState newState) { 307 | if (newState == newState.CONNECTED) { 308 | latch.countDown(); 309 | } 310 | } 311 | } 312 | 313 | @Override 314 | public boolean checkExist(String path) { 315 | try { 316 | Stat stat = client.checkExists().forPath(path); 317 | return stat != null; 318 | } catch (Exception e) { 319 | LOGGER.error("check exist error", e); 320 | return false; 321 | } 322 | } 323 | 324 | protected void incrementReference() { 325 | REFERENCE_COUNT.incrementAndGet(); 326 | } 327 | 328 | } 329 | 330 | -------------------------------------------------------------------------------- /nz-client/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | zk.servers=127.0.0.1:2181 2 | zk.config.root.path=/myapp/config 3 | zk.config.nodes.path=/myapp/config/nodes 4 | zk.config.commoncfg.path=/myapp/config/commoncfg 5 | 6 | local.config.nodes.path=local-nodes.properties 7 | local.config.commoncfg.path=local-commoncfg.properties 8 | -------------------------------------------------------------------------------- /nz-client/src/main/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | classpath:application.properties 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /nz-client/src/main/resources/local-commoncfg.properties: -------------------------------------------------------------------------------- 1 | ### commoncfg ### 2 | random.file.count=100 3 | random.file.size.min=10K 4 | random.file.size.max=10M 5 | randmo.file.last=You are Best! -------------------------------------------------------------------------------- /nz-client/src/main/resources/local-nodes.properties: -------------------------------------------------------------------------------- 1 | ### nodes ### 2 | ##name-port## 3 | Leader-1=8411 4 | Leader-2=8412 5 | Leader-3=8413 6 | Leader-4=8414 7 | Leader-5=8415 -------------------------------------------------------------------------------- /nz-client/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=info, stdout, R 2 | 3 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 4 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 5 | 6 | # Pattern to output the caller's file name and line number. 7 | log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n 8 | 9 | log4j.appender.R=org.apache.log4j.RollingFileAppender 10 | log4j.appender.R.File=Logs/nz.log 11 | 12 | log4j.appender.R.MaxFileSize=100KB 13 | # Keep one backup file 14 | log4j.appender.R.MaxBackupIndex=1 15 | 16 | log4j.appender.R.layout=org.apache.log4j.PatternLayout 17 | log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n -------------------------------------------------------------------------------- /nz-client/src/main/resources/name.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoan/codes-scratch-zookeeper-netty/f3e2988374ddb677bc6b10f6c3b45ee359771aea/nz-client/src/main/resources/name.properties -------------------------------------------------------------------------------- /nz-client/src/test/java/com/anduo/log4j/LogTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.log4j; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * ━━━━━━神兽出没━━━━━━ 10 | *    ┏┓   ┏┓ 11 | *   ┏┛┻━━━┛┻┓ 12 | *   ┃       ┃ 13 | *   ┃   ━   ┃ 14 | *   ┃ ┳┛ ┗┳ ┃ 15 | *   ┃       ┃ 16 | *   ┃   ┻   ┃ 17 | *   ┃       ┃ 18 | *   ┗━┓   ┏━┛ 19 | *     ┃   ┃神兽保佑, 永无BUG! 20 | *     ┃   ┃Code is far away from bug with the animal protecting 21 | *     ┃   ┗━━━┓ 22 | *     ┃       ┣┓ 23 | *     ┃       ┏┛ 24 | *     ┗┓┓┏━┳┓┏┛ 25 | *      ┃┫┫ ┃┫┫ 26 | *      ┗┻┛ ┗┻┛ 27 | * ━━━━━━感觉萌萌哒━━━━━━ 28 | * Summary: log4jtest 29 | * Author : anduo@qq.com 30 | * Version: 1.0 31 | * Date : 15/7/2 32 | * time : 22:26 33 | */ 34 | public class LogTest { 35 | private static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class); 36 | 37 | public static void main(String[] args) { 38 | LOGGER.info("Hello World"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /nz-client/src/test/java/com/anduo/log4j/SampleAnt.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.log4j; 4 | 5 | import org.kohsuke.args4j.Argument; 6 | import org.kohsuke.args4j.CmdLineException; 7 | import org.kohsuke.args4j.CmdLineParser; 8 | import org.kohsuke.args4j.Option; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.util.List; 13 | import java.util.TreeMap; 14 | import java.util.Vector; 15 | 16 | /** 17 | * ━━━━━━神兽出没━━━━━━ 18 | *    ┏┓   ┏┓ 19 | *   ┏┛┻━━━┛┻┓ 20 | *   ┃       ┃ 21 | *   ┃   ━   ┃ 22 | *   ┃ ┳┛ ┗┳ ┃ 23 | *   ┃       ┃ 24 | *   ┃   ┻   ┃ 25 | *   ┃       ┃ 26 | *   ┗━┓   ┏━┛ 27 | *     ┃   ┃神兽保佑, 永无BUG! 28 | *     ┃   ┃Code is far away from bug with the animal protecting 29 | *     ┃   ┗━━━┓ 30 | *     ┃       ┣┓ 31 | *     ┃       ┏┛ 32 | *     ┗┓┓┏━┳┓┏┛ 33 | *      ┃┫┫ ┃┫┫ 34 | *      ┗┻┛ ┗┻┛ 35 | * ━━━━━━感觉萌萌哒━━━━━━ 36 | * Summary: 命令行工具测试 37 | * Author : anduo@qq.com 38 | * Version: 1.0 39 | * Date : 15/7/3 40 | * time : 21:06 41 | */ 42 | public class SampleAnt { 43 | // 'normal' use 44 | @Option(name = "-help", usage = "print this message") 45 | private boolean help = false; 46 | 47 | // Ant uses abbreviations - a second option to set the 48 | // same attribute. Because only one @Option is allowed per element, 49 | // we use a workaround: a setter gets the abbreviation 50 | // A 'usage' would duplicate the info message, but without the possibility 51 | // of '-h' is hidden. 52 | @Option(name = "-h") 53 | private void setHelp(boolean h) { help = h; } 54 | 55 | @Option(name = "-projecthelp", usage = "print project help information") 56 | private boolean projecthelp; 57 | 58 | @Option(name = "-p") 59 | private void setProjecthelp(boolean p) { projecthelp = p; } 60 | 61 | @Option(name = "-version", usage = "print the version information and exit") 62 | private boolean version; 63 | 64 | @Option(name = "-diagnostics", usage = "print information that might be helpful to\ndiagnose or report problems.") 65 | private boolean diagnostics; 66 | 67 | @Option(name = "-quiet", usage = "be extra quiet") 68 | private boolean quiet; 69 | 70 | @Option(name = "-q") 71 | private void setQuiet(boolean q) { quiet = q; } 72 | 73 | @Option(name = "-verbose", usage = "be extra verbose") 74 | private boolean verbose; 75 | 76 | @Option(name = "-v") 77 | private void setVerbose(boolean v) { verbose = v; } 78 | 79 | @Option(name = "-debug", usage = "print debugging information") 80 | private boolean debug; 81 | 82 | @Option(name = "-d") 83 | private void setDebug(boolean d) { debug = d; } 84 | 85 | @Option(name = "-emacs", usage = "produce logging information without adornments") 86 | private boolean emacs; 87 | 88 | @Option(name = "-e") 89 | private void setEmacs(boolean e) { emacs = e; } 90 | 91 | @Option(name = "-lib", usage = "specifies a path to search for jars and classes", metaVar = "") 92 | private void setLib(String s) { 93 | String[] files = s.split(System.getProperty("path.separator")); 94 | for (int i = 0; i < files.length; i++) { 95 | lib.add(new File(files[i])); 96 | } 97 | } 98 | 99 | private List lib = new Vector(); 100 | 101 | @Option(name = "-logger", usage = "the class which is to perform logging", metaVar = "") 102 | private String logger; 103 | 104 | @Option(name = "-listener", usage = "the class which is to perform", metaVar = "") 105 | private String listener; 106 | 107 | @Option(name = "-noinput", usage = "do not allow interactive input") 108 | private boolean noinput; 109 | 110 | @Option(name = "-buildfile", usage = "use given buildfile", metaVar = "") 111 | private File buildfile; 112 | 113 | // Ant's original is 114 | // -buildfile use given buildfile 115 | // -file '' 116 | // -f '' 117 | // How to handle this? Args4J prints the arguments sorted alphabetically. 118 | @Option(name = "-file", usage = " ''", metaVar = "") 119 | private void setBuildfile1(File f) { buildfile = f; } 120 | 121 | @Option(name = "-f", usage = " ''", metaVar = "") 122 | private void setBuildfile2(File f) { buildfile = f; } 123 | 124 | //TODO: How to handle that? 125 | // -D= use value for given property 126 | private TreeMap props = new TreeMap(); 127 | 128 | @Option(name = "-keep-going", usage = "execute all targets that do not depend\non failed target(s)") 129 | private boolean keepGoing; 130 | 131 | @Option(name = "-k") 132 | private void setKeepGoing(boolean kg) { keepGoing = kg; } 133 | 134 | @Option(name = "-propertyfile", usage = "load all properties from file with -D\nproperties taking precedence", 135 | metaVar = "") 136 | private File propertyfile; 137 | 138 | @Option(name = "-inputhandler", usage = "the class which will handle input requests", metaVar = "") 139 | private String inputhandler; 140 | 141 | //TODO: Another problem 142 | // -find (s)earch for buildfile towards the root of 143 | // -s the filesystem and use it 144 | //problem is: info text has two lines and there are two lines for different option strings 145 | @Option(name = "-find", usage = "(s)earch for buildfile towards the root of\nthe filesystem and use it", 146 | metaVar = "") 147 | private File file; 148 | 149 | @Option(name = "-s", metaVar = "") 150 | private void setFile(File f) { file = f; } 151 | 152 | @Option(name = "-nice", 153 | usage = "A niceness value for the main thread:\n1 (lowest) to 10 (highest); 5 is the default") 154 | private void setNice(int n) { 155 | if (n > 0 && n < 11) { nice = n; } 156 | } 157 | 158 | private int nice = 5; 159 | 160 | @Option(name = "-nouserlib", usage = "Run ant without using the jar files from\n${user.home}/.ant/lib") 161 | private boolean nouserlib; 162 | 163 | @Option(name = "-noclasspath", usage = "Run ant without using CLASSPATH") 164 | private boolean noclasspath; 165 | 166 | @Argument 167 | private List targets; 168 | 169 | public static void main(String[] args) 170 | throws IOException { 171 | SampleAnt bean = new SampleAnt(); 172 | try { 173 | bean.parseArgs(args); 174 | bean.showResult(); 175 | } catch (IOException ioe) { 176 | //no-op 177 | } 178 | } 179 | 180 | public void parseArgs(String[] args) 181 | throws IOException { 182 | CmdLineParser parser = new CmdLineParser(this); 183 | try { 184 | parser.parseArgument(args); 185 | } catch (CmdLineException e) { 186 | int start = e.getMessage().indexOf('"') + 1; 187 | int end = e.getMessage().lastIndexOf('"'); 188 | String wrongArgument = e.getMessage().substring(start, end); 189 | System.err.println("Unknown argument: " + wrongArgument); 190 | System.err.println("ant [options] [target [target2 [target3] ...]]"); 191 | parser.printUsage(System.err); 192 | System.err.println(); 193 | throw new IOException(); 194 | } 195 | } 196 | 197 | public void showResult() { 198 | System.out.println("SampleAnt was configured with..."); 199 | System.out.println(" help : " + help); 200 | System.out.println(" projecthelp : " + projecthelp); 201 | System.out.println(" version : " + version); 202 | System.out.println(" diagnostics : " + diagnostics); 203 | System.out.println(" quiet : " + quiet); 204 | System.out.println(" verbose : " + verbose); 205 | System.out.println(" debug : " + debug); 206 | System.out.println(" emacs : " + emacs); 207 | System.out.println(" lib"); 208 | for (File f : lib) { 209 | System.out.println(" - " + f); 210 | } 211 | System.out.println(" logger : " + logger); 212 | System.out.println(" listener : " + listener); 213 | System.out.println(" noinput : " + noinput); 214 | System.out.println(" buildfile : " + buildfile); 215 | System.out.println(" properties"); 216 | for (String key : props.keySet()) { 217 | System.out.println(" - " + key + " = " + props.get(key)); 218 | } 219 | System.out.println(" keepGoing : " + keepGoing); 220 | System.out.println(" propertyfile : " + propertyfile); 221 | System.out.println(" inputhandler : " + inputhandler); 222 | System.out.println(" buildfile : " + file); 223 | System.out.println(" nice : " + nice); 224 | System.out.println(" nouserlib : " + nouserlib); 225 | System.out.println(" noclasspath : " + noclasspath); 226 | System.out.println(""); 227 | System.out.println(" targets"); 228 | for (String t : targets) { 229 | System.out.println(" - " + t); 230 | } 231 | System.out.println(""); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /nz-client/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=debug, stdout 2 | 3 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 4 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 5 | 6 | # Pattern to output the caller's file name and line number. 7 | log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n 8 | -------------------------------------------------------------------------------- /nz-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | nz 7 | com.anduo 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | com.anduo.nz 13 | server 14 | jar 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.apache.curator 26 | curator-recipes 27 | 28 | 29 | 30 | org.apache.commons 31 | commons-lang3 32 | 33 | 34 | 35 | 36 | net.coobird 37 | thumbnailator 38 | 39 | 40 | 41 | com.alibaba 42 | fastjson 43 | 44 | 45 | 46 | io.netty 47 | netty-all 48 | 49 | 50 | 51 | log4j 52 | log4j 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/client/FileUploadClient.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.client; 4 | 5 | import java.io.File; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | /** 10 | * Summary: TODO 描述信息 11 | * Author : anduo@qq.com 12 | * Version: 1.0 13 | * Date : 15/7/1 14 | * time : 23:57 15 | */ 16 | public class FileUploadClient { 17 | public static final int CLIENT_COUNT = 100; 18 | 19 | public static void main(String args[]) { 20 | 21 | for (int i = 0; i < CLIENT_COUNT; i++) { 22 | new Thread(new Runnable() { 23 | 24 | @Override 25 | public void run() { 26 | try { 27 | uploadFile(); 28 | } catch (Exception ex) { 29 | Logger.getLogger(FileUploadClient.class.getName()).log(Level.SEVERE, null, ex); 30 | } 31 | } 32 | }).start(); 33 | 34 | } 35 | 36 | } 37 | 38 | private static void uploadFile() 39 | throws Exception { 40 | File file = new File("small.jpg"); 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/common/ConfigFile.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.common; 4 | 5 | import io.netty.util.internal.StringUtil; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.apache.log4j.LogManager; 8 | import org.apache.log4j.Logger; 9 | import org.w3c.dom.Node; 10 | 11 | import java.util.ResourceBundle; 12 | 13 | /** 14 | * ━━━━━━神兽出没━━━━━━ 15 | *    ┏┓   ┏┓ 16 | *   ┏┛┻━━━┛┻┓ 17 | *   ┃       ┃ 18 | *   ┃   ━   ┃ 19 | *   ┃ ┳┛ ┗┳ ┃ 20 | *   ┃       ┃ 21 | *   ┃   ┻   ┃ 22 | *   ┃       ┃ 23 | *   ┗━┓   ┏━┛ 24 | *     ┃   ┃神兽保佑, 永无BUG! 25 | *     ┃   ┃Code is far away from bug with the animal protecting 26 | *     ┃   ┗━━━┓ 27 | *     ┃       ┣┓ 28 | *     ┃       ┏┛ 29 | *     ┗┓┓┏━┳┓┏┛ 30 | *      ┃┫┫ ┃┫┫ 31 | *      ┗┻┛ ┗┻┛ 32 | * ━━━━━━感觉萌萌哒━━━━━━ 33 | * Summary: TODO 描述信息 34 | * Author : anduo@qq.com 35 | * Version: 1.0 36 | * Date : 15/7/2 37 | * time : 00:57 38 | */ 39 | public class ConfigFile { 40 | 41 | private static final Logger LOGGER = LogManager.getLogger(ConfigFile.class); 42 | 43 | private ResourceBundle rb = null; 44 | private static ConfigFile commonConfig = null; 45 | private static ConfigFile zkConfig = null; 46 | 47 | static { 48 | LOGGER.info("load commonConfig&zkConfig properities."); 49 | commonConfig = new ConfigFile(ResourceBundle.getBundle("commonConfig")); 50 | zkConfig = new ConfigFile(ResourceBundle.getBundle("zkConfig")); 51 | } 52 | 53 | public ConfigFile(ResourceBundle rb) { 54 | this.rb = rb; 55 | } 56 | 57 | public static ConfigFile commonConfig() { 58 | return commonConfig; 59 | } 60 | 61 | public static ConfigFile zkConfig() {return zkConfig;} 62 | 63 | public String getItem(String item, String defaultValue) { 64 | String value = null; 65 | if (this.rb != null) { 66 | try { 67 | value = this.rb.getString(item.trim()); 68 | value = value.trim(); 69 | } catch (Exception e) { 70 | value = defaultValue; 71 | } 72 | } 73 | if (StringUtils.isEmpty(value)) { 74 | value = defaultValue; 75 | } 76 | return value; 77 | } 78 | 79 | public int getIntItem(String item, String defaultValue) { 80 | int i = 0; 81 | String value = getItem(item, defaultValue); 82 | try { 83 | i = Integer.parseInt(value); 84 | } catch (NumberFormatException e) { 85 | LOGGER.info(e.getMessage()); 86 | } 87 | return i; 88 | } 89 | 90 | public long getLongItem(String item, String defaultValue) { 91 | long i = 0; 92 | String value = getItem(item, defaultValue); 93 | try { 94 | i = Long.valueOf(value); 95 | } catch (NumberFormatException e) { 96 | LOGGER.info(e.getMessage()); 97 | } 98 | return i; 99 | } 100 | 101 | public double getDoubleItem(String item, String defaultValue) { 102 | double i = 0; 103 | String value = getItem(item, defaultValue); 104 | try { 105 | i = Double.valueOf(value); 106 | } catch (NumberFormatException e) { 107 | LOGGER.info(e.getMessage()); 108 | } 109 | return i; 110 | } 111 | 112 | public boolean getBooleanItem(String item, boolean defaultValue) { 113 | boolean b = false; 114 | String value = getItem(item, new Boolean(defaultValue).toString()); 115 | if ((value != null) && (value.equalsIgnoreCase("true"))) { 116 | b = true; 117 | } 118 | return b; 119 | } 120 | 121 | protected String getNodeValue(Node _node) { 122 | if (_node == null) { 123 | return null; 124 | } 125 | Node _firstChild = _node.getFirstChild(); 126 | if (_firstChild == null) { 127 | return null; 128 | } 129 | String _text = _firstChild.getNodeValue(); 130 | if (_text != null) { 131 | _text = _text.trim(); 132 | } 133 | return _text; 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/common/Constants.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.common; 4 | 5 | /** 6 | * ━━━━━━神兽出没━━━━━━ 7 | *    ┏┓   ┏┓ 8 | *   ┏┛┻━━━┛┻┓ 9 | *   ┃       ┃ 10 | *   ┃   ━   ┃ 11 | *   ┃ ┳┛ ┗┳ ┃ 12 | *   ┃       ┃ 13 | *   ┃   ┻   ┃ 14 | *   ┃       ┃ 15 | *   ┗━┓   ┏━┛ 16 | *     ┃   ┃神兽保佑, 永无BUG! 17 | *     ┃   ┃Code is far away from bug with the animal protecting 18 | *     ┃   ┗━━━┓ 19 | *     ┃       ┣┓ 20 | *     ┃       ┏┛ 21 | *     ┗┓┓┏━┳┓┏┛ 22 | *      ┃┫┫ ┃┫┫ 23 | *      ┗┻┛ ┗┻┛ 24 | * ━━━━━━感觉萌萌哒━━━━━━ 25 | * Summary: 公共常量类 26 | * Author : anduo@qq.com 27 | * Version: 1.0 28 | * Date : 15/7/2 29 | * time : 00:54 30 | */ 31 | public class Constants { 32 | /** 33 | * MODULE_TYPE Server 34 | */ 35 | public static final String MODULE_TYPE_SERVER = "server"; 36 | /** 37 | * MODULE_TYPE Client 38 | */ 39 | public static final String MODULE_TYPE_CLIENT = "client"; 40 | /** 41 | * MODULE_TYPE Server With Admin 42 | */ 43 | public static final String MODULE_TYPE_SERVER_WITH_ADMIN = "serverWithAdmin"; 44 | 45 | /** 46 | * MODULE_TYPE Client With Admin 47 | */ 48 | public static final String MODULE_TYPE_CLIENT_WITH_ADMIN = "clientWithAdmin"; 49 | 50 | /** 51 | * MODULE_TYPE Server With Monitor 52 | */ 53 | public static final String MODULE_TYPE_SERVER_WITH_MONITOR = "serverWithMonitor"; 54 | 55 | /** 56 | * zero mq 服务端 启动消费者 端口 57 | */ 58 | public static final String ZMQ_SERVER_PORT; 59 | 60 | /** 61 | * NETTY 服务端 启动消费者 端口 62 | */ 63 | public static final int NETTY_SERVER_PORT; 64 | 65 | /** 66 | * job type local:本地job 67 | */ 68 | public static final String JOB_TYPE_LOCAL = "LOCAL"; 69 | /** 70 | * job type remote:分布式job 71 | */ 72 | public static final String JOB_TYPE_REMOTE = "REMOTE"; 73 | 74 | /** 75 | * job status 1:初始化状态 76 | */ 77 | public static final String JOB_STATUS_1 = "1"; 78 | /** 79 | * job status 2:运行中状态 80 | */ 81 | public static final String JOB_STATUS_2 = "2"; 82 | 83 | /** 84 | * job status 3:结束状态 85 | */ 86 | public static final String JOB_STATUS_3 = "3"; 87 | 88 | public static final String ALIVE = "alive"; 89 | 90 | public static final String ALIVE_STATUS_0 = "0";//死亡 91 | public static final String ALIVE_STATUS_1 = "1";//存活 92 | 93 | public static final long SERVER_DIFFER_MILLI_SECONDS; //服务端HeartBeat间隔时间2分钟 94 | 95 | public static final long CLIENT_DIFFER_MILLI_SECONDS; //客户端HeartBeat间隔时间2分钟 96 | 97 | public static final long MONITOR_DIFFER_MILLI_SECONDS; //monitor HeartBeat间隔时间2分钟 98 | 99 | public static final String TOPIC_CLOVER_SERVER = "TOPIC_CLOVER_SERVER"; 100 | public static final String TOPIC_CLOVER_CLIENT = "TOPIC_CLOVER_CLIENT"; 101 | 102 | public static final int port = 8888;//netty port 103 | 104 | public static final String DEFAULT_COMPANY_EMAIL; 105 | public static final String DEFAULT_PRIVATE_EMAIL; 106 | public static final String SYSTEM_ID_CLOVER = "clover"; 107 | 108 | public static final String REMOTE_JOB_GROUP = "remote-jobs"; 109 | public static final String SPLIT_CHARACTER_FALG = "_#_"; 110 | public static final String SERVER_JOB_INFO = "serverJobInfo"; 111 | public static final String CLIENT_JOB_INFO = "clientJobInfo"; 112 | public static final String CLIENT_JOB_PATH = "clientJobPath"; 113 | 114 | public static String ZK_CONNECT_STRING = null; 115 | public static String ZK_ROOT_PATH = null; 116 | public static String ZK_USER_NAME = null; 117 | public static String ZK_PASSWORD = null; 118 | public static int ZOO_KEEPER_TIMEOUT; 119 | 120 | public static long ZMQ_SLEEP_CLIENT_MILLIS; //zeromq消费者端 等待一分钟后启动 121 | public static long ZMQ_SLEEP_SERVER_MILLIS;//zeromq生产者端 等待一分钟后启动 122 | 123 | public static String token; 124 | 125 | public static String DISABLED_DB; //是否启动DB 126 | public static String TYPE_DB = "typeDB"; //DB类型 127 | 128 | public static int POOL_SIZE; 129 | 130 | public static int MAX_FAIL_TIMES; //max fail times 131 | public static double MAX_MEM_RATIO; //max mem ratio 132 | public static double MAX_CPU_RATIO; //max cpu ratio 133 | public static String SERVER_JOB_STRATEGY; //server job strategy 134 | 135 | public static String ID = "id"; 136 | public static String JOB_CLASS = "jobClass"; 137 | public static String PORT = "port"; 138 | public static String IP = "ip"; 139 | public static String TS = "ts"; 140 | public static String MEM_RATIO = "memRatio"; 141 | public static String CPU_RATIO = "cpuRatio"; 142 | public static String TOTAL_THREAD = "totalThread"; 143 | 144 | public static String SUCCESS = "success"; 145 | public static String ERROR_CODE = "errorCode"; 146 | 147 | public static String ERROR_CODE_101 = "101"; //serverInfo |clientInfo is null 148 | public static String ERROR_CODE_102 = "102"; //over max mem ratio 149 | public static String ERROR_CODE_103 = "103"; // other error 150 | 151 | static { 152 | ZMQ_SERVER_PORT = ConfigFile.commonConfig().getItem("zmqServerPort", "1688"); 153 | NETTY_SERVER_PORT = ConfigFile.commonConfig().getIntItem("nettyServerPort", "8087"); 154 | SERVER_DIFFER_MILLI_SECONDS = ConfigFile.commonConfig().getLongItem("serverDifferMilliSeconds", "2*60*1000"); 155 | CLIENT_DIFFER_MILLI_SECONDS = ConfigFile.commonConfig().getLongItem("clientDifferMilliSeconds", "2*60*1000"); 156 | MONITOR_DIFFER_MILLI_SECONDS = ConfigFile.commonConfig().getLongItem("monitorDifferMilliSeconds", "2*60*1000"); 157 | DEFAULT_COMPANY_EMAIL = ConfigFile.commonConfig().getItem("defaultCompanyEmail", "xiaoxiangxu@yolo24.com"); 158 | DEFAULT_PRIVATE_EMAIL = ConfigFile.commonConfig().getItem("defaultPrivateEmail", "zhutouzan@163.com"); 159 | ZMQ_SLEEP_CLIENT_MILLIS = ConfigFile.commonConfig().getLongItem("zmqSleepClientMillis", "1000"); 160 | ZMQ_SLEEP_SERVER_MILLIS = ConfigFile.commonConfig().getLongItem("zmqSleepServerMillis", "1000"); 161 | token = ConfigFile.commonConfig().getItem("token", "6fb8535d703f2492704aefc212b7cd41"); 162 | DISABLED_DB = ConfigFile.commonConfig().getItem("disabledDB", "disabledDB"); 163 | POOL_SIZE = ConfigFile.commonConfig().getIntItem("poolSize", "100"); 164 | MAX_FAIL_TIMES = ConfigFile.commonConfig().getIntItem("maxFailTimes", "5"); 165 | MAX_MEM_RATIO = ConfigFile.commonConfig().getDoubleItem("maxMemRatio", "0.95"); 166 | MAX_CPU_RATIO = ConfigFile.commonConfig().getDoubleItem("maxCpuRatio", "0.95"); 167 | SERVER_JOB_STRATEGY = ConfigFile.commonConfig().getItem("serverJobStrategy", "SYSTEM_CAPACITY"); 168 | 169 | ZK_CONNECT_STRING = ConfigFile.zkConfig().getItem("zkConnectString", "127.0.0.1:2181"); 170 | ZK_ROOT_PATH = ConfigFile.zkConfig().getItem("rootPath", "/clover"); 171 | ZK_USER_NAME = ConfigFile.zkConfig().getItem("userName", "cloverAdmin"); 172 | ZK_PASSWORD = ConfigFile.zkConfig().getItem("password", "password"); 173 | ZOO_KEEPER_TIMEOUT = ConfigFile.zkConfig().getIntItem("zkSessionTimeout", "60000000"); 174 | } 175 | 176 | } 177 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/entity/EchoFile.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.entity; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * Summary: EchoFile 9 | * Author : anduo@qq.com 10 | * Version: 1.0 11 | * Date : 15/7/2 12 | * time : 00:18 13 | */ 14 | public class EchoFile implements Serializable { 15 | private static final long serialVersionUID = 8953150675564212795L; 16 | /** 17 | * 总包数. 18 | */ 19 | private int sumCountPackage; 20 | /** 21 | * 当前包数. 22 | */ 23 | private int countPackage; 24 | /** 25 | * 文件名 26 | */ 27 | private String file_md5;// 28 | /** 29 | * 文件内容字节数组 30 | */ 31 | private byte[] bytes;// 32 | 33 | public EchoFile() { 34 | } 35 | 36 | public EchoFile(byte[] bytes) { 37 | this.bytes = bytes; 38 | } 39 | 40 | /** 41 | * @return the sumCountPackage 42 | */ 43 | public int getSumCountPackage() { 44 | return sumCountPackage; 45 | } 46 | 47 | /** 48 | * @param sumCountPackage the sumCountPackage to set 49 | */ 50 | public void setSumCountPackage(int sumCountPackage) { 51 | this.sumCountPackage = sumCountPackage; 52 | } 53 | 54 | /** 55 | * @return the countPackage 56 | */ 57 | public int getCountPackage() { 58 | return countPackage; 59 | } 60 | 61 | public void setCountPackage(int countPackage) { 62 | this.countPackage = countPackage; 63 | } 64 | 65 | public byte[] getBytes() { 66 | return bytes; 67 | } 68 | 69 | public void setBytes(byte[] bytes) { 70 | this.bytes = bytes; 71 | } 72 | 73 | public String getFile_md5() { 74 | return file_md5; 75 | } 76 | 77 | public void setFile_md5(String file_md5) { 78 | this.file_md5 = file_md5; 79 | } 80 | } -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/netty/EchoClient.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty; 4 | 5 | import com.anduo.nz.netty.handler.EchoClientHandler; 6 | import io.netty.bootstrap.Bootstrap; 7 | import io.netty.channel.ChannelFuture; 8 | import io.netty.channel.ChannelInitializer; 9 | import io.netty.channel.ChannelOption; 10 | import io.netty.channel.EventLoopGroup; 11 | import io.netty.channel.nio.NioEventLoopGroup; 12 | import io.netty.channel.socket.SocketChannel; 13 | import io.netty.channel.socket.nio.NioSocketChannel; 14 | import io.netty.handler.codec.serialization.ClassResolvers; 15 | import io.netty.handler.codec.serialization.ObjectDecoder; 16 | import io.netty.handler.codec.serialization.ObjectEncoder; 17 | 18 | /** 19 | * Summary: TODO 描述信息 20 | * Author : anduo@qq.com 21 | * Version: 1.0 22 | * Date : 15/7/2 23 | * time : 00:40 24 | */ 25 | public class EchoClient { 26 | public void connect(int port, String host, final String filePath) throws Exception { 27 | EventLoopGroup group = new NioEventLoopGroup(); 28 | try { 29 | Bootstrap b = new Bootstrap(); 30 | b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer() { 31 | 32 | @Override 33 | protected void initChannel(SocketChannel ch) throws Exception { 34 | // ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); 35 | // ch.pipeline().addLast(new StringEncoder()); 36 | // ch.pipeline().addLast(new FixedLengthFrameDecoder(100)); 37 | // ch.pipeline().addLast(new ChunkedWriteHandler()); 38 | // ch.pipeline().addLast(new StringDecoder()); 39 | // ch.pipeline().addLast(new EchoClientHandler()); 40 | // ch.pipeline().addLast(new 41 | // LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4)); 42 | // ch.pipeline().addLast(new LengthFieldPrepender(4,false)); 43 | ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(null))); 44 | ch.pipeline().addLast(new ObjectEncoder()); 45 | ch.pipeline().addLast(new EchoClientHandler(filePath)); 46 | } 47 | }); 48 | ChannelFuture f = b.connect(host, port).sync(); 49 | f.channel().closeFuture().sync(); 50 | } finally { 51 | group.shutdownGracefully(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/netty/InitializerPipeline.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty; 4 | 5 | import com.anduo.nz.netty.handler.EchoServerHandler; 6 | import io.netty.channel.ChannelInitializer; 7 | import io.netty.channel.ChannelPipeline; 8 | import io.netty.channel.socket.SocketChannel; 9 | import io.netty.handler.codec.serialization.ClassResolvers; 10 | import io.netty.handler.codec.serialization.ObjectDecoder; 11 | import io.netty.handler.codec.serialization.ObjectEncoder; 12 | 13 | /** 14 | * Summary: 装载Netty处理链路. 15 | * Author : anduo@qq.com 16 | * Version: 1.0 17 | * Date : 15/7/2 18 | * time : 00:20 19 | */ 20 | public class InitializerPipeline extends ChannelInitializer { 21 | @Override 22 | protected void initChannel(SocketChannel ch) 23 | throws Exception { 24 | //使用Netty实现的线程池 25 | // DefaultEventExecutorGroup e1=new DefaultEventExecutorGroup(16); 26 | ChannelPipeline pipeline = ch.pipeline(); 27 | // pipeline.addLast("decoder", new MessageDecoder()); 28 | // pipeline.addLast("encoder", new MessageEncoder()); 29 | // pipeline.addLast(e1,"handler", new CommonHandler()); 30 | ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(null))); 31 | ch.pipeline().addLast(new ObjectEncoder()); 32 | pipeline.addLast("handler", new EchoServerHandler()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/netty/NettyProtoServer.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty; 4 | 5 | import io.netty.bootstrap.ServerBootstrap; 6 | import io.netty.channel.ChannelFuture; 7 | import io.netty.channel.ChannelOption; 8 | import io.netty.channel.EventLoopGroup; 9 | import io.netty.channel.nio.NioEventLoopGroup; 10 | import io.netty.channel.socket.nio.NioServerSocketChannel; 11 | import org.apache.logging.log4j.LogManager; 12 | import org.apache.logging.log4j.Logger; 13 | 14 | /** 15 | * Summary: NettyProtoServer 16 | * Author : anduo@qq.com 17 | * Version: 1.0 18 | * Date : 15/7/2 19 | * time : 00:21 20 | */ 21 | public class NettyProtoServer { 22 | 23 | private static final Logger LOGGER = LogManager.getLogger(NettyProtoServer.class); 24 | 25 | /** 26 | * 服务端口. 27 | */ 28 | public static final int PORT = 7766; 29 | 30 | public NettyProtoServer() { 31 | } 32 | 33 | /** 34 | * 启动Netty的方法. 35 | */ 36 | public void initialize() { 37 | ServerBootstrap server = new ServerBootstrap(); 38 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 39 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 40 | try { 41 | server.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) 42 | .childHandler(new InitializerPipeline()).option(ChannelOption.SO_BACKLOG, 128) 43 | .childOption(ChannelOption.SO_KEEPALIVE, true).childOption(ChannelOption.TCP_NODELAY, true); 44 | ChannelFuture f = server.bind(PORT).sync(); 45 | LOGGER.debug("服务端口为:" + PORT); 46 | f.channel().closeFuture().sync(); 47 | } catch (InterruptedException e) { 48 | LOGGER.error("Netty启动异常:", e); 49 | } finally { 50 | workerGroup.shutdownGracefully(); 51 | bossGroup.shutdownGracefully(); 52 | } 53 | } 54 | 55 | /** 56 | * 应用程序入口. 57 | */ 58 | public static void main(String[] args) { 59 | new NettyProtoServer().initialize(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/netty/codec/MessageDecoder.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty.codec; 4 | 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.buffer.ByteBufUtil; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.ByteToMessageDecoder; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * Summary: 自定义解码器(具体功能:可以解决TCP粘包分包问题). 16 | * Author : anduo@qq.com 17 | * Version: 1.0 18 | * Date : 15/7/2 19 | * time : 00:33 20 | */ 21 | public class MessageDecoder extends ByteToMessageDecoder { 22 | 23 | private static final Logger LOGGER = LoggerFactory.getLogger(MessageDecoder.class); 24 | 25 | @Override 26 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) 27 | throws Exception { 28 | // 防止不发报文就关闭连接出现的错误 29 | if (!in.isReadable()) { 30 | return; 31 | } 32 | LOGGER.info(String.format("[%s]收到的的报文:[%s]", ctx.channel().localAddress().toString(), ByteBufUtil.hexDump(in))); 33 | byte[] ss = new byte[in.readableBytes()]; 34 | in.readBytes(ss); 35 | out.add(in); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/netty/codec/MessageEncoder.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty.codec; 4 | 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.buffer.ByteBufUtil; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.MessageToByteEncoder; 9 | import org.apache.logging.log4j.LogManager; 10 | import org.apache.logging.log4j.Logger; 11 | 12 | /** 13 | * Summary: 自定义编码器 14 | * Author : anduo@qq.com 15 | * Version: 1.0 16 | * Date : 15/7/2 17 | * time : 00:27 18 | */ 19 | public class MessageEncoder extends MessageToByteEncoder { 20 | 21 | private static final Logger LOGGER = LogManager.getLogger(MessageEncoder.class); 22 | 23 | @Override 24 | protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) 25 | throws Exception { 26 | LOGGER.info(String.format("[%s]发送出的报文:[%s]", 27 | ctx.channel().localAddress().toString(), 28 | ByteBufUtil.hexDump((ByteBuf) msg))); 29 | out.writeBytes((byte[]) msg); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/netty/handler/EchoClientHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty.handler; 4 | 5 | import com.anduo.nz.entity.EchoFile; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.ChannelInboundHandlerAdapter; 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | 11 | import java.io.File; 12 | import java.io.FileNotFoundException; 13 | import java.io.IOException; 14 | import java.io.RandomAccessFile; 15 | 16 | /** 17 | * Summary: TODO 描述信息 18 | * Author : anduo@qq.com 19 | * Version: 1.0 20 | * Date : 15/7/2 21 | * time : 00:41 22 | */ 23 | public class EchoClientHandler extends ChannelInboundHandlerAdapter { 24 | private static final Logger LOGGER = LogManager.getLogger(EchoClientHandler.class); 25 | private int dataLength = 1024; 26 | public RandomAccessFile randomAccessFile; 27 | private int sumCountpackage = 0; 28 | private String filePath; 29 | 30 | public EchoClientHandler(String filePath) { 31 | this.filePath = filePath; 32 | } 33 | 34 | public void channelActive(ChannelHandlerContext ctx) { 35 | try { 36 | File file = new File(filePath); 37 | 38 | randomAccessFile = new RandomAccessFile(file, "r"); 39 | randomAccessFile.seek(0); 40 | 41 | if ((randomAccessFile.length() % dataLength) == 0) { 42 | sumCountpackage = (int) (randomAccessFile.length() / dataLength); 43 | } else { 44 | sumCountpackage = (int) (randomAccessFile.length() / dataLength) + 1; 45 | } 46 | byte[] bytes = new byte[dataLength]; 47 | 48 | LOGGER.debug("文件总长度:" + randomAccessFile.length()); 49 | if (randomAccessFile.read(bytes) != -1) { 50 | EchoFile msgFile = new EchoFile(); 51 | msgFile.setSumCountPackage(sumCountpackage); 52 | msgFile.setCountPackage(1); 53 | msgFile.setBytes(bytes); 54 | msgFile.setFile_md5(file.getName()); 55 | ctx.writeAndFlush(msgFile); 56 | 57 | } else { 58 | System.out.println("文件已经读完"); 59 | } 60 | } catch (FileNotFoundException e) { 61 | e.printStackTrace(); 62 | } catch (IOException i) { 63 | i.printStackTrace(); 64 | } 65 | } 66 | 67 | @Override 68 | public void channelRead(ChannelHandlerContext ctx, Object msg) 69 | throws Exception { 70 | if (msg instanceof EchoFile) { 71 | EchoFile msgEchoFile = (EchoFile) msg; 72 | int countPackage = msgEchoFile.getCountPackage(); 73 | randomAccessFile.seek(countPackage * dataLength - dataLength); 74 | int byteLength = 0; 75 | // 剩余的文件长度 76 | long remainderFileCount = randomAccessFile.length() - randomAccessFile.getFilePointer(); 77 | LOGGER.debug("剩余文件长度:" + remainderFileCount); 78 | if (remainderFileCount < dataLength) { 79 | LOGGER.debug("小于固定长度:" + remainderFileCount); 80 | byteLength = (int) remainderFileCount; 81 | 82 | } else { 83 | byteLength = dataLength; 84 | } 85 | byte[] bytes = new byte[byteLength]; 86 | if (randomAccessFile.read(bytes) != -1 && remainderFileCount > 0) { 87 | msgEchoFile.setCountPackage(countPackage); 88 | msgEchoFile.setBytes(bytes); 89 | ctx.writeAndFlush(msgEchoFile); 90 | } else { 91 | randomAccessFile.close(); 92 | ctx.close(); 93 | LOGGER.debug("文件已经读完--------" + remainderFileCount); 94 | } 95 | } 96 | 97 | } 98 | 99 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 100 | cause.printStackTrace(); 101 | try { 102 | randomAccessFile.close(); 103 | } catch (IOException e) { 104 | e.printStackTrace(); 105 | } 106 | ctx.close(); 107 | } 108 | 109 | @Override 110 | public void channelInactive(ChannelHandlerContext ctx) 111 | throws Exception { 112 | LOGGER.debug("服务器断开连接"); 113 | randomAccessFile.close(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/netty/handler/EchoServerHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty.handler; 4 | 5 | import com.anduo.nz.entity.EchoFile; 6 | import com.anduo.nz.netty.NettyProtoServer; 7 | import io.netty.channel.ChannelHandlerAdapter; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.channel.ChannelInboundHandlerAdapter; 10 | import io.netty.util.AttributeKey; 11 | import org.apache.logging.log4j.LogManager; 12 | import org.apache.logging.log4j.Logger; 13 | 14 | import java.io.File; 15 | import java.io.RandomAccessFile; 16 | 17 | /** 18 | * Summary: EchoServerHandler 19 | * Author : anduo@qq.com 20 | * Version: 1.0 21 | * Date : 15/7/2 22 | * time : 00:23 23 | */ 24 | public class EchoServerHandler extends ChannelInboundHandlerAdapter { 25 | 26 | private static final Logger LOGGER = LogManager.getLogger(NettyProtoServer.class); 27 | 28 | private String file_dir = "D:"; 29 | private int dataLength = 1024; 30 | 31 | @Override 32 | public void channelActive(ChannelHandlerContext ctx) 33 | throws Exception { 34 | LOGGER.debug(String.format("[%s]\n========打开连接=======", ctx.channel().localAddress().toString())); 35 | ctx.channel().attr(AttributeKey.valueOf("haha")).set("1"); 36 | } 37 | 38 | @Override 39 | public void channelInactive(ChannelHandlerContext ctx) 40 | throws Exception { 41 | LOGGER.debug(String.format("[%s]\n========关闭连接=======", ctx.channel().localAddress().toString())); 42 | LOGGER.debug(ctx.channel().remoteAddress().toString()); 43 | LOGGER.debug(ctx.channel().attr(AttributeKey.valueOf("haha")).get().toString()); 44 | } 45 | 46 | @Override 47 | public void channelRead(ChannelHandlerContext ctx, Object msg) 48 | throws Exception { 49 | if (msg instanceof EchoFile) { 50 | EchoFile ef = (EchoFile) msg; 51 | int SumCountPackage = ef.getSumCountPackage(); 52 | int countPackage = ef.getCountPackage(); 53 | byte[] bytes = ef.getBytes(); 54 | String md5 = ef.getFile_md5();//文件名 55 | 56 | String path = file_dir + File.separator + md5; 57 | File file = new File(path); 58 | RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); 59 | randomAccessFile.seek(countPackage * dataLength - dataLength); 60 | randomAccessFile.write(bytes); 61 | LOGGER.debug("总包数:" + ef.getSumCountPackage()); 62 | LOGGER.debug("收到第" + countPackage + "包"); 63 | LOGGER.debug("本包字节数:" + bytes.length); 64 | countPackage = countPackage + 1; 65 | 66 | if (countPackage <= SumCountPackage) { 67 | ef.setCountPackage(countPackage); 68 | ctx.writeAndFlush(ef); 69 | randomAccessFile.close(); 70 | } else { 71 | randomAccessFile.close(); 72 | ctx.close(); 73 | } 74 | } 75 | } 76 | 77 | @Override 78 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) 79 | throws Exception { 80 | LOGGER.error("[" + ctx.channel().localAddress().toString() + "]" + "通讯异常:", cause); 81 | ctx.close(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/server/HttpStaticFileServer.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.server; 4 | 5 | import io.netty.bootstrap.ServerBootstrap; 6 | import io.netty.channel.Channel; 7 | import io.netty.channel.EventLoopGroup; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.nio.NioServerSocketChannel; 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | 13 | /** 14 | * Summary: HttpStaticFileServer 15 | * Author : anduo@qq.com 16 | * Version: 1.0 17 | * Date : 15/7/1 18 | * time : 23:39 19 | */ 20 | public class HttpStaticFileServer { 21 | 22 | private static final Logger LOGGER = LogManager.getLogger(HttpStaticFileServer.class); 23 | 24 | private final int port; 25 | 26 | public HttpStaticFileServer(int port) { 27 | this.port = port; 28 | } 29 | 30 | public void run() 31 | throws Exception { 32 | EventLoopGroup bossGroup = new NioEventLoopGroup(1); 33 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 34 | try { 35 | ServerBootstrap b = new ServerBootstrap(); 36 | b.group(bossGroup, workerGroup) 37 | .channel(NioServerSocketChannel.class) 38 | .childHandler(new HttpStaticFileServerInitializer()); 39 | 40 | Channel ch = b.bind(port).sync().channel(); 41 | LOGGER.info("File server started at port " + port + '.'); 42 | ch.closeFuture().sync(); 43 | } finally { 44 | bossGroup.shutdownGracefully(); 45 | workerGroup.shutdownGracefully(); 46 | } 47 | } 48 | 49 | public static void main(String[] args) 50 | throws Exception { 51 | int port; 52 | if (args.length > 0) { 53 | port = Integer.parseInt(args[0]); 54 | } else { 55 | port = 8080; 56 | } 57 | new HttpStaticFileServer(port).run(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/server/HttpStaticFileServerHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.server; 4 | 5 | import com.alibaba.fastjson.JSONObject; 6 | import io.netty.buffer.Unpooled; 7 | import io.netty.channel.*; 8 | import io.netty.handler.codec.http.*; 9 | import io.netty.handler.codec.http.multipart.*; 10 | import io.netty.util.CharsetUtil; 11 | import net.coobird.thumbnailator.Thumbnails; 12 | import org.apache.logging.log4j.LogManager; 13 | import org.apache.logging.log4j.Logger; 14 | import org.apache.logging.log4j.Level; 15 | 16 | import javax.activation.MimetypesFileTypeMap; 17 | import java.awt.*; 18 | import java.awt.image.BufferedImage; 19 | import java.io.File; 20 | import java.io.FileNotFoundException; 21 | import java.io.IOException; 22 | import java.io.RandomAccessFile; 23 | import java.net.URI; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.UUID; 27 | 28 | import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION; 29 | import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE; 30 | import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive; 31 | import static io.netty.handler.codec.http.HttpHeaders.setContentLength; 32 | import static io.netty.handler.codec.http.HttpResponseStatus.*; 33 | import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; 34 | 35 | /** 36 | * Summary: HttpStaticFileServerHandler 37 | * Author : anduo@qq.com 38 | * Version: 1.0 39 | * Date : 15/7/1 40 | * time : 23:33 41 | */ 42 | public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler { 43 | 44 | private static final Logger LOGGER = LogManager.getLogger(HttpStaticFileServerHandler.class); 45 | 46 | // where to store the files 47 | private static final String BASE_PATH = "uploads/"; 48 | 49 | // query param used to download a file 50 | private static final String FILE_QUERY_PARAM = "file"; 51 | 52 | private HttpPostRequestDecoder decoder; 53 | private static final HttpDataFactory factory = new DefaultHttpDataFactory(true); 54 | 55 | private boolean readingChunks; 56 | 57 | private static final int THUMB_MAX_WIDTH = 100; 58 | private static final int THUMB_MAX_HEIGHT = 100; 59 | 60 | @Override 61 | protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) 62 | throws Exception { 63 | // get the URL 64 | URI uri = new URI(request.getUri()); 65 | String uriStr = uri.getPath(); 66 | 67 | System.out.println(request.getMethod() + " request received"); 68 | 69 | if (request.getMethod() == HttpMethod.GET) { 70 | serveFile(ctx, request); // user requested a file, serve it 71 | } else if (request.getMethod() == HttpMethod.POST) { 72 | uploadFile(ctx, request); // user requested to upload file, handle request 73 | } else { 74 | // unknown request, send error message 75 | System.out.println(request.getMethod() + " request received, sending 405"); 76 | sendError(ctx, HttpResponseStatus.METHOD_NOT_ALLOWED); 77 | } 78 | 79 | } 80 | 81 | //当绑定到服务端的时候触发 82 | @Override 83 | public void channelActive(ChannelHandlerContext ctx) 84 | throws Exception { 85 | System.out.println("hello this is server!"); 86 | } 87 | 88 | private void serveFile(ChannelHandlerContext ctx, FullHttpRequest request) { 89 | 90 | // decode the query string 91 | QueryStringDecoder decoderQuery = new QueryStringDecoder(request.getUri()); 92 | Map> uriAttributes = decoderQuery.parameters(); 93 | 94 | // get the requested file name 95 | String fileName = ""; 96 | try { 97 | fileName = uriAttributes.get(FILE_QUERY_PARAM).get(0); 98 | } catch (Exception e) { 99 | sendError(ctx, HttpResponseStatus.BAD_REQUEST, FILE_QUERY_PARAM + " query param not found"); 100 | return; 101 | } 102 | 103 | // start serving the requested file 104 | sendFile(ctx, fileName, request); 105 | } 106 | 107 | /** 108 | * This method reads the requested file from disk and sends it as response. 109 | * It also sets proper content-type of the response header 110 | * 111 | * @param fileName name of the requested file 112 | */ 113 | private void sendFile(ChannelHandlerContext ctx, String fileName, FullHttpRequest request) { 114 | File file = new File(BASE_PATH + fileName); 115 | if (file.isDirectory() || file.isHidden() || !file.exists()) { 116 | sendError(ctx, NOT_FOUND); 117 | return; 118 | } 119 | 120 | if (!file.isFile()) { 121 | sendError(ctx, FORBIDDEN); 122 | return; 123 | } 124 | 125 | RandomAccessFile raf; 126 | 127 | try { 128 | raf = new RandomAccessFile(file, "r"); 129 | } catch (FileNotFoundException fnfe) { 130 | sendError(ctx, NOT_FOUND); 131 | return; 132 | } 133 | 134 | long fileLength = 0; 135 | try { 136 | fileLength = raf.length(); 137 | } catch (IOException ex) { 138 | LOGGER.log(Level.ERROR, "fileLength error", ex); 139 | } 140 | 141 | HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); 142 | setContentLength(response, fileLength); 143 | setContentTypeHeader(response, file); 144 | 145 | //setDateAndCacheHeaders(response, file); 146 | if (isKeepAlive(request)) { 147 | response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); 148 | } 149 | 150 | // Write the initial line and the header. 151 | ctx.write(response); 152 | 153 | // Write the content. 154 | ChannelFuture sendFileFuture; 155 | DefaultFileRegion defaultRegion = new DefaultFileRegion(raf.getChannel(), 0, fileLength); 156 | sendFileFuture = ctx.write(defaultRegion); 157 | 158 | // Write the end marker 159 | ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); 160 | 161 | // Decide whether to close the connection or not. 162 | if (!isKeepAlive(request)) { 163 | // Close the connection when the whole content is written out. 164 | lastContentFuture.addListener(ChannelFutureListener.CLOSE); 165 | } 166 | } 167 | 168 | /** 169 | * This will set the content types of files. If you want to support any 170 | * files add the content type and corresponding file extension here. 171 | * 172 | * @param response 173 | * @param file 174 | */ 175 | private static void setContentTypeHeader(HttpResponse response, File file) { 176 | MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); 177 | mimeTypesMap.addMimeTypes("image png tif jpg jpeg bmp"); 178 | mimeTypesMap.addMimeTypes("text/plain txt"); 179 | mimeTypesMap.addMimeTypes("application/pdf pdf"); 180 | 181 | String mimeType = mimeTypesMap.getContentType(file); 182 | 183 | response.headers().set(CONTENT_TYPE, mimeType); 184 | } 185 | 186 | private void uploadFile(ChannelHandlerContext ctx, FullHttpRequest request) { 187 | 188 | // test comment 189 | try { 190 | decoder = new HttpPostRequestDecoder(factory, request); 191 | //System.out.println("decoder created"); 192 | } catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) { 193 | e1.printStackTrace(); 194 | sendError(ctx, HttpResponseStatus.BAD_REQUEST, "Failed to decode file data"); 195 | return; 196 | } 197 | 198 | readingChunks = HttpHeaders.isTransferEncodingChunked(request); 199 | 200 | if (decoder != null) { 201 | if (request instanceof HttpContent) { 202 | 203 | // New chunk is received 204 | HttpContent chunk = (HttpContent) request; 205 | try { 206 | decoder.offer(chunk); 207 | } catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) { 208 | e1.printStackTrace(); 209 | sendError(ctx, HttpResponseStatus.BAD_REQUEST, "Failed to decode file data"); 210 | return; 211 | } 212 | 213 | readHttpDataChunkByChunk(ctx); 214 | // example of reading only if at the end 215 | if (chunk instanceof LastHttpContent) { 216 | readingChunks = false; 217 | reset(); 218 | } 219 | } else { 220 | sendError(ctx, HttpResponseStatus.BAD_REQUEST, "Not a http request"); 221 | } 222 | } else { 223 | sendError(ctx, HttpResponseStatus.BAD_REQUEST, "Failed to decode file data"); 224 | } 225 | 226 | } 227 | 228 | private void sendOptionsRequestResponse(ChannelHandlerContext ctx) { 229 | HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); 230 | ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); 231 | } 232 | 233 | private void sendResponse(ChannelHandlerContext ctx, String responseString, String contentType, 234 | HttpResponseStatus status) { 235 | FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, 236 | status, 237 | Unpooled.copiedBuffer(responseString, 238 | CharsetUtil.UTF_8)); 239 | 240 | response.headers().set(CONTENT_TYPE, contentType); 241 | response.headers().add("Access-Control-Allow-Origin", "*"); 242 | 243 | // Close the connection as soon as the error message is sent. 244 | ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); 245 | } 246 | 247 | private void sendUploadedFileName(JSONObject fileName, ChannelHandlerContext ctx) { 248 | JSONObject jsonObj = new JSONObject(); 249 | 250 | String msg = "Unexpected error occurred"; 251 | String contentType = "application/json; charset=UTF-8"; 252 | HttpResponseStatus status = HttpResponseStatus.OK; 253 | 254 | if (fileName != null) { 255 | msg = fileName.toString(); 256 | } else { 257 | LOGGER.error("uploaded file names are blank"); 258 | status = HttpResponseStatus.BAD_REQUEST; 259 | contentType = "text/plain; charset=UTF-8"; 260 | } 261 | 262 | sendResponse(ctx, msg, contentType, status); 263 | 264 | } 265 | 266 | private void reset() { 267 | //request = null; 268 | 269 | // destroy the decoder to release all resources 270 | decoder.destroy(); 271 | decoder = null; 272 | } 273 | 274 | /** 275 | * Example of reading request by chunk and getting values from chunk to 276 | * chunk 277 | */ 278 | private void readHttpDataChunkByChunk(ChannelHandlerContext ctx) { 279 | //decoder.isMultipart(); 280 | if (decoder.isMultipart()) { 281 | try { 282 | while (decoder.hasNext()) { 283 | //System.out.println("decoder has next"); 284 | InterfaceHttpData data = decoder.next(); 285 | if (data != null) { 286 | writeHttpData(data, ctx); 287 | data.release(); 288 | } 289 | } 290 | } catch (Exception e) { 291 | //e.printStackTrace(); 292 | } 293 | } else { 294 | sendError(ctx, HttpResponseStatus.BAD_REQUEST, "Not a multipart request"); 295 | } 296 | 297 | //System.out.println("decoder has no next"); 298 | } 299 | 300 | private void writeHttpData(InterfaceHttpData data, ChannelHandlerContext ctx) { 301 | 302 | if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) { 303 | FileUpload fileUpload = (FileUpload) data; 304 | 305 | if (fileUpload.isCompleted()) { 306 | JSONObject json = saveFileToDisk(fileUpload); 307 | sendUploadedFileName(json, ctx); 308 | } else { 309 | //responseContent.append("\tFile to be continued but should not!\r\n"); 310 | sendError(ctx, HttpResponseStatus.BAD_REQUEST, "Unknown error occurred"); 311 | } 312 | } 313 | } 314 | 315 | /** 316 | * generates and returns a unique string that'll be used to save an uploaded 317 | * file to disk 318 | * 319 | * @return generated unique string 320 | */ 321 | private String getUniqueId() { 322 | UUID uniqueId = UUID.randomUUID(); 323 | 324 | return uniqueId.toString(); 325 | } 326 | 327 | /** 328 | * Saves the uploaded file to disk. 329 | * 330 | * @param fileUpload FileUpload object that'll be saved 331 | * @return name of the saved file. null if error occurred 332 | */ 333 | private JSONObject saveFileToDisk(FileUpload fileUpload) { 334 | 335 | JSONObject responseJson = new JSONObject(); 336 | 337 | String filePath = null; // full path of the new file to be saved 338 | String upoadedFileName = fileUpload.getFilename(); 339 | 340 | // get the extension of the uploaded file 341 | String extension = ""; 342 | int i = upoadedFileName.lastIndexOf('.'); 343 | if (i > 0) { 344 | // get extension including the "." 345 | extension = upoadedFileName.substring(i); 346 | } 347 | 348 | String uniqueBaseName = getUniqueId(); 349 | String fileName = uniqueBaseName + extension; 350 | 351 | try { 352 | filePath = BASE_PATH + fileName; 353 | fileUpload.renameTo(new File(filePath)); // enable to move into another 354 | responseJson.put("file", fileName); 355 | if (isImageExtension(extension)) { 356 | String thumbname = createThumbnail(filePath, uniqueBaseName, extension); 357 | responseJson.put("thumb", thumbname); 358 | } 359 | } catch (Exception ex) { 360 | LOGGER.error(ex); 361 | responseJson = null; 362 | } 363 | 364 | return responseJson; 365 | } 366 | 367 | /** 368 | * Creates a thumbnail of an image file 369 | * 370 | * @param fileFullPath full path of the source image 371 | * @param fileNameBase Base name of the file i.e without extension 372 | * @param extension extension of the file 373 | */ 374 | private String createThumbnail(String fileFullPath, String fileNameBase, String extension) { 375 | String thumbImgName = fileNameBase + "_thumb" + extension; // thumbnail image base name 376 | String thumbImageFullPath = BASE_PATH + thumbImgName; // all thumbs are jpg files 377 | 378 | try { 379 | Thumbnails.of(new File(fileFullPath)).size(100, 100).toFile(new File(thumbImageFullPath)); 380 | } catch (IOException ex) { 381 | LOGGER.error(ex); 382 | thumbImgName = ""; 383 | } 384 | return thumbImgName; 385 | 386 | } 387 | 388 | private static boolean isImageExtension(String extension) { 389 | boolean isImageFile = false; 390 | String extensionInLowerCase = extension.toLowerCase(); 391 | isImageFile |= extensionInLowerCase.equals(".jpg"); 392 | isImageFile |= extensionInLowerCase.equals(".png"); 393 | isImageFile |= extensionInLowerCase.equals(".jpeg"); 394 | isImageFile |= extensionInLowerCase.equals(".gif"); 395 | return isImageFile; 396 | 397 | } 398 | 399 | private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status, String msg) { 400 | FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, 401 | status, 402 | Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8)); 403 | response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); 404 | 405 | // Close the connection as soon as the error message is sent. 406 | ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); 407 | } 408 | 409 | private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { 410 | sendError(ctx, status, "Failure: " + status.toString() + "\r\n"); 411 | } 412 | 413 | private static BufferedImage resizeImage(BufferedImage originalImage, int type) { 414 | 415 | BufferedImage resizedImage = new BufferedImage(THUMB_MAX_WIDTH, THUMB_MAX_HEIGHT, type); 416 | Graphics2D g = resizedImage.createGraphics(); 417 | g.drawImage(originalImage, 0, 0, THUMB_MAX_WIDTH, THUMB_MAX_HEIGHT, null); 418 | g.dispose(); 419 | 420 | return resizedImage; 421 | } 422 | 423 | } 424 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/server/HttpStaticFileServerInitializer.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.server; 4 | 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelPipeline; 7 | import io.netty.channel.socket.SocketChannel; 8 | import io.netty.handler.codec.http.HttpObjectAggregator; 9 | import io.netty.handler.codec.http.HttpRequestDecoder; 10 | import io.netty.handler.codec.http.HttpResponseEncoder; 11 | import io.netty.handler.codec.http.cors.CorsConfig; 12 | import io.netty.handler.codec.http.cors.CorsHandler; 13 | 14 | import java.util.logging.SocketHandler; 15 | 16 | /** 17 | * Summary: HttpStaticFileServerInitializer 18 | * Author : anduo@qq.com 19 | * Version: 1.0 20 | * Date : 15/7/1 21 | * time : 23:31 22 | */ 23 | public class HttpStaticFileServerInitializer extends ChannelInitializer { 24 | 25 | @Override 26 | protected void initChannel(SocketChannel ch) 27 | throws Exception { 28 | // Create a default pipeline implementation. 29 | CorsConfig corsConfig = CorsConfig.withAnyOrigin().build(); 30 | ChannelPipeline pipeline = ch.pipeline(); 31 | // Uncomment the following line if you want HTTPS 32 | //SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine(); 33 | //engine.setUseClientMode(false); 34 | //pipeline.addLast("ssl", new SslHandler(engine)); 35 | 36 | pipeline.addLast("encoder", new HttpResponseEncoder()); 37 | pipeline.addLast("decoder", new HttpRequestDecoder()); 38 | pipeline.addLast("aggregator", new HttpObjectAggregator(8388608)); // 8MB 39 | //pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); 40 | pipeline.addLast("cors", new CorsHandler(corsConfig)); 41 | pipeline.addLast("handler", new HttpStaticFileServerHandler()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/zk/ClientDict.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.zk; 4 | 5 | import org.apache.log4j.LogManager; 6 | import org.apache.log4j.Logger; 7 | 8 | /** 9 | * ━━━━━━神兽出没━━━━━━ 10 | *    ┏┓   ┏┓ 11 | *   ┏┛┻━━━┛┻┓ 12 | *   ┃       ┃ 13 | *   ┃   ━   ┃ 14 | *   ┃ ┳┛ ┗┳ ┃ 15 | *   ┃       ┃ 16 | *   ┃   ┻   ┃ 17 | *   ┃       ┃ 18 | *   ┗━┓   ┏━┛ 19 | *     ┃   ┃神兽保佑, 永无BUG! 20 | *     ┃   ┃Code is far away from bug with the animal protecting 21 | *     ┃   ┗━━━┓ 22 | *     ┃       ┣┓ 23 | *     ┃       ┏┛ 24 | *     ┗┓┓┏━┳┓┏┛ 25 | *      ┃┫┫ ┃┫┫ 26 | *      ┗┻┛ ┗┻┛ 27 | * ━━━━━━感觉萌萌哒━━━━━━ 28 | * Summary: TODO 描述信息 29 | * Author : anduo@qq.com 30 | * Version: 1.0 31 | * Date : 15/7/2 32 | * time : 01:07 33 | */ 34 | public class ClientDict { 35 | public static ClientDict self = null; 36 | protected static Logger logger = LogManager.getLogger(ClientDict.class); 37 | protected ZKConnect ZKConnect = null; 38 | public ClientListener clientListener = null; 39 | 40 | static { 41 | self = new ClientDict(); 42 | } 43 | 44 | public ClientDict() { 45 | init(); 46 | } 47 | 48 | public boolean init() { 49 | logger.info("ClientDict init zookeeper..."); 50 | this.ZKConnect = new ZKConnect(); 51 | if ((this.ZKConnect == null) || (!this.ZKConnect.start())) { 52 | return false; 53 | } 54 | this.clientListener = new ClientListener(CommonConstants.ZK_ROOT_PATH + "/client", this.ZKConnect); 55 | this.ZKConnect.addClientListener(this.clientListener); 56 | this.clientListener.reload(); 57 | return true; 58 | } 59 | 60 | public CuratorFramework getZK() { 61 | return this.ZKConnect.instance; 62 | } 63 | 64 | public BasicDBObject hashClient(String jobClass, String hashKey) { 65 | return this.clientListener.clientNodes.hashClient(jobClass, hashKey); 66 | } 67 | 68 | /** 69 | * hashClient4S 70 | * @param jobClass 71 | * @param hashKey 72 | * @return 73 | */ 74 | public Map hashClient4S(String jobClass, String hashKey) { 75 | return this.clientListener.clientNodes.hashClient4S(jobClass, hashKey); 76 | } 77 | 78 | public BasicDBObject hashClientByFixedClientIps(String jobClass,String[] fixedClientIps) { 79 | return this.clientListener.clientNodes.hashClientByFixedClientIps(jobClass, fixedClientIps); 80 | } 81 | 82 | /** 83 | * hashClientByFixedClientIps升级版本(4S==For Super) 84 | * @param jobClass 85 | * @param fixedClientIps 86 | * @return 87 | */ 88 | public Map hashClient4SByFixedClientIps(String jobClass,String[] fixedClientIps) { 89 | return this.clientListener.clientNodes.hashClient4SByFixedClientIps(jobClass, fixedClientIps); 90 | } 91 | 92 | public BasicDBObject hashClientBySystemCapacity(String jobClass) { 93 | return this.clientListener.clientNodes.hashClientBySystemCapacity(jobClass); 94 | } 95 | 96 | /** 97 | * hashClientBySystemCapacity升级版本(4S==For Super) 98 | * @param jobClass 99 | * @return 100 | */ 101 | public Map hashClient4SBySystemCapacity(String jobClass) { 102 | return this.clientListener.clientNodes.hashClient4SBySystemCapacity(jobClass); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/zk/ClientListener.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.zk; 4 | 5 | import org.apache.log4j.LogManager; 6 | import org.apache.log4j.Logger; 7 | import org.apache.zookeeper.WatchedEvent; 8 | import org.apache.zookeeper.Watcher; 9 | 10 | /** 11 | * ━━━━━━神兽出没━━━━━━ 12 | *    ┏┓   ┏┓ 13 | *   ┏┛┻━━━┛┻┓ 14 | *   ┃       ┃ 15 | *   ┃   ━   ┃ 16 | *   ┃ ┳┛ ┗┳ ┃ 17 | *   ┃       ┃ 18 | *   ┃   ┻   ┃ 19 | *   ┃       ┃ 20 | *   ┗━┓   ┏━┛ 21 | *     ┃   ┃神兽保佑, 永无BUG! 22 | *     ┃   ┃Code is far away from bug with the animal protecting 23 | *     ┃   ┗━━━┓ 24 | *     ┃       ┣┓ 25 | *     ┃       ┏┛ 26 | *     ┗┓┓┏━┳┓┏┛ 27 | *      ┃┫┫ ┃┫┫ 28 | *      ┗┻┛ ┗┻┛ 29 | * ━━━━━━感觉萌萌哒━━━━━━ 30 | * Summary: TODO 描述信息 31 | * Author : anduo@qq.com 32 | * Version: 1.0 33 | * Date : 15/7/2 34 | * time : 01:08 35 | */ 36 | public class ClientListener implements Watcher { 37 | protected static Logger logger = LogManager.getLogger(ClientListener.class); 38 | protected String prefixPath = ""; 39 | protected ClientNodes clientNodes = new ClientNodes(); 40 | protected ZKConnect ZKConnect = null; 41 | protected Integer LOCK = Integer.valueOf(0); 42 | 43 | public ClientListener(String prefixPath, ZKConnect ZKConnect) { 44 | this.prefixPath = prefixPath; 45 | this.ZKConnect = ZKConnect; 46 | } 47 | 48 | public BasicDBList records() { 49 | return this.clientNodes.records; 50 | } 51 | 52 | public BasicDBObject hashClient(String jobClass, String hashKey) { 53 | return this.clientNodes.hashClient(jobClass, hashKey); 54 | } 55 | 56 | public void removeRecord(String id) { 57 | this.clientNodes.remove(id); 58 | } 59 | 60 | public void addRecord(String id) { 61 | if (!this.ZKConnect.checkAlive()) 62 | return; 63 | try { 64 | String c = this.ZKConnect.getData(this.prefixPath + "/" + id); 65 | if (c == null) { 66 | return; 67 | } 68 | BasicDBObject record = (BasicDBObject) JSON.parse(c); 69 | this.clientNodes.add(record); 70 | } catch (Exception e) { 71 | logger.error("ClientListener-->>ServerNodeListener error ", e); 72 | } 73 | } 74 | 75 | public void reload() { 76 | if (!this.ZKConnect.checkAlive()) { 77 | return; 78 | } 79 | if (!this.ZKConnect.exists(this.prefixPath)) 80 | return; 81 | try { 82 | String timeStamp = DateUtil.currentDateTime(); 83 | List items = this.ZKConnect.getChildrens(this.prefixPath); 84 | for (int i = 0; (items != null) && (i < items.size()); i++) { 85 | String id = (String) items.get(i); 86 | String c = this.ZKConnect.getData(this.prefixPath + "/" + id); 87 | if (c == null) { 88 | continue; 89 | } 90 | BasicDBObject record = (BasicDBObject) JSON.parse(c); 91 | record.put("timeStamp", timeStamp); 92 | this.clientNodes.add(record); 93 | } 94 | int index = 0; 95 | while (index < this.clientNodes.records.size()) { 96 | BasicDBObject record = (BasicDBObject) this.clientNodes.records.get(index); 97 | if (record.getString("timeStamp").equalsIgnoreCase(timeStamp)) { 98 | index++; 99 | } else if (!this.clientNodes.remove(record.getString("id"))) { 100 | index++; 101 | } 102 | } 103 | } catch (Exception e) { 104 | logger.error("ServerListener-->>reload() error.", e); 105 | } 106 | } 107 | 108 | public void process(WatchedEvent event) { 109 | if (event == null) { 110 | return; 111 | } 112 | String path = event.getPath(); 113 | if ((path == null) || (!path.startsWith(this.prefixPath))) { 114 | return; 115 | } 116 | synchronized (this.LOCK) { 117 | try { 118 | String[] values = StringUtil.split(path, '/'); 119 | String id = values.length >= 2 ? values[1] : ""; 120 | BasicDBObject oServer = null; 121 | if (event.getType() == Event.EventType.NodeCreated) { 122 | addRecord(id); 123 | } else if (event.getType() == Event.EventType.NodeDeleted) { 124 | removeRecord(id); 125 | } else if (event.getType() == Event.EventType.NodeDataChanged) { 126 | addRecord(id); 127 | } else if (event.getType() == Event.EventType.NodeChildrenChanged) { 128 | reload(); 129 | } 130 | } catch (Exception e) { 131 | logger.error("ServerListener-->>process(" + com.alibaba.fastjson.JSON.toJSONString(event) + ") error.", e); 132 | } 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/zk/ClientNodes.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.zk; 4 | 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | import java.util.HashMap; 8 | import java.util.HashSet; 9 | import java.util.Iterator; 10 | 11 | /** 12 | * ━━━━━━神兽出没━━━━━━ 13 | *    ┏┓   ┏┓ 14 | *   ┏┛┻━━━┛┻┓ 15 | *   ┃       ┃ 16 | *   ┃   ━   ┃ 17 | *   ┃ ┳┛ ┗┳ ┃ 18 | *   ┃       ┃ 19 | *   ┃   ┻   ┃ 20 | *   ┃       ┃ 21 | *   ┗━┓   ┏━┛ 22 | *     ┃   ┃神兽保佑, 永无BUG! 23 | *     ┃   ┃Code is far away from bug with the animal protecting 24 | *     ┃   ┗━━━┓ 25 | *     ┃       ┣┓ 26 | *     ┃       ┏┛ 27 | *     ┗┓┓┏━┳┓┏┛ 28 | *      ┃┫┫ ┃┫┫ 29 | *      ┗┻┛ ┗┻┛ 30 | * ━━━━━━感觉萌萌哒━━━━━━ 31 | * Summary: TODO 描述信息 32 | * Author : anduo@qq.com 33 | * Version: 1.0 34 | * Date : 15/7/2 35 | * time : 01:11 36 | */ 37 | public class ClientNodes extends CommonNodes{ 38 | protected static Logger logger = LoggerFactory.getLogger(ServerNodes.class); 39 | protected HashMap recordsById = new HashMap(); 40 | protected HashMap recordsByJobClass = new HashMap(); 41 | protected BasicDBList records = new BasicDBList(); 42 | protected HashSet jobClassSet = new HashSet(); 43 | 44 | public BasicDBObject hashClient(String jobClass, String hashKey) { 45 | BasicDBList items = getRecordsByJobClass(jobClass); 46 | if ((items == null) || (items.size() == 0)) { 47 | return null; 48 | } 49 | if (StringUtils.isEmpty(hashKey)) { 50 | hashKey = allocateNo(); 51 | } 52 | int index = HashTimes.use33(hashKey) % items.size(); 53 | return (BasicDBObject) items.get(index); 54 | } 55 | 56 | /** 57 | * hashClient升级版本(4S==For Super) 58 | * @param jobClass 59 | * @param hashKey 60 | * @return 61 | */ 62 | public Map hashClient4S(String jobClass, String hashKey) { 63 | Map map = new HashMap(); 64 | BasicDBList items = getRecordsByJobClass(jobClass); 65 | if ((items == null) || (items.size() == 0)) { 66 | map.put(CommonConstants.SUCCESS, false); 67 | map.put(CommonConstants.ERROR_CODE, CommonConstants.ERROR_CODE_101); 68 | return map; 69 | } 70 | if (StringUtil.isEmpty(hashKey)) { 71 | hashKey = allocateNo(); 72 | } 73 | int index = HashTimes.use33(hashKey) % items.size(); 74 | map.put(CommonConstants.SUCCESS, true); 75 | map.put(CommonConstants.CLIENT_JOB_INFO, items.get(index)); 76 | return map; 77 | } 78 | 79 | public BasicDBObject hashClientByFixedClientIps(String jobClass, String[] fixedClientIps) { 80 | BasicDBList items = getRecordsByJobClass(jobClass); 81 | if ((items == null) || (items.size() == 0)) { 82 | return null; 83 | } 84 | int index = -1; 85 | for (String fixedClientIp : fixedClientIps) { 86 | index = findIndexByIp(fixedClientIp, items); 87 | if (index == -1) continue; 88 | } 89 | if (index == -1) { 90 | return null; 91 | } 92 | return (BasicDBObject) items.get(index); 93 | } 94 | 95 | /** 96 | * hashClientByFixedClientIps升级版本(4S==For Super) 97 | * @param jobClass 98 | * @param fixedClientIps 99 | * @return 100 | */ 101 | public Map hashClient4SByFixedClientIps(String jobClass, String[] fixedClientIps) { 102 | Map map = new HashMap(); 103 | BasicDBList items = getRecordsByJobClass(jobClass); 104 | if ((items == null) || (items.size() == 0)) { 105 | map.put(CommonConstants.SUCCESS, false); 106 | map.put(CommonConstants.ERROR_CODE, CommonConstants.ERROR_CODE_101); 107 | } 108 | int index = -1; 109 | for (String fixedClientIp : fixedClientIps) { 110 | index = findIndexByIp(fixedClientIp, items); 111 | if (index == -1) continue; 112 | } 113 | if (index == -1) { 114 | map.put(CommonConstants.SUCCESS, false); 115 | map.put(CommonConstants.ERROR_CODE, CommonConstants.ERROR_CODE_101); 116 | } 117 | map.put(CommonConstants.SUCCESS, true); 118 | map.put(CommonConstants.CLIENT_JOB_INFO, items.get(index)); 119 | return map; 120 | } 121 | 122 | public BasicDBObject hashClientBySystemCapacity(String jobClass) { 123 | BasicDBList items = getRecordsByJobClass(jobClass); 124 | int itemLen; 125 | if ((items == null) || ((itemLen = items.size()) == 0)) { 126 | return null; 127 | } else if (1 == itemLen) { 128 | BasicDBObject item = (BasicDBObject) items.get(0); 129 | double memRatio = Double.valueOf(String.valueOf(item.get(CommonConstants.MEM_RATIO))); 130 | double cpuRatio = Double.valueOf(String.valueOf(item.get(CommonConstants.CPU_RATIO))); 131 | if (memRatio > CommonConstants.MAX_MEM_RATIO || cpuRatio > CommonConstants.MAX_CPU_RATIO ) { 132 | logger.error("ClientNodes-->>hashClientBySystemCapacity(" + jobClass + ") the memRatio(" + memRatio + ")" + 133 | " over max mem ratio("+CommonConstants.MAX_MEM_RATIO+") or the cpuRatio("+cpuRatio+") " + 134 | "over max cpu ratio("+CommonConstants.MAX_CPU_RATIO+") "); 135 | return null; 136 | } else { 137 | return item; 138 | } 139 | } else if (itemLen > 1) { 140 | BasicDBObject tempItem = (BasicDBObject) items.get(0); 141 | int minMemRatioIndex = 0; 142 | double minMemRatioValue = Double.valueOf(String.valueOf(tempItem.get(CommonConstants.MEM_RATIO))); 143 | double minCpuRatioValue = Double.valueOf(String.valueOf(tempItem.get(CommonConstants.CPU_RATIO))); 144 | int minTotalThreadValue = Integer.valueOf(String.valueOf(tempItem.get(CommonConstants.TOTAL_THREAD))); 145 | double minTotalValue = minMemRatioValue + minCpuRatioValue + minTotalThreadValue; 146 | for (int i = 1; i < itemLen; i++) { 147 | tempItem = (BasicDBObject) items.get(i); 148 | double tempMemRatioValue = Double.valueOf(String.valueOf(tempItem.get(CommonConstants.MEM_RATIO))); 149 | double tempCpuRatioValue = Double.valueOf(String.valueOf(tempItem.get(CommonConstants.CPU_RATIO))); 150 | int tempTotalThreadValue = Integer.valueOf(String.valueOf(tempItem.get(CommonConstants.TOTAL_THREAD))); 151 | double tempTotalValue = tempMemRatioValue + tempCpuRatioValue + tempTotalThreadValue; 152 | if (tempTotalValue < minTotalValue) { 153 | minMemRatioValue = tempMemRatioValue; 154 | minCpuRatioValue = tempCpuRatioValue; 155 | minTotalThreadValue = tempTotalThreadValue; 156 | minTotalValue = minMemRatioValue + minCpuRatioValue + minTotalThreadValue; 157 | minMemRatioIndex = i; 158 | } 159 | } 160 | if (minMemRatioValue > CommonConstants.MAX_MEM_RATIO || minCpuRatioValue > CommonConstants.MAX_CPU_RATIO) { 161 | logger.error("ClientNodes-->>hashClientBySystemCapacity(" + jobClass + ") " + 162 | "the memRatio(" + minMemRatioValue + ") over max mem ratio(" + CommonConstants.MAX_MEM_RATIO + ")" + 163 | "or the cpuRatio("+minCpuRatioValue+") over max cpu ration ("+CommonConstants.MAX_CPU_RATIO+") "); 164 | return null; 165 | } else { 166 | return (BasicDBObject) items.get(minMemRatioIndex); 167 | } 168 | } 169 | return null; 170 | } 171 | 172 | /** 173 | * hashClientBySystemCapacity升级版本(4S==For Super) 174 | * 175 | * @param jobClass 176 | * @return 177 | */ 178 | public Map hashClient4SBySystemCapacity(String jobClass) { 179 | Map map = new HashMap(); 180 | BasicDBList items = getRecordsByJobClass(jobClass); 181 | int itemLen; 182 | if ((items == null) || ((itemLen = items.size()) == 0)) { 183 | map.put(CommonConstants.SUCCESS, false); 184 | map.put(CommonConstants.ERROR_CODE, CommonConstants.ERROR_CODE_101); 185 | return map; 186 | } else if (1 == itemLen) { 187 | BasicDBObject item = (BasicDBObject) items.get(0); 188 | double memRatio = Double.valueOf(String.valueOf(item.get(CommonConstants.MEM_RATIO))); 189 | double cpuRatio = Double.valueOf(String.valueOf(item.get(CommonConstants.CPU_RATIO))); 190 | if (memRatio > CommonConstants.MAX_MEM_RATIO || cpuRatio > CommonConstants.MAX_CPU_RATIO) { 191 | logger.error("ClientNodes-->>hashClient4SBySystemCapacity(" + jobClass + ") the memRatio(" + memRatio + ")" + 192 | " over max mem ratio(" + CommonConstants.MAX_MEM_RATIO + ") or the cpuRatio("+cpuRatio+")" + 193 | " over max cpu ratio("+CommonConstants.MAX_CPU_RATIO+") "); 194 | map.put(CommonConstants.SUCCESS, false); 195 | map.put(CommonConstants.ERROR_CODE, CommonConstants.ERROR_CODE_102); 196 | map.put(CommonConstants.MEM_RATIO, memRatio); 197 | map.put(CommonConstants.CPU_RATIO, cpuRatio); 198 | return map; 199 | } else { 200 | map.put(CommonConstants.SUCCESS, true); 201 | map.put(CommonConstants.CLIENT_JOB_INFO, item); 202 | return map; 203 | } 204 | } else if (itemLen > 1) { 205 | BasicDBObject tempItem = (BasicDBObject) items.get(0); 206 | int minMemRatioIndex = 0; 207 | double minMemRatioValue = Double.valueOf(String.valueOf(tempItem.get(CommonConstants.MEM_RATIO))); 208 | double minCpuRatioValue = Double.valueOf(String.valueOf(tempItem.get(CommonConstants.CPU_RATIO))); 209 | int minTotalThreadValue = Integer.valueOf(String.valueOf(tempItem.get(CommonConstants.TOTAL_THREAD))); 210 | double minTotalValue = minMemRatioValue + minCpuRatioValue + minTotalThreadValue; 211 | for (int i = 1; i < itemLen; i++) { 212 | tempItem = (BasicDBObject) items.get(i); 213 | double tempMemRatioValue = Double.valueOf(String.valueOf(tempItem.get(CommonConstants.MEM_RATIO))); 214 | double tempCpuRatioValue = Double.valueOf(String.valueOf(tempItem.get(CommonConstants.CPU_RATIO))); 215 | int tempTotalThreadValue = Integer.valueOf(String.valueOf(tempItem.get(CommonConstants.TOTAL_THREAD))); 216 | double tempTotalValue = tempMemRatioValue + tempCpuRatioValue + tempTotalThreadValue; 217 | if (tempTotalValue < minTotalValue) { 218 | minMemRatioValue = tempMemRatioValue; 219 | minCpuRatioValue = tempCpuRatioValue; 220 | minTotalThreadValue = tempTotalThreadValue; 221 | minTotalValue = minMemRatioValue + minCpuRatioValue + minTotalThreadValue; 222 | minMemRatioIndex = i; 223 | } 224 | } 225 | if (minMemRatioValue > CommonConstants.MAX_MEM_RATIO || minCpuRatioValue > CommonConstants.MAX_CPU_RATIO) { 226 | logger.error("ClientNodes-->>hashClient4SBySystemCapacity(" + jobClass + ") " + 227 | "the memRatio(" + minMemRatioValue + ") over max mem ratio(" + CommonConstants.MAX_MEM_RATIO + ")" + 228 | "or the cpuRatio("+minCpuRatioValue+") over max cpu ration ("+CommonConstants.MAX_CPU_RATIO+") "); 229 | map.put(CommonConstants.SUCCESS, false); 230 | map.put(CommonConstants.ERROR_CODE, CommonConstants.ERROR_CODE_102); 231 | map.put(CommonConstants.MEM_RATIO, minMemRatioValue); 232 | map.put(CommonConstants.CPU_RATIO, minCpuRatioValue); 233 | return map; 234 | } else { 235 | map.put(CommonConstants.SUCCESS, true); 236 | map.put(CommonConstants.CLIENT_JOB_INFO, items.get(minMemRatioIndex)); 237 | return map; 238 | } 239 | } 240 | map.put(CommonConstants.SUCCESS, false); 241 | map.put(CommonConstants.ERROR_CODE, CommonConstants.ERROR_CODE_103); 242 | return map; 243 | } 244 | 245 | public boolean containsKey(String id) { 246 | return this.recordsById.containsKey(id); 247 | } 248 | 249 | public BasicDBList getRecordsByJobClass(String jobClass) { 250 | return this.recordsByJobClass.get(jobClass); 251 | } 252 | 253 | public BasicDBList findRecordsByIP(String ip) { 254 | BasicDBList basicDBList = new BasicDBList(); 255 | for (int i = 0; i < this.records.size(); i++) { 256 | BasicDBObject oServer = (BasicDBObject) this.records.get(i); 257 | if (oServer.getString(CommonConstants.IP).equalsIgnoreCase(ip)) { 258 | basicDBList.add(oServer); 259 | } 260 | } 261 | return basicDBList; 262 | } 263 | 264 | public synchronized void add(BasicDBObject record) { 265 | String id = record.getString(CommonConstants.ID); 266 | int index = findIndexById(id, this.records); 267 | if (index == -1) 268 | this.records.add(record); 269 | else { 270 | this.records.set(index, record); 271 | } 272 | this.recordsById.put(id, record); 273 | if (!record.containsField(CommonConstants.JOB_CLASS)) { 274 | return; 275 | } 276 | BasicDBList jobClassList = (BasicDBList) record.get(CommonConstants.JOB_CLASS); 277 | for (int i = 0; (jobClassList != null) && (i < jobClassList.size()); i++) { 278 | String tempJobClass = jobClassList.get(i).toString(); 279 | this.jobClassSet.add(tempJobClass); 280 | BasicDBList items = this.recordsByJobClass.get(tempJobClass); 281 | if (items == null) { 282 | items = new BasicDBList(); 283 | this.recordsByJobClass.put(tempJobClass, items); 284 | } 285 | index = findIndexById(id, items); 286 | if (index == -1) 287 | items.add(record); 288 | else { 289 | items.set(index, record); 290 | } 291 | } 292 | } 293 | 294 | public synchronized boolean remove(String id) { 295 | this.recordsById.remove(id); 296 | int index = findIndexById(id, this.records); 297 | boolean flag = index != -1; 298 | if (flag) { 299 | this.records.remove(index); 300 | } 301 | for (Iterator i = this.recordsByJobClass.entrySet().iterator(); i.hasNext();) { 302 | Map.Entry entry = (Map.Entry) i.next(); 303 | BasicDBList items = (BasicDBList) entry.getValue(); 304 | index = findIndexById(id, items); 305 | if (index != -1) { 306 | items.remove(index); 307 | } 308 | if (items.size() == 0) { 309 | this.jobClassSet.remove(entry.getKey()); 310 | } 311 | } 312 | return flag; 313 | } 314 | 315 | public void clear() { 316 | this.recordsById.clear(); 317 | this.recordsByJobClass.clear(); 318 | this.records.clear(); 319 | this.jobClassSet.clear(); 320 | } 321 | 322 | } 323 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/zk/CommonNodes.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.zk; 4 | 5 | import java.util.concurrent.atomic.AtomicLong; 6 | 7 | /** 8 | * ━━━━━━神兽出没━━━━━━ 9 | *    ┏┓   ┏┓ 10 | *   ┏┛┻━━━┛┻┓ 11 | *   ┃       ┃ 12 | *   ┃   ━   ┃ 13 | *   ┃ ┳┛ ┗┳ ┃ 14 | *   ┃       ┃ 15 | *   ┃   ┻   ┃ 16 | *   ┃       ┃ 17 | *   ┗━┓   ┏━┛ 18 | *     ┃   ┃神兽保佑, 永无BUG! 19 | *     ┃   ┃Code is far away from bug with the animal protecting 20 | *     ┃   ┗━━━┓ 21 | *     ┃       ┣┓ 22 | *     ┃       ┏┛ 23 | *     ┗┓┓┏━┳┓┏┛ 24 | *      ┃┫┫ ┃┫┫ 25 | *      ┗┻┛ ┗┻┛ 26 | * ━━━━━━感觉萌萌哒━━━━━━ 27 | * Summary: TODO 描述信息 28 | * Author : anduo@qq.com 29 | * Version: 1.0 30 | * Date : 15/7/2 31 | * time : 01:09 32 | */ 33 | public class CommonNodes { 34 | public static String allocateNo() { 35 | AtomicLong count = new AtomicLong(0L); 36 | if (count.get() == 9223372036854775807L) { 37 | count.set(0L); 38 | } 39 | return String.valueOf(count.addAndGet(1L)); 40 | } 41 | 42 | public static int findIndexById(String id, BasicDBList items) { 43 | int result = -1; 44 | if (null != items) { 45 | for (int i = 0; i < items.size(); i++) { 46 | BasicDBObject oItem = (BasicDBObject) items.get(i); 47 | if (oItem.getString("id").equalsIgnoreCase(id)) { 48 | result = i; 49 | } 50 | } 51 | } 52 | return result; 53 | } 54 | 55 | public static int findIndexByIp(String ip, BasicDBList items) { 56 | int result = -1; 57 | for (int i = 0; i < items.size(); i++) { 58 | BasicDBObject oItem = (BasicDBObject) items.get(i); 59 | if (oItem.getString("ip").equalsIgnoreCase(ip)) { 60 | result = i; 61 | } 62 | } 63 | return result; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /nz-server/src/main/java/com/anduo/nz/zk/ZKUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.zk; 4 | 5 | import com.anduo.nz.common.Constants; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.apache.curator.framework.CuratorFramework; 8 | import org.apache.curator.framework.CuratorFrameworkFactory; 9 | import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable; 10 | import org.apache.curator.framework.api.BackgroundPathable; 11 | import org.apache.curator.framework.api.CuratorWatcher; 12 | import org.apache.curator.retry.RetryNTimes; 13 | import org.apache.log4j.LogManager; 14 | import org.apache.log4j.Logger; 15 | import org.apache.zookeeper.CreateMode; 16 | import org.apache.zookeeper.ZooDefs; 17 | import org.apache.zookeeper.data.ACL; 18 | import org.apache.zookeeper.data.Id; 19 | import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; 20 | 21 | import java.security.NoSuchAlgorithmException; 22 | import java.sql.Timestamp; 23 | import java.text.DateFormat; 24 | import java.text.SimpleDateFormat; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | /** 29 | * ━━━━━━神兽出没━━━━━━ 30 | *    ┏┓   ┏┓ 31 | *   ┏┛┻━━━┛┻┓ 32 | *   ┃       ┃ 33 | *   ┃   ━   ┃ 34 | *   ┃ ┳┛ ┗┳ ┃ 35 | *   ┃       ┃ 36 | *   ┃   ┻   ┃ 37 | *   ┃       ┃ 38 | *   ┗━┓   ┏━┛ 39 | *     ┃   ┃神兽保佑, 永无BUG! 40 | *     ┃   ┃Code is far away from bug with the animal protecting 41 | *     ┃   ┗━━━┓ 42 | *     ┃       ┣┓ 43 | *     ┃       ┏┛ 44 | *     ┗┓┓┏━┳┓┏┛ 45 | *      ┃┫┫ ┃┫┫ 46 | *      ┗┻┛ ┗┻┛ 47 | * ━━━━━━感觉萌萌哒━━━━━━ 48 | * Summary: ZKUtil 49 | * Author : anduo@qq.com 50 | * Version: 1.0 51 | * Date : 15/7/2 52 | * time : 00:50 53 | */ 54 | public class ZKUtil { 55 | 56 | private static final Logger LOGGER = LogManager.getLogger(ConfigFile.class); 57 | 58 | private static List acl = new ArrayList(); 59 | 60 | public static CuratorFramework create() { 61 | RetryNTimes retryPolicy = new RetryNTimes(5, 5000); 62 | String authString = Constants.ZK_USER_NAME + ":" + Constants.ZK_PASSWORD; 63 | CuratorFramework client = CuratorFrameworkFactory.builder().connectString(Constants.ZK_CONNECT_STRING) 64 | .retryPolicy(retryPolicy) 65 | .connectionTimeoutMs(Constants.ZOO_KEEPER_TIMEOUT) 66 | .sessionTimeoutMs(Constants.ZOO_KEEPER_TIMEOUT * 3) 67 | .authorization("digest", authString.getBytes()).build(); 68 | try { 69 | acl.clear(); 70 | acl.add(new ACL(ZooDefs.Perms.ALL, 71 | new Id("digest", DigestAuthenticationProvider.generateDigest(authString)))); 72 | acl.add(new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE)); 73 | } catch (NoSuchAlgorithmException e) { 74 | e.printStackTrace(); 75 | LOGGER.error("ZKUtil-->>create() error,", e); 76 | } 77 | return client; 78 | } 79 | 80 | public static boolean exists(CuratorFramework client, String path, CuratorWatcher watcher) { 81 | try { 82 | if (watcher != null) { 83 | return ((BackgroundPathable) client.checkExists().usingWatcher(watcher)).forPath(path) != null; 84 | } 85 | return client.checkExists().forPath(path) != null; 86 | } catch (Exception e) { 87 | LOGGER.error("ZKUtil-->>exists(CuratorFramework client, String path, CuratorWatcher watcher) error, ", e); 88 | } 89 | return false; 90 | } 91 | 92 | public static boolean exists(CuratorFramework client, String path) { 93 | return exists(client, path, null); 94 | } 95 | 96 | public static void createPath(CuratorFramework client, String path, String content, CreateMode mode) { 97 | try { 98 | ((ACLBackgroundPathAndBytesable) client.create().creatingParentsIfNeeded().withMode(mode)) 99 | .forPath(path, List2StringUtil.toBytes(content)); 100 | } catch (Exception e) { 101 | LOGGER.error("ZKUtil-->>createPath(CuratorFramework client, String path, String content, CreateMode mode) error,", e); 102 | } 103 | } 104 | 105 | public static void setPath(CuratorFramework client, String path, String content, CreateMode mode) { 106 | try { 107 | if (client.checkExists().forPath(path) == null) { 108 | ((ACLBackgroundPathAndBytesable) client.create().creatingParentsIfNeeded().withMode(mode)) 109 | .forPath(path, List2StringUtil.toBytes(content)); 110 | } else { client.setData().forPath(path, List2StringUtil.toBytes(content)); } 111 | } catch (Exception e) { 112 | LOGGER.error("ZKUtil-->>setPath(CuratorFramework client, String path, String content, CreateMode mode) error,", e); 113 | } 114 | } 115 | 116 | public static void updateServerTimestamp(long timestamp, String key) { 117 | if (timestamp == 0L) { return; } 118 | DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 119 | Timestamp startime = new Timestamp(timestamp); 120 | String startDate = format.format(startime); 121 | setPath(ServerDict.self.getZK(), key, String.valueOf(timestamp), CreateMode.PERSISTENT); 122 | logger.info("ZKUtil-->> update key[{}] timestamp[{}] ", new Object[] { key, startDate }); 123 | } 124 | 125 | public static long readServerTimestamp(String key) { 126 | String c = getData(ServerDict.self.getZK(), key); 127 | if (StringUtils.isEmpty(c)) { 128 | return 0L; 129 | } 130 | return Long.parseLong(c); 131 | } 132 | 133 | public static String getData(CuratorFramework client, String path) { 134 | return getData(client, path, null); 135 | } 136 | 137 | public static String getData(CuratorFramework client, String path, CuratorWatcher watcher) { 138 | try { 139 | if (client.checkExists().forPath(path) == null) { 140 | return null; 141 | } 142 | if (watcher != null) { 143 | return List2StringUtil 144 | .toString((byte[]) ((BackgroundPathable) client.getData().usingWatcher(watcher)).forPath(path)); 145 | } 146 | return List2StringUtil.toString((byte[]) client.getData().forPath(path)); 147 | } catch (Exception e) { 148 | LOGGER.error("ZKUtil-->>getData(CuratorFramework client, String path, CuratorWatcher watcher) error ", e); 149 | } 150 | return null; 151 | } 152 | 153 | public static String createEphemeralSequential(CuratorFramework client, String path, byte[] payload) { 154 | try { 155 | return (String) ((ACLBackgroundPathAndBytesable) client.create().withProtection() 156 | .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)) 157 | .forPath(path, payload); 158 | } catch (Exception e) { 159 | LOGGER.error("ZKUtil-->>createEphemeralSequential", e); 160 | } 161 | return null; 162 | } 163 | 164 | public static void remove(CuratorFramework client, String path) { 165 | try { 166 | if (client.checkExists().forPath(path) == null) { 167 | LOGGER.info("ZKUtil-->>remove(CuratorFramework client, String path) this Path not exists"); 168 | } 169 | client.delete().forPath(path); 170 | } catch (Exception e) { 171 | LOGGER.error("ZKUtil-->>remove(CuratorFramework client, String path) error,", e); 172 | } 173 | } 174 | 175 | public static void delete(CuratorFramework client, String path) { 176 | try { 177 | client.delete().guaranteed().forPath(path); 178 | } catch (Exception e) { 179 | LOGGER.error("ZKUtil-->>delete(CuratorFramework client, String path) error,", e); 180 | } 181 | } 182 | 183 | public static List getChilds(CuratorFramework client, String path) { 184 | return getChilds(client, path, null); 185 | } 186 | 187 | public static List getChilds(CuratorFramework client, String path, CuratorWatcher watcher) { 188 | try { 189 | if (watcher != null) { 190 | return (List) ((BackgroundPathable) client.getChildren().usingWatcher(watcher)).forPath(path); 191 | } 192 | return (List) client.getChildren().forPath(path); 193 | } catch (Exception e) { 194 | LOGGER.error("ZKUtil-->>getChilds(CuratorFramework client, String path, CuratorWatcher watcher) error,", e); 195 | } 196 | return null; 197 | } 198 | 199 | public static String getDataByParameter(String path, String qKey, String qValue, String resultValue) { 200 | CuratorFramework curatorFramework = ZKUtil.create(); 201 | if (!curatorFramework.isStarted()) { curatorFramework.start(); } 202 | List nodeList = ZKUtil.getChilds(curatorFramework, path); 203 | for (int i = 0; (nodeList != null) && (i < nodeList.size()); i++) { 204 | String id = (String) nodeList.get(i); 205 | String c = ZKUtil.getData(curatorFramework, path + "/" + id); 206 | if (c == null) { 207 | continue; 208 | } 209 | BasicDBObject record = (BasicDBObject) com.mongodb.util.JSON.parse(c); 210 | String tempValue = (String) record.get(qKey); 211 | if (tempValue.equals(qValue)) { 212 | return (String) record.get(resultValue); 213 | } else { 214 | continue; 215 | } 216 | } 217 | return null; 218 | } 219 | 220 | public static BasicDBObject getDataByParameter(String path, String qKey, String qValue) { 221 | CuratorFramework curatorFramework = ZKUtil.create(); 222 | List nodeList = ZKUtil.getChilds(curatorFramework, path); 223 | for (int i = 0; (nodeList != null) && (i < nodeList.size()); i++) { 224 | String id = (String) nodeList.get(i); 225 | String c = ZKUtil.getData(curatorFramework, path + "/" + id); 226 | if (c == null) { 227 | continue; 228 | } 229 | BasicDBObject record = (BasicDBObject) com.mongodb.util.JSON.parse(c); 230 | String tempValue = (String) record.get(qKey); 231 | if (tempValue.equals(qValue)) { 232 | return record; 233 | } else { 234 | continue; 235 | } 236 | } 237 | return null; 238 | } 239 | 240 | public static void main(String[] args) { 241 | ZKUtil.setPath(ClientDict.self.getZK(), "/test/wy/test1", "test2", CreateMode.EPHEMERAL); 242 | 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /nz-server/src/test/java/com/anduo/nz/netty/DiscardServer.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty; 4 | 5 | import io.netty.bootstrap.ServerBootstrap; 6 | import io.netty.channel.ChannelFuture; 7 | import io.netty.channel.ChannelInitializer; 8 | import io.netty.channel.ChannelOption; 9 | import io.netty.channel.EventLoopGroup; 10 | import io.netty.channel.nio.NioEventLoopGroup; 11 | import io.netty.channel.socket.SocketChannel; 12 | import io.netty.channel.socket.nio.NioServerSocketChannel; 13 | 14 | /** 15 | * Summary: netty测试 16 | * Author : anduo@qq.com 17 | * Version: 1.0 18 | * Date : 15/7/1 19 | * time : 22:34 20 | */ 21 | public class DiscardServer { 22 | private int port; 23 | 24 | public DiscardServer(int port) { 25 | this.port = port; 26 | } 27 | 28 | public void run() 29 | throws Exception { 30 | EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) 31 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 32 | try { 33 | ServerBootstrap b = new ServerBootstrap(); // (2) 34 | b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // (3) 35 | .childHandler(new ChannelInitializer() { // (4) 36 | @Override 37 | public void initChannel(SocketChannel ch) 38 | throws Exception { 39 | ch.pipeline().addLast(new DiscardServerHandler()); 40 | } 41 | }).option(ChannelOption.SO_BACKLOG, 128) // (5) 42 | .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) 43 | 44 | // Bind and start to accept incoming connections. 45 | ChannelFuture f = b.bind(port).sync(); // (7) 46 | 47 | // Wait until the server socket is closed. 48 | // In this example, this does not happen, but you can do that to gracefully 49 | // shut down your server. 50 | f.channel().closeFuture().sync(); 51 | } finally { 52 | workerGroup.shutdownGracefully(); 53 | bossGroup.shutdownGracefully(); 54 | } 55 | } 56 | 57 | public static void main(String[] args) 58 | throws Exception { 59 | int port; 60 | if (args.length > 0) { 61 | port = Integer.parseInt(args[0]); 62 | } else { 63 | port = 8080; 64 | } 65 | new DiscardServer(port).run(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /nz-server/src/test/java/com/anduo/nz/netty/DiscardServerHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty; 4 | 5 | /** 6 | * Summary: handler 7 | * Author : anduo@qq.com 8 | * Version: 1.0 9 | * Date : 15/7/1 10 | * time : 22:36 11 | */ 12 | 13 | import io.netty.buffer.ByteBuf; 14 | import io.netty.channel.ChannelHandlerContext; 15 | import io.netty.channel.ChannelInboundHandlerAdapter; 16 | import io.netty.util.ReferenceCountUtil; 17 | 18 | /** 19 | * Handles a server-side channel. 20 | */ 21 | public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1) 22 | 23 | @Override 24 | public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2) 25 | // Discard the received data silently. 26 | ((ByteBuf) msg).release(); // (3) 27 | ByteBuf in = (ByteBuf) msg; 28 | try { 29 | while (in.isReadable()) { // (1) 30 | System.out.print((char) in.readByte()); 31 | System.out.flush(); 32 | } 33 | } finally { 34 | ReferenceCountUtil.release(msg); // (2) 35 | } 36 | } 37 | 38 | @Override 39 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4) 40 | // Close the connection when an exception is raised. 41 | cause.printStackTrace(); 42 | ctx.close(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /nz-server/src/test/java/com/anduo/nz/netty/HelloWorldClient.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty; 4 | 5 | import io.netty.bootstrap.Bootstrap; 6 | import io.netty.channel.*; 7 | import io.netty.channel.nio.NioEventLoopGroup; 8 | import io.netty.channel.socket.SocketChannel; 9 | import io.netty.channel.socket.nio.NioSocketChannel; 10 | import io.netty.handler.codec.serialization.ClassResolvers; 11 | import io.netty.handler.codec.serialization.ObjectDecoder; 12 | import io.netty.handler.codec.serialization.ObjectEncoder; 13 | import io.netty.handler.ssl.SslContext; 14 | import io.netty.handler.ssl.util.InsecureTrustManagerFactory; 15 | 16 | /** 17 | * Summary: HelloWorldClient 18 | * Author : anduo@qq.com 19 | * Version: 1.0 20 | * Date : 15/7/1 21 | * time : 23:15 22 | */ 23 | public class HelloWorldClient { 24 | static final boolean SSL = System.getProperty("ssl") != null; 25 | static final String HOST = System.getProperty("host", "127.0.0.1"); 26 | static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); 27 | 28 | public static void main(String[] args) 29 | throws Exception { 30 | // Configure SSL.git 31 | final SslContext sslCtx; 32 | if (SSL) { 33 | sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE); 34 | } else { 35 | sslCtx = null; 36 | } 37 | 38 | // Configure the client. 39 | EventLoopGroup group = new NioEventLoopGroup(); 40 | try { 41 | Bootstrap bootstrap = new Bootstrap(); 42 | bootstrap.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true) 43 | .handler(new ChannelInitializer() { 44 | @Override 45 | public void initChannel(SocketChannel ch) 46 | throws Exception { 47 | ChannelPipeline p = ch.pipeline(); 48 | if (sslCtx != null) { 49 | p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); 50 | } 51 | p.addLast(new ObjectEncoder(), 52 | new ObjectDecoder(ClassResolvers.cacheDisabled(null)), 53 | new HelloWorldClientHandler()); 54 | } 55 | }); 56 | 57 | // Start the client. 58 | ChannelFuture channelFuture = bootstrap.connect(HOST, PORT).sync(); 59 | 60 | // Wait until the connection is closed. 61 | channelFuture.channel().closeFuture().sync(); 62 | } finally { 63 | // Shut down the event loop to terminate all threads. 64 | group.shutdownGracefully(); 65 | } 66 | } 67 | } 68 | 69 | class HelloWorldClientHandler extends ChannelInboundHandlerAdapter { 70 | 71 | private final String msg = "hello java world"; 72 | 73 | /** 74 | * Creates a client-side handler. 75 | */ 76 | public HelloWorldClientHandler() { 77 | //TODO 78 | } 79 | 80 | @Override 81 | public void channelActive(ChannelHandlerContext ctx) { 82 | ctx.writeAndFlush(msg); 83 | } 84 | 85 | @Override 86 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 87 | System.out.println(msg); 88 | } 89 | 90 | @Override 91 | public void channelReadComplete(ChannelHandlerContext ctx) { 92 | ctx.flush(); 93 | } 94 | 95 | @Override 96 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 97 | // Close the connection when an exception is raised. 98 | cause.printStackTrace(); 99 | ctx.close(); 100 | } 101 | 102 | @Override 103 | public void channelInactive(ChannelHandlerContext ctx) 104 | throws Exception { 105 | System.out.println("hello this is client"); 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /nz-server/src/test/java/com/anduo/nz/netty/HelloWorldServer.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty; 4 | 5 | import io.netty.bootstrap.ServerBootstrap; 6 | import io.netty.channel.*; 7 | import io.netty.channel.nio.NioEventLoopGroup; 8 | import io.netty.channel.socket.SocketChannel; 9 | import io.netty.channel.socket.nio.NioServerSocketChannel; 10 | import io.netty.handler.codec.serialization.ClassResolvers; 11 | import io.netty.handler.codec.serialization.ObjectDecoder; 12 | import io.netty.handler.codec.serialization.ObjectEncoder; 13 | import io.netty.handler.logging.LogLevel; 14 | import io.netty.handler.logging.LoggingHandler; 15 | import io.netty.handler.ssl.SslContext; 16 | import io.netty.handler.ssl.util.SelfSignedCertificate; 17 | 18 | /** 19 | * Summary: HelloWorldServer 20 | * Author : anduo@qq.com 21 | * Version: 1.0 22 | * Date : 15/7/1 23 | * time : 23:13 24 | */ 25 | public class HelloWorldServer { 26 | static final boolean SSL = System.getProperty("ssl") != null; 27 | static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); 28 | 29 | public static void main(String[] args) 30 | throws Exception { 31 | // Configure SSL. 32 | final SslContext sslCtx; 33 | if (SSL) { 34 | SelfSignedCertificate ssc = new SelfSignedCertificate(); 35 | sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey()); 36 | } else { 37 | sslCtx = null; 38 | } 39 | 40 | // Configure the server. 41 | EventLoopGroup bossGroup = new NioEventLoopGroup(1); 42 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 43 | try { 44 | ServerBootstrap server = new ServerBootstrap(); 45 | server.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) 46 | .option(ChannelOption.SO_BACKLOG, 100).handler(new LoggingHandler(LogLevel.INFO)) 47 | .childHandler(new ChannelInitializer() { 48 | @Override 49 | public void initChannel(SocketChannel ch) 50 | throws Exception { 51 | ChannelPipeline p = ch.pipeline(); 52 | if (sslCtx != null) { 53 | p.addLast(sslCtx.newHandler(ch.alloc())); 54 | } 55 | p.addLast(new LoggingHandler(LogLevel.INFO)); 56 | p.addLast(new ObjectEncoder(), 57 | new ObjectDecoder(ClassResolvers.cacheDisabled(null)), 58 | new HelloWorldServerHandler()); 59 | } 60 | }); 61 | 62 | // Start the server. 63 | ChannelFuture f = server.bind(PORT).sync(); 64 | 65 | // Wait until the server socket is closed. 66 | f.channel().closeFuture().sync(); 67 | } finally { 68 | // Shut down all event loops to terminate all threads. 69 | bossGroup.shutdownGracefully(); 70 | workerGroup.shutdownGracefully(); 71 | } 72 | } 73 | } 74 | 75 | class HelloWorldServerHandler extends ChannelInboundHandlerAdapter { 76 | 77 | @Override 78 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 79 | ctx.write("server write msg:" + msg); 80 | } 81 | 82 | @Override 83 | public void channelReadComplete(ChannelHandlerContext ctx) { 84 | ctx.flush(); 85 | } 86 | 87 | @Override 88 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 89 | // Close the connection when an exception is raised. 90 | cause.printStackTrace(); 91 | ctx.close(); 92 | } 93 | 94 | //当绑定到服务端的时候触发 95 | @Override 96 | public void channelActive(ChannelHandlerContext ctx) 97 | throws Exception { 98 | System.out.println("hello this is server!"); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /nz-server/src/test/java/com/anduo/nz/netty/TimeClient.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty; 4 | 5 | import io.netty.bootstrap.Bootstrap; 6 | import io.netty.channel.ChannelFuture; 7 | import io.netty.channel.ChannelInitializer; 8 | import io.netty.channel.ChannelOption; 9 | import io.netty.channel.EventLoopGroup; 10 | import io.netty.channel.nio.NioEventLoopGroup; 11 | import io.netty.channel.socket.SocketChannel; 12 | import io.netty.channel.socket.nio.NioSocketChannel; 13 | 14 | /** 15 | * Summary: TODO 描述信息 16 | * Author : anduo@qq.com 17 | * Version: 1.0 18 | * Date : 15/7/1 19 | * time : 22:40 20 | */ 21 | public class TimeClient { 22 | public static void main(String[] args) throws Exception { 23 | String host = "127.0.0.1";//args[0]; 24 | int port = 8080;//Integer.parseInt(args[1]); 25 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 26 | 27 | try { 28 | Bootstrap b = new Bootstrap(); // (1) 29 | b.group(workerGroup); // (2) 30 | b.channel(NioSocketChannel.class); // (3) 31 | b.option(ChannelOption.SO_KEEPALIVE, true); // (4) 32 | b.handler(new ChannelInitializer() { 33 | @Override 34 | public void initChannel(SocketChannel ch) 35 | throws Exception { 36 | ch.pipeline().addLast(new TimeClientHandler()); 37 | } 38 | }); 39 | 40 | // Start the client. 41 | ChannelFuture f = b.connect(host, port).sync(); // (5) 42 | 43 | // Wait until the connection is closed. 44 | f.channel().closeFuture().sync(); 45 | } finally { 46 | workerGroup.shutdownGracefully(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /nz-server/src/test/java/com/anduo/nz/netty/TimeClientHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty; 4 | 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.ChannelInboundHandlerAdapter; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * Summary: TODO 描述信息 13 | * Author : anduo@qq.com 14 | * Version: 1.0 15 | * Date : 15/7/1 16 | * time : 22:41 17 | */ 18 | public class TimeClientHandler extends ChannelInboundHandlerAdapter { 19 | private ByteBuf buf; 20 | 21 | @Override 22 | public void handlerAdded(ChannelHandlerContext ctx) { 23 | buf = ctx.alloc().buffer(4); // (1) 24 | } 25 | 26 | @Override 27 | public void handlerRemoved(ChannelHandlerContext ctx) { 28 | buf.release(); // (1) 29 | buf = null; 30 | } 31 | 32 | @Override 33 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 34 | ByteBuf m = (ByteBuf) msg; 35 | buf.writeBytes(m); // (2) 36 | m.release(); 37 | 38 | if (buf.readableBytes() >= 4) { // (3) 39 | long currentTimeMillis = (buf.readUnsignedInt() - 2208988800L) * 1000L; 40 | System.out.println(new Date(currentTimeMillis)); 41 | ctx.close(); 42 | } 43 | } 44 | 45 | @Override 46 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 47 | cause.printStackTrace(); 48 | ctx.close(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /nz-server/src/test/java/com/anduo/nz/netty/TimeServerHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 anduo 2 | // All rights reserved 3 | package com.anduo.nz.netty; 4 | 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelFuture; 7 | import io.netty.channel.ChannelFutureListener; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.channel.ChannelInboundHandlerAdapter; 10 | 11 | /** 12 | * Summary: TimeServerHandler 13 | * Author : anduo@qq.com 14 | * Version: 1.0 15 | * Date : 15/7/1 16 | * time : 22:39 17 | */ 18 | public class TimeServerHandler extends ChannelInboundHandlerAdapter { 19 | @Override 20 | public void channelActive(final ChannelHandlerContext ctx) { // (1) 21 | final ByteBuf time = ctx.alloc().buffer(4); // (2) 22 | time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L)); 23 | 24 | final ChannelFuture f = ctx.writeAndFlush(time); // (3) 25 | f.addListener(new ChannelFutureListener() { 26 | @Override 27 | public void operationComplete(ChannelFuture future) { 28 | assert f == future; 29 | ctx.close(); 30 | } 31 | }); // (4) 32 | } 33 | 34 | @Override 35 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 36 | cause.printStackTrace(); 37 | ctx.close(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.anduo 8 | nz 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | nz-server 13 | nz-client 14 | 15 | 16 | 17 | 3.7.0.Final 18 | 3.4.6 19 | 2.3 20 | 1.8 21 | 22 | 23 | 24 | 25 | 26 | 27 | io.netty 28 | netty-all 29 | 4.0.24.Final 30 | 31 | 32 | com.google.guava 33 | guava 34 | 18.0 35 | 36 | 37 | 38 | org.apache.curator 39 | curator-recipes 40 | 2.8.0 41 | 42 | 43 | slf4j-log4j12 44 | org.slf4j 45 | 46 | 47 | log4j 48 | log4j 49 | 50 | 51 | netty 52 | org.jboss.netty 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.apache.commons 60 | commons-lang3 61 | 3.4 62 | 63 | 64 | 65 | com.alibaba 66 | fastjson 67 | 1.2.6 68 | 69 | 70 | 71 | net.coobird 72 | thumbnailator 73 | 0.4.8 74 | 75 | 76 | 77 | log4j 78 | log4j 79 | 1.2.17 80 | 81 | 82 | org.slf4j 83 | slf4j-log4j12 84 | 1.7.12 85 | 86 | 87 | 88 | 89 | --------------------------------------------------------------------------------