├── samples ├── chapter8 │ ├── hello-world-docker │ │ ├── src │ │ │ ├── main │ │ │ │ ├── resources │ │ │ │ │ └── application.properties │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── waylau │ │ │ │ │ └── spring │ │ │ │ │ └── cloud │ │ │ │ │ └── weather │ │ │ │ │ ├── Application.java │ │ │ │ │ └── controller │ │ │ │ │ └── HelloController.java │ │ │ └── test │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── waylau │ │ │ │ └── spring │ │ │ │ └── cloud │ │ │ │ └── weather │ │ │ │ ├── ApplicationTests.java │ │ │ │ └── controller │ │ │ │ └── HelloControllerTest.java │ │ ├── Dockerfile │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradlew.bat │ │ └── gradlew │ └── sse-real-time-web │ │ ├── .gitignore │ │ ├── src │ │ └── main │ │ │ ├── webapp │ │ │ ├── sse_cors.html │ │ │ ├── sse_ie.html │ │ │ ├── sse_cors_alarm.html │ │ │ ├── index.html │ │ │ ├── sse_broadcast.html │ │ │ ├── WEB-INF │ │ │ │ └── web.xml │ │ │ └── scripts │ │ │ │ ├── sse_real_time_pub.js │ │ │ │ ├── sse_real_time_cors.js │ │ │ │ ├── sse_real_time_cors_alarm.js │ │ │ │ ├── sse_real_time_broadcast.js │ │ │ │ └── vendor │ │ │ │ └── eventsource.js │ │ │ └── java │ │ │ └── com │ │ │ └── waylau │ │ │ └── rest │ │ │ ├── RestApplication.java │ │ │ ├── filter │ │ │ └── CrossDomainFilter.java │ │ │ ├── resource │ │ │ ├── AlarmResource.java │ │ │ ├── SseResource.java │ │ │ └── SseChatResource.java │ │ │ └── bean │ │ │ └── Alarm.java │ │ └── pom.xml ├── chapter2 │ ├── javase-rest │ │ ├── .gitignore │ │ ├── src │ │ │ └── main │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── waylau │ │ │ │ └── rest │ │ │ │ ├── bean │ │ │ │ └── MyBean.java │ │ │ │ ├── RestApplication.java │ │ │ │ ├── App.java │ │ │ │ └── resource │ │ │ │ └── MyResource.java │ │ └── pom.xml │ └── game-sesrver │ │ └── aws_game_server.js ├── chapter3 │ ├── activemq-demo │ │ ├── .gitignore │ │ ├── src │ │ │ └── main │ │ │ │ ├── resources │ │ │ │ └── log4j2.xml │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── waylau │ │ │ │ └── activemq │ │ │ │ ├── Producer.java │ │ │ │ └── Consumer.java │ │ └── pom.xml │ ├── rabbitmq-demo │ │ ├── .gitignore │ │ ├── src │ │ │ └── main │ │ │ │ ├── resources │ │ │ │ └── log4j2.xml │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── waylau │ │ │ │ └── rabbitmq │ │ │ │ ├── ReceiveLogs.java │ │ │ │ ├── EmitLog.java │ │ │ │ ├── ReceiveLogsTopic.java │ │ │ │ ├── ReceiveLogsDirect.java │ │ │ │ ├── NewTask.java │ │ │ │ ├── EmitLogDirect.java │ │ │ │ ├── Worker.java │ │ │ │ ├── EmitLogTopic.java │ │ │ │ ├── RPCServer.java │ │ │ │ └── RPCClient.java │ │ ├── dependency-reduced-pom.xml │ │ └── pom.xml │ ├── rocketmq-demo │ │ ├── .gitignore │ │ ├── src │ │ │ └── main │ │ │ │ ├── resources │ │ │ │ └── log4j2.xml │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── waylau │ │ │ │ └── rocketmq │ │ │ │ ├── App.java │ │ │ │ ├── Producer.java │ │ │ │ └── Consumer.java │ │ ├── dependency-reduced-pom.xml │ │ └── pom.xml │ └── jms-msg │ │ ├── src │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── waylau │ │ │ │ │ └── spring │ │ │ │ │ └── jms │ │ │ │ │ ├── queue │ │ │ │ │ ├── ConsumerService.java │ │ │ │ │ ├── ConsumerListener.java │ │ │ │ │ ├── QueueMessageListener.java │ │ │ │ │ ├── ConsumerServiceImpl.java │ │ │ │ │ ├── ProducerService.java │ │ │ │ │ ├── ConsumerSessionAwareMessageListener.java │ │ │ │ │ └── ProducerServiceImpl.java │ │ │ │ │ └── topic │ │ │ │ │ ├── TopicMessageListener.java │ │ │ │ │ ├── TopicMessageListener2.java │ │ │ │ │ └── TopicProvider.java │ │ │ └── resources │ │ │ │ └── spring.xml │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── waylau │ │ │ └── spring │ │ │ └── jms │ │ │ └── SpringJmsTest.java │ │ ├── .gitignore │ │ └── pom.xml ├── chapter6 │ ├── zookeeper-demo │ │ ├── .gitignore │ │ ├── src │ │ │ └── main │ │ │ │ ├── resources │ │ │ │ └── log4j2.xml │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── waylau │ │ │ │ └── zookeeper │ │ │ │ └── SyncPrimitive.java │ │ └── pom.xml │ └── zk-registry-discovery │ │ ├── .gitignore │ │ ├── src │ │ ├── main │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── waylau │ │ │ │ └── zk │ │ │ │ ├── discovery │ │ │ │ ├── ServiceDiscovery.java │ │ │ │ └── ZkServiceDiscovery.java │ │ │ │ ├── Constant.java │ │ │ │ └── registry │ │ │ │ ├── ServiceRegistry.java │ │ │ │ └── ZkServiceRegistry.java │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── waylau │ │ │ └── zk │ │ │ └── ApplicationTests.java │ │ └── pom.xml ├── chapter1 │ └── distributed-systems-technologies-and-cases-analysis │ │ ├── src │ │ └── com │ │ │ └── waylau │ │ │ └── essentialjava │ │ │ ├── iomode │ │ │ ├── package-info.java │ │ │ ├── MultiThreadEchoServer.java │ │ │ ├── EchoServerHandler.java │ │ │ ├── ThreadPoolEchoServer.java │ │ │ ├── EchoServer.java │ │ │ └── NonBlokingEchoServer.java │ │ │ ├── concurrency │ │ │ ├── package-info.java │ │ │ └── Deadlock.java │ │ │ └── thread │ │ │ ├── package-info.java │ │ │ ├── HelloThread.java │ │ │ ├── HelloRunnable.java │ │ │ ├── SleepMessages.java │ │ │ └── SimpleThreads.java │ │ ├── .classpath │ │ └── .project ├── chapter5 │ └── redis-lock │ │ ├── .gitignore │ │ ├── src │ │ ├── main │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── waylau │ │ │ │ └── redis │ │ │ │ ├── lock │ │ │ │ ├── IRedisLock.java │ │ │ │ └── RedisLock.java │ │ │ │ ├── Application.java │ │ │ │ └── config │ │ │ │ └── JedisConfig.java │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── waylau │ │ │ └── redis │ │ │ └── ApplicationTests.java │ │ └── pom.xml └── chapter4 │ └── spark-word-count │ ├── .gitignore │ ├── pom.xml │ └── src │ └── main │ └── java │ └── com │ └── waylau │ └── spark │ └── JavaWordCount.java ├── images ├── logo.png └── 3.3 rocketmq.jpg ├── .gitignore ├── docs ├── Introduction.md └── Preface.md ├── README.md └── SUMMARY.md /samples/chapter8/hello-world-docker/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/chapter2/javase-rest/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | /.settings/ 4 | .classpath 5 | .project -------------------------------------------------------------------------------- /samples/chapter3/activemq-demo/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | /.settings/ 4 | .classpath 5 | .project -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | /.settings/ 4 | .classpath 5 | .project -------------------------------------------------------------------------------- /samples/chapter3/rocketmq-demo/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | /.settings/ 4 | .classpath 5 | .project -------------------------------------------------------------------------------- /samples/chapter6/zookeeper-demo/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | /.settings/ 4 | .classpath 5 | .project -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | /.settings/ 4 | .classpath 5 | .project -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/distributed-systems-technologies-and-cases-analysis/HEAD/images/logo.png -------------------------------------------------------------------------------- /images/3.3 rocketmq.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/distributed-systems-technologies-and-cases-analysis/HEAD/images/3.3 rocketmq.jpg -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | VOLUME /tmp 3 | ARG JAR_FILE 4 | ADD ${JAR_FILE} app.jar 5 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/distributed-systems-technologies-and-cases-analysis/HEAD/samples/chapter8/hello-world-docker/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/iomode/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 常见 IO 模型 3 | * @author waylau.com 4 | * @date 2016年7月29日 5 | */ 6 | package com.waylau.essentialjava.iomode; -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/concurrency/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 并发 3 | * 4 | * @author waylau.com 5 | * @date 2016年7月29日 6 | */ 7 | package com.waylau.essentialjava.concurrency; -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/thread/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 定义和启动一个线程 3 | * 4 | * @author waylau.com 5 | * @date 2016年7月28日 6 | */ 7 | package com.waylau.essentialjava.thread; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | .project 14 | *.md.html -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jul 28 13:37:07 BST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | #distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip 7 | distributionUrl=file\:/D:/software/webdev/java/gradle-4.0-all.zip -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/java/com/waylau/spring/jms/queue/ConsumerService.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms.queue; 2 | 3 | import javax.jms.Destination; 4 | 5 | /** 6 | * Consumer Service. 7 | * 8 | * @since 1.0.0 2018年4月15日 9 | * @author Way Lau 10 | */ 11 | public interface ConsumerService { 12 | public void receive(Destination queueDestination); 13 | } 14 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ 26 | /bin/ 27 | /target/ 28 | -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ 26 | /bin/ 27 | -------------------------------------------------------------------------------- /samples/chapter5/redis-lock/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ 26 | /bin/ 27 | /target/ 28 | -------------------------------------------------------------------------------- /samples/chapter4/spark-word-count/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ 26 | /bin/ 27 | /target/ 28 | -------------------------------------------------------------------------------- /samples/chapter3/activemq-demo/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/chapter3/rocketmq-demo/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/chapter6/zk-registry-discovery/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ 26 | /bin/ 27 | /target/ 28 | -------------------------------------------------------------------------------- /samples/chapter6/zookeeper-demo/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/chapter6/zk-registry-discovery/src/main/java/com/waylau/zk/discovery/ServiceDiscovery.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to https://waylau.com 3 | */ 4 | package com.waylau.zk.discovery; 5 | 6 | /** 7 | * Service Discovery. 8 | * 9 | * @since 1.0.0 2018年5月16日 10 | * @author Way Lau 11 | */ 12 | public interface ServiceDiscovery { 13 | 14 | /** 15 | * 服务发现. 16 | * 17 | * @param name 18 | * @return 19 | */ 20 | String discover(String name); 21 | } 22 | -------------------------------------------------------------------------------- /samples/chapter5/redis-lock/src/main/java/com/waylau/redis/lock/IRedisLock.java: -------------------------------------------------------------------------------- 1 | package com.waylau.redis.lock; 2 | /** 3 | * Welcome to https://waylau.com 4 | */ 5 | 6 | /** 7 | * Redis Lock. 8 | * 9 | * @since 1.0.0 2018年5月15日 10 | * @author Way Lau 11 | */ 12 | public interface IRedisLock { 13 | 14 | Boolean lock(int lockKeyExpireSecond, String lockName, Boolean isWait) throws Exception; 15 | 16 | Boolean unlock(String lockName) throws Exception; 17 | } 18 | -------------------------------------------------------------------------------- /samples/chapter6/zk-registry-discovery/src/main/java/com/waylau/zk/Constant.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to https://waylau.com 3 | */ 4 | package com.waylau.zk; 5 | 6 | /** 7 | * Constant. 8 | * 9 | * @since 1.0.0 2018年5月16日 10 | * @author Way Lau 11 | */ 12 | public interface Constant { 13 | 14 | /**会话超时时间*/ 15 | int ZK_SESSION_TIMEOUT = 5000; 16 | 17 | /**连接超时时间*/ 18 | int ZK_CONNECTION_TIMEOUT = 1000; 19 | 20 | String ZK_REGISTRY = "/registry"; 21 | } 22 | -------------------------------------------------------------------------------- /samples/chapter6/zk-registry-discovery/src/main/java/com/waylau/zk/registry/ServiceRegistry.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to https://waylau.com 3 | */ 4 | package com.waylau.zk.registry; 5 | 6 | /** 7 | * 8 | * @since 1.0.0 2018年5月16日 9 | * @author Way Lau 10 | */ 11 | public interface ServiceRegistry { 12 | 13 | /** 14 | * 注册服务. 15 | * 16 | * @param serviceName 17 | * @param serviceAddress 18 | */ 19 | void registry(String serviceName, String serviceAddress); 20 | } 21 | -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | distributed-systems-java-demos 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/chapter2/javase-rest/src/main/java/com/waylau/rest/bean/MyBean.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rest.bean; 2 | 3 | import javax.xml.bind.annotation.XmlRootElement; 4 | 5 | @XmlRootElement 6 | public class MyBean { 7 | 8 | private String name; 9 | private int age; 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | public void setName(String name) { 15 | this.name = name; 16 | } 17 | public int getAge() { 18 | return age; 19 | } 20 | public void setAge(int age) { 21 | this.age = age; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/src/main/java/com/waylau/spring/cloud/weather/Application.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.cloud.weather; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * 主应用程序. 8 | * 9 | * @since 1.0.0 2017年9月27日 10 | * @author Way Lau 11 | */ 12 | @SpringBootApplication 13 | public class Application { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(Application.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/chapter2/javase-rest/src/main/java/com/waylau/rest/RestApplication.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rest; 2 | 3 | 4 | import org.glassfish.jersey.media.multipart.MultiPartFeature; 5 | import org.glassfish.jersey.server.ResourceConfig; 6 | 7 | /** 8 | * REST 主应用 9 | * 10 | * @author waylau.com 11 | * 2015年3月3日 12 | */ 13 | public class RestApplication extends ResourceConfig { 14 | 15 | public RestApplication() { 16 | // 资源类所在的包路径 17 | packages("com.waylau.rest.resource"); 18 | 19 | // 注册 MultiPart 20 | register(MultiPartFeature.class); 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/thread/HelloThread.java: -------------------------------------------------------------------------------- 1 | package com.waylau.essentialjava.thread; 2 | 3 | /** 4 | * 继承 Thread 定义和启动一个线程 5 | * 6 | * @author waylau.com 7 | * @date 2016年7月28日 8 | */ 9 | public class HelloThread extends Thread { 10 | 11 | @Override 12 | public void run() { 13 | System.out.println("Hello from a thread!"); 14 | } 15 | /** 16 | * @param args 17 | */ 18 | public static void main(String[] args) { 19 | (new HelloThread()).start(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /samples/chapter5/redis-lock/src/main/java/com/waylau/redis/Application.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to https://waylau.com 3 | */ 4 | package com.waylau.redis; 5 | 6 | /** 7 | * Application. 8 | * 9 | * @since 1.0.0 2018年5月15日 10 | * @author Way Lau 11 | */ 12 | import org.springframework.boot.SpringApplication; 13 | import org.springframework.boot.autoconfigure.SpringBootApplication; 14 | 15 | @SpringBootApplication 16 | public class Application { 17 | public static void main(String[] args) { 18 | SpringApplication.run(Application.class, args); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/thread/HelloRunnable.java: -------------------------------------------------------------------------------- 1 | package com.waylau.essentialjava.thread; 2 | 3 | /** 4 | * 实现 Runnable 定义和启动一个线程 5 | * 6 | * @author waylau.com 7 | * @date 2016年7月28日 8 | */ 9 | public class HelloRunnable implements Runnable { 10 | 11 | @Override 12 | public void run() { 13 | System.out.println("Hello from runnable!"); 14 | } 15 | 16 | /** 17 | * @param args 18 | */ 19 | public static void main(String[] args) { 20 | (new Thread(new HelloRunnable())).start(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/src/test/java/com/waylau/spring/cloud/weather/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.cloud.weather; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | /** 9 | * 主应用测试用例. 10 | * 11 | * @since 1.0.0 2017年9月27日 12 | * @author Way Lau 13 | */ 14 | @RunWith(SpringRunner.class) 15 | @SpringBootTest 16 | public class ApplicationTests { 17 | 18 | @Test 19 | public void contextLoads() { 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/java/com/waylau/spring/jms/queue/ConsumerListener.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms.queue; 2 | 3 | /** 4 | * Consumer Listener. 5 | * 1. MessageListenerAdapter可以把一个普通的java当做MessageListener来处理消息。 6 | * 2. 返回类型不是void的时候,MessageListenerAdapter会自动把返回值封装成Message,并回复。 7 | * 8 | * @since 1.0.0 2018年4月15日 9 | * @author Way Lau 10 | */ 11 | public class ConsumerListener { 12 | 13 | public String receiveMessage(String message) { 14 | System.out.println("ConsumerListener接收到一个Text消息:\t" + message); 15 | 16 | return "I am ConsumerListener response"; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/src/main/java/com/waylau/spring/cloud/weather/controller/HelloController.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.cloud.weather.controller; 2 | 3 | import org.springframework.web.bind.annotation.RequestMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | /** 7 | * Hello Controller. 8 | * 9 | * @since 1.0.0 2017年9月27日 10 | * @author Way Lau 11 | */ 12 | @RestController 13 | public class HelloController { 14 | 15 | @RequestMapping("/hello") 16 | public String hello() { 17 | return "Hello World! Welcome to visit waylau.com!"; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /samples/chapter3/rocketmq-demo/src/main/java/com/waylau/rocketmq/App.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rocketmq; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /** 7 | * 主应用入口 8 | * 9 | * @author Way Lau 10 | * @date 2016年8月18日 11 | */ 12 | public class App { 13 | private static final Logger LOGGER = LoggerFactory.getLogger(App.class); 14 | /** 15 | * 16 | */ 17 | public App() { 18 | // TODO Auto-generated constructor stub 19 | } 20 | 21 | /** 22 | * @param args 23 | */ 24 | public static void main(String[] args) { 25 | LOGGER.info("Welcome to waylau.com!"); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/sse_cors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SSE 的 CORS| www.waylau.com 7 | 8 | 9 |

SSE 的 CORS

10 | 11 |

12 | Index 13 |

Get time from server

14 |
Initializing...
15 |

16 | Visit www.waylau.com for more information 17 | on Jersey! 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/sse_ie.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SSE 发布-订阅模式,支持IE| www.waylau.com 7 | 8 | 9 |

SSE 发布-订阅模式,支持IE

10 | 11 |

12 | Index 13 |

Get time from server

14 |
Initializing...
15 |

16 | Visit www.waylau.com for more information 17 | on Jersey! 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/Introduction.md: -------------------------------------------------------------------------------- 1 | # 内容简介 2 | 3 | 《分布式系统常用技术及案例分析》全面介绍在设计分布式系统时所要考虑的技术方案,内容丰富、案例新颖,相关理论与技术实践较为前瞻。《分布式系统常用技术及案例分析》不仅仅介绍了分布式系统的原理、基础理论,同时还引入了大量市面上常用的最新分布式系统技术,不仅告诉读者怎么用,同时也分析了为什么这么用,并阐述了这些技术的优缺点。希望《分布式系统常用技术及案例分析》可以成为读者案头的工具书,供读者随手翻阅。 4 | 5 | 《分布式系统常用技术及案例分析》分为三大部分,即分布式系统基础理论、分布式系统常用技术以及经典的分布式系统案例分析。第一部分主要介绍分布式系统基础理论知识,总结一些在设计分布式系统时需要考虑的范式、知识点以及可能会面临的问题,其中包括线程、通信、一致性、容错性、CAP理论、安全性和并发等相关内容;同时讲述分布式系统的常见架构体系,其中也包括最近比较火的RESTful风格架构、微服务、容器技术等。第二部分主要列举了在分布式系统应用中经常用到的一些主流技术,并介绍这些技术的作用和用法;这些技术涵盖了分布式消息服务、分布式计算、分布式存储、分布式监控系统、分布式版本控制、RESTful、微服务、容器等领域的内容。第三部分选取了以淘宝网和Twitter为代表的国内外知名互联网企业的大型分布式系统案例,分析其架构设计以及演变过程;这部分相当于是对第二部分零散的技术点做一个"串烧",让读者可以结合技术的理论,看到实战的效果。 6 | 7 | 《分布式系统常用技术及案例分析》主要面向的读者是对分布式系统感兴趣的计算机专业的学生、软件工程师、系统架构师等。 -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/sse_cors_alarm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 告警 SSE(CORS)| www.waylau.com 7 | 8 | 9 |

告警 SSE(CORS)

10 | 11 |

12 | Index 13 |

Get alarm info from server

14 |
Initializing...
15 |

