├── 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 |
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 | 
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 | 
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));
--------------------------------------------------------------------------------