16 | Visit www.waylau.com for more information 17 | on Jersey! 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/java/com/waylau/rest/RestApplication.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rest; 2 | 3 | import org.glassfish.jersey.media.multipart.MultiPartFeature; 4 | import org.glassfish.jersey.server.ResourceConfig; 5 | 6 | import com.waylau.rest.filter.CrossDomainFilter; 7 | 8 | /** 9 | * REST 主应用 10 | * 11 | * @author waylau.com 12 | * 2015年3月3日 13 | */ 14 | public class RestApplication extends ResourceConfig { 15 | 16 | public RestApplication() { 17 | // 资源类所在的包路径 18 | packages("com.waylau.rest.resource"); 19 | 20 | // 注册 MultiPart 21 | register(MultiPartFeature.class); 22 | 23 | // 注册CORS过滤器 24 | register(CrossDomainFilter.class); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SSE 发布-订阅模式 | www.waylau.com 6 | 7 | 8 |

Jersey RESTful Web Application!

9 | 10 |

11 | 进入聊天室 12 |

13 | IE 浏览器的 SSE 14 |

15 | SSE 的 CORS 16 | 17 |

Get time from server

18 |
Initializing...
19 |

20 | Visit www.waylau.com for more information 21 | on Jersey! 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/java/com/waylau/spring/jms/topic/TopicMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms.topic; 2 | 3 | import javax.jms.JMSException; 4 | import javax.jms.Message; 5 | import javax.jms.MessageListener; 6 | import javax.jms.TextMessage; 7 | 8 | /** 9 | * Topic Message Listener. 10 | * 11 | * @since 1.0.0 2018年4月15日 12 | * @author Way Lau 13 | */ 14 | public class TopicMessageListener implements MessageListener { 15 | 16 | public void onMessage(Message message) { 17 | TextMessage tm = (TextMessage) message; 18 | try { 19 | System.out.println("TopicMessageListener 监听到消息:\t" + tm.getText()); 20 | } catch (JMSException e) { 21 | e.printStackTrace(); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/java/com/waylau/spring/jms/queue/QueueMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms.queue; 2 | 3 | import javax.jms.JMSException; 4 | import javax.jms.Message; 5 | import javax.jms.MessageListener; 6 | import javax.jms.TextMessage; 7 | 8 | /** 9 | * Queue Message Listener. 10 | * 11 | * @since 1.0.0 2018年4月15日 12 | * @author Way Lau 13 | */ 14 | public class QueueMessageListener implements MessageListener { 15 | 16 | public void onMessage(Message message) { 17 | TextMessage tm = (TextMessage) message; 18 | try { 19 | System.out.println("ConsumerMessageListener收到了文本消息:\t" + tm.getText()); 20 | } catch (JMSException e) { 21 | e.printStackTrace(); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/java/com/waylau/spring/jms/topic/TopicMessageListener2.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms.topic; 2 | 3 | import javax.jms.JMSException; 4 | 5 | import javax.jms.Message; 6 | import javax.jms.MessageListener; 7 | import javax.jms.TextMessage; 8 | 9 | /** 10 | * Topic Message Listener 2. 11 | * 12 | * @since 1.0.0 2018年4月15日 13 | * @author Way Lau 14 | */ 15 | public class TopicMessageListener2 implements MessageListener { 16 | 17 | public void onMessage(Message message) { 18 | TextMessage tm = (TextMessage) message; 19 | try { 20 | System.out.println("TopicMessageListener2监听到消息 \t" + tm.getText()); 21 | } catch (JMSException e) { 22 | e.printStackTrace(); 23 | } 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /samples/chapter5/redis-lock/src/main/java/com/waylau/redis/config/JedisConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to https://waylau.com 3 | */ 4 | package com.waylau.redis.config; 5 | 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | import redis.clients.jedis.Jedis; 10 | import redis.clients.jedis.JedisPool; 11 | 12 | /** 13 | * Jedis Configuration. 14 | * 15 | * @since 1.0.0 2018年5月15日 16 | * @author Way Lau 17 | */ 18 | @Configuration 19 | public class JedisConfig { 20 | 21 | private static JedisPool jedisPool; 22 | 23 | @Bean 24 | public Jedis getBuild() { 25 | jedisPool = new JedisPool("localhost", 6379); 26 | Jedis jedis = jedisPool.getResource(); 27 | return jedis; 28 | } 29 | } -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/thread/SleepMessages.java: -------------------------------------------------------------------------------- 1 | package com.waylau.essentialjava.thread; 2 | 3 | /** 4 | * Thread.sleep 可以让当前线程执行暂停一个时间段 5 | * 6 | * @author waylau.com 7 | * @date 2016年7月28日 8 | */ 9 | public class SleepMessages { 10 | 11 | /** 12 | * @param args 13 | * @throws InterruptedException 14 | */ 15 | public static void main(String[] args) throws InterruptedException { 16 | String importantInfo[] = { 17 | "Mares eat oats", 18 | "Does eat oats", 19 | "Little lambs eat ivy", 20 | "A kid will eat ivy too" }; 21 | 22 | for (int i = 0; i < importantInfo.length; i++) { 23 | 24 | // Pause for 4 seconds 25 | Thread.sleep(4000); 26 | 27 | // Print a message 28 | System.out.println(importantInfo[i]); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/java/com/waylau/spring/jms/queue/ConsumerServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms.queue; 2 | 3 | 4 | import javax.jms.Destination; 5 | import javax.jms.JMSException; 6 | import javax.jms.TextMessage; 7 | 8 | import org.springframework.jms.core.JmsTemplate; 9 | /** 10 | * Consumer Service. 11 | * 12 | * @since 1.0.0 2018年4月15日 13 | * @author Way Lau 14 | */ 15 | public class ConsumerServiceImpl implements ConsumerService { 16 | 17 | private JmsTemplate jmsTemplate; 18 | 19 | /** 20 | * 接受消息 21 | */ 22 | public void receive(Destination destination) { 23 | TextMessage tm = (TextMessage) jmsTemplate.receive(destination); 24 | try { 25 | System.out.println("ConsumerService从队列" 26 | + destination.toString() + "收到了消息:\t" + tm.getText()); 27 | } catch (JMSException e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | 32 | public void setJmsTemplate(JmsTemplate jmsTemplate) { 33 | this.jmsTemplate = jmsTemplate; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /samples/chapter6/zk-registry-discovery/src/test/java/com/waylau/zk/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to https://waylau.com 3 | */ 4 | package com.waylau.zk; 5 | 6 | import org.junit.Test; 7 | 8 | import com.waylau.zk.discovery.ZkServiceDiscovery; 9 | import com.waylau.zk.registry.ZkServiceRegistry; 10 | 11 | 12 | /** 13 | * Application Tests. 14 | * 15 | * @since 1.0.0 2018年5月15日 16 | * @author Way Lau 17 | */ 18 | public class ApplicationTests { 19 | 20 | private static final String SERVER_NAME = "waylau.com"; 21 | private static final String SERVER_ADDRESS = "localhost:2181"; 22 | 23 | @Test 24 | public void testClient() throws Exception { 25 | 26 | ZkServiceRegistry registry = new ZkServiceRegistry(); 27 | registry.init(); 28 | registry.registry(SERVER_NAME, SERVER_ADDRESS); 29 | 30 | ZkServiceDiscovery discovery = new ZkServiceDiscovery(); 31 | discovery.init(); 32 | discovery.discover(SERVER_NAME); 33 | 34 | // 永不停止 35 | while(true) { 36 | } 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/java/com/waylau/spring/jms/queue/ProducerService.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms.queue; 2 | 3 | import javax.jms.Destination; 4 | 5 | /** 6 | * Producer Service. 7 | * 8 | * @since 1.0.0 2018年4月15日 9 | * @author Way Lau 10 | */ 11 | public interface ProducerService { 12 | 13 | /** 14 | * 发消息,向默认的 destination 15 | * 16 | * @param msg 17 | * String 消息内容 18 | */ 19 | public void sendMessage(String msg); 20 | 21 | /** 22 | * 发消息,向指定的 destination 23 | * 24 | * @param destination 25 | * 目的地 26 | * @param msg 27 | * String 消息内容 28 | */ 29 | public void sendMessage(Destination destination, String msg); 30 | 31 | /** 32 | * 向指定的destination发送消息,消费者接受消息后,把回复的消息写到response队列 33 | * 34 | * @param destination 35 | * 目的地 36 | * @param msg 37 | * String 消息内容 38 | * @param response 39 | * 回复消息的队列 40 | */ 41 | public void sendMessage(Destination destination, String msg, Destination response); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/sse_broadcast.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SSE 广播模式-聊天 | www.waylau.com 6 | 7 | 8 |

Jersey RESTful Web Application!

9 |

10 | Index 11 | 12 |

13 |

SSE 聊天室:

14 | 15 |
16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 |

24 | Visit www.waylau.com for more information 25 | on Jersey! 26 | 27 | 28 | -------------------------------------------------------------------------------- /samples/chapter5/redis-lock/src/test/java/com/waylau/redis/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to https://waylau.com 3 | */ 4 | package com.waylau.redis; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | import com.waylau.redis.lock.RedisLock; 13 | 14 | /** 15 | * Application Tests. 16 | * 17 | * @since 1.0.0 2018年5月15日 18 | * @author Way Lau 19 | */ 20 | @RunWith(SpringRunner.class) 21 | @SpringBootTest 22 | public class ApplicationTests { 23 | 24 | @Autowired 25 | private RedisLock redisLock; 26 | 27 | @Test 28 | public void contextLoads() throws Exception { 29 | String lockName = "waylau.com"; 30 | int lockNameExpireSecond = 10; 31 | 32 | for (int i = 0; i < 4; i++) { 33 | redisLock.lock(lockNameExpireSecond, lockName, true); 34 | 35 | Thread.sleep(3000); 36 | 37 | redisLock.unlock(lockName); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 10 | Jersey Web Application 11 | org.glassfish.jersey.servlet.ServletContainer 12 | 13 | javax.ws.rs.Application 14 | com.waylau.rest.RestApplication 15 | 16 | 1 17 | true 18 | 19 | 20 | Jersey Web Application 21 | /webapi/* 22 | 23 | -------------------------------------------------------------------------------- /samples/chapter4/spark-word-count/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.waylau.spark 5 | spark-word-count 6 | 1.0.0 7 | spark-word-count 8 | jar 9 | 10 | waylau.com 11 | https://waylau.com 12 | 13 | 14 | 15 | UTF-8 16 | 2.3.0 17 | 18 | 19 | 20 | 21 | org.apache.spark 22 | spark-core_2.11 23 | ${spark.version} 24 | 25 | 26 | org.apache.spark 27 | spark-sql_2.11 28 | ${spark.version} 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/java/com/waylau/rest/filter/CrossDomainFilter.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rest.filter; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.ws.rs.container.ContainerRequestContext; 6 | import javax.ws.rs.container.ContainerResponseContext; 7 | import javax.ws.rs.container.ContainerResponseFilter; 8 | 9 | /** 10 | * 说明:支持 Cross-domain 请求 11 | * 12 | * @author waylau.com 2015年8月22日 13 | */ 14 | public class CrossDomainFilter implements ContainerResponseFilter { 15 | 16 | /** 17 | * 18 | */ 19 | public CrossDomainFilter() { 20 | // TODO Auto-generated constructor stub 21 | } 22 | 23 | /* (non-Javadoc) 24 | * @see javax.ws.rs.container.ContainerResponseFilter#filter(javax.ws.rs.container.ContainerRequestContext, javax.ws.rs.container.ContainerResponseContext) 25 | */ 26 | @Override 27 | public void filter(ContainerRequestContext requestContext, 28 | ContainerResponseContext responseContext) throws IOException { 29 | 30 | // 响应头添加了对允许访问的域,* 代表是全部域 31 | responseContext.getHeaders().add("Access-Control-Allow-Origin", "*"); 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/java/com/waylau/spring/jms/topic/TopicProvider.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms.topic; 2 | 3 | import javax.jms.Destination; 4 | import javax.jms.JMSException; 5 | import javax.jms.Message; 6 | import javax.jms.Session; 7 | 8 | import org.springframework.jms.core.JmsTemplate; 9 | import org.springframework.jms.core.MessageCreator; 10 | 11 | /** 12 | * Topic Provider. 13 | * 14 | * @since 1.0.0 2018年4月15日 15 | * @author Way Lau 16 | */ 17 | public class TopicProvider { 18 | 19 | private JmsTemplate topicJmsTemplate; 20 | 21 | /** 22 | * 向指定的topic发布消息 23 | * 24 | * @param topic 25 | * @param msg 26 | */ 27 | public void publish(final Destination topic, final String msg) { 28 | 29 | topicJmsTemplate.send(topic, new MessageCreator() { 30 | public Message createMessage(Session session) throws JMSException { 31 | System.out.println("TopicProvider 发布了主题:\t" 32 | + topic.toString() + ",发布消息内容为:\t" + msg); 33 | return session.createTextMessage(msg); 34 | } 35 | }); 36 | } 37 | 38 | public void setTopicJmsTemplate(JmsTemplate topicJmsTemplate) { 39 | this.topicJmsTemplate = topicJmsTemplate; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/iomode/MultiThreadEchoServer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.waylau.essentialjava.iomode; 5 | import java.io.IOException; 6 | import java.net.ServerSocket; 7 | import java.net.Socket; 8 | 9 | /** 10 | * “阻塞I/O+多线程”模式。使用多线程来支持多个客户端访问服务器 11 | * 12 | * @author waylau.com 13 | * @date 2016年7月29日 14 | */ 15 | public class MultiThreadEchoServer { 16 | public static int DEFAULT_PORT = 7; 17 | 18 | public static void main(String[] args) throws IOException { 19 | 20 | int port; 21 | 22 | try { 23 | port = Integer.parseInt(args[0]); 24 | } catch (RuntimeException ex) { 25 | port = DEFAULT_PORT; 26 | } 27 | Socket clientSocket = null; 28 | try (ServerSocket serverSocket = new ServerSocket(port);) { 29 | while (true) { 30 | clientSocket = serverSocket.accept(); 31 | 32 | // 多线程 33 | new Thread(new EchoServerHandler(clientSocket)).start(); 34 | } 35 | } catch (IOException e) { 36 | System.out.println( 37 | "Exception caught when trying to listen on port " 38 | + port + " or listening for a connection"); 39 | System.out.println(e.getMessage()); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/iomode/EchoServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.waylau.essentialjava.iomode; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.io.PrintWriter; 7 | import java.net.Socket; 8 | 9 | /** 10 | * 处理器类 11 | * 12 | * @author waylau.com 13 | * @date 2016年7月29日 14 | */ 15 | public class EchoServerHandler implements Runnable { 16 | private Socket clientSocket; 17 | 18 | public EchoServerHandler(Socket clientSocket) { 19 | this.clientSocket = clientSocket; 20 | } 21 | 22 | @Override 23 | public void run() { 24 | try ( 25 | PrintWriter out = new PrintWriter(clientSocket.getOutputStream() 26 | , true); 27 | BufferedReader in = new BufferedReader( 28 | new InputStreamReader(clientSocket.getInputStream()));) { 29 | 30 | String inputLine; 31 | while ((inputLine = in.readLine()) != null) { 32 | out.println(inputLine); 33 | } 34 | } catch (IOException e) { 35 | System.out.println(e.getMessage()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/scripts/sse_real_time_pub.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Created by waylau.com on 2015/8/18. 4 | */ 5 | 6 | //判断浏览器是否支持 EventSource 7 | if (typeof (EventSource) !== "undefined") { 8 | var source = new EventSource("webapi/see-events"); 9 | 10 | // 当通往服务器的连接被打开 11 | source.onopen = function(event) { 12 | console.log("连接开启!"); 13 | 14 | }; 15 | 16 | // 当接收到消息。只能是事件名称是 message 17 | source.onmessage = function(event) { 18 | console.log(event.data); 19 | var data = event.data; 20 | var lastEventId = event.lastEventId; 21 | document.getElementById("x").innerHTML += "\n" + 'lastEventId:'+lastEventId+';data:'+data; 22 | }; 23 | 24 | //可以是任意命名的事件名称 25 | /* 26 | source.addEventListener('message', function(event) { 27 | console.log(event.data); 28 | var data = event.data; 29 | var lastEventId = event.lastEventId; 30 | document.getElementById("x").innerHTML += "\n" + 'lastEventId:'+lastEventId+';data:'+data; 31 | }); 32 | */ 33 | 34 | // 当错误发生 35 | source.onerror = function(event) { 36 | console.log("连接错误!"); 37 | 38 | }; 39 | } else { 40 | document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events..." 41 | } -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/scripts/sse_real_time_cors.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Created by waylau.com on 2015/8/22 4 | */ 5 | 6 | //判断浏览器是否支持 EventSource 7 | if (typeof (EventSource) !== "undefined") { 8 | var source = new EventSource("http://192.168.11.125:8081/webapi/see-events"); 9 | 10 | // 当通往服务器的连接被打开 11 | source.onopen = function(event) { 12 | console.log("连接开启!"); 13 | 14 | }; 15 | 16 | // 当接收到消息。只能是事件名称是 message 17 | source.onmessage = function(event) { 18 | console.log(event.data); 19 | var data = event.data; 20 | var lastEventId = event.lastEventId; 21 | document.getElementById("x").innerHTML += "\n" + 'lastEventId:'+lastEventId+';data:'+data; 22 | }; 23 | 24 | //可以是任意命名的事件名称 25 | /* 26 | source.addEventListener('message', function(event) { 27 | console.log(event.data); 28 | var data = event.data; 29 | var lastEventId = event.lastEventId; 30 | document.getElementById("x").innerHTML += "\n" + 'lastEventId:'+lastEventId+';data:'+data; 31 | }); 32 | */ 33 | 34 | // 当错误发生 35 | source.onerror = function(event) { 36 | console.log("连接错误!"); 37 | 38 | }; 39 | } else { 40 | document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events..." 41 | } -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/java/com/waylau/spring/jms/queue/ConsumerSessionAwareMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms.queue; 2 | 3 | import javax.jms.Destination; 4 | import javax.jms.JMSException; 5 | import javax.jms.MessageProducer; 6 | import javax.jms.Session; 7 | import javax.jms.TextMessage; 8 | 9 | import org.springframework.jms.listener.SessionAwareMessageListener; 10 | 11 | /** 12 | * Consumer SessionAwareMessageListener. 13 | * SessionAwareMessageListener提供了一个快捷的方法,方便我们在接受消息以后,发送一条回复消息 * 14 | * onMessage()有2个参数,一个是收到的消息,另一个Session可以用于发送回复消息。 15 | * 16 | * @since 1.0.0 2018年4月15日 17 | * @author Way Lau 18 | */ 19 | public class ConsumerSessionAwareMessageListener 20 | implements SessionAwareMessageListener { 21 | 22 | private Destination destination; 23 | 24 | public void onMessage(TextMessage message, Session session) throws JMSException { 25 | // 接受消息 26 | System.out.println("SessionAwareMessageListener收到一条消息:\t" + message.getText()); 27 | 28 | // 发送消息 29 | MessageProducer producer = session.createProducer(destination); 30 | TextMessage tm = 31 | session.createTextMessage("I am ConsumerSessionAwareMessageListener"); 32 | producer.send(tm); 33 | 34 | } 35 | 36 | public void setDestination(Destination destination) { 37 | this.destination = destination; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/scripts/sse_real_time_cors_alarm.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Created by waylau.com on 2015/8/22 4 | */ 5 | 6 | //判断浏览器是否支持 EventSource 7 | if (typeof (EventSource) !== "undefined") { 8 | var source = new EventSource("http://192.168.11.220:8081/webapi/alarm-events?entId="+22); 9 | 10 | // 当通往服务器的连接被打开 11 | source.onopen = function(event) { 12 | console.log("连接开启!"); 13 | 14 | }; 15 | 16 | // 当接收到消息。只能是事件名称是 message 17 | source.onmessage = function(event) { 18 | console.log(event.data); 19 | var data = event.data; 20 | var lastEventId = event.lastEventId; 21 | document.getElementById("x").innerHTML += "\n" + 'lastEventId:'+lastEventId+';data:'+data; 22 | }; 23 | 24 | //可以是任意命名的事件名称 25 | /* 26 | source.addEventListener('message', function(event) { 27 | console.log(event.data); 28 | var data = event.data; 29 | var lastEventId = event.lastEventId; 30 | document.getElementById("x").innerHTML += "\n" + 'lastEventId:'+lastEventId+';data:'+data; 31 | }); 32 | */ 33 | 34 | // 当错误发生 35 | source.onerror = function(event) { 36 | console.log("连接错误!"); 37 | 38 | }; 39 | } else { 40 | document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events..." 41 | } -------------------------------------------------------------------------------- /samples/chapter2/javase-rest/src/main/java/com/waylau/rest/App.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rest; 2 | 3 | //import org.glassfish.grizzly.http.server.HttpServer; 4 | //import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; 5 | //import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory; 6 | //import org.glassfish.jersey.simple.SimpleContainerFactory; 7 | 8 | import java.io.IOException; 9 | import java.net.URI; 10 | 11 | import org.glassfish.jersey.jetty.JettyHttpContainerFactory; 12 | 13 | /** 14 | * 主应用 15 | * 16 | */ 17 | public class App { 18 | // HTTP server 所要监听的 uri 19 | public static final String BASE_URI = "http://192.168.11.125:8081/"; 20 | 21 | /** 22 | * Main 方法. 23 | * 24 | * @param args 25 | * @throws IOException 26 | */ 27 | public static void main(String[] args) throws IOException { 28 | 29 | // 若使用 Jdk Http Server请去掉下面的注释 30 | // JdkHttpServerFactory.createHttpServer(URI.create(BASE_URI), new 31 | // RestApplication()); 32 | 33 | // 若使用 Grizzly Http Server请去掉下面的注释 34 | // GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), new 35 | // RestApplication()); 36 | 37 | // 若使用 Simple Http Server请去掉下面的注释 38 | // SimpleContainerFactory.create(URI.create(BASE_URI), new 39 | // RestApplication()); 40 | // 若使用 Jetty Http Server请去掉下面的注释 41 | JettyHttpContainerFactory.createServer(URI.create(BASE_URI), 42 | new RestApplication()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /samples/chapter4/spark-word-count/src/main/java/com/waylau/spark/JavaWordCount.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spark; 2 | import scala.Tuple2; 3 | 4 | import org.apache.spark.api.java.JavaPairRDD; 5 | import org.apache.spark.api.java.JavaRDD; 6 | import org.apache.spark.sql.SparkSession; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.regex.Pattern; 11 | 12 | /** 13 | * 词频统计 14 | * 15 | * @since 1.0.0 2018年5月14日 16 | * @author Way Lau 17 | */ 18 | public final class JavaWordCount { 19 | private static final Pattern SPACE = Pattern.compile(" "); 20 | 21 | public static void main(String[] args) throws Exception { 22 | 23 | if (args.length < 1) { 24 | System.err.println("Usage: JavaWordCount "); 25 | System.exit(1); 26 | } 27 | 28 | SparkSession spark = SparkSession.builder().appName("JavaWordCount").getOrCreate(); 29 | 30 | JavaRDD lines = spark.read().textFile(args[0]).javaRDD(); 31 | 32 | JavaRDD words = lines.flatMap(s -> Arrays.asList(SPACE.split(s)).iterator()); 33 | 34 | JavaPairRDD ones = words.mapToPair(s -> new Tuple2<>(s, 1)); 35 | 36 | JavaPairRDD counts = ones.reduceByKey((i1, i2) -> i1 + i2); 37 | 38 | List> output = counts.collect(); 39 | for (Tuple2 tuple : output) { 40 | System.out.println(tuple._1() + ": " + tuple._2()); 41 | } 42 | spark.stop(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/concurrency/Deadlock.java: -------------------------------------------------------------------------------- 1 | package com.waylau.essentialjava.concurrency; 2 | 3 | /** 4 | * 死锁 5 | * 6 | * @author waylau.com 7 | * @date 2016年7月29日 8 | */ 9 | public class Deadlock { 10 | static class Friend { 11 | private final String name; 12 | 13 | public Friend(String name) { 14 | this.name = name; 15 | } 16 | 17 | public String getName() { 18 | return this.name; 19 | } 20 | 21 | public synchronized void bow(Friend bower) { 22 | System.out.format("%s: %s" + " has bowed to me!%n", 23 | this.name, bower.getName()); 24 | bower.bowBack(this); 25 | } 26 | 27 | public synchronized void bowBack(Friend bower) { 28 | System.out.format("%s: %s" + " has bowed back to me!%n", 29 | this.name, bower.getName()); 30 | } 31 | } 32 | 33 | public static void main(String[] args) { 34 | final Friend alphonse = new Friend("Alphonse"); 35 | final Friend gaston = new Friend("Gaston"); 36 | new Thread(new Runnable() { 37 | public void run() { 38 | alphonse.bow(gaston); 39 | } 40 | }).start(); 41 | new Thread(new Runnable() { 42 | public void run() { 43 | gaston.bow(alphonse); 44 | } 45 | }).start(); 46 | } 47 | } -------------------------------------------------------------------------------- /samples/chapter5/redis-lock/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.waylau.spring5 5 | redis-lock 6 | 1.0.0 7 | redis-lock 8 | jar 9 | 10 | waylau.com 11 | https://waylau.com 12 | 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-parent 23 | 2.0.2.RELEASE 24 | 25 | 26 | 27 | 28 | redis.clients 29 | jedis 30 | 2.9.0 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-data-redis 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/src/test/java/com/waylau/spring/cloud/weather/controller/HelloControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.cloud.weather.controller; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | import org.springframework.test.web.servlet.MockMvc; 11 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 12 | import static org.hamcrest.Matchers.equalTo; 13 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 14 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 15 | 16 | /** 17 | * HelloController Test. 18 | * 19 | * @since 1.0.0 2017年9月27日 20 | * @author Way Lau 21 | */ 22 | @RunWith(SpringRunner.class) 23 | @SpringBootTest 24 | @AutoConfigureMockMvc 25 | public class HelloControllerTest { 26 | 27 | @Autowired 28 | private MockMvc mockMvc; 29 | 30 | @Test 31 | public void testHello() throws Exception { 32 | mockMvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)) 33 | .andExpect(status().isOk()) 34 | .andExpect(content().string(equalTo("Hello World! Welcome to visit waylau.com!"))); 35 | } 36 | } -------------------------------------------------------------------------------- /samples/chapter6/zk-registry-discovery/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.waylau.spring5 5 | zk-registry-discovery 6 | 1.0.0 7 | zk-registry-discovery 8 | jar 9 | 10 | waylau.com 11 | https://waylau.com 12 | 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 19 | 20 | 21 | 22 | com.101tec 23 | zkclient 24 | 0.10 25 | 26 | 27 | org.springframework 28 | spring-context 29 | 5.0.6.RELEASE 30 | 31 | 32 | com.google.guava 33 | guava 34 | 25.0-jre 35 | 36 | 37 | junit 38 | junit 39 | 4.12 40 | test 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/iomode/ThreadPoolEchoServer.java: -------------------------------------------------------------------------------- 1 | package com.waylau.essentialjava.iomode; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.Executors; 8 | 9 | /** 10 | * “阻塞I/O+线程池”模式 11 | * 12 | * @author waylau.com 13 | * @date 2016年7月29日 14 | */ 15 | public class ThreadPoolEchoServer { 16 | 17 | public static int DEFAULT_PORT = 7; 18 | 19 | public static void main(String[] args) throws IOException { 20 | 21 | int port; 22 | 23 | try { 24 | port = Integer.parseInt(args[0]); 25 | } catch (RuntimeException ex) { 26 | port = DEFAULT_PORT; 27 | } 28 | ExecutorService threadPool = Executors.newFixedThreadPool(5); 29 | Socket clientSocket = null; 30 | try (ServerSocket serverSocket = new ServerSocket(port);) { 31 | while (true) { 32 | clientSocket = serverSocket.accept(); 33 | 34 | // 线程池 35 | threadPool.submit(new Thread(new EchoServerHandler(clientSocket))); 36 | } 37 | } catch (IOException e) { 38 | System.out.println( 39 | "Exception caught when trying to listen on port " 40 | + port + " or listening for a connection"); 41 | System.out.println(e.getMessage()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/build.gradle: -------------------------------------------------------------------------------- 1 | // buildscript 代码块中脚本优先执行 2 | buildscript { 3 | 4 | // ext 用于定义动态属性 5 | ext { 6 | springBootVersion = '2.0.2.RELEASE' 7 | } 8 | 9 | // 使用了Maven的中央仓库及Spring自己的仓库(也可以指定其他仓库) 10 | repositories { 11 | //mavenCentral() 12 | maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } 13 | } 14 | 15 | // 依赖关系 16 | dependencies { 17 | 18 | // classpath 声明了在执行其余的脚本时,ClassLoader 可以使用这些依赖项 19 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 20 | 21 | classpath('gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.17.2') 22 | } 23 | } 24 | 25 | // 使用插件 26 | apply plugin: 'java' 27 | apply plugin: 'eclipse' 28 | apply plugin: 'org.springframework.boot' 29 | apply plugin: 'io.spring.dependency-management' 30 | 31 | apply plugin: 'com.palantir.docker' 32 | 33 | // 指定了生成的编译文件的版本,默认是打成了 jar 包 34 | group = 'com.waylau.spring.cloud' 35 | version = '1.0.0' 36 | 37 | // 指定编译 .java 文件的 JDK 版本 38 | sourceCompatibility = 1.8 39 | 40 | docker { 41 | name "${project.group}/${jar.baseName}" 42 | files jar.archivePath 43 | buildArgs(['JAR_FILE': "${jar.archiveName}"]) 44 | } 45 | 46 | // 使用了Maven的中央仓库及Spring自己的仓库(也可以指定其他仓库) 47 | repositories { 48 | //mavenCentral() 49 | maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } 50 | } 51 | 52 | // 依赖关系 53 | dependencies { 54 | 55 | // 该依赖用于编译阶段 56 | compile('org.springframework.boot:spring-boot-starter-web') 57 | 58 | // 该依赖用于测试阶段 59 | testCompile('org.springframework.boot:spring-boot-starter-test') 60 | } 61 | -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/java/com/waylau/rabbitmq/ReceiveLogs.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rabbitmq; 2 | 3 | import com.rabbitmq.client.*; 4 | 5 | import java.io.IOException; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | /** 10 | * 日志接收程序 11 | * 12 | * @author waylau.com 13 | * @date 2016年8月12日 14 | */ 15 | public class ReceiveLogs { 16 | 17 | private static final Logger LOGGER = LoggerFactory.getLogger(ReceiveLogs.class); 18 | private static final String EXCHANGE_NAME = "logs"; 19 | 20 | public static void main(String[] argv) throws Exception { 21 | ConnectionFactory factory = new ConnectionFactory(); 22 | factory.setHost("localhost"); 23 | Connection connection = factory.newConnection(); 24 | Channel channel = connection.createChannel(); 25 | 26 | channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); 27 | 28 | // 不传递任何参数来创建一个非持久的、唯一的、动删除的队列,该队列名称由服务器随机产生。 29 | String queueName = channel.queueDeclare().getQueue(); 30 | 31 | // 为交换器指定队列,设置 binding 32 | channel.queueBind(queueName, EXCHANGE_NAME, ""); 33 | 34 | LOGGER.info(" [*] Waiting for messages. To exit press CTRL+C"); 35 | 36 | Consumer consumer = new DefaultConsumer(channel) { 37 | @Override 38 | public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, 39 | byte[] body) throws IOException { 40 | String message = new String(body, "UTF-8"); 41 | LOGGER.info(" [x] Received '" + message + "'"); 42 | } 43 | }; 44 | channel.basicConsume(queueName, true, consumer); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/iomode/EchoServer.java: -------------------------------------------------------------------------------- 1 | package com.waylau.essentialjava.iomode; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.io.PrintWriter; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | 10 | /** 11 | * “阻塞I/O”模式 12 | * 13 | * @author waylau.com 14 | * @date 2016年7月29日 15 | */ 16 | public class EchoServer { 17 | public static int DEFAULT_PORT = 7; 18 | 19 | public static void main(String[] args) throws IOException { 20 | 21 | int port; 22 | 23 | try { 24 | port = Integer.parseInt(args[0]); 25 | } catch (RuntimeException ex) { 26 | port = DEFAULT_PORT; 27 | } 28 | 29 | try ( 30 | ServerSocket serverSocket = 31 | new ServerSocket(port); 32 | Socket clientSocket = serverSocket.accept(); 33 | PrintWriter out = 34 | new PrintWriter(clientSocket.getOutputStream(), true); 35 | BufferedReader in = new BufferedReader( 36 | new InputStreamReader(clientSocket.getInputStream())); 37 | ) { 38 | String inputLine; 39 | while ((inputLine = in.readLine()) != null) { 40 | out.println(inputLine); 41 | } 42 | } catch (IOException e) { 43 | System.out.println("Exception caught when trying to listen" 44 | + " on port " + port 45 | + " or listening for a connection"); 46 | System.out.println(e.getMessage()); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/scripts/sse_real_time_broadcast.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Created by waylau.com on 2015/8/18. 4 | */ 5 | 6 | //判断浏览器是否支持 EventSource 7 | if (typeof (EventSource) !== "undefined") { 8 | var source = new EventSource("webapi/sse-chat"); 9 | 10 | // 当通往服务器的连接被打开 11 | source.onopen = function(event) { 12 | var ta = document.getElementById('response_text'); 13 | ta.value = '连接开启!'; 14 | }; 15 | 16 | // 当接收到消息。只能是事件名称是 message 17 | source.onmessage = function(event) { 18 | var ta = document.getElementById('response_text'); 19 | ta.value = ta.value + '\n' + event.data; 20 | }; 21 | 22 | //可以是任意命名的事件名称 23 | /* 24 | source.addEventListener('message', function(event) { 25 | var ta = document.getElementById('response_text'); 26 | ta.value = ta.value + '\n' + event.data; 27 | }); 28 | */ 29 | 30 | // 当错误发生 31 | source.onerror = function(event) { 32 | var ta = document.getElementById('response_text'); 33 | ta.value = ta.value + '\n' + "连接出错!"; 34 | 35 | }; 36 | } else { 37 | alert("Sorry, your browser does not support server-sent events"); 38 | } 39 | 40 | function send(message) { 41 | var xmlhttp; 42 | var name = document.getElementById('name_id').value; 43 | 44 | if (window.XMLHttpRequest) 45 | {// code for IE7+, Firefox, Chrome, Opera, Safari 46 | xmlhttp=new XMLHttpRequest(); 47 | } 48 | else 49 | {// code for IE6, IE5 50 | xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); 51 | } 52 | 53 | xmlhttp.open("POST","webapi/sse-chat?message=" + message +'&name=' + name ,true); 54 | xmlhttp.send(); 55 | } -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/java/com/waylau/rabbitmq/EmitLog.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rabbitmq; 2 | 3 | import com.rabbitmq.client.ConnectionFactory; 4 | import com.rabbitmq.client.Connection; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import com.rabbitmq.client.Channel; 10 | 11 | /** 12 | * 日志发送程序 13 | * 14 | * @author waylau.com 15 | * @date 2016年8月12日 16 | */ 17 | public class EmitLog { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(EmitLog.class); 20 | private static final String EXCHANGE_NAME = "logs"; 21 | 22 | public static void main(String[] argv) throws Exception { 23 | ConnectionFactory factory = new ConnectionFactory(); 24 | factory.setHost("localhost"); 25 | Connection connection = factory.newConnection(); 26 | Channel channel = connection.createChannel(); 27 | 28 | // 声明交换器和类型 29 | channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); 30 | 31 | String message = getMessage(argv); 32 | 33 | // 往交换器上发送消息 34 | channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8")); 35 | LOGGER.info(" [x] Sent '" + message + "'"); 36 | 37 | channel.close(); 38 | connection.close(); 39 | } 40 | 41 | private static String getMessage(String[] strings) { 42 | if (strings.length < 1) 43 | return "info: Hello World!"; 44 | return joinStrings(strings, " "); 45 | } 46 | 47 | private static String joinStrings(String[] strings, String delimiter) { 48 | int length = strings.length; 49 | if (length == 0) 50 | return ""; 51 | StringBuilder words = new StringBuilder(strings[0]); 52 | for (int i = 1; i < length; i++) { 53 | words.append(delimiter).append(strings[i]); 54 | } 55 | return words.toString(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/java/com/waylau/rabbitmq/ReceiveLogsTopic.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rabbitmq; 2 | 3 | import com.rabbitmq.client.*; 4 | 5 | import java.io.IOException; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * 日志接收程序 12 | * 13 | * @author Way Lau 14 | * @date 2016年8月13日 15 | */ 16 | public class ReceiveLogsTopic { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(ReceiveLogsTopic.class); 19 | private static final String EXCHANGE_NAME = "topic_logs"; 20 | 21 | public static void main(String[] argv) throws Exception { 22 | ConnectionFactory factory = new ConnectionFactory(); 23 | factory.setHost("localhost"); 24 | Connection connection = factory.newConnection(); 25 | Channel channel = connection.createChannel(); 26 | 27 | // 声明一个 topic 类型的 exchange 28 | channel.exchangeDeclare(EXCHANGE_NAME, "topic"); 29 | String queueName = channel.queueDeclare().getQueue(); 30 | 31 | if (argv.length < 1) { 32 | LOGGER.error("Usage: ReceiveLogsTopic [binding_key]..."); 33 | System.exit(1); 34 | } 35 | 36 | for (String bindingKey : argv) { 37 | channel.queueBind(queueName, EXCHANGE_NAME, bindingKey); 38 | } 39 | 40 | LOGGER.info(" [*] Waiting for messages. To exit press CTRL+C"); 41 | 42 | Consumer consumer = new DefaultConsumer(channel) { 43 | @Override 44 | public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, 45 | byte[] body) throws IOException { 46 | String message = new String(body, "UTF-8"); 47 | LOGGER.info(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'"); 48 | } 49 | }; 50 | channel.basicConsume(queueName, true, consumer); 51 | } 52 | } -------------------------------------------------------------------------------- /samples/chapter6/zk-registry-discovery/src/main/java/com/waylau/zk/registry/ZkServiceRegistry.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to https://waylau.com 3 | */ 4 | package com.waylau.zk.registry; 5 | 6 | import org.I0Itec.zkclient.ZkClient; 7 | 8 | import com.waylau.zk.Constant; 9 | 10 | /** 11 | * ZK Service Registry. 12 | * 13 | * @since 1.0.0 2018年5月16日 14 | * @author Way Lau 15 | */ 16 | public class ZkServiceRegistry implements ServiceRegistry { 17 | 18 | /** 19 | * ZK 地址. 20 | */ 21 | private String zkAddress = "localhost"; 22 | 23 | /** 24 | * ZK 客户端. 25 | */ 26 | private ZkClient zkClient; 27 | 28 | public void init() { 29 | zkClient = new ZkClient(zkAddress, 30 | Constant.ZK_SESSION_TIMEOUT, 31 | Constant.ZK_CONNECTION_TIMEOUT); 32 | 33 | System.out.println(">>>connect to zookeeper"); 34 | } 35 | 36 | @Override 37 | public void registry(String serviceName, String serviceAddress) { 38 | // 创建registry节点(持久) 39 | String registryPath = Constant.ZK_REGISTRY; 40 | if (!zkClient.exists(registryPath)) { 41 | zkClient.createPersistent(registryPath); 42 | 43 | System.out.println(">>>create registry node:" + registryPath); 44 | } 45 | 46 | // 创建service节点(持久) 47 | String servicePath = registryPath + "/" + serviceName; 48 | if (!zkClient.exists(servicePath)) { 49 | zkClient.createPersistent(servicePath); 50 | System.out.println(">>>create service node:" + servicePath); 51 | } 52 | 53 | // 创建address节点(临时) 54 | String addressPath = servicePath + "/address-"; 55 | String addressNode = zkClient.createEphemeralSequential(addressPath, serviceAddress); 56 | 57 | System.out.println(">>>create address node:" + addressNode); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/dependency-reduced-pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.waylau 5 | rabbitmq-demo 6 | rabbitmq-demo 7 | 1.0.0 8 | 9 | 10 | 11 | maven-compiler-plugin 12 | 3.2 13 | 14 | 1.7 15 | 1.7 16 | 17 | 18 | 19 | maven-shade-plugin 20 | 2.4.3 21 | 22 | 23 | package 24 | 25 | shade 26 | 27 | 28 | 29 | 30 | com.waylau.rabbitmq.Worker 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | junit 42 | junit 43 | 4.12 44 | test 45 | 46 | 47 | hamcrest-core 48 | org.hamcrest 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /samples/chapter3/rocketmq-demo/dependency-reduced-pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.waylau 5 | rocketmq-demo 6 | rocketmq-demo 7 | 1.0.0 8 | 9 | 10 | 11 | maven-compiler-plugin 12 | 3.2 13 | 14 | 1.7 15 | 1.7 16 | 17 | 18 | 19 | maven-shade-plugin 20 | 2.4.3 21 | 22 | 23 | package 24 | 25 | shade 26 | 27 | 28 | 29 | 30 | com.waylau.rocketmq.App 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | junit 42 | junit 43 | 4.12 44 | test 45 | 46 | 47 | hamcrest-core 48 | org.hamcrest 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /samples/chapter3/rocketmq-demo/src/main/java/com/waylau/rocketmq/Producer.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rocketmq; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.alibaba.rocketmq.client.exception.MQClientException; 7 | import com.alibaba.rocketmq.client.producer.DefaultMQProducer; 8 | import com.alibaba.rocketmq.client.producer.SendResult; 9 | import com.alibaba.rocketmq.common.message.Message; 10 | 11 | /** 12 | * 生产者,用于发送消息 13 | * 14 | * @author waylau.com 15 | * @date 2016年8月18日 16 | */ 17 | public class Producer { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(Producer.class); 20 | private static final String GROUP_NAME = "waylau_com_producer_gorup"; 21 | private static final String NAME_SERVER = "10.30.22.108:9876"; 22 | 23 | public static void main(String[] args) throws MQClientException, InterruptedException { 24 | 25 | // producerGroup 必须唯一 26 | DefaultMQProducer producer = new DefaultMQProducer(GROUP_NAME); 27 | 28 | // 设置 name server 地址 29 | producer.setNamesrvAddr(NAME_SERVER); 30 | 31 | // 在发送消息前,必须调用 start 方法来启动Producer,只需调用一次即可 32 | producer.start(); 33 | 34 | // 模拟发送十条数据 35 | for (int i = 0; i < 10; i++) { 36 | try { 37 | Message msg = new Message( 38 | // Message Topic 39 | "TopicTest", 40 | // Message Tag,对消息进行再归类,方便Consumer指定过滤条件在MQ服务器过滤 41 | "TagA", 42 | // Message Body,任何二进制形式的数据,需要Producer与Consumer协商好一致的序列化和反序列化方式 43 | ("Welcome to waylau.com! " + i).getBytes()); 44 | 45 | // 发送消息 46 | SendResult sendResult = producer.send(msg); 47 | 48 | LOGGER.info(sendResult.toString()); 49 | } catch (Exception e) { 50 | LOGGER.error(e.getMessage()); 51 | Thread.sleep(1000); 52 | } 53 | } 54 | 55 | // 在应用退出前,可以销毁Producer对象 56 | producer.shutdown(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.waylau.spring5 5 | jms-msg 6 | 1.0.0 7 | jms-msg 8 | jar 9 | 10 | waylau.com 11 | https://waylau.com 12 | 13 | 14 | 15 | UTF-8 16 | 4.3.30.RELEASE 17 | 4.12 18 | 5.15.3 19 | 20 | 21 | 22 | 23 | junit 24 | junit 25 | ${junit.version} 26 | test 27 | 28 | 29 | org.apache.activemq 30 | activemq-all 31 | ${activemq.version} 32 | 33 | 34 | org.springframework 35 | spring-core 36 | ${spring.version} 37 | 38 | 39 | org.springframework 40 | spring-aop 41 | ${spring.version} 42 | 43 | 44 | org.springframework 45 | spring-jms 46 | ${spring.version} 47 | 48 | 49 | org.springframework 50 | spring-test 51 | ${spring.version} 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/java/com/waylau/rest/resource/AlarmResource.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rest.resource; 2 | 3 | import java.io.IOException; 4 | import java.util.Date; 5 | 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.Path; 8 | import javax.ws.rs.Produces; 9 | import javax.ws.rs.core.MediaType; 10 | 11 | import org.glassfish.jersey.media.sse.EventOutput; 12 | import org.glassfish.jersey.media.sse.OutboundEvent; 13 | import org.glassfish.jersey.media.sse.SseFeature; 14 | 15 | import com.waylau.rest.bean.Alarm; 16 | 17 | /** 18 | * 说明:告警 SSE 19 | * 20 | * @author waylau.com 2015年9月8日 21 | */ 22 | @Path("alarm-events") 23 | public class AlarmResource { 24 | 25 | private EventOutput eventOutput = new EventOutput(); 26 | private OutboundEvent.Builder eventBuilder; 27 | private OutboundEvent event ; 28 | 29 | /** 30 | * 提供 SSE 事件输出通道的资源方法 31 | * @return eventOutput 32 | */ 33 | @GET 34 | @Produces(SseFeature.SERVER_SENT_EVENTS) 35 | public EventOutput getServerSentEvents() { 36 | 37 | // 不断循环执行 38 | while (true) { 39 | Alarm message = new Alarm(new Date(),Short.valueOf("11"), "10", "锅炉砸掉了"); 40 | 41 | eventBuilder = new OutboundEvent.Builder(); 42 | eventBuilder.id(message.getId()+""); 43 | eventBuilder.name("message");; 44 | eventBuilder.mediaType(MediaType.APPLICATION_JSON_TYPE); 45 | eventBuilder.data(Alarm.class, 46 | message ); // 推送服务器时间的信息给客户端 47 | event = eventBuilder.build(); 48 | try { 49 | eventOutput.write(event); 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | } finally { 53 | try { 54 | eventOutput.close(); 55 | return eventOutput; 56 | } catch (IOException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/java/com/waylau/rest/resource/SseResource.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rest.resource; 2 | 3 | import java.io.IOException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | 7 | import javax.ws.rs.GET; 8 | import javax.ws.rs.Path; 9 | import javax.ws.rs.Produces; 10 | 11 | import org.glassfish.jersey.media.sse.EventOutput; 12 | import org.glassfish.jersey.media.sse.OutboundEvent; 13 | import org.glassfish.jersey.media.sse.SseFeature; 14 | 15 | /** 16 | * 说明:SSE 发布-订阅模式 17 | * 18 | * @author waylau.com 2015年8月18日 19 | */ 20 | @Path("see-events") 21 | public class SseResource { 22 | 23 | private EventOutput eventOutput = new EventOutput(); 24 | private OutboundEvent.Builder eventBuilder; 25 | private OutboundEvent event ; 26 | 27 | /** 28 | * 提供 SSE 事件输出通道的资源方法 29 | * @return eventOutput 30 | */ 31 | @GET 32 | @Produces(SseFeature.SERVER_SENT_EVENTS) 33 | public EventOutput getServerSentEvents() { 34 | 35 | // 不断循环执行 36 | while (true) { 37 | SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 38 | String now = df.format(new Date()); //获取当前系统时间 39 | String message = "Server Time:" + now; 40 | System.out.println( message ); 41 | 42 | eventBuilder = new OutboundEvent.Builder(); 43 | eventBuilder.id(now); 44 | eventBuilder.name("message"); 45 | eventBuilder.data(String.class, 46 | message ); // 推送服务器时间的信息给客户端 47 | event = eventBuilder.build(); 48 | try { 49 | eventOutput.write(event); 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | } finally { 53 | try { 54 | eventOutput.close(); 55 | return eventOutput; 56 | } catch (IOException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/java/com/waylau/rabbitmq/ReceiveLogsDirect.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rabbitmq; 2 | 3 | import java.io.IOException; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import com.rabbitmq.client.AMQP; 9 | import com.rabbitmq.client.Channel; 10 | import com.rabbitmq.client.Connection; 11 | import com.rabbitmq.client.ConnectionFactory; 12 | import com.rabbitmq.client.Consumer; 13 | import com.rabbitmq.client.DefaultConsumer; 14 | import com.rabbitmq.client.Envelope; 15 | 16 | /** 17 | * 日志接收程序 18 | * 19 | * @author waylau.com 20 | * @date 2016年8月13日 21 | */ 22 | public class ReceiveLogsDirect { 23 | 24 | private static final Logger LOGGER = LoggerFactory.getLogger(ReceiveLogsDirect.class); 25 | 26 | private static final String EXCHANGE_NAME = "direct_logs"; 27 | 28 | public static void main(String[] argv) throws Exception { 29 | ConnectionFactory factory = new ConnectionFactory(); 30 | factory.setHost("localhost"); 31 | Connection connection = factory.newConnection(); 32 | Channel channel = connection.createChannel(); 33 | 34 | channel.exchangeDeclare(EXCHANGE_NAME, "direct"); 35 | String queueName = channel.queueDeclare().getQueue(); 36 | 37 | if (argv.length < 1) { 38 | LOGGER.error("Usage: ReceiveLogsDirect [info] [warning] [error]"); 39 | System.exit(1); 40 | } 41 | 42 | // 每一个我们感兴趣的 severity 创建一个新的绑定 43 | for (String severity : argv) { 44 | channel.queueBind(queueName, EXCHANGE_NAME, severity); 45 | } 46 | 47 | LOGGER.info(" [*] Waiting for messages. To exit press CTRL+C"); 48 | 49 | Consumer consumer = new DefaultConsumer(channel) { 50 | @Override 51 | public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, 52 | byte[] body) throws IOException { 53 | String message = new String(body, "UTF-8"); 54 | LOGGER.info(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'"); 55 | } 56 | }; 57 | channel.basicConsume(queueName, true, consumer); 58 | } 59 | } -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/java/com/waylau/rabbitmq/NewTask.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rabbitmq; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.rabbitmq.client.Channel; 7 | import com.rabbitmq.client.Connection; 8 | import com.rabbitmq.client.ConnectionFactory; 9 | import com.rabbitmq.client.MessageProperties; 10 | 11 | /** 12 | * 发送消息的任务发送程序 13 | * 14 | * @author Way Lau 15 | * @date 2016年8月12日 16 | */ 17 | public class NewTask { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(NewTask.class); 20 | private static final String TASK_QUEUE_NAME = "waylau_queue"; 21 | 22 | public static void main(String[] argv) throws Exception { 23 | 24 | // 初始化连接工厂 25 | ConnectionFactory factory = new ConnectionFactory(); 26 | 27 | // 设置连接的地址 28 | factory.setHost("localhost"); 29 | 30 | // 获得连接 31 | Connection connection = factory.newConnection(); 32 | 33 | // 创建 Channel 34 | Channel channel = connection.createChannel(); 35 | 36 | // 声明队列 37 | channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null); 38 | 39 | // 从控制台参数中,获取消息 40 | String message = getMessage(argv); 41 | 42 | // 发送消息。发送10条 43 | for (int i = 0; i < 10; i++) { 44 | String msg = message + " " + i; 45 | channel.basicPublish("", TASK_QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, 46 | (msg).getBytes("UTF-8")); 47 | 48 | LOGGER.info(" [x] Sent '" + msg + "'"); 49 | } 50 | 51 | channel.close(); 52 | connection.close(); 53 | } 54 | 55 | private static String getMessage(String[] strings) { 56 | if (strings.length < 1) 57 | return "Hello World!"; 58 | return joinStrings(strings, " "); 59 | } 60 | 61 | private static String joinStrings(String[] strings, String delimiter) { 62 | int length = strings.length; 63 | if (length == 0) 64 | return ""; 65 | StringBuilder words = new StringBuilder(strings[0]); 66 | for (int i = 1; i < length; i++) { 67 | words.append(delimiter).append(strings[i]); 68 | } 69 | return words.toString(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/java/com/waylau/rabbitmq/EmitLogDirect.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rabbitmq; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.rabbitmq.client.Channel; 7 | import com.rabbitmq.client.Connection; 8 | import com.rabbitmq.client.ConnectionFactory; 9 | 10 | /** 11 | * 日志发送程序 12 | * 13 | * @author waylau.com 14 | * @date 2016年8月13日 15 | */ 16 | public class EmitLogDirect { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(EmitLogDirect.class); 19 | private static final String EXCHANGE_NAME = "direct_logs"; 20 | 21 | public static void main(String[] argv) throws Exception { 22 | 23 | ConnectionFactory factory = new ConnectionFactory(); 24 | factory.setHost("localhost"); 25 | Connection connection = factory.newConnection(); 26 | Channel channel = connection.createChannel(); 27 | 28 | channel.exchangeDeclare(EXCHANGE_NAME, "direct"); 29 | 30 | String severity = getSeverity(argv); 31 | String message = getMessage(argv); 32 | 33 | // 为简化程序,这里 的 severity 是 inof、warning、error 中的一个 34 | channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8")); 35 | LOGGER.info(" [x] Sent '" + severity + "':'" + message + "'"); 36 | 37 | channel.close(); 38 | connection.close(); 39 | } 40 | 41 | private static String getSeverity(String[] strings) { 42 | if (strings.length < 1) 43 | return "info"; 44 | return strings[0]; 45 | } 46 | 47 | private static String getMessage(String[] strings) { 48 | if (strings.length < 2) 49 | return "Hello World!"; 50 | return joinStrings(strings, " ", 1); 51 | } 52 | 53 | private static String joinStrings(String[] strings, String delimiter, int startIndex) { 54 | int length = strings.length; 55 | if (length == 0) 56 | return ""; 57 | if (length < startIndex) 58 | return ""; 59 | StringBuilder words = new StringBuilder(strings[startIndex]); 60 | for (int i = startIndex + 1; i < length; i++) { 61 | words.append(delimiter).append(strings[i]); 62 | } 63 | return words.toString(); 64 | } 65 | } -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/java/com/waylau/rabbitmq/Worker.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rabbitmq; 2 | 3 | import com.rabbitmq.client.*; 4 | 5 | import java.io.IOException; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * 接收消息的工作者 12 | * 13 | * @author Way Lau 14 | * @date 2016年8月12日 15 | */ 16 | public class Worker { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(Worker.class); 19 | private static final String TASK_QUEUE_NAME = "waylau_queue"; 20 | 21 | public static void main(String[] argv) throws Exception { 22 | 23 | // 初始化连接工厂 24 | ConnectionFactory factory = new ConnectionFactory(); 25 | 26 | // 设置连接的地址 27 | factory.setHost("localhost"); 28 | 29 | // 获得连接 30 | final Connection connection = factory.newConnection(); 31 | 32 | // 创建 Channel 33 | final Channel channel = connection.createChannel(); 34 | 35 | // 声明队列 36 | channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null); 37 | channel.basicQos(1); 38 | 39 | LOGGER.info(" [*] Waiting for messages. To exit press CTRL+C"); 40 | 41 | // 消费者 42 | final Consumer consumer = new DefaultConsumer(channel) { 43 | @Override 44 | public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, 45 | byte[] body) throws IOException { 46 | String message = new String(body, "UTF-8"); 47 | 48 | LOGGER.info(" [x] Received '" + message + "'"); 49 | try { 50 | doWork(message); 51 | } finally { 52 | LOGGER.info(" [x] Done"); 53 | 54 | // 确认消息 55 | channel.basicAck(envelope.getDeliveryTag(), false); 56 | } 57 | } 58 | }; 59 | 60 | // 取消 autoAck 61 | channel.basicConsume(TASK_QUEUE_NAME, false, consumer); 62 | } 63 | 64 | private static void doWork(String task) { 65 | for (char ch : task.toCharArray()) { 66 | if (ch == '.') { 67 | try { 68 | Thread.sleep(1000); 69 | } catch (InterruptedException _ignored) { 70 | Thread.currentThread().interrupt(); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /samples/chapter3/activemq-demo/src/main/java/com/waylau/activemq/Producer.java: -------------------------------------------------------------------------------- 1 | package com.waylau.activemq; 2 | 3 | import javax.jms.Connection; 4 | import javax.jms.ConnectionFactory; 5 | import javax.jms.Destination; 6 | import javax.jms.JMSException; 7 | import javax.jms.MessageProducer; 8 | import javax.jms.Session; 9 | import javax.jms.TextMessage; 10 | 11 | import org.apache.activemq.ActiveMQConnection; 12 | import org.apache.activemq.ActiveMQConnectionFactory; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | /** 17 | * MQ 生产者 18 | * 19 | * @author Way Lau 20 | * @date 2016年8月7日 21 | */ 22 | public class Producer { 23 | 24 | private static final Logger LOGGER = LoggerFactory.getLogger(Producer.class); 25 | private static final String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL; 26 | private static final String SUBJECT = "waylau-queue"; 27 | 28 | public static void main(String[] args) throws JMSException { 29 | 30 | // 初始化连接工厂 31 | ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL); 32 | 33 | // 获得连接 34 | Connection conn = connectionFactory.createConnection(); 35 | 36 | // 启动连接 37 | conn.start(); 38 | 39 | // 创建Session,第一个参数表示会话是否在事务中执行,第二个参数设定会话的应答模式 40 | Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); 41 | 42 | // 创建队列 43 | Destination dest = session.createQueue(SUBJECT); 44 | 45 | //createTopic方法用来创建Topic 46 | //session.createTopic("TOPIC"); 47 | 48 | // 通过 session 可以创建消息的生产者 49 | MessageProducer producer = session.createProducer(dest); 50 | for (int i=0;i<10;i++) { 51 | 52 | //初始化一个 MQ 消息 53 | TextMessage message = session.createTextMessage("Welcome to waylau.com " + i); 54 | 55 | //发送消息 56 | producer.send(message); 57 | 58 | LOGGER.info("send message {}", i); 59 | } 60 | 61 | //关闭 MQ 连接 62 | conn.close(); 63 | } 64 | } -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/java/com/waylau/spring/jms/queue/ProducerServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms.queue; 2 | 3 | import javax.jms.Destination; 4 | import javax.jms.JMSException; 5 | import javax.jms.Message; 6 | import javax.jms.Session; 7 | import javax.jms.TextMessage; 8 | 9 | import org.springframework.jms.core.JmsTemplate; 10 | import org.springframework.jms.core.MessageCreator; 11 | 12 | /** 13 | * Producer Service. 14 | * 15 | * @since 1.0.0 2018年4月15日 16 | * @author Way Lau 17 | */ 18 | public class ProducerServiceImpl implements ProducerService { 19 | 20 | private JmsTemplate jmsTemplate; 21 | 22 | /** 23 | * 向指定队列发送消息 24 | */ 25 | public void sendMessage(Destination destination, final String msg) { 26 | System.out.println("ProducerService向队列" 27 | + destination.toString() + "发送了消息:\t" + msg); 28 | jmsTemplate.send(destination, new MessageCreator() { 29 | public Message createMessage(Session session) throws JMSException { 30 | return session.createTextMessage(msg); 31 | } 32 | }); 33 | } 34 | 35 | /** 36 | * 向默认队列发送消息 37 | */ 38 | public void sendMessage(final String msg) { 39 | String destination = jmsTemplate.getDefaultDestination().toString(); 40 | System.out.println("ProducerService向队列" 41 | + destination + "发送了消息:\t" + msg); 42 | jmsTemplate.send(new MessageCreator() { 43 | public Message createMessage(Session session) throws JMSException { 44 | return session.createTextMessage(msg); 45 | } 46 | }); 47 | 48 | } 49 | 50 | public void sendMessage(Destination destination, 51 | final String msg, final Destination response) { 52 | System.out.println("ProducerService向队列" 53 | + destination + "发送了消息:\t" + msg); 54 | jmsTemplate.send(destination, new MessageCreator() { 55 | public Message createMessage(Session session) throws JMSException { 56 | TextMessage textMessage = session.createTextMessage(msg); 57 | textMessage.setJMSReplyTo(response); 58 | return textMessage; 59 | } 60 | }); 61 | } 62 | 63 | public void setJmsTemplate(JmsTemplate jmsTemplate) { 64 | this.jmsTemplate = jmsTemplate; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/java/com/waylau/rest/bean/Alarm.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rest.bean; 2 | 3 | import java.util.Date; 4 | 5 | import javax.xml.bind.annotation.XmlRootElement; 6 | 7 | /** 8 | * 说明:告警 对应 d_alarm 9 | * 10 | * @author waylau.com 2015年4月18日 11 | */ 12 | @XmlRootElement 13 | public class Alarm { 14 | 15 | private Integer id; 16 | private Date alarmTime; 17 | private Date alarmEndTime; 18 | private Short measurePointId; 19 | private String alarmCode; 20 | private String alarmDetail; 21 | private Short eId; 22 | 23 | public Alarm() { 24 | } 25 | 26 | public Alarm(Date alarmTime, Short measurePointId, String alarmCode, String alarmDetail) { 27 | this.alarmTime = alarmTime; 28 | this.measurePointId = measurePointId; 29 | this.alarmCode = alarmCode; 30 | this.alarmDetail = alarmDetail; 31 | } 32 | 33 | 34 | public Integer getId() { 35 | return this.id; 36 | } 37 | 38 | public void setId(Integer id) { 39 | this.id = id; 40 | } 41 | public Date getAlarmTime() { 42 | return this.alarmTime; 43 | } 44 | 45 | public void setAlarmTime(Date alarmTime) { 46 | this.alarmTime = alarmTime; 47 | } 48 | 49 | public String getAlarmCode() { 50 | return this.alarmCode; 51 | } 52 | 53 | public void setAlarmCode(String alarmCode) { 54 | this.alarmCode = alarmCode; 55 | } 56 | 57 | public String getAlarmDetail() { 58 | return this.alarmDetail; 59 | } 60 | 61 | public void setAlarmDetail(String alarmDetail) { 62 | this.alarmDetail = alarmDetail; 63 | } 64 | 65 | public Short geteId() { 66 | return eId; 67 | } 68 | 69 | public void seteId(Short eId) { 70 | this.eId = eId; 71 | } 72 | public Date getAlarmEndTime() { 73 | return alarmEndTime; 74 | } 75 | 76 | public void setAlarmEndTime(Date alarmEndTime) { 77 | this.alarmEndTime = alarmEndTime; 78 | } 79 | 80 | public Short getMeasurePointId() { 81 | return measurePointId; 82 | } 83 | 84 | public void setMeasurePointId(Short measurePointId) { 85 | this.measurePointId = measurePointId; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /samples/chapter3/activemq-demo/src/main/java/com/waylau/activemq/Consumer.java: -------------------------------------------------------------------------------- 1 | package com.waylau.activemq; 2 | 3 | import javax.jms.Connection; 4 | import javax.jms.ConnectionFactory; 5 | import javax.jms.Destination; 6 | import javax.jms.JMSException; 7 | import javax.jms.Message; 8 | import javax.jms.MessageConsumer; 9 | import javax.jms.MessageListener; 10 | import javax.jms.Session; 11 | import javax.jms.TextMessage; 12 | 13 | import org.apache.activemq.ActiveMQConnection; 14 | import org.apache.activemq.ActiveMQConnectionFactory; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | /** 19 | * MQ 消费者 20 | * 21 | * @author Way Lau 22 | * @date 2016年8月7日 23 | */ 24 | public class Consumer implements MessageListener { 25 | 26 | private static final Logger LOGGER = LoggerFactory.getLogger(Consumer.class); 27 | private static final String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL; 28 | private static final String SUBJECT = "waylau-queue"; 29 | 30 | public static void main(String[] args) throws JMSException { 31 | 32 | //初始化 ConnectionFactory 33 | ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL); 34 | 35 | //创建 MQ 连接 36 | Connection conn = connectionFactory.createConnection(); 37 | //启动连接 38 | conn.start(); 39 | 40 | //创建会话 41 | Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); 42 | 43 | //通过会话创建目标 44 | Destination dest = session.createQueue(SUBJECT); 45 | 46 | //创建 MQ 消息的消费者 47 | MessageConsumer consumer = session.createConsumer(dest); 48 | 49 | //初始化 MessageListener 50 | Consumer me = new Consumer(); 51 | 52 | //给消费者设定监听对象 53 | consumer.setMessageListener(me); 54 | } 55 | 56 | @Override 57 | public void onMessage(Message message) { 58 | TextMessage txtMessage = (TextMessage)message; 59 | try { 60 | LOGGER.info ("get message " + txtMessage.getText()); 61 | } catch (JMSException e) { 62 | LOGGER.error("error {}", e); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/java/com/waylau/rabbitmq/EmitLogTopic.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rabbitmq; 2 | 3 | import com.rabbitmq.client.ConnectionFactory; 4 | import com.rabbitmq.client.Connection; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import com.rabbitmq.client.Channel; 10 | 11 | /** 12 | * 日志发送程序 13 | * 14 | * @author Way Lau 15 | * @date 2016年8月13日 16 | */ 17 | public class EmitLogTopic { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(EmitLogTopic.class); 20 | private static final String EXCHANGE_NAME = "topic_logs"; 21 | 22 | public static void main(String[] argv) { 23 | Connection connection = null; 24 | Channel channel = null; 25 | try { 26 | ConnectionFactory factory = new ConnectionFactory(); 27 | factory.setHost("localhost"); 28 | 29 | connection = factory.newConnection(); 30 | channel = connection.createChannel(); 31 | 32 | // 声明一个 topic 类型的 exchange 33 | channel.exchangeDeclare(EXCHANGE_NAME, "topic"); 34 | 35 | String routingKey = getRouting(argv); 36 | String message = getMessage(argv); 37 | 38 | channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("UTF-8")); 39 | LOGGER.info(" [x] Sent '" + routingKey + "':'" + message + "'"); 40 | 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | } finally { 44 | if (connection != null) { 45 | try { 46 | connection.close(); 47 | } catch (Exception ignore) { 48 | } 49 | } 50 | } 51 | } 52 | 53 | private static String getRouting(String[] strings) { 54 | if (strings.length < 1) 55 | return "anonymous.info"; 56 | return strings[0]; 57 | } 58 | 59 | private static String getMessage(String[] strings) { 60 | if (strings.length < 2) 61 | return "Hello World!"; 62 | return joinStrings(strings, " ", 1); 63 | } 64 | 65 | private static String joinStrings(String[] strings, String delimiter, int startIndex) { 66 | int length = strings.length; 67 | if (length == 0) 68 | return ""; 69 | if (length < startIndex) 70 | return ""; 71 | StringBuilder words = new StringBuilder(strings[startIndex]); 72 | for (int i = startIndex + 1; i < length; i++) { 73 | words.append(delimiter).append(strings[i]); 74 | } 75 | return words.toString(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /samples/chapter2/javase-rest/src/main/java/com/waylau/rest/resource/MyResource.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rest.resource; 2 | 3 | import javax.ws.rs.Consumes; 4 | import javax.ws.rs.DELETE; 5 | import javax.ws.rs.GET; 6 | import javax.ws.rs.Path; 7 | import javax.ws.rs.PathParam; 8 | import javax.ws.rs.Produces; 9 | import javax.ws.rs.QueryParam; 10 | import javax.ws.rs.core.MediaType; 11 | 12 | import com.waylau.rest.bean.MyBean; 13 | 14 | /** 15 | * 根资源 (暴露在"myresource"路径) 16 | * 17 | * @author waylau.com 18 | * 2015-3-1 19 | */ 20 | @Path("myresource") 21 | public class MyResource { 22 | 23 | /** 24 | * 方法处理 HTTP GET 请求。返回的对象以"text/plain"媒体类型 25 | * 给客户端 26 | * 27 | * @return String 以 text/plain 形式响应 28 | */ 29 | @GET 30 | @Produces(MediaType.TEXT_PLAIN) 31 | public String getIt() { 32 | return "Got it!"; 33 | } 34 | 35 | 36 | /** 37 | * 方法处理 HTTP GET 请求。返回的对象以"application/xml"媒体类型 38 | * 给客户端 39 | * 40 | * @return MyPojo 以 application/xml 形式响应 41 | */ 42 | @GET 43 | @Path("pojoxml") 44 | @Produces(MediaType.APPLICATION_XML) 45 | public MyBean getPojoXml() { 46 | MyBean pojo = new MyBean(); 47 | pojo.setName("waylau.com"); 48 | pojo.setAge(28); 49 | return pojo; 50 | } 51 | 52 | /** 53 | * 方法处理 HTTP GET 请求。返回的对象以"application/json"媒体类型 54 | * 给客户端 55 | * 56 | * @return MyPojo 以 application/json 形式响应 57 | */ 58 | @GET 59 | @Path("pojojson") 60 | @Produces(MediaType.APPLICATION_JSON) 61 | public MyBean getPojoJson() { 62 | MyBean pojo = new MyBean(); 63 | pojo.setName("waylau.com"); 64 | pojo.setAge(28); 65 | return pojo; 66 | } 67 | 68 | @DELETE 69 | @Path("pojojson") 70 | @Produces(MediaType.TEXT_PLAIN) 71 | public String deletePojoJson(@QueryParam("name") String name ) { 72 | return "You delete " + name; 73 | } 74 | 75 | @DELETE 76 | @Path("pojojson/{name}") 77 | @Produces(MediaType.TEXT_PLAIN) 78 | public String deletePojoJsonPath(@PathParam("name") String name ) { 79 | return "You delete " + name; 80 | } 81 | 82 | //下面的例子是无法接收到 body 作为参数的反面例子 83 | // @DELETE 84 | // @Path("pojojson") 85 | // @Consumes(MediaType.APPLICATION_JSON) 86 | // public String deletePojoJsonBody(@QueryParam("name") String name ) { 87 | // return "You delete " + name; 88 | // } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/java/com/waylau/rabbitmq/RPCServer.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rabbitmq; 2 | 3 | import com.rabbitmq.client.ConnectionFactory; 4 | import com.rabbitmq.client.Connection; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import com.rabbitmq.client.Channel; 10 | import com.rabbitmq.client.QueueingConsumer; 11 | import com.rabbitmq.client.AMQP.BasicProperties; 12 | 13 | /** 14 | * RPC 服务器 15 | * 16 | * @author Way Lau 17 | * @date 2016年8月13日 18 | */ 19 | public class RPCServer { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(RPCServer.class); 22 | private static final String RPC_QUEUE_NAME = "rpc_queue"; 23 | 24 | private static int fib(int n) { 25 | if (n == 0) 26 | return 0; 27 | if (n == 1) 28 | return 1; 29 | return fib(n - 1) + fib(n - 2); 30 | } 31 | 32 | public static void main(String[] argv) { 33 | Connection connection = null; 34 | Channel channel = null; 35 | try { 36 | ConnectionFactory factory = new ConnectionFactory(); 37 | factory.setHost("localhost"); 38 | 39 | connection = factory.newConnection(); 40 | channel = connection.createChannel(); 41 | 42 | channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null); 43 | 44 | channel.basicQos(1); 45 | 46 | QueueingConsumer consumer = new QueueingConsumer(channel); 47 | channel.basicConsume(RPC_QUEUE_NAME, false, consumer); 48 | 49 | LOGGER.info(" [x] Awaiting RPC requests"); 50 | 51 | while (true) { 52 | String response = null; 53 | 54 | QueueingConsumer.Delivery delivery = consumer.nextDelivery(); 55 | 56 | BasicProperties props = delivery.getProperties(); 57 | BasicProperties replyProps = new BasicProperties.Builder().correlationId(props.getCorrelationId()) 58 | .build(); 59 | 60 | try { 61 | String message = new String(delivery.getBody(), "UTF-8"); 62 | int n = Integer.parseInt(message); 63 | 64 | LOGGER.info(" [.] fib(" + message + ")"); 65 | response = "" + fib(n); 66 | } catch (Exception e) { 67 | LOGGER.error(" [.] " + e.toString()); 68 | response = ""; 69 | } finally { 70 | channel.basicPublish("", props.getReplyTo(), replyProps, response.getBytes("UTF-8")); 71 | 72 | channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); 73 | } 74 | } 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | } finally { 78 | if (connection != null) { 79 | try { 80 | connection.close(); 81 | } catch (Exception ignore) { 82 | } 83 | } 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /samples/chapter2/game-sesrver/aws_game_server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by waylau on 2016/8/6. 3 | */ 4 | var AWS = require('aws-sdk'); 5 | exports.handler = function (event, context) { 6 | console.log("Received data as:", event); 7 | var ec2 = new AWS.EC2({region: 'ap-northeast-1'}); 8 | var params = { 9 | ImageId: 'ami-29160d47', 10 | InstanceType: 't2.micro', 11 | KeyName: 'Tech-labs', 12 | SecurityGroupIds: ['sg-d0aa1bb4'], 13 | IamInstanceProfile: {Name: 'EC2-Admin'}, 14 | MinCount: 1, 15 | MaxCount: 1 16 | }; 17 | 18 | // 创建实例 19 | ec2.runInstances(params, function (err, data) { 20 | if (err) { 21 | console.log("Could not create instance", err); 22 | context.fail(err); 23 | } 24 | var instanceId = data.Instances[0].InstanceId; 25 | console.log("Created instance", instanceId); 26 | 27 | //TODO(待完成)持久化示例 id 并设置状态为启动状态 28 | context.succeed(instanceId); 29 | }); 30 | }; 31 | 32 | 33 | var AWS = require('aws-sdk'); 34 | exports.handler = function (event, context) { 35 | console.log("Received data as:", event); 36 | var instanceId = event.instanceId; 37 | var region = event.region; 38 | var publicIp = event.publicIp; 39 | var version = event.version; 40 | // ... 41 | 42 | //TODO(待完成)检测示例 id 并更新示例状态到线上 43 | }; 44 | 45 | 46 | var AWS = require('aws-sdk'); 47 | exports.handler = function (event, context) { 48 | console.log("Received data as:", event); 49 | var message = JSON.parse(event.Records[0].Sns.Message); 50 | var region = event.Records[0].EventSubscriptionArn.split(":")[3]; 51 | console.log("Need to terminate the server in region:", region); 52 | var ec2 = new AWS.EC2({region: region}); 53 | console.log("Need to terminate the server:", message); 54 | var instanceId = message.Trigger.Dimensions[0].value; 55 | console.log("Need to terminate the server:", instanceId); 56 | 57 | //TODO(待完成)检测实例是否可被从 DynamoDB 终止,而后更新示例为终止状态 58 | var params = {InstanceIds: [instanceId]}; 59 | 60 | //终止实例 61 | ec2.terminateInstances(params, function (err, data) { 62 | if (err) { 63 | console.log("Could not terminate instance", err); 64 | 65 | //TODO 回滚终止实例 66 | context.fail(err); 67 | } 68 | for (var i in data.TerminatingInstances) { 69 | var instance = data.TerminatingInstances[i]; 70 | console.log('TERM:\t' + instance.InstanceId); 71 | 72 | //TODO 删除终止的实例 73 | } 74 | context.succeed(data.TerminatingInstances); 75 | }); 76 | }; -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/src/main/java/com/waylau/rabbitmq/RPCClient.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rabbitmq; 2 | 3 | import com.rabbitmq.client.ConnectionFactory; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.Channel; 6 | import com.rabbitmq.client.QueueingConsumer; 7 | import com.rabbitmq.client.AMQP.BasicProperties; 8 | import java.util.UUID; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * RPC 客户端 15 | * 16 | * @author Way Lau 17 | * @date 2016年8月13日 18 | */ 19 | public class RPCClient { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(RPCClient.class); 22 | private Connection connection; 23 | private Channel channel; 24 | private String requestQueueName = "rpc_queue"; 25 | private String replyQueueName; 26 | private QueueingConsumer consumer; 27 | 28 | public RPCClient() throws Exception { 29 | ConnectionFactory factory = new ConnectionFactory(); 30 | factory.setHost("localhost"); 31 | connection = factory.newConnection(); 32 | channel = connection.createChannel(); 33 | 34 | replyQueueName = channel.queueDeclare().getQueue(); 35 | consumer = new QueueingConsumer(channel); 36 | channel.basicConsume(replyQueueName, true, consumer); 37 | } 38 | 39 | public String call(String message) throws Exception { 40 | String response = null; 41 | String corrId = UUID.randomUUID().toString(); 42 | 43 | // 每个请求设置唯一值 correlationId 44 | BasicProperties props = new BasicProperties.Builder().correlationId(corrId).replyTo(replyQueueName).build(); 45 | 46 | channel.basicPublish("", requestQueueName, props, message.getBytes("UTF-8")); 47 | 48 | while (true) { 49 | QueueingConsumer.Delivery delivery = consumer.nextDelivery(); 50 | 51 | // 检查 correlationId 属性,是否和它请求的值一致 52 | if (delivery.getProperties().getCorrelationId().equals(corrId)) { 53 | response = new String(delivery.getBody(), "UTF-8"); 54 | break; 55 | } 56 | } 57 | 58 | return response; 59 | } 60 | 61 | public void close() throws Exception { 62 | connection.close(); 63 | } 64 | 65 | public static void main(String[] argv) { 66 | RPCClient fibonacciRpc = null; 67 | String response = null; 68 | try { 69 | fibonacciRpc = new RPCClient(); 70 | 71 | LOGGER.info(" [x] Requesting fib(30)"); 72 | response = fibonacciRpc.call("30"); 73 | LOGGER.info(" [.] Got '" + response + "'"); 74 | } catch (Exception e) { 75 | e.printStackTrace(); 76 | } finally { 77 | if (fibonacciRpc != null) { 78 | try { 79 | fibonacciRpc.close(); 80 | } catch (Exception ignore) { 81 | } 82 | } 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/java/com/waylau/rest/resource/SseChatResource.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rest.resource; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import javax.inject.Singleton; 9 | import javax.ws.rs.DefaultValue; 10 | import javax.ws.rs.GET; 11 | import javax.ws.rs.POST; 12 | import javax.ws.rs.Path; 13 | import javax.ws.rs.Produces; 14 | import javax.ws.rs.QueryParam; 15 | import javax.ws.rs.core.MediaType; 16 | 17 | import org.glassfish.jersey.media.sse.EventOutput; 18 | import org.glassfish.jersey.media.sse.OutboundEvent; 19 | import org.glassfish.jersey.media.sse.SseBroadcaster; 20 | import org.glassfish.jersey.media.sse.SseFeature; 21 | 22 | /** 23 | * 说明:SSE 广播模式-聊天 24 | * 25 | * @author waylau.com 2015年8月18日 26 | */ 27 | @Singleton 28 | @Path("sse-chat") 29 | public class SseChatResource { 30 | 31 | private SseBroadcaster broadcaster = new SseBroadcaster(); 32 | 33 | private Map eventOutputMap = new HashMap();// 存储连接数 34 | 35 | private long countId = 0; 36 | /** 37 | * 提供 SSE 事件输出通道的资源方法 38 | * @return eventOutput 39 | */ 40 | @GET 41 | @Produces(SseFeature.SERVER_SENT_EVENTS) 42 | public EventOutput listenToBroadcast() { 43 | EventOutput eventOutput = new EventOutput(); 44 | this.eventOutputMap.put(countId+"", eventOutput); 45 | this.broadcaster.add(eventOutput); 46 | this.countId ++; 47 | System.out.println( "countId:"+countId + "加入,共计:"+ eventOutputMap.size()); 48 | return eventOutput; 49 | } 50 | 51 | /** 52 | * 提供 写入 SSE 事件通道的资源方法 53 | * @param message 54 | * @param name 55 | */ 56 | @POST 57 | @Produces(MediaType.TEXT_PLAIN) 58 | public void broadcastMessage(@DefaultValue("waylau.com") @QueryParam("message") String message, 59 | @DefaultValue("waylau") @QueryParam("name") String name) { 60 | SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 61 | String now = df.format(new Date()); //获取当前系统时间 62 | message = now +":"+ name +":"+ message; // 发送的消息带上当前的时间 63 | 64 | OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder(); 65 | OutboundEvent event = eventBuilder.name("message") 66 | .mediaType(MediaType.TEXT_PLAIN_TYPE) 67 | .data(String.class, message) 68 | .build(); 69 | 70 | // 发送广播 71 | broadcaster.broadcast(event); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /samples/chapter3/rocketmq-demo/src/main/java/com/waylau/rocketmq/Consumer.java: -------------------------------------------------------------------------------- 1 | package com.waylau.rocketmq; 2 | 3 | import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer; 4 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 5 | import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; 6 | import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently; 7 | import com.alibaba.rocketmq.client.exception.MQClientException; 8 | import com.alibaba.rocketmq.common.consumer.ConsumeFromWhere; 9 | import com.alibaba.rocketmq.common.message.MessageConst; 10 | import com.alibaba.rocketmq.common.message.MessageExt; 11 | 12 | import java.util.List; 13 | 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | /** 18 | * 消费者,接收和处理消息 19 | * 20 | * @author waylau.com 21 | * @date 2016年8月18日 22 | */ 23 | public class Consumer { 24 | 25 | private static final Logger LOGGER = LoggerFactory.getLogger(Consumer.class); 26 | private static final String GROUP_NAME = "waylau_com_consumer_gorup"; 27 | private static final String NAME_SERVER = "10.30.22.108:9876"; 28 | 29 | public static void main(String[] args) throws InterruptedException, MQClientException { 30 | 31 | // consumerGroup 必须唯一 32 | DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(GROUP_NAME); 33 | 34 | // 设置 Consumer 第一次启动是从队列头部开始消费还是队列尾部开始消费 35 | // 如果非第一次启动,那么按照上次消费的位置继续消费 36 | consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); 37 | 38 | // 设置 name server 地址 39 | consumer.setNamesrvAddr(NAME_SERVER); 40 | 41 | // 订阅 topic 42 | consumer.subscribe("TopicTest", "*"); 43 | 44 | // 监听消息 45 | consumer.registerMessageListener(new MessageListenerConcurrently() { 46 | 47 | @Override 48 | public ConsumeConcurrentlyStatus consumeMessage( 49 | List msgs, ConsumeConcurrentlyContext context) { 50 | LOGGER.info(Thread.currentThread().getName() 51 | + " Receive New Messages: " + new String(msgs.get(0).getBody())); 52 | 53 | /* 54 | // 跳过非重要消息。当某个队列的消息数堆积到 100000 条以上, 55 | // 则尝试丢弃部分或全部消息,这样就可以快速追上发送消息的速度 56 | long offset = msgs.get(0).getQueueOffset(); 57 | String maxOffset = msgs.get(0).getProperty(MessageConst.PROPERTY_MAX_OFFSET); 58 | long diff = Long.parseLong(maxOffset) - offset; 59 | if (diff > 100000) { 60 | 61 | // TODO 消息堆积情况的特殊处理 62 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 63 | } 64 | */ 65 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 66 | } 67 | }); 68 | 69 | consumer.start(); 70 | 71 | LOGGER.info("Consumer Started."); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | 6 | com.waylau 7 | sse-real-time-web 8 | war 9 | 1.0-SNAPSHOT 10 | sse-real-time-web 11 | 12 | 13 | sse-real-time-web 14 | 15 | 16 | org.apache.maven.plugins 17 | maven-compiler-plugin 18 | 2.5.1 19 | true 20 | 21 | 1.7 22 | 1.7 23 | 24 | 25 | 26 | 32 | 33 | org.eclipse.jetty 34 | jetty-maven-plugin 35 | ${jetty.version} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.glassfish.jersey 44 | jersey-bom 45 | ${jersey.version} 46 | pom 47 | import 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.glassfish.jersey.containers 55 | jersey-container-servlet 56 | 57 | 58 | org.glassfish.jersey.media 59 | jersey-media-multipart 60 | 61 | 62 | org.glassfish.jersey.media 63 | jersey-media-moxy 64 | 65 | 66 | 71 | 72 | org.glassfish.jersey.media 73 | jersey-media-sse 74 | 75 | 76 | 77 | 2.23.2 78 | 2.2 79 | 9.3.12.v20160915 80 | UTF-8 81 | 82 | -------------------------------------------------------------------------------- /docs/Preface.md: -------------------------------------------------------------------------------- 1 | # 前言 2 | 3 | 4 | ## 写作背景 5 | 6 |   我一直想写一本关于分布式系统方面的书。一方面是想把个人多年工作中涉及的分布式技术做一下总结,另一方面也想把个人的经验分享给广大的读者朋友。由于我的开发工作大都以Java为主,所以一开始的主题设想是"分布式Java",书也以开源方式发布在互联网上(网址为https://github.com/waylau/distributed-java)。 7 | 8 |   后来,陈晓猛编辑看到了这本开源书,以及我关于分布式系统方面的博文,问我是否有兴趣出版分布式相关题材的图书。当然书的内容不仅仅是"分布式Java"。 9 | 10 |   对于出书一事,我犹豫良久。首先,本身工作挺忙,实在无暇顾及其他;其次,虽然我之前写过超过一打的书籍(可见https://waylau.com/books/),但多是开源电子书,时间、内容方面自然也就不会有太多约束,几乎是"想写就写,没有时间就不写",这个跟正式出版还是存在比较大的差异的;最后,这本书涉及面相对较广,需要查阅大量资料,实在是太耗费精力。 11 | 12 |   但陈晓猛编辑还是鼓励我能够去尝试做这个事情。思索再三,于是我便答应。当然,最后这本书还是在规定时间内完成了。它几乎耗尽了我写作期间所有的业余和休息时间。 13 | 14 |   "不积跬步,无以至千里;不积小流,无以成江海。"虽然整本书从构思到编写完成的时间不足一年,但书中的大部分知识点,却是我在多年的学习、工作中积累下来的。之所以能够实现快速写作,一方面是做了比较严格的时间管理,另一方面也得益于我多年坚持写博客和开源书的习惯 15 | 16 | ## 内容简介 17 | 18 |   本书分为三大部分,即分布式系统基础理论、分布式系统常用技术以及经典的分布式系统案例分析。第一部分为第1章和第2章,主要介绍分布式系统基础理论知识,总结一些在设计分布式系统时需要考虑的范式、知识点以及可能会面临的问题。 第二部分为第3章到第8章,主要列举了在分布式系统应用中经常用到的一些主流技术,并介绍这些技术的作用和用法。第三部分为第9章和第10章,选取了以淘宝网和Twitter为代表的国内外知名互联网企业的大型分布式系统案例,分析其架构设计以及演变过程。 19 | 20 | * 第1章介绍分布式系统基础理论知识,总结一些在设计分布式系统时需要考虑的范式、知识点以及可能会面临的问题,其中包括线程、通信、一致性、容错性、CAP理论、安全性和并发等相关内容。 21 | * 第2章详细介绍分布式系统的架构体系,包括传统的基于对象的体系结构、SOA,也包括最近比较火的RESTful风格架构、微服务、容器技术、Serverless架构等。 22 | * 第3章介绍常用的分布式消息服务框架,包括Apache ActiveMQ、RabbitMQ、RocketMQ、Apache Kafka等。 23 | * 第4章介绍分布式计算理论和应用框架方面的内容,包括MapReduce、Apache Hadoop、Apache Spark、Apache Mesos 等。 24 | * 第5章介绍分布式存储理论和应用框架方面的内容,包括Bigtable、Apache HBase、Apache Cassandra、Memcached、Redis、MongoDB等。 25 | * 第6章介绍分布式监控方面常用的技术,包括Nagios、Zabbix、Consul、ZooKeeper等。 26 | * 第7章介绍常用的分布式版本控制工具,包括Bazaar、Mercurial、Git等。 27 | * 第8章介绍RESTful API、微服务及容器相关的技术,着重介绍Jersey、Spring Boot、Docker等技术的应用。 28 | * 第9章和第10章分别介绍以淘宝网和Twitter为代表的国内外知名互联网企业的大型分布式系统案例,分析其架构设计以及演变过程。 29 | 30 | 31 | ## 源代码 32 | 33 |   本书提供源代码下载,下载地址为 。 34 | 35 | ## 勘误和交流 36 | 37 |   本书如有勘误,会在 上进行发布。由于笔者能力有限,时间仓促,难免错漏,欢迎读者批评指正。读者也可以到博文视点官网的本书页面进行交流(www.broadview.com.cn/30771)。 38 | 39 |   您也可以直接联系我: 40 | 41 | * 博客: 42 | * 邮箱: 43 | * 微博: 44 | * 开源: 45 | 46 | ## 致谢 47 | 48 |   首先,感谢电子工业出版社博文视点公司的陈晓猛编辑,是您鼓励我将本书付诸成册,并在我写作过程中审阅了大量稿件,给予了我很多指导和帮助。感谢工作在幕后的电子工业出版社评审团队对于本书在校对、排版、审核、封面设计、错误改进方面所给予的帮助,使本书得以顺利出版发行。 49 | 50 |   其次,感谢在我十几年求学生涯中教育过我的所有老师。是你们将知识和学习方法传递给了我。感谢我曾经工作过的公司和单位,感谢和我一起共事过的同事和战友,你们的优秀一直是我追逐的目标,你们所给予的压力正是我不断改进自己的动力。 51 | 52 |   感谢我的父母、妻子Funny和两个女儿。由于撰写本书,牺牲了很多陪伴家人的时间。感谢你们对于我工作的理解和支持。 53 | 54 |   最后,特别要感谢这个时代,互联网让所有人可以公平地享受这个时代的成果。感谢那些为计算机、互联网所做出贡献的先驱,是你们让我可以站在更高的"肩膀"上!感谢那些为本书提供灵感的佳作,包括《分布式系统原理与范型》《Unix Network Programming》《Enterprise SOA》《MapReduce Design Patterns》《Hadoop: The Definitive Guide》《Learning Hbase》《Advanced Analytics with Spark》《Pro Git》《Docker in Action》《淘宝技术这十年》《Hatching Twitter》,等等,详细的书单可以参阅本书最后的"参考文献"部分。 55 | 56 | 57 | 柳伟卫 58 | 59 | 2016年11月13日于杭州 60 | -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/thread/SimpleThreads.java: -------------------------------------------------------------------------------- 1 | package com.waylau.essentialjava.thread; 2 | 3 | /** 4 | * SimpleThreads 示例有两个线程。第一个线程是每个 Java 应用程序都有的主线程。 5 | * 主线程创建的Runnable 对象的MessageLoop,并等待它完成。如果 MessageLoop 6 | * 需要很长时间才能完成,主线程就中断它。 7 | * 8 | * @author waylau.com 9 | * @date 2016年1月21日 10 | */ 11 | public class SimpleThreads { 12 | // Display a message, preceded by 13 | // the name of the current thread 14 | static void threadMessage(String message) { 15 | String threadName = 16 | Thread.currentThread().getName(); 17 | System.out.format("%s: %s%n", 18 | threadName, 19 | message); 20 | } 21 | 22 | private static class MessageLoop 23 | implements Runnable { 24 | public void run() { 25 | String importantInfo[] = { 26 | "Mares eat oats", 27 | "Does eat oats", 28 | "Little lambs eat ivy", 29 | "A kid will eat ivy too" 30 | }; 31 | try { 32 | for (int i = 0; 33 | i < importantInfo.length; 34 | i++) { 35 | // Pause for 4 seconds 36 | Thread.sleep(4000); 37 | // Print a message 38 | threadMessage(importantInfo[i]); 39 | } 40 | } catch (InterruptedException e) { 41 | threadMessage("I wasn't done!"); 42 | } 43 | } 44 | } 45 | 46 | public static void main(String args[]) 47 | throws InterruptedException { 48 | 49 | // Delay, in milliseconds before 50 | // we interrupt MessageLoop 51 | // thread (default one hour). 52 | long patience = 1000 * 60 * 60; 53 | 54 | // If command line argument 55 | // present, gives patience 56 | // in seconds. 57 | if (args.length > 0) { 58 | try { 59 | patience = Long.parseLong(args[0]) * 1000; 60 | } catch (NumberFormatException e) { 61 | System.err.println("Argument must be an integer."); 62 | System.exit(1); 63 | } 64 | } 65 | 66 | threadMessage("Starting MessageLoop thread"); 67 | long startTime = System.currentTimeMillis(); 68 | Thread t = new Thread(new MessageLoop()); 69 | t.start(); 70 | 71 | threadMessage("Waiting for MessageLoop thread to finish"); 72 | // loop until MessageLoop 73 | // thread exits 74 | while (t.isAlive()) { 75 | threadMessage("Still waiting..."); 76 | // Wait maximum of 1 second 77 | // for MessageLoop thread 78 | // to finish. 79 | t.join(1000); 80 | if (((System.currentTimeMillis() - startTime) > patience) 81 | && t.isAlive()) { 82 | threadMessage("Tired of waiting!"); 83 | t.interrupt(); 84 | // Shouldn't be long now 85 | // -- wait indefinitely 86 | t.join(); 87 | } 88 | } 89 | threadMessage("Finally!"); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /samples/chapter3/rabbitmq-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.waylau 6 | rabbitmq-demo 7 | jar 8 | 1.0.0 9 | rabbitmq-demo 10 | 11 | 12 | 13 | 14 | 15 | javax.jms 16 | javax.jms-api 17 | 2.0.1 18 | 19 | 20 | 21 | 22 | com.rabbitmq 23 | amqp-client 24 | 3.6.5 25 | 26 | 27 | 28 | 29 | junit 30 | junit 31 | 4.12 32 | test 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.apache.logging.log4j 40 | log4j-core 41 | 2.6.2 42 | 43 | 44 | 45 | org.apache.logging.log4j 46 | log4j-api 47 | 2.6.2 48 | 49 | 50 | 51 | 52 | org.apache.logging.log4j 53 | log4j-slf4j-impl 54 | 2.6.2 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-compiler-plugin 65 | 3.2 66 | 67 | 1.7 68 | 1.7 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-shade-plugin 75 | 2.4.3 76 | 77 | 78 | package 79 | 80 | shade 81 | 82 | 83 | 84 | 85 | com.waylau.rabbitmq.Worker 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /samples/chapter6/zookeeper-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.waylau 6 | zookeeper-demo 7 | jar 8 | 1.0.0 9 | zookeeper-demo 10 | 11 | 12 | 13 | 14 | 15 | javax.jms 16 | javax.jms-api 17 | 2.0.1 18 | 19 | 20 | 21 | 22 | org.apache.zookeeper 23 | zookeeper 24 | 3.4.9 25 | 26 | 27 | 28 | junit 29 | junit 30 | 4.12 31 | test 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.logging.log4j 39 | log4j-core 40 | 2.6.2 41 | 42 | 43 | 44 | org.apache.logging.log4j 45 | log4j-api 46 | 2.6.2 47 | 48 | 49 | 50 | 51 | org.apache.logging.log4j 52 | log4j-slf4j-impl 53 | 2.6.2 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-compiler-plugin 64 | 3.2 65 | 66 | 1.7 67 | 1.7 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-shade-plugin 73 | 2.4.3 74 | 75 | 76 | package 77 | 78 | shade 79 | 80 | 81 | 82 | 83 | com.waylau.zookeeper.SyncPrimitive 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /samples/chapter3/rocketmq-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.waylau 6 | rocketmq-demo 7 | jar 8 | 1.0.0 9 | rocketmq-demo 10 | 11 | 12 | 13 | 14 | 15 | javax.jms 16 | javax.jms-api 17 | 2.0.1 18 | 19 | 20 | 21 | 22 | com.alibaba.rocketmq 23 | rocketmq-client 24 | 3.5.7 25 | 26 | 27 | 28 | junit 29 | junit 30 | 4.12 31 | test 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.logging.log4j 39 | log4j-core 40 | 2.6.2 41 | 42 | 43 | 44 | org.apache.logging.log4j 45 | log4j-api 46 | 2.6.2 47 | 48 | 49 | 50 | 51 | org.apache.logging.log4j 52 | log4j-slf4j-impl 53 | 2.6.2 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-compiler-plugin 64 | 3.2 65 | 66 | 1.7 67 | 1.7 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-shade-plugin 74 | 2.4.3 75 | 76 | 77 | package 78 | 79 | shade 80 | 81 | 82 | 83 | 84 | com.waylau.rocketmq.App 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /samples/chapter6/zk-registry-discovery/src/main/java/com/waylau/zk/discovery/ZkServiceDiscovery.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to https://waylau.com 3 | */ 4 | package com.waylau.zk.discovery; 5 | 6 | import java.util.List; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | 9 | import org.I0Itec.zkclient.ZkClient; 10 | import org.springframework.util.CollectionUtils; 11 | 12 | import com.google.common.collect.Lists; 13 | import com.waylau.zk.Constant; 14 | 15 | /** 16 | * ZooKeeper Service Discovery. 17 | * 18 | * @since 1.0.0 2018年5月16日 19 | * @author Way Lau 20 | */ 21 | public class ZkServiceDiscovery implements ServiceDiscovery { 22 | 23 | /** 24 | * ZK 地址. 25 | */ 26 | private String zkAddress = "localhost"; 27 | 28 | /** 29 | * 缓存所有的服务 IP 和 端口. 30 | */ 31 | private final List addressCache = Lists.newCopyOnWriteArrayList(); 32 | 33 | /** 34 | * ZK 客户端. 35 | */ 36 | private ZkClient zkClient; 37 | 38 | public void init() { 39 | zkClient = new ZkClient(zkAddress, 40 | Constant.ZK_SESSION_TIMEOUT, 41 | Constant.ZK_CONNECTION_TIMEOUT); 42 | 43 | System.out.println(">>>connect to zookeeper"); 44 | } 45 | 46 | @Override 47 | public String discover(String name) { 48 | try { 49 | String servicePath = Constant.ZK_REGISTRY + "/" + name; 50 | 51 | // 获取服务节点 52 | if (!zkClient.exists(servicePath)) { 53 | throw new RuntimeException( 54 | String.format(">>>can't find any service node on path {}", 55 | servicePath)); 56 | } 57 | 58 | // 从本地缓存获取某个服务地址 59 | String address; 60 | int addressCacheSize = addressCache.size(); 61 | if (addressCacheSize > 0) { 62 | if (addressCacheSize == 1) { 63 | address = addressCache.get(0); 64 | } else { 65 | address = addressCache.get(ThreadLocalRandom.current().nextInt(addressCacheSize)); 66 | 67 | System.out.println(">>>get only address node:" + address); 68 | } 69 | 70 | // 从zk服务注册中心获取某个服务地址 71 | } else { 72 | List addressList = zkClient.getChildren(servicePath); 73 | addressCache.addAll(addressList); 74 | 75 | // 监听servicePath下的子文件是否发生变化 76 | zkClient.subscribeChildChanges(servicePath, (parentPath, currentChilds) -> { 77 | System.out.println(">>>servicePath is changed:" + parentPath); 78 | 79 | addressCache.clear(); 80 | addressCache.addAll(currentChilds); 81 | }); 82 | 83 | if (CollectionUtils.isEmpty(addressList)) { 84 | throw new RuntimeException( 85 | String.format(">>>can’t find any address node on path {}", servicePath)); 86 | } 87 | 88 | int nodes = addressList.size(); 89 | if (nodes == 1) { 90 | address = addressList.get(0); 91 | } else { 92 | 93 | // 如果多个,随机取一个 94 | address = addressList.get(ThreadLocalRandom.current().nextInt(nodes)); 95 | } 96 | 97 | System.out.println(">>>get address node:" + address); 98 | } 99 | 100 | // 获取ip和端口号 101 | String addressPath = servicePath + "/" + address; 102 | String hostAndPort = zkClient.readData(addressPath); 103 | return hostAndPort; 104 | } catch (Exception e) { 105 | 106 | System.out.println(">>>service discovery exception" + e.getMessage()); 107 | 108 | zkClient.close(); 109 | } 110 | return null; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/test/java/com/waylau/spring/jms/SpringJmsTest.java: -------------------------------------------------------------------------------- 1 | package com.waylau.spring.jms; 2 | 3 | import javax.jms.Destination; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Qualifier; 9 | import org.springframework.test.context.ContextConfiguration; 10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 11 | 12 | import com.waylau.spring.jms.queue.ConsumerService; 13 | import com.waylau.spring.jms.queue.ProducerService; 14 | import com.waylau.spring.jms.topic.TopicProvider; 15 | 16 | /** 17 | * Spring JMS Test. 18 | * 19 | * @since 1.0.0 2018年4月15日 20 | * @author Way Lau 21 | */ 22 | @RunWith(SpringJUnit4ClassRunner.class) 23 | @ContextConfiguration("/spring.xml") 24 | public class SpringJmsTest { 25 | 26 | /** 27 | * 队列名queue1 28 | */ 29 | @Autowired 30 | private Destination queueDestination; 31 | 32 | /** 33 | * 队列名queue2 34 | */ 35 | @Autowired 36 | private Destination queueDestination2; 37 | 38 | /** 39 | * 队列名sessionAwareQueue 40 | */ 41 | @Autowired 42 | private Destination sessionAwareQueue; 43 | 44 | /** 45 | * 队列名adapterQueue 46 | */ 47 | @Autowired 48 | private Destination adapterQueue; 49 | 50 | /** 51 | * 主题 guo_topic 52 | */ 53 | @Autowired 54 | @Qualifier("topicDestination") 55 | private Destination topic; 56 | 57 | /** 58 | * 主题消息发布者 59 | */ 60 | @Autowired 61 | private TopicProvider topicProvider; 62 | 63 | /** 64 | * 队列消息生产者 65 | */ 66 | @Autowired 67 | @Qualifier("producerService") 68 | private ProducerService producer; 69 | 70 | /** 71 | * 队列消息生产者 72 | */ 73 | @Autowired 74 | @Qualifier("consumerService") 75 | private ConsumerService consumer; 76 | 77 | /** 78 | * 测试生产者向queue1发送消息 79 | */ 80 | @Test 81 | public void testProduce() { 82 | String msg = "Hello world!"; 83 | producer.sendMessage(msg); 84 | } 85 | 86 | /** 87 | * 测试消费者从queue1接受消息 88 | */ 89 | @Test 90 | public void testConsume() { 91 | consumer.receive(queueDestination); 92 | } 93 | 94 | /** 95 | * 测试消息监听 96 | * 1.生产者向队列queue2发送消息 97 | * 2.ConsumerMessageListener监听队列,并消费消息 98 | */ 99 | @Test 100 | public void testSend() { 101 | producer.sendMessage(queueDestination2, "Hello R2"); 102 | } 103 | 104 | /** 105 | * 测试主题监听 106 | * 1.生产者向主题发布消息 107 | * 2.ConsumerMessageListener监听主题,并消费消息 108 | */ 109 | @Test 110 | public void testTopic() { 111 | topicProvider.publish(topic, "Hello Topic!"); 112 | } 113 | 114 | /** 115 | * 测试SessionAwareMessageListener 116 | * 1. 生产者向队列sessionAwareQueue发送消息 117 | * 2. SessionAwareMessageListener接受消息,并向queue1队列发送回复消息 118 | * 3. 消费者从queue1消费消息 119 | * 120 | */ 121 | @Test 122 | public void testAware() { 123 | producer.sendMessage(sessionAwareQueue, "Hello sessionAware"); 124 | consumer.receive(queueDestination); 125 | } 126 | 127 | /** 128 | * 测试MessageListenerAdapter 129 | * 1. 生产者向队列adapterQueue发送消息 130 | * 2. MessageListenerAdapter使ConsumerListener接受消息,并向queue1队列发送回复消息 131 | * 3. 消费者从queue1消费消息 132 | * 133 | */ 134 | @Test 135 | public void testAdapter() { 136 | producer.sendMessage(adapterQueue, "Hello adapterQueue", queueDestination); 137 | consumer.receive(queueDestination); 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /samples/chapter5/redis-lock/src/main/java/com/waylau/redis/lock/RedisLock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to https://waylau.com 3 | */ 4 | package com.waylau.redis.lock; 5 | 6 | import java.util.UUID; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | import org.springframework.util.StringUtils; 11 | 12 | import redis.clients.jedis.Jedis; 13 | 14 | /** 15 | * Redis Lock. 16 | * 17 | * @since 1.0.0 2018年5月15日 18 | * @author Way Lau 19 | */ 20 | @Component 21 | public class RedisLock implements IRedisLock { 22 | 23 | private static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(RedisLock.class); 24 | 25 | /** 26 | * 当前机器节点锁标识。 27 | */ 28 | private static String redisIdentityKey = UUID.randomUUID().toString(); 29 | 30 | /** 31 | * 获取当前机器节点在锁中的标示符。 32 | */ 33 | public static String getRedisIdentityKey() { 34 | return redisIdentityKey; 35 | } 36 | 37 | /** 38 | * 等待获取锁的时间,可以根据当前任务的执行时间来设置。 39 | */ 40 | private static final long WaitLockTimeSecond = 2000; 41 | 42 | /** 43 | * 重试获取锁的次数,可以根据当前任务的执行时间来设置。 44 | * 需要时间=RetryCount*(WaitLockTimeSecond/1000) 45 | */ 46 | private static final int RetryCount = 10; 47 | 48 | @Autowired 49 | private Jedis jedis; 50 | 51 | @Override 52 | public Boolean lock(int lockNameExpireSecond, String lockName, Boolean isWait) throws Exception { 53 | if (StringUtils.isEmpty(lockName)) 54 | throw new Exception("lockName is empty."); 55 | 56 | int retryCounts = 0; 57 | while (true) { 58 | Long status, expire = 0L; 59 | status = jedis.setnx(lockName, redisIdentityKey); 60 | if (status > 0) { 61 | expire = jedis.expire(lockName, lockNameExpireSecond); 62 | } 63 | if (status > 0 && expire > 0) { 64 | logger.info(String.format("t:%s,当前节点:%s,获取到锁:%s", 65 | Thread.currentThread().getId(), 66 | getRedisIdentityKey(), 67 | lockName)); 68 | return true;/** 获取到lock */ 69 | } 70 | 71 | try { 72 | if (isWait && retryCounts < RetryCount) { 73 | retryCounts++; 74 | synchronized (this) { 75 | logger.info(String.format("t:%s,当前节点:%s,尝试等待获取锁:%s", 76 | Thread.currentThread().getId(), 77 | getRedisIdentityKey(), 78 | lockName)); 79 | 80 | // 未能获取到lock,进行指定时间的wait再重试. 81 | this.wait(WaitLockTimeSecond); 82 | } 83 | } else if (retryCounts == RetryCount) { 84 | logger.info(String.format("t:%s,当前节点:%s,指定时间内获取锁失败:%s", 85 | Thread.currentThread().getId(), 86 | getRedisIdentityKey(), 87 | lockName)); 88 | return false; 89 | } else { 90 | return false; 91 | } 92 | } catch (InterruptedException e) { 93 | e.printStackTrace(); 94 | } 95 | } 96 | } 97 | 98 | @Override 99 | public Boolean unlock(String lockName) throws Exception { 100 | if (StringUtils.isEmpty(lockName)) 101 | throw new Exception("lockName is empty."); 102 | 103 | long status = jedis.del(lockName); 104 | if (status > 0) { 105 | logger.info(String.format("t:%s,当前节点:%s,释放锁:%s 成功。", 106 | Thread.currentThread().getId(), 107 | getRedisIdentityKey(), 108 | lockName)); 109 | return true; 110 | } 111 | logger.info(String.format("t:%s,当前节点:%s,释放锁:%s 失败。", 112 | Thread.currentThread().getId(), 113 | getRedisIdentityKey(), 114 | lockName)); 115 | return false; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /samples/chapter3/activemq-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.waylau 6 | activemq-demo 7 | jar 8 | 1.0.0 9 | activemq-demo 10 | 11 | 12 | 13 | 14 | 15 | javax.jms 16 | javax.jms-api 17 | 2.0.1 18 | 19 | 20 | 21 | 22 | org.apache.activemq 23 | activemq-core 24 | 5.7.0 25 | 26 | 27 | 28 | 29 | org.apache.activemq 30 | activemq-broker 31 | 5.13.4 32 | 33 | 34 | 35 | junit 36 | junit 37 | 4.12 38 | test 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.apache.logging.log4j 46 | log4j-core 47 | 2.6.2 48 | 49 | 50 | 51 | org.apache.logging.log4j 52 | log4j-api 53 | 2.6.2 54 | 55 | 56 | 57 | 58 | org.apache.logging.log4j 59 | log4j-slf4j-impl 60 | 2.6.2 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-compiler-plugin 71 | 3.2 72 | 73 | 1.7 74 | 1.7 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-shade-plugin 80 | 2.4.3 81 | 82 | 83 | package 84 | 85 | shade 86 | 87 | 88 | 89 | 90 | com.waylau.activemq.Producer 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /samples/chapter2/javase-rest/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | 6 | com.waylau 7 | javase-rest 8 | jar 9 | 1.0.0 10 | javase-rest 11 | 12 | 13 | 14 | 15 | org.glassfish.jersey 16 | jersey-bom 17 | ${jersey.version} 18 | pom 19 | import 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.glassfish.jersey.containers 43 | jersey-container-jetty-http 44 | 45 | 46 | org.glassfish.jersey.media 47 | jersey-media-multipart 48 | 49 | 50 | org.glassfish.jersey.media 51 | jersey-media-moxy 52 | 53 | 54 | 56 | 57 | org.glassfish.jersey.media 58 | jersey-media-sse 59 | 60 | 61 | 62 | junit 63 | junit 64 | 4.12 65 | test 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-compiler-plugin 74 | 2.5.1 75 | true 76 | 77 | 1.7 78 | 1.7 79 | 80 | 81 | 82 | org.codehaus.mojo 83 | exec-maven-plugin 84 | 1.2.1 85 | 86 | 87 | 88 | java 89 | 90 | 91 | 92 | 93 | com.waylau.rest.App 94 | 95 | 96 | 97 | 98 | 99 | 100 | 2.23.2 101 | UTF-8 102 | 103 | 104 | -------------------------------------------------------------------------------- /samples/chapter1/distributed-systems-technologies-and-cases-analysis/src/com/waylau/essentialjava/iomode/NonBlokingEchoServer.java: -------------------------------------------------------------------------------- 1 | package com.waylau.essentialjava.iomode; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.SelectionKey; 7 | import java.nio.channels.Selector; 8 | import java.nio.channels.ServerSocketChannel; 9 | import java.nio.channels.SocketChannel; 10 | import java.util.Iterator; 11 | import java.util.Set; 12 | 13 | /** 14 | * “非阻塞I/O”模式 15 | * 16 | * @author waylau.com 17 | * @date 2016年7月29日 18 | */ 19 | public class NonBlokingEchoServer { 20 | 21 | public static int DEFAULT_PORT = 7; 22 | 23 | public static void main(String[] args) throws IOException { 24 | 25 | int port; 26 | 27 | try { 28 | port = Integer.parseInt(args[0]); 29 | } catch (RuntimeException ex) { 30 | port = DEFAULT_PORT; 31 | } 32 | System.out.println("Listening for connections on port " + port); 33 | 34 | ServerSocketChannel serverChannel; 35 | Selector selector; 36 | try { 37 | serverChannel = ServerSocketChannel.open(); 38 | InetSocketAddress address = new InetSocketAddress(port); 39 | serverChannel.bind(address); 40 | serverChannel.configureBlocking(false); 41 | selector = Selector.open(); 42 | serverChannel.register(selector, SelectionKey.OP_ACCEPT); 43 | } catch (IOException ex) { 44 | ex.printStackTrace(); 45 | return; 46 | } 47 | 48 | while (true) { 49 | try { 50 | selector.select(); 51 | } catch (IOException ex) { 52 | ex.printStackTrace(); 53 | break; 54 | } 55 | Set readyKeys = selector.selectedKeys(); 56 | Iterator iterator = readyKeys.iterator(); 57 | while (iterator.hasNext()) { 58 | SelectionKey key = iterator.next(); 59 | iterator.remove(); 60 | try { 61 | if (key.isAcceptable()) { 62 | ServerSocketChannel server = (ServerSocketChannel) key.channel(); 63 | SocketChannel client = server.accept(); 64 | System.out.println("Accepted connection from " + client); 65 | client.configureBlocking(false); 66 | SelectionKey clientKey = client.register(selector, 67 | SelectionKey.OP_WRITE | SelectionKey.OP_READ); 68 | ByteBuffer buffer = ByteBuffer.allocate(100); 69 | clientKey.attach(buffer); 70 | } 71 | if (key.isReadable()) { 72 | SocketChannel client = (SocketChannel) key.channel(); 73 | ByteBuffer output = (ByteBuffer) key.attachment(); 74 | client.read(output); 75 | } 76 | if (key.isWritable()) { 77 | SocketChannel client = (SocketChannel) key.channel(); 78 | ByteBuffer output = (ByteBuffer) key.attachment(); 79 | output.flip(); 80 | client.write(output); 81 | 82 | output.compact(); 83 | } 84 | } catch (IOException ex) { 85 | key.cancel(); 86 | try { 87 | key.channel().close(); 88 | } catch (IOException cex) { 89 | } 90 | } 91 | } 92 | } 93 | 94 | } 95 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 《分布式系统常用技术及案例分析(第2版)》 2 | 3 | ## 再版序 4 | 5 | 6 | 时光荏苒,岁月匆匆,距离《分布式系统常用技术及案例分析》2017年2月第1版已经一载有余。热心的读者对于本书也投以了极大的关注,伴随着本书的成长,提了很多中肯的建议。对于这些意见,不管褒贬,一并全收,于是才有了第2版的可能。 7 | 对于技术型的书籍创作,笔者更加倾向于采用当今软件开发主流的方式——敏捷。敏捷写作打通了编写、校稿、出版、发行的整个流程,让知识可以第一时间呈现给读者。读者在阅读本书之后,也可以及时对书中的内容进行反馈,从而帮助作者完善书中内容,最终形成了良好的反馈闭环。所以,第2版所更新的内容,应该正是读者所期待的。 8 | 9 | 第2版修改篇幅较大,其余章节都做了大幅度更新。整改内容大致分为以下几个方面: 10 | 11 | 1. 删除安装介绍等比较简单的内容。 12 | 2. 每个章节开头,新增“概述”让各个章节的技术点可以关联起来。 13 | 3. 每个章节增加“实战”案例,让技术点更具有可操作性。 14 | 4. 修改原文中措辞、插图。 15 | 5. 删除第9-10章。 16 | 17 | 完整的修改内容,可以参阅本书后面部分“附录A:本书1版与2版的差异对比”章节内容。 18 | 19 | ## 本书与开源的关系 20 | 21 | 本书所列之技术多为开源技术,本书所撰写的大部分内容也多取材自我个人的博客以及个人编写的开源书(也算是为开源事业贡献微薄之力吧),符合“取自开源,回馈开源”之宗旨。不想看本书的也可以直接关注我博客()或者开源书()的内容。当然,博客相对来说比较零散,没有这本书来的严谨。 22 | 23 | 24 | ## 内容介绍 25 | 26 | 本书分为两大部分,即分布式系统基础理论及分布式系统常用技术。第一部分为第1章和第2章,主要介绍分布式系统基础理论知识,总结一些在设计分布式系统时需要考虑的范式、知识点以及可能会面临的问题。 第二部分为第3章到第8章,主要列举了在分布式系统应用中经常用到的一些主流技术,并介绍这些技术的作用和用法。 27 | 28 | * 第1章介绍分布式系统基础理论知识,总结一些在设计分布式系统时需要考虑的范式、知识点以及可能会面临的问题,其中包括线程、通信、一致性、容错性、CAP理论、安全性和并发等相关内容。 29 | * 第2章详细介绍分布式系统的架构体系,包括传统的基于对象的体系结构、SOA,也包括最近比较火的RESTful风格架构、微服务、容器技术、Serverless架构等。 30 | * 第3章介绍常用的分布式消息服务框架,包括Apache ActiveMQ、RabbitMQ、RocketMQ、Apache Kafka等。 31 | * 第4章介绍分布式计算理论和应用框架方面的内容,包括MapReduce、Apache Hadoop、Apache Spark、Apache Mesos 等。 32 | * 第5章介绍分布式存储理论和应用框架方面的内容,包括Bigtable、Apache HBase、Apache Cassandra、Memcached、Redis、MongoDB等。 33 | * 第6章介绍分布式监控方面常用的技术,包括Nagios、Zabbix、Consul、ZooKeeper等。 34 | * 第7章介绍常用的分布式版本控制工具,包括Bazaar、Mercurial、Git等。 35 | * 第8章介绍RESTful API、微服务及容器相关的技术,着重介绍Jersey、Spring Boot、Docker等技术的应用。 36 | 37 | 38 | 您可以查看详细的书籍[目录](SUMMARY.md)。 39 | 40 | 本书涉及面相当之广,可以说涵盖了在设计分布式系统时,所要考虑的大部分问题及解决方案。 41 | 42 | ![《分布式系统常用技术及案例分析》封面](images/logo.png) 43 | 44 | ## 源代码 45 | 46 | 本书提供源代码下载,地址位于[本项目](https://github.com/waylau/distributed-systems-technologies-and-cases-analysis)下的`samples`目录。 47 | 48 | ## 分支说明 49 | 50 | 这个分支,是2017年2月第1版 51 | 这个分支,是2018年12月第2版 52 | 53 | ## 勘误和交流 54 | 55 | 本书如有勘误,会在上进行发布。由于笔者能力有限,时间仓促,难免错漏,欢迎读者批评指正。 56 | 57 | 读者也可以到博文视点官网的本书页面进行交流()。 58 | 59 | 您也可以上[豆瓣](https://book.douban.com/subject/30414784/)给老卫打Call。 60 | 61 | ## 联系作者 62 | 63 | 您也可以直接联系我: 64 | 65 | * 博客:https://waylau.com 66 | * 邮箱:[waylau521(at)gmail.com](mailto:waylau521@gmail.com) 67 | * 微博:http://weibo.com/waylau521 68 | * 开源:https://github.com/waylau 69 | 70 | ## 如何获取本书 71 | 72 | 实体店及部分网店有售,据我所知有如下网站供应: 73 | 74 | * [京东](https://search.jd.com/Search?keyword=%E6%9F%B3%E4%BC%9F%E5%8D%AB%20%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E5%B8%B8%E7%94%A8%E6%8A%80%E6%9C%AF%E5%8F%8A%E6%A1%88%E4%BE%8B%E5%88%86%E6%9E%90%EF%BC%88%E7%AC%AC2%E7%89%88%EF%BC%89&enc=utf-8&wq=%E6%9F%B3%E4%BC%9F%E5%8D%AB%20%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E5%B8%B8%E7%94%A8%E6%8A%80%E6%9C%AF%E5%8F%8A%E6%A1%88%E4%BE%8B%E5%88%86%E6%9E%90%EF%BC%88%E7%AC%AC2%E7%89%88%EF%BC%89&pvid=29e3bd72b6064ff796818f3914d82ca5) 75 | * [当当](http://search.dangdang.com/?key=%C1%F8%CE%B0%CE%C0%20%B7%D6%B2%BC%CA%BD%CF%B5%CD%B3%B3%A3%D3%C3%BC%BC%CA%F5%BC%B0%B0%B8%C0%FD%B7%D6%CE%F6%A3%A8%B5%DA2%B0%E6%A3%A9&act=input) 76 | * [淘宝](https://s.taobao.com/search?q=%E6%9F%B3%E4%BC%9F%E5%8D%AB+%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E5%B8%B8%E7%94%A8%E6%8A%80%E6%9C%AF%E5%8F%8A%E6%A1%88%E4%BE%8B%E5%88%86%E6%9E%90%EF%BC%88%E7%AC%AC2%E7%89%88%EF%BC%89&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.2017.201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170306) 77 | * [china-pub](http://search.china-pub.com/s/?key1=%c1%f8%ce%b0%ce%c0+%b7%d6%b2%bc%ca%bd%cf%b5%cd%b3%b3%a3%d3%c3%bc%bc%ca%f5%bc%b0%b0%b8%c0%fd%b7%d6%ce%f6%a3%a8%b5%da2%b0%e6%a3%a9&type=&pz=1) 78 | * [亚马逊](https://www.amazon.cn/s/ref=nb_sb_noss?__mk_zh_CN=%E4%BA%9A%E9%A9%AC%E9%80%8A%E7%BD%91%E7%AB%99&url=search-alias%3Daps&field-keywords=%E6%9F%B3%E4%BC%9F%E5%8D%AB+%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E5%B8%B8%E7%94%A8%E6%8A%80%E6%9C%AF%E5%8F%8A%E6%A1%88%E4%BE%8B%E5%88%86%E6%9E%90%EF%BC%88%E7%AC%AC2%E7%89%88%EF%BC%89C) 79 | 80 | 81 | 想低于市价得到本书?来[二手书集市](https://github.com/waylau/second-hand-books)试试看。 82 | 83 | 84 | 也可以直接关注我博客()或者我的开源书()了解更多免费咨询。 85 | 86 | 87 | 88 | ## 其他书籍 89 | 90 | 若您对本书不感冒,笔者还写了其他方面的超过一打的书籍(可见),多是开源电子书。 91 | 92 | 本人也维护了一个[books-collection](https://github.com/waylau/books-collection)项目,里面提供了优质的专门给程序员的开源、免费图书集合。 93 | 94 | ## 开源捐赠 95 | 96 | 97 | ![开源捐赠](https://waylau.com/images/showmethemoney-sm.jpg) 98 | 99 | 捐赠所得所有款项将用于开源事业! 100 | -------------------------------------------------------------------------------- /samples/chapter8/hello-world-docker/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /samples/chapter3/jms-msg/src/main/resources/spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | queue1 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | queue2 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | guo_topic 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | sessionAwareQueue 106 | 107 | 108 | 109 | 110 | 112 | 113 | 114 | 115 | 116 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | adapterQueue 127 | 128 | 129 | 130 | 131 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | 第1章 分布式系统基础知识 4 | 5 | 1.1 概述 6 | 7 | 1.1.1 什么是分布式系统 8 | 9 | 1.1.2 集中式系统与分布式系统 10 | 11 | 1.1.3 如何设计分布式系统 12 | 13 | 1.1.4 分布式系统所面临的挑战 14 | 15 | 1.2 线程 16 | 17 | 1.2.1 什么是线程 18 | 19 | 1.2.2 进程和线程 20 | 21 | 1.2.3 线程和纤程 22 | 23 | 1.2.4 编程语言中的线程对象 24 | 25 | 1.2.5 SimpleThreads示例 26 | 27 | 1.3 通信 28 | 29 | 1.3.1 网络I/O模型的演进 30 | 31 | 1.3.2 远程过程调用(RPC) 32 | 33 | 1.3.3 面向消息的通信 34 | 35 | 1.4 一致性 36 | 37 | 1.4.1 以数据为中心的一致性模型 38 | 39 | 1.4.2 以客户为中心的一致性 40 | 41 | 1.5 容错性 42 | 43 | 1.5.1 基本概念 44 | 45 | 1.5.2 故障分类 46 | 47 | 1.5.3 使用冗余来掩盖故障 48 | 49 | 1.5.4 分布式提交 50 | 51 | 1.6 CAP理论 52 | 53 | 1.6.1 什么是CAP理论 54 | 55 | 1.6.2 为什么CAP只能三选二 56 | 57 | 1.6.3 CAP常见模型 58 | 59 | 1.6.4 CAP的意义 60 | 61 | 1.6.5 CAP新发展 62 | 63 | 1.7 安全性 64 | 65 | 1.7.1 基本概念 66 | 67 | 1.7.2 加密算法 68 | 69 | 1.7.3 安全通道 70 | 71 | 1.7.4 访问控制 72 | 73 | 1.8 并发 74 | 75 | 1.8.1 线程与并发 76 | 77 | 1.8.2 并发与并行 78 | 79 | 1.8.3 并发带来的风险 80 | 81 | 1.8.4 同步(Synchronization) 82 | 83 | 1.8.5 原子访问(Atomic Access) 84 | 85 | 1.8.6 无锁化设计提升并发能力 86 | 87 | 1.8.7 缓存提升并发能力 88 | 89 | 1.8.8 更细颗粒度的并发单元 90 | 91 | 第2章 分布式系统架构体系 92 | 93 | 2.1 基于对象的体系结构 94 | 95 | 2.1.1 分布式对象 96 | 97 | 2.1.2 Java RMI 98 | 99 | 2.2 面向服务的架构(SOA) 100 | 101 | 2.2.1 SOA的基本概念 102 | 103 | 2.2.2 基于Web Services的SOA 104 | 105 | 2.2.3 SOA的演变 106 | 107 | 2.3 REST风格的架构 108 | 109 | 2.3.1 什么是REST 110 | 111 | 2.3.2 REST有哪些特征 112 | 113 | 2.3.3 Java实现REST的例子 114 | 115 | 2.3.4 REST API佳实践 116 | 117 | 2.4 微服务架构(MSA) 118 | 119 | 2.4.1 什么是MSA 120 | 121 | 2.4.2 MSA与SOA 122 | 123 | 2.4.3 何时采用MSA 124 | 125 | 2.4.4 如何构建微服务 126 | 127 | 2.5 容器技术 128 | 129 | 2.5.1 虚拟化技术 130 | 131 | 2.5.2 容器与虚拟机 132 | 133 | 2.5.3 基于容器的持续部署 134 | 135 | 2.6 Serverless架构 136 | 137 | 2.6.1 什么是Serverless架构 138 | 139 | 2.6.2 Serverless典型的应用场景 140 | 141 | 2.6.3 Serverless架构原则 142 | 143 | 2.6.4 例子:使用Serverless实现游戏全球同服 144 | 145 | 第3章 分布式消息服务 146 | 147 | 3.1 分布式消息概述 148 | 149 | 3.1.1 基本概念 150 | 151 | 3.1.2 使用场景 152 | 153 | 3.1.3 常用技术 154 | 155 | 3.2 Apache ActiveMQ 156 | 157 | 3.2.1 例子:producer-consumer 158 | 159 | 3.2.2 例子:使用JMX来监控ActiveMQ 160 | 161 | 3.2.3 例子:使用Java实现producer-consumer 162 | 163 | 3.3 RabbitMQ 164 | 165 | 3.3.1 例子:Work Queues 166 | 167 | 3.3.2 例子:Publish/Subscribe 168 | 169 | 3.3.3 例子:Routing 170 | 171 | 3.3.4 例子:Topics 172 | 173 | 3.3.5 例子:RPC 174 | 175 | 3.4 Apache RocketMQ 176 | 177 | 3.4.1 例子:使用Java实现producer-consumer 178 | 179 | 3.4.2 RocketMQ佳实践 180 | 181 | 3.5 Apache Kafka 182 | 183 | 3.5.1 Apache Kafka的核心概念 184 | 185 | 3.5.2 Apache Kafka的使用场景 186 | 187 | 3.6 实战:基于JMS的消息发送和接收 188 | 189 | 3.6.1 项目概述 190 | 191 | 3.6.2 项目配置 192 | 193 | 3.6.3 编码实现 194 | 195 | 3.6.4 运行 196 | 197 | 第4章 分布式计算 198 | 199 | 4.1 分布式计算概述 200 | 201 | 4.1.1 使用场景 202 | 203 | 4.1.2 常用技术 204 | 205 | 4.2 MapReduce 206 | 207 | 4.2.1 MapReduce简介 208 | 209 | 4.2.2 MapReduce的编程模型 210 | 211 | 4.2.3 MapReduce接口实现 212 | 213 | 4.2.4 MapReduce的使用技巧 214 | 215 | 4.3 Apache Hadoop 216 | 217 | 4.3.1 Apache Hadoop的核心组件 218 | 219 | 4.3.2 例子:词频统计WordCount程序 220 | 221 | 4.4 Spark 222 | 223 | 4.4.1 Spark简介 224 | 225 | 4.4.2 Spark与Hadoop的关系 226 | 227 | 4.4.3 Spark 2.0的新特性 228 | 229 | 4.4.4 Spark集群模式 230 | 231 | 4.5 Mesos 232 | 233 | 4.5.1 Mesos简介 234 | 235 | 4.5.2 设计高可用的Mesos framework 236 | 237 | 4.6 实战:基于Spark的词频统计 238 | 239 | 4.6.1 项目概述 240 | 241 | 4.6.2 项目配置 242 | 243 | 4.6.3 编码实现 244 | 245 | 4.6.4 运行 246 | 247 | 第5章 分布式存储 248 | 249 | 5.1 分布式存储概述 250 | 251 | 5.1.1 使用场景 252 | 253 | 5.1.2 常用技术 254 | 255 | 5.2 Bigtable 256 | 257 | 5.2.1 Bigtable的数据模型 258 | 259 | 5.2.2 Bigtable的实现 260 | 261 | 5.2.3 Bigtable的性能优化 262 | 263 | 5.3 Apache HBase 264 | 265 | 5.3.1 Apache HBase的基本概念 266 | 267 | 5.3.2 Apache HBase的架构 268 | 269 | 5.4 Apache Cassandra 270 | 271 | 5.4.1 Apache Cassandra简介 272 | 273 | 5.4.2 Apache Cassandra的应用场景 274 | 275 | 5.4.3 Apache Cassandra的架构和数据模型 276 | 277 | 5.4.4 用于配置Apache Cassandra的核心组件 278 | 279 | 5.5 Memcached 280 | 281 | 5.5.1 Memcached简介 282 | 283 | 5.5.2 Memcached的架构 284 | 285 | 5.5.3 Memcached客户端 286 | 287 | 5.6 Redis 288 | 289 | 5.6.1 Redis简介 290 | 291 | 5.6.2 Redis的下载与简单使用 292 | 293 | 5.6.3 Redis的数据类型及抽象 294 | 295 | 5.7 MongoDB 296 | 297 | 5.7.1 MongoDB简介 298 | 299 | 5.7.2 MongoDB核心概念 300 | 301 | 5.7.3 MongoDB的数据模型 302 | 303 | 5.7.4 示例:Java连接MongoDB 304 | 305 | 5.8 实战:基于Redis的分布式锁 306 | 307 | 5.8.1 项目概述 308 | 309 | 5.8.2 项目配置 310 | 311 | 5.8.3 编码实现 312 | 313 | 5.8.4 运行 314 | 315 | 第6章 分布式监控 316 | 317 | 6.1 分布式监控概述 318 | 319 | 6.1.1 使用场景 320 | 321 | 6.1.2 常用技术 322 | 323 | 6.2 Nagios 324 | 325 | 6.2.1 Nagios监控 326 | 327 | 6.2.2 Nagios插件 328 | 329 | 6.3 Zabbix 330 | 331 | 6.3.1 Zabbix对容器的支持 332 | 333 | 6.3.2 Zabbix的基本概念 334 | 335 | 6.4 Consul 336 | 337 | 6.4.1 Consul架构 338 | 339 | 6.4.2 Consul agent 340 | 341 | 6.5 ZooKeeper 342 | 343 | 6.5.1 ZooKeeper简介 344 | 345 | 6.5.2 ZooKeeper内部工作原理 346 | 347 | 6.5.3 例子:ZooKeeper实现barrier和producer-consumer queue 348 | 349 | 6.6 实战:基于ZooKeeper的服务注册和发现 350 | 351 | 6.6.1 项目概述 352 | 353 | 6.6.2 项目配置 354 | 355 | 6.6.3 编码实现 356 | 357 | 6.6.4 运行 358 | 359 | 第7章 分布式版本控制系统 360 | 361 | 7.1 分布式版本控制系统概述 362 | 363 | 7.1.1 集中式与分布式 364 | 365 | 7.1.2 分布式版本控制系统的核心概念 366 | 367 | 7.2 Bazaar 368 | 369 | 7.2.1 Bazaar的核心概念 370 | 371 | 7.2.2 Bazaar的使用 372 | 373 | 7.3 Mercurial 374 | 375 | 7.3.1 Mercurial的核心概念 376 | 377 | 7.3.2 Mercurial的使用 378 | 379 | 7.4 Git 380 | 381 | 7.4.1 Git的基础概念 382 | 383 | 7.4.2 Git的使用 384 | 385 | 7.5 Git Flow—团队协作佳实践 386 | 387 | 7.5.1 分支定义 388 | 389 | 7.5.2 新功能开发工作流 390 | 391 | 7.5.3 Bug修复工作流 392 | 393 | 7.5.4 版本发布工作流 394 | 395 | 第8章 RESTful API、微服务及容器技术 396 | 397 | 8.1 Jersey 398 | 399 | 8.1.1 Jersey简介 400 | 401 | 8.1.2 Jersey的模块和依赖 402 | 403 | 8.1.3 JAX-RS核心概念 404 | 405 | 8.1.4 例子:用SSE构建实时Web应用 406 | 407 | 8.2 Spring Boot 408 | 409 | 8.2.1 Spring Boot简介 410 | 411 | 8.2.2 Spring Boot的安装 412 | 413 | 8.2.3 Spring Boot的使用 414 | 415 | 8.2.4 Spring Boot的属性与配置 416 | 417 | 8.3 Docker 418 | 419 | 8.3.1 Docker简介 420 | 421 | 8.3.2 Docker的核心组成、架构及工作原理 422 | 423 | 8.3.3 Docker的使用 424 | 425 | 8.4 实战:基于Docker构建、运行、发布微服务 426 | 427 | 8.4.1 编写微服务 428 | 429 | 8.4.2 微服务容器化 430 | 431 | 8.4.3 构建Docker image 432 | 433 | 8.4.4 运行image 434 | 435 | 8.4.5 访问应用 436 | 437 | 438 | 8.4.6 发布微服务 -------------------------------------------------------------------------------- /samples/chapter6/zookeeper-demo/src/main/java/com/waylau/zookeeper/SyncPrimitive.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.UnknownHostException; 6 | import java.nio.ByteBuffer; 7 | import java.util.List; 8 | import java.util.Random; 9 | 10 | import org.apache.zookeeper.CreateMode; 11 | import org.apache.zookeeper.KeeperException; 12 | import org.apache.zookeeper.WatchedEvent; 13 | import org.apache.zookeeper.Watcher; 14 | import org.apache.zookeeper.ZooKeeper; 15 | import org.apache.zookeeper.ZooDefs.Ids; 16 | import org.apache.zookeeper.data.Stat; 17 | 18 | /** 19 | * ZooKeeper 实现 barrier 和 producer-consumer queue 20 | * 21 | * @author Way Lau 22 | * @date 2016年10月6日 23 | */ 24 | public class SyncPrimitive implements Watcher { 25 | 26 | static ZooKeeper zk = null; 27 | static Integer mutex; 28 | 29 | String root; 30 | 31 | SyncPrimitive(String address) { 32 | if(zk == null){ 33 | try { 34 | System.out.println("Starting ZK:"); 35 | zk = new ZooKeeper(address, 3000, this); 36 | mutex = new Integer(-1); 37 | System.out.println("Finished starting ZK: " + zk); 38 | } catch (IOException e) { 39 | System.out.println(e.toString()); 40 | zk = null; 41 | } 42 | } 43 | //else mutex = new Integer(-1); 44 | } 45 | 46 | synchronized public void process(WatchedEvent event) { 47 | synchronized (mutex) { 48 | //System.out.println("Process: " + event.getType()); 49 | mutex.notify(); 50 | } 51 | } 52 | 53 | /** 54 | * Barrier 55 | */ 56 | static public class Barrier extends SyncPrimitive { 57 | int size; 58 | String name; 59 | 60 | /** 61 | * Barrier 构造函数 62 | * 63 | * @param address 64 | * @param root 65 | * @param size 66 | */ 67 | Barrier(String address, String root, int size) { 68 | super(address); 69 | this.root = root; 70 | this.size = size; 71 | 72 | // 创建 barrier 节点 73 | if (zk != null) { 74 | try { 75 | Stat s = zk.exists(root, false); 76 | if (s == null) { 77 | zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, 78 | CreateMode.PERSISTENT); 79 | } 80 | } catch (KeeperException e) { 81 | System.out 82 | .println("Keeper exception when instantiating queue: " 83 | + e.toString()); 84 | } catch (InterruptedException e) { 85 | System.out.println("Interrupted exception"); 86 | } 87 | } 88 | 89 | // 节点名称 90 | try { 91 | name = new String(InetAddress.getLocalHost().getCanonicalHostName().toString()); 92 | } catch (UnknownHostException e) { 93 | System.out.println(e.toString()); 94 | } 95 | } 96 | 97 | /** 98 | * 加入 barrier 99 | * 100 | * @return 101 | * @throws KeeperException 102 | * @throws InterruptedException 103 | */ 104 | boolean enter() throws KeeperException, InterruptedException{ 105 | zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE, 106 | CreateMode.EPHEMERAL_SEQUENTIAL); 107 | while (true) { 108 | synchronized (mutex) { 109 | List list = zk.getChildren(root, true); 110 | 111 | if (list.size() < size) { 112 | mutex.wait(); 113 | } else { 114 | return true; 115 | } 116 | } 117 | } 118 | } 119 | 120 | /** 121 | * 等待所有到达的 barrier 122 | * 123 | * @return 124 | * @throws KeeperException 125 | * @throws InterruptedException 126 | */ 127 | boolean leave() throws KeeperException, InterruptedException{ 128 | zk.delete(root + "/" + name, 0); 129 | while (true) { 130 | synchronized (mutex) { 131 | List list = zk.getChildren(root, true); 132 | if (list.size() > 0) { 133 | mutex.wait(); 134 | } else { 135 | return true; 136 | } 137 | } 138 | } 139 | } 140 | } 141 | 142 | /** 143 | * Producer-Consumer queue 144 | */ 145 | static public class Queue extends SyncPrimitive { 146 | 147 | /** 148 | * producer-consumer queue 构造函数 149 | * 150 | * @param address 151 | * @param name 152 | */ 153 | Queue(String address, String name) { 154 | super(address); 155 | this.root = name; 156 | // 创建 ZooKeeper 节点名称 157 | if (zk != null) { 158 | try { 159 | Stat s = zk.exists(root, false); 160 | if (s == null) { 161 | zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, 162 | CreateMode.PERSISTENT); 163 | } 164 | } catch (KeeperException e) { 165 | System.out 166 | .println("Keeper exception when instantiating queue: " 167 | + e.toString()); 168 | } catch (InterruptedException e) { 169 | System.out.println("Interrupted exception"); 170 | } 171 | } 172 | } 173 | 174 | /** 175 | * 添加元素到 queue. 176 | * 177 | * @param i 178 | * @return 179 | */ 180 | boolean produce(int i) throws KeeperException, InterruptedException{ 181 | ByteBuffer b = ByteBuffer.allocate(4); 182 | byte[] value; 183 | 184 | // 添加孩子 185 | b.putInt(i); 186 | value = b.array(); 187 | zk.create(root + "/element", value, Ids.OPEN_ACL_UNSAFE, 188 | CreateMode.PERSISTENT_SEQUENTIAL); 189 | 190 | return true; 191 | } 192 | 193 | 194 | /** 195 | * 移除 queue 中的第一个元素 196 | * 197 | * @return 198 | * @throws KeeperException 199 | * @throws InterruptedException 200 | */ 201 | int consume() throws KeeperException, InterruptedException{ 202 | int retvalue = -1; 203 | Stat stat = null; 204 | 205 | // 获取存在的第一个元素 206 | while (true) { 207 | synchronized (mutex) { 208 | List list = zk.getChildren(root, true); 209 | if (list.size() == 0) { 210 | System.out.println("Going to wait"); 211 | mutex.wait(); 212 | } else { 213 | Integer min = new Integer(list.get(0).substring(7)); 214 | for(String s : list){ 215 | Integer tempValue = new Integer(s.substring(7)); 216 | //System.out.println("Temporary value: " + tempValue); 217 | if(tempValue < min) min = tempValue; 218 | } 219 | System.out.println("Temporary value: " + root + "/element" + min); 220 | byte[] b = zk.getData(root + "/element" + min, 221 | false, stat); 222 | zk.delete(root + "/element" + min, 0); 223 | ByteBuffer buffer = ByteBuffer.wrap(b); 224 | retvalue = buffer.getInt(); 225 | 226 | return retvalue; 227 | } 228 | } 229 | } 230 | } 231 | } 232 | 233 | public static void main(String args[]) { 234 | if (args[0].equals("qTest")) 235 | queueTest(args); 236 | else 237 | barrierTest(args); 238 | 239 | } 240 | 241 | public static void queueTest(String args[]) { 242 | Queue q = new Queue(args[1], "/app1"); 243 | 244 | System.out.println("Input: " + args[1]); 245 | int i; 246 | Integer max = new Integer(args[2]); 247 | 248 | if (args[3].equals("p")) { 249 | System.out.println("Producer"); 250 | for (i = 0; i < max; i++) 251 | try{ 252 | q.produce(10 + i); 253 | } catch (KeeperException e){ 254 | 255 | } catch (InterruptedException e){ 256 | 257 | } 258 | } else { 259 | System.out.println("Consumer"); 260 | 261 | for (i = 0; i < max; i++) { 262 | try{ 263 | int r = q.consume(); 264 | System.out.println("Item: " + r); 265 | } catch (KeeperException e){ 266 | i--; 267 | } catch (InterruptedException e){ 268 | 269 | } 270 | } 271 | } 272 | } 273 | 274 | public static void barrierTest(String args[]) { 275 | Barrier b = new Barrier(args[1], "/b1", new Integer(args[2])); 276 | try{ 277 | boolean flag = b.enter(); 278 | System.out.println("Entered barrier: " + args[2]); 279 | if(!flag) System.out.println("Error when entering the barrier"); 280 | } catch (KeeperException e){ 281 | 282 | } catch (InterruptedException e){ 283 | 284 | } 285 | 286 | // 生成随机数 287 | Random rand = new Random(); 288 | int r = rand.nextInt(100); 289 | 290 | for (int i = 0; i < r; i++) { 291 | try { 292 | Thread.sleep(100); 293 | } catch (InterruptedException e) { 294 | 295 | } 296 | } 297 | try{ 298 | b.leave(); 299 | } catch (KeeperException e){ 300 | 301 | } catch (InterruptedException e){ 302 | 303 | } 304 | System.out.println("Left barrier"); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /samples/chapter8/sse-real-time-web/src/main/webapp/scripts/vendor/eventsource.js: -------------------------------------------------------------------------------- 1 | /** @license 2 | * eventsource.js 3 | * Available under MIT License (MIT) 4 | * https://github.com/Yaffle/EventSource/ 5 | */ 6 | 7 | /*jslint indent: 2, vars: true, plusplus: true */ 8 | /*global setTimeout, clearTimeout */ 9 | 10 | (function (global) { 11 | "use strict"; 12 | 13 | var setTimeout = global.setTimeout; 14 | var clearTimeout = global.clearTimeout; 15 | 16 | function Map() { 17 | this.data = {}; 18 | } 19 | 20 | Map.prototype.get = function (key) { 21 | return this.data[key + "~"]; 22 | }; 23 | Map.prototype.set = function (key, value) { 24 | this.data[key + "~"] = value; 25 | }; 26 | Map.prototype["delete"] = function (key) { 27 | delete this.data[key + "~"]; 28 | }; 29 | 30 | function EventTarget() { 31 | this.listeners = new Map(); 32 | } 33 | 34 | function throwError(e) { 35 | setTimeout(function () { 36 | throw e; 37 | }, 0); 38 | } 39 | 40 | EventTarget.prototype.dispatchEvent = function (event) { 41 | event.target = this; 42 | var type = event.type.toString(); 43 | var listeners = this.listeners; 44 | var typeListeners = listeners.get(type); 45 | if (typeListeners == undefined) { 46 | return; 47 | } 48 | var length = typeListeners.length; 49 | var i = -1; 50 | var listener = undefined; 51 | while (++i < length) { 52 | listener = typeListeners[i]; 53 | try { 54 | listener.call(this, event); 55 | } catch (e) { 56 | throwError(e); 57 | } 58 | } 59 | }; 60 | EventTarget.prototype.addEventListener = function (type, callback) { 61 | type = type.toString(); 62 | var listeners = this.listeners; 63 | var typeListeners = listeners.get(type); 64 | if (typeListeners == undefined) { 65 | typeListeners = []; 66 | listeners.set(type, typeListeners); 67 | } 68 | var i = typeListeners.length; 69 | while (--i >= 0) { 70 | if (typeListeners[i] === callback) { 71 | return; 72 | } 73 | } 74 | typeListeners.push(callback); 75 | }; 76 | EventTarget.prototype.removeEventListener = function (type, callback) { 77 | type = type.toString(); 78 | var listeners = this.listeners; 79 | var typeListeners = listeners.get(type); 80 | if (typeListeners == undefined) { 81 | return; 82 | } 83 | var length = typeListeners.length; 84 | var filtered = []; 85 | var i = -1; 86 | while (++i < length) { 87 | if (typeListeners[i] !== callback) { 88 | filtered.push(typeListeners[i]); 89 | } 90 | } 91 | if (filtered.length === 0) { 92 | listeners["delete"](type); 93 | } else { 94 | listeners.set(type, filtered); 95 | } 96 | }; 97 | 98 | function Event(type) { 99 | this.type = type; 100 | this.target = undefined; 101 | } 102 | 103 | function MessageEvent(type, options) { 104 | Event.call(this, type); 105 | this.data = options.data; 106 | this.lastEventId = options.lastEventId; 107 | } 108 | 109 | MessageEvent.prototype = Event.prototype; 110 | 111 | var XHR = global.XMLHttpRequest; 112 | var XDR = global.XDomainRequest; 113 | var isCORSSupported = XHR != undefined && (new XHR()).withCredentials != undefined; 114 | var Transport = isCORSSupported || (XHR != undefined && XDR == undefined) ? XHR : XDR; 115 | 116 | var WAITING = -1; 117 | var CONNECTING = 0; 118 | var OPEN = 1; 119 | var CLOSED = 2; 120 | var AFTER_CR = 3; 121 | var FIELD_START = 4; 122 | var FIELD = 5; 123 | var VALUE_START = 6; 124 | var VALUE = 7; 125 | var contentTypeRegExp = /^text\/event\-stream;?(\s*charset\=utf\-8)?$/i; 126 | 127 | var MINIMUM_DURATION = 1000; 128 | var MAXIMUM_DURATION = 18000000; 129 | 130 | function getDuration(value, def) { 131 | var n = value; 132 | if (n !== n) { 133 | n = def; 134 | } 135 | return (n < MINIMUM_DURATION ? MINIMUM_DURATION : (n > MAXIMUM_DURATION ? MAXIMUM_DURATION : n)); 136 | } 137 | 138 | function fire(that, f, event) { 139 | try { 140 | if (typeof f === "function") { 141 | f.call(that, event); 142 | } 143 | } catch (e) { 144 | throwError(e); 145 | } 146 | } 147 | 148 | function EventSource(url, options) { 149 | url = url.toString(); 150 | 151 | var withCredentials = isCORSSupported && options != undefined && Boolean(options.withCredentials); 152 | var initialRetry = getDuration(1000, 0); 153 | var heartbeatTimeout = getDuration(45000, 0); 154 | 155 | var lastEventId = ""; 156 | var that = this; 157 | var retry = initialRetry; 158 | var wasActivity = false; 159 | var CurrentTransport = options != undefined && options.Transport != undefined ? options.Transport : Transport; 160 | var xhr = new CurrentTransport(); 161 | var isXHR = CurrentTransport !== XDR; 162 | var timeout = 0; 163 | var timeout0 = 0; 164 | var charOffset = 0; 165 | var currentState = WAITING; 166 | var dataBuffer = []; 167 | var lastEventIdBuffer = ""; 168 | var eventTypeBuffer = ""; 169 | var onTimeout = undefined; 170 | 171 | var state = FIELD_START; 172 | var field = ""; 173 | var value = ""; 174 | 175 | function close() { 176 | currentState = CLOSED; 177 | if (xhr != undefined) { 178 | xhr.abort(); 179 | xhr = undefined; 180 | } 181 | if (timeout !== 0) { 182 | clearTimeout(timeout); 183 | timeout = 0; 184 | } 185 | if (timeout0 !== 0) { 186 | clearTimeout(timeout0); 187 | timeout0 = 0; 188 | } 189 | that.readyState = CLOSED; 190 | } 191 | 192 | function onEvent(type) { 193 | var responseText = ""; 194 | if (currentState === OPEN || currentState === CONNECTING) { 195 | try { 196 | responseText = xhr.responseText; 197 | } catch (error) { 198 | // IE 8 - 9 with XMLHttpRequest 199 | } 200 | } 201 | var event = undefined; 202 | var isWrongStatusCodeOrContentType = false; 203 | 204 | if (currentState === CONNECTING) { 205 | var status = 0; 206 | var statusText = ""; 207 | var contentType = undefined; 208 | if (isXHR) { 209 | try { 210 | status = xhr.status; 211 | statusText = xhr.statusText; 212 | contentType = xhr.getResponseHeader("Content-Type"); 213 | } catch (error) { 214 | // https://bugs.webkit.org/show_bug.cgi?id=29121 215 | status = 0; 216 | statusText = ""; 217 | contentType = undefined; 218 | // FF < 14, WebKit 219 | // https://bugs.webkit.org/show_bug.cgi?id=29658 220 | // https://bugs.webkit.org/show_bug.cgi?id=77854 221 | } 222 | } else if (type !== "" && type !== "error") { 223 | status = 200; 224 | statusText = "OK"; 225 | contentType = xhr.contentType; 226 | } 227 | if (contentType == undefined) { 228 | contentType = ""; 229 | } 230 | if (status === 0 && statusText === "" && type === "load" && responseText !== "") { 231 | status = 200; 232 | statusText = "OK"; 233 | if (contentType === "") { // Opera 12 234 | var tmp = (/^data\:([^,]*?)(?:;base64)?,[\S]*$/).exec(url); 235 | if (tmp != undefined) { 236 | contentType = tmp[1]; 237 | } 238 | } 239 | } 240 | if (status === 200 && contentTypeRegExp.test(contentType)) { 241 | currentState = OPEN; 242 | wasActivity = true; 243 | retry = initialRetry; 244 | that.readyState = OPEN; 245 | event = new Event("open"); 246 | that.dispatchEvent(event); 247 | fire(that, that.onopen, event); 248 | if (currentState === CLOSED) { 249 | return; 250 | } 251 | } else { 252 | // Opera 12 253 | if (status !== 0 && (status !== 200 || contentType !== "")) { 254 | var message = ""; 255 | if (status !== 200) { 256 | message = "EventSource's response has a status " + status + " " + statusText.replace(/\s+/g, " ") + " that is not 200. Aborting the connection."; 257 | } else { 258 | message = "EventSource's response has a Content-Type specifying an unsupported type: " + contentType.replace(/\s+/g, " ") + ". Aborting the connection."; 259 | } 260 | setTimeout(function () { 261 | throw new Error(message); 262 | }, 0); 263 | isWrongStatusCodeOrContentType = true; 264 | } 265 | } 266 | } 267 | 268 | if (currentState === OPEN) { 269 | if (responseText.length > charOffset) { 270 | wasActivity = true; 271 | } 272 | var i = charOffset - 1; 273 | var length = responseText.length; 274 | var c = "\n"; 275 | while (++i < length) { 276 | c = responseText.charAt(i); 277 | if (state === AFTER_CR && c === "\n") { 278 | state = FIELD_START; 279 | } else { 280 | if (state === AFTER_CR) { 281 | state = FIELD_START; 282 | } 283 | if (c === "\r" || c === "\n") { 284 | if (field === "data") { 285 | dataBuffer.push(value); 286 | } else if (field === "id") { 287 | lastEventIdBuffer = value; 288 | } else if (field === "event") { 289 | eventTypeBuffer = value; 290 | } else if (field === "retry") { 291 | initialRetry = getDuration(Number(value), initialRetry); 292 | retry = initialRetry; 293 | } else if (field === "heartbeatTimeout") { 294 | heartbeatTimeout = getDuration(Number(value), heartbeatTimeout); 295 | if (timeout !== 0) { 296 | clearTimeout(timeout); 297 | timeout = setTimeout(onTimeout, heartbeatTimeout); 298 | } 299 | } 300 | value = ""; 301 | field = ""; 302 | if (state === FIELD_START) { 303 | if (dataBuffer.length !== 0) { 304 | lastEventId = lastEventIdBuffer; 305 | if (eventTypeBuffer === "") { 306 | eventTypeBuffer = "message"; 307 | } 308 | event = new MessageEvent(eventTypeBuffer, { 309 | data: dataBuffer.join("\n"), 310 | lastEventId: lastEventIdBuffer 311 | }); 312 | that.dispatchEvent(event); 313 | if (eventTypeBuffer === "message") { 314 | fire(that, that.onmessage, event); 315 | } 316 | if (currentState === CLOSED) { 317 | return; 318 | } 319 | } 320 | dataBuffer.length = 0; 321 | eventTypeBuffer = ""; 322 | } 323 | state = c === "\r" ? AFTER_CR : FIELD_START; 324 | } else { 325 | if (state === FIELD_START) { 326 | state = FIELD; 327 | } 328 | if (state === FIELD) { 329 | if (c === ":") { 330 | state = VALUE_START; 331 | } else { 332 | field += c; 333 | } 334 | } else if (state === VALUE_START) { 335 | if (c !== " ") { 336 | value += c; 337 | } 338 | state = VALUE; 339 | } else if (state === VALUE) { 340 | value += c; 341 | } 342 | } 343 | } 344 | } 345 | charOffset = length; 346 | } 347 | 348 | if ((currentState === OPEN || currentState === CONNECTING) && 349 | (type === "load" || type === "error" || isWrongStatusCodeOrContentType || (charOffset > 1024 * 1024) || (timeout === 0 && !wasActivity))) { 350 | if (isWrongStatusCodeOrContentType) { 351 | close(); 352 | } else { 353 | if (type === "" && timeout === 0 && !wasActivity) { 354 | setTimeout(function () { 355 | throw new Error("No activity within " + heartbeatTimeout + " milliseconds. Reconnecting."); 356 | }, 0); 357 | } 358 | currentState = WAITING; 359 | xhr.abort(); 360 | if (timeout !== 0) { 361 | clearTimeout(timeout); 362 | timeout = 0; 363 | } 364 | if (retry > initialRetry * 16) { 365 | retry = initialRetry * 16; 366 | } 367 | if (retry > MAXIMUM_DURATION) { 368 | retry = MAXIMUM_DURATION; 369 | } 370 | timeout = setTimeout(onTimeout, retry); 371 | retry = retry * 2 + 1; 372 | 373 | that.readyState = CONNECTING; 374 | } 375 | event = new Event("error"); 376 | that.dispatchEvent(event); 377 | fire(that, that.onerror, event); 378 | } else { 379 | if (timeout === 0) { 380 | wasActivity = false; 381 | timeout = setTimeout(onTimeout, heartbeatTimeout); 382 | } 383 | } 384 | } 385 | 386 | function onProgress() { 387 | onEvent("progress"); 388 | } 389 | 390 | function onLoad() { 391 | onEvent("load"); 392 | } 393 | 394 | function onError() { 395 | onEvent("error"); 396 | } 397 | 398 | function onReadyStateChange() { 399 | if (xhr.readyState === 4) { 400 | if (xhr.status === 0) { 401 | onEvent("error"); 402 | } else { 403 | onEvent("load"); 404 | } 405 | } else { 406 | onEvent("progress"); 407 | } 408 | } 409 | 410 | if (isXHR && global.opera != undefined) { 411 | // workaround for Opera issue with "progress" events 412 | timeout0 = setTimeout(function f() { 413 | if (xhr.readyState === 3) { 414 | onEvent("progress"); 415 | } 416 | timeout0 = setTimeout(f, 500); 417 | }, 0); 418 | } 419 | 420 | onTimeout = function () { 421 | timeout = 0; 422 | if (currentState !== WAITING) { 423 | onEvent(""); 424 | return; 425 | } 426 | 427 | // loading indicator in Safari, Chrome < 14 428 | if (isXHR && !("onloadend" in xhr) && global.document != undefined && global.document.readyState != undefined && global.document.readyState !== "complete") { 429 | timeout = setTimeout(onTimeout, 4); 430 | return; 431 | } 432 | 433 | // XDomainRequest#abort removes onprogress, onerror, onload 434 | xhr.onload = onLoad; 435 | xhr.onerror = onError; 436 | 437 | if (isXHR) { 438 | // improper fix to match Firefox behaviour, but it is better than just ignore abort 439 | // see https://bugzilla.mozilla.org/show_bug.cgi?id=768596 440 | // https://bugzilla.mozilla.org/show_bug.cgi?id=880200 441 | // https://code.google.com/p/chromium/issues/detail?id=153570 442 | xhr.onabort = onError; 443 | } 444 | if (isXHR && !("ontimeout" in xhr) || !("onloadend" in xhr)) { 445 | // Firefox 3.5 - 3.6 - ? < 9.0 446 | // onprogress is not fired sometimes or delayed 447 | // IE 8-9 (XMLHTTPRequest) 448 | xhr.onreadystatechange = onReadyStateChange; 449 | } 450 | 451 | // loading indicator in Firefox 452 | // https://bugzilla.mozilla.org/show_bug.cgi?id=736723 453 | if (xhr.sendAsBinary == undefined) { 454 | xhr.onprogress = onProgress; 455 | } 456 | 457 | wasActivity = false; 458 | timeout = setTimeout(onTimeout, heartbeatTimeout); 459 | 460 | charOffset = 0; 461 | currentState = CONNECTING; 462 | dataBuffer.length = 0; 463 | eventTypeBuffer = ""; 464 | lastEventIdBuffer = lastEventId; 465 | value = ""; 466 | field = ""; 467 | state = FIELD_START; 468 | 469 | var s = url.slice(0, 5); 470 | if (s !== "data:" && s !== "blob:") { 471 | s = url + ((url.indexOf("?", 0) === -1 ? "?" : "&") + "lastEventId=" + encodeURIComponent(lastEventId) + "&r=" + (Math.random() + 1).toString().slice(2)); 472 | } else { 473 | s = url; 474 | } 475 | xhr.open("GET", s, true); 476 | 477 | if (isXHR) { 478 | // withCredentials should be set after "open" for Safari and Chrome (< 19 ?) 479 | xhr.withCredentials = withCredentials; 480 | 481 | xhr.responseType = "text"; 482 | 483 | // Request header field Cache-Control is not allowed by Access-Control-Allow-Headers. 484 | // "Cache-control: no-cache" are not honored in Chrome and Firefox 485 | // https://bugzilla.mozilla.org/show_bug.cgi?id=428916 486 | //xhr.setRequestHeader("Cache-Control", "no-cache"); 487 | xhr.setRequestHeader("Accept", "text/event-stream"); 488 | // Request header field Last-Event-ID is not allowed by Access-Control-Allow-Headers. 489 | //xhr.setRequestHeader("Last-Event-ID", lastEventId); 490 | } 491 | 492 | xhr.send(undefined); 493 | }; 494 | 495 | EventTarget.call(this); 496 | this.close = close; 497 | this.url = url; 498 | this.readyState = CONNECTING; 499 | this.withCredentials = withCredentials; 500 | 501 | this.onopen = undefined; 502 | this.onmessage = undefined; 503 | this.onerror = undefined; 504 | 505 | onTimeout(); 506 | } 507 | 508 | function F() { 509 | this.CONNECTING = CONNECTING; 510 | this.OPEN = OPEN; 511 | this.CLOSED = CLOSED; 512 | } 513 | F.prototype = EventTarget.prototype; 514 | 515 | EventSource.prototype = new F(); 516 | F.call(EventSource); 517 | if (isCORSSupported) { 518 | EventSource.prototype.withCredentials = undefined; 519 | } 520 | 521 | var isEventSourceSupported = function () { 522 | // Opera 12 fails this test, but this is fine. 523 | return global.EventSource != undefined && ("withCredentials" in global.EventSource.prototype); 524 | }; 525 | 526 | if (Transport != undefined && (global.EventSource == undefined || (isCORSSupported && !isEventSourceSupported()))) { 527 | // Why replace a native EventSource ? 528 | // https://bugzilla.mozilla.org/show_bug.cgi?id=444328 529 | // https://bugzilla.mozilla.org/show_bug.cgi?id=831392 530 | // https://code.google.com/p/chromium/issues/detail?id=260144 531 | // https://code.google.com/p/chromium/issues/detail?id=225654 532 | // ... 533 | global.NativeEventSource = global.EventSource; 534 | global.EventSource = EventSource; 535 | } 536 | 537 | }(this)); --------------------------------------------------------------------------------