├── .gitignore
├── README.md
├── code_count.sh
├── common
├── pom.xml
└── src
│ ├── main
│ ├── assembly
│ │ ├── package-common-jar.xml
│ │ └── shared-jar-pom.xml
│ └── java
│ │ └── com
│ │ └── sumory
│ │ └── gru
│ │ └── common
│ │ ├── config
│ │ └── Config.java
│ │ ├── domain
│ │ └── StatObject.java
│ │ ├── id
│ │ ├── BaseN.java
│ │ └── IdWorker.java
│ │ ├── utils
│ │ ├── BitSetUtil.java
│ │ ├── CollectionUtils.java
│ │ ├── DataCodec.java
│ │ ├── DateUtil.java
│ │ ├── IdUtil.java
│ │ ├── RedisUtil.java
│ │ └── TokenUtil.java
│ │ └── zk
│ │ ├── NotifyListener.java
│ │ ├── ZkClientWatcher.java
│ │ └── ZkNode.java
│ └── test
│ └── java
│ └── com
│ └── sumory
│ └── gru
│ └── common
│ └── utils
│ ├── DataCodecTest.java
│ ├── DateUtilsTest.java
│ └── IdUtilsTest.java
├── docs
├── cluster_install.md
├── dashboard-client.png
├── dashboard-monitor.png
├── gru_api.md
├── 架构草图0.2.jpg
└── 登陆策略.md
├── idgen
├── pom.xml
└── src
│ └── main
│ ├── assembly
│ ├── package-idgen-jar.xml
│ └── shared-jar-pom.xml
│ ├── java
│ └── com
│ │ └── sumory
│ │ └── gru
│ │ └── idgen
│ │ ├── id
│ │ ├── IdUtils.java
│ │ └── IdWorker.java
│ │ ├── main
│ │ └── IdGenMain.java
│ │ └── service
│ │ ├── IdService.java
│ │ └── impl
│ │ └── IdServiceImpl.java
│ └── resources
│ ├── bin
│ ├── dump.sh
│ ├── start.sh
│ └── stop.sh
│ ├── profiles
│ ├── dev
│ │ ├── dubbo.properties
│ │ ├── log4j2.xml
│ │ └── system.properties
│ └── test1
│ │ ├── dubbo.properties
│ │ ├── log4j2.xml
│ │ └── system.properties
│ └── spring
│ ├── applicationContext-provider-idgen.xml
│ ├── applicationContext.xml
│ └── dubbo.xsd
├── minions
├── .gitignore
├── Makefile
├── README.md
├── app.js
├── config
│ ├── dev.js
│ ├── index.js
│ ├── prod.js
│ └── test.js
├── lib
│ ├── log.js
│ ├── redisUtils.js
│ ├── utils.js
│ └── zkUtils.js
├── models
│ └── adminModel.js
├── package.json
├── public
│ ├── css
│ │ ├── animate.min.css
│ │ ├── bootstrap.css
│ │ └── minions.css
│ ├── img
│ │ └── gru.png
│ ├── js
│ │ ├── gru
│ │ │ └── monitor.js
│ │ ├── jquery-2.1.3.min.js
│ │ ├── moment.min.js
│ │ └── socket.io
│ │ │ └── socket.io.js
│ └── semantic
│ │ ├── components
│ │ ├── accordion.css
│ │ ├── accordion.js
│ │ ├── accordion.min.css
│ │ ├── accordion.min.js
│ │ ├── ad.css
│ │ ├── ad.min.css
│ │ ├── api.js
│ │ ├── api.min.js
│ │ ├── breadcrumb.css
│ │ ├── breadcrumb.min.css
│ │ ├── button.css
│ │ ├── button.min.css
│ │ ├── card.css
│ │ ├── card.min.css
│ │ ├── checkbox.css
│ │ ├── checkbox.js
│ │ ├── checkbox.min.css
│ │ ├── checkbox.min.js
│ │ ├── comment.css
│ │ ├── comment.min.css
│ │ ├── dimmer.css
│ │ ├── dimmer.js
│ │ ├── dimmer.min.css
│ │ ├── dimmer.min.js
│ │ ├── divider.css
│ │ ├── divider.min.css
│ │ ├── dropdown.css
│ │ ├── dropdown.js
│ │ ├── dropdown.min.css
│ │ ├── dropdown.min.js
│ │ ├── feed.css
│ │ ├── feed.min.css
│ │ ├── flag.css
│ │ ├── flag.min.css
│ │ ├── form.css
│ │ ├── form.js
│ │ ├── form.min.css
│ │ ├── form.min.js
│ │ ├── grid.css
│ │ ├── grid.min.css
│ │ ├── header.css
│ │ ├── header.min.css
│ │ ├── icon.css
│ │ ├── icon.min.css
│ │ ├── image.css
│ │ ├── image.min.css
│ │ ├── input.css
│ │ ├── input.min.css
│ │ ├── item.css
│ │ ├── item.min.css
│ │ ├── label.css
│ │ ├── label.min.css
│ │ ├── list.css
│ │ ├── list.min.css
│ │ ├── loader.css
│ │ ├── loader.min.css
│ │ ├── menu.css
│ │ ├── menu.min.css
│ │ ├── message.css
│ │ ├── message.min.css
│ │ ├── modal.css
│ │ ├── modal.js
│ │ ├── modal.min.css
│ │ ├── modal.min.js
│ │ ├── nag.css
│ │ ├── nag.js
│ │ ├── nag.min.css
│ │ ├── nag.min.js
│ │ ├── popup.css
│ │ ├── popup.js
│ │ ├── popup.min.css
│ │ ├── popup.min.js
│ │ ├── progress.css
│ │ ├── progress.js
│ │ ├── progress.min.css
│ │ ├── progress.min.js
│ │ ├── rail.css
│ │ ├── rail.min.css
│ │ ├── rating.css
│ │ ├── rating.js
│ │ ├── rating.min.css
│ │ ├── rating.min.js
│ │ ├── reset.css
│ │ ├── reset.min.css
│ │ ├── reveal.css
│ │ ├── reveal.min.css
│ │ ├── search.css
│ │ ├── search.js
│ │ ├── search.min.css
│ │ ├── search.min.js
│ │ ├── segment.css
│ │ ├── segment.min.css
│ │ ├── shape.css
│ │ ├── shape.js
│ │ ├── shape.min.css
│ │ ├── shape.min.js
│ │ ├── sidebar.css
│ │ ├── sidebar.js
│ │ ├── sidebar.min.css
│ │ ├── sidebar.min.js
│ │ ├── site.css
│ │ ├── site.js
│ │ ├── site.min.css
│ │ ├── site.min.js
│ │ ├── state.js
│ │ ├── state.min.js
│ │ ├── statistic.css
│ │ ├── statistic.min.css
│ │ ├── step.css
│ │ ├── step.min.css
│ │ ├── sticky.css
│ │ ├── sticky.js
│ │ ├── sticky.min.css
│ │ ├── sticky.min.js
│ │ ├── tab.css
│ │ ├── tab.js
│ │ ├── tab.min.css
│ │ ├── tab.min.js
│ │ ├── table.css
│ │ ├── table.min.css
│ │ ├── transition.css
│ │ ├── transition.js
│ │ ├── transition.min.css
│ │ ├── transition.min.js
│ │ ├── video.css
│ │ ├── video.js
│ │ ├── video.min.css
│ │ ├── video.min.js
│ │ ├── visibility.js
│ │ └── visibility.min.js
│ │ ├── semantic.css
│ │ ├── semantic.js
│ │ ├── semantic.min.css
│ │ ├── semantic.min.js
│ │ └── themes
│ │ ├── basic
│ │ └── assets
│ │ │ └── fonts
│ │ │ ├── icons.eot
│ │ │ ├── icons.svg
│ │ │ ├── icons.ttf
│ │ │ └── icons.woff
│ │ └── default
│ │ └── assets
│ │ ├── fonts
│ │ ├── icons.eot
│ │ ├── icons.otf
│ │ ├── icons.svg
│ │ ├── icons.ttf
│ │ ├── icons.woff
│ │ └── icons.woff2
│ │ └── images
│ │ └── flags.png
├── route.js
├── routes
│ ├── mockRouter.js
│ └── monitorRouter.js
├── start_prod.sh
├── start_test.sh
├── test
│ ├── redis.js
│ └── utils.js
└── views
│ ├── common
│ ├── header.ejs
│ ├── meta.ejs
│ └── sidebar.ejs
│ ├── error.ejs
│ ├── index.ejs
│ └── monitor.ejs
├── pom.xml
├── spear-client
├── docs
│ └── nett-socket.io.bench.txt
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sumory
│ │ │ └── gru
│ │ │ └── spear
│ │ │ └── client
│ │ │ ├── Main.java
│ │ │ ├── msg
│ │ │ └── MsgMain.java
│ │ │ └── pojo
│ │ │ ├── ChatObject.java
│ │ │ └── User.java
│ └── resources
│ │ ├── bin
│ │ ├── start.sh
│ │ └── start_msg.sh
│ │ └── profiles
│ │ └── test
│ │ └── log4j.properties
│ └── test
│ └── java
│ └── com
│ └── sumory
│ └── gru
│ └── spear
│ └── client
│ └── Test.java
├── spear
├── docs
│ ├── bench_1000clients.txt
│ ├── bench_200clients.txt
│ ├── bench_log.txt
│ ├── nett-socket.io.bench.txt
│ ├── performance.txt
│ └── 选型.md
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sumory
│ │ │ └── gru
│ │ │ └── spear
│ │ │ ├── SpearContext.java
│ │ │ ├── domain
│ │ │ ├── AuthObject.java
│ │ │ ├── Client.java
│ │ │ ├── CommonResult.java
│ │ │ ├── Group.java
│ │ │ ├── MsgObject.java
│ │ │ ├── MsgType.java
│ │ │ ├── ResultCode.java
│ │ │ ├── SubscribeObject.java
│ │ │ └── User.java
│ │ │ ├── extention
│ │ │ ├── IAck.java
│ │ │ ├── LogAck.java
│ │ │ ├── RabbitMQAck.java
│ │ │ ├── ReceiverDataListener.java
│ │ │ └── SenderDataListener.java
│ │ │ ├── main
│ │ │ └── SpearMain.java
│ │ │ ├── message
│ │ │ ├── BaseMessage.java
│ │ │ └── StringMessage.java
│ │ │ ├── monitor
│ │ │ └── MonitorServer.java
│ │ │ ├── server
│ │ │ ├── ActionListener.java
│ │ │ └── SpearServer.java
│ │ │ ├── service
│ │ │ ├── InnerIdService.java
│ │ │ └── InnerStatService.java
│ │ │ ├── task
│ │ │ └── UserStat.java
│ │ │ ├── thread
│ │ │ ├── ExecutesManager.java
│ │ │ ├── NameThreadFactory.java
│ │ │ └── NamedThreadFactory.java
│ │ │ ├── transport
│ │ │ ├── IReceiver.java
│ │ │ ├── ISender.java
│ │ │ ├── inner
│ │ │ │ ├── InnerReceiver.java
│ │ │ │ └── InnerSender.java
│ │ │ ├── redis
│ │ │ │ ├── RedisListener.java
│ │ │ │ ├── RedisReceiver.java
│ │ │ │ └── RedisSender.java
│ │ │ └── rocketmq
│ │ │ │ ├── RocketMQReceiver.java
│ │ │ │ └── RocketMQSender.java
│ │ │ └── zk
│ │ │ ├── SpearNotifyListener.java
│ │ │ ├── SpearZkClientWatcher.java
│ │ │ └── ZkUtil.java
│ └── resources
│ │ ├── bin
│ │ └── start.sh
│ │ ├── profiles
│ │ ├── dev
│ │ │ ├── dubbo.properties
│ │ │ ├── log4j2.xml
│ │ │ └── system.properties
│ │ ├── test1
│ │ │ ├── dubbo.properties
│ │ │ ├── log4j2.xml
│ │ │ └── system.properties
│ │ └── test2
│ │ │ ├── dubbo.properties
│ │ │ ├── log4j2.xml
│ │ │ └── system.properties
│ │ └── spring
│ │ ├── applicationContext-consumer-idgen.xml
│ │ ├── applicationContext-consumer-stat.xml
│ │ └── applicationContext.xml
│ └── test
│ └── java
│ └── com
│ └── sumory
│ └── gru
│ └── spear
│ ├── Test.java
│ ├── mq
│ ├── Consumer.java
│ └── Producer.java
│ └── zk
│ └── ZkTest.java
├── stat
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ ├── assembly
│ │ ├── package-stat-jar.xml
│ │ └── shared-jar-pom.xml
│ ├── java
│ │ └── com
│ │ │ └── sumory
│ │ │ └── gru
│ │ │ └── stat
│ │ │ ├── context
│ │ │ └── StatContext.java
│ │ │ ├── main
│ │ │ └── StatMain.java
│ │ │ ├── redis
│ │ │ └── RedisStore.java
│ │ │ ├── service
│ │ │ ├── StatService.java
│ │ │ └── impl
│ │ │ │ └── StatServiceImpl.java
│ │ │ ├── servlet
│ │ │ ├── StatServlet.java
│ │ │ └── User.java
│ │ │ └── zk
│ │ │ ├── StatNotifyListener.java
│ │ │ ├── StatZkClientWatcher.java
│ │ │ └── ZkUtil.java
│ └── resources
│ │ ├── bin
│ │ ├── dump.sh
│ │ ├── start.sh
│ │ └── stop.sh
│ │ ├── profiles
│ │ ├── dev
│ │ │ ├── dubbo.properties
│ │ │ ├── log4j2.xml
│ │ │ └── system.properties
│ │ ├── test1
│ │ │ ├── dubbo.properties
│ │ │ ├── log4j2.xml
│ │ │ └── system.properties
│ │ └── test2
│ │ │ ├── dubbo.properties
│ │ │ ├── log4j2.xml
│ │ │ └── system.properties
│ │ └── spring
│ │ ├── applicationContext-provider-stat.xml
│ │ ├── applicationContext.xml
│ │ └── dubbo.xsd
│ └── test
│ └── java
│ └── com
│ └── sumory
│ └── gru
│ └── common
│ └── utils
│ └── RedisStoreTest.java
└── ticket
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── sumory
│ │ └── gru
│ │ └── ticket
│ │ ├── common
│ │ ├── Node.java
│ │ └── Shard.java
│ │ ├── domain
│ │ └── Ticket.java
│ │ ├── main
│ │ └── TicketMain.java
│ │ ├── server
│ │ ├── HttpHandler.java
│ │ └── TicketServer.java
│ │ └── zk
│ │ ├── TicketNotifyListener.java
│ │ ├── TicketZkClientWatcher.java
│ │ └── ZkUtil.java
└── resources
│ ├── bin
│ ├── start.sh
│ └── stop.sh
│ └── profiles
│ ├── dev
│ ├── log4j2.xml
│ └── system.properties
│ ├── test1
│ ├── log4j2.xml
│ └── system.properties
│ └── test2
│ ├── log4j2.xml
│ └── system.properties
└── test
└── java
└── com
└── sumory
└── gru
└── ticket
└── common
└── test
├── ConsistentHash.java
├── ConsistentHashing.java
├── MockSpearNode.java
├── ShardTest.java
└── ZooKeeperTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | /test/
2 | /out/
3 | .svn
4 | .idea
5 | .DS_Store
6 | .project
7 | .metadata/
8 | .settings/
9 | .classpath
10 | target
11 | *.via
12 | *.tmp
13 | *.err
14 | *.log
15 | *.log.*
16 | *.iml
17 | .factorypath
18 | node_modules
19 | myconf
20 | trash
21 | src/main/resources/profiles/myself
22 | dump.rdb
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Gru
2 |
3 | **Gru**是一个长连接服务解决方案,可用于各种类型的实时交互应用。
4 |
5 |
6 | #### 目前主要模块
7 |
8 | - common 通用的一些代码、工具类等
9 | - idgen 消息id、某些业务id生成服务
10 | - ticket
11 | - 处于最前端,客户端首先请求该服务拿到ticket才能建立长连接
12 | - 目前实现了基于一致性hash的负载均衡,将客户端路由到不同的spear节点
13 | - 其它位于前端的策略,如准入机制等可在此模块扩展
14 | - stat 统计模块、业务模块
15 | - 目前实现了在线统计,数据存储在redis
16 | - 若还需要后端接入其他业务服务,可在此模块扩展
17 | - spear 长连接服务模块
18 | - 提供长连接接入的模块
19 | - 最小化模式部署时只需要此模块的一个实例即可
20 | - minions 监控模块
21 | - 目前可监控集群长连接服务spear节点数、每个节点的用户数等
22 | - 节点间管理、用户管理可在此模块扩展
23 | - spear-client 客户端模拟,压测示例代码
24 | - 示例项目: [gru-example](https://github.com/sumory/gru-example)
25 | - 以IM作为示例展示基于Gru构建实时应用
26 | - 支持群聊和单聊
27 |
28 |
29 | #### 特性
30 |
31 | - 支持单点部署和集群模式部署
32 | - 采用[socket.io](http://socket.io)协议
33 | - 各模块均支持水平扩展
34 | - 单节点可服务10W+以上长连接,具体为在不断发消息的情况下(1000条/秒),单长连接服务节点支持的稳定连接数量在10W+(8核16G)
35 | - 节点间通讯支持多种方式:进程内、redis、rocketmq
36 |
37 |
38 | #### 安装部署
39 |
40 | git clone https://github.com/sumory/gru.git /data/tmp/gru
41 |
42 | ##### 最小安装,单点部署
43 |
44 | 如果只想体验单节点的部署模式,不需要监控、负载均衡及其它业务服务,只需要部署一个spear节点即可。部署方式如下:
45 |
46 | ```
47 | cd /data/tmp/gru
48 | mvn install #本地安装
49 | cd spear
50 | mvn clean package -Pdev #生成spear可运行包,使用dev的配置文件
51 | cd target/release #该目录下有所有spear运行需要的文件
52 | ll conf # 主要配置文件位于conf/system.properties下,默认以single模式运行,不依赖zookeeper/redis等
53 | sh bin/start.sh # 启动
54 |
55 | ```
56 | 以上步骤完成后即可在本地的31001端口开启一个spear实例监听,等待客户端连接。
57 |
58 | 若想查看如何使用该spear实例,请移步[gru-example](https://github.com/sumory/gru-example),启动该项目,访问http://127.0.0.1:60000即可。
59 |
60 |
61 | ##### 集群方式部署
62 |
63 | 集群的安装配置较为复杂,详见[Gru集群安装配置](docs/cluster_install.md)
64 |
65 |
66 | #### 监控后台screenshots
67 |
68 |
69 |
70 |  |
71 |  |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/code_count.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cloc .
4 | #cloc . --exclude-lang=Javascript,CSS
5 |
--------------------------------------------------------------------------------
/common/src/main/assembly/package-common-jar.xml:
--------------------------------------------------------------------------------
1 |
5 | dist
6 |
7 | jar
8 |
9 | false
10 |
11 |
12 | ${project.build.directory}/classes
13 |
16 |
17 |
18 | *.xml
19 |
20 | /
21 |
22 |
23 |
24 |
31 |
32 |
--------------------------------------------------------------------------------
/common/src/main/assembly/shared-jar-pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | gru
6 | common
7 | gru-common-shared-jar
8 | 0.0.1-SNAPSHOT
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/common/src/main/java/com/sumory/gru/common/config/Config.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.config;
2 |
3 | import java.io.IOException;
4 | import java.util.Enumeration;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.Properties;
8 |
9 | /**
10 | * 配置
11 | *
12 | * @author sumory.wu
13 | * @date 2015年1月21日 上午10:41:42
14 | */
15 | public class Config {
16 | private static Properties properties;
17 | private static volatile boolean isLoad;
18 |
19 | static {
20 | synchronized (Config.class) {
21 | load();
22 | }
23 | }
24 |
25 | public static String get(String key) {
26 | if (!isLoad)
27 | load();
28 | return properties.getProperty(key);
29 | }
30 |
31 | public static Map getConfig() {
32 | Map result = new HashMap<>();
33 | if (!isLoad)
34 | load();
35 | Enumeration e = (Enumeration) properties.propertyNames();
36 | while (e.hasMoreElements()) {
37 | String k = e.nextElement();
38 | result.put(k, get(k));
39 | }
40 | return result;
41 | }
42 |
43 | private static void load() {
44 | properties = new Properties();
45 | try {
46 | properties.load(Config.class.getClassLoader().getResourceAsStream("system.properties"));
47 | isLoad = true;
48 | }
49 | catch (IOException e) {
50 | System.out.println("can not load the properties");
51 | e.printStackTrace();
52 | System.exit(-1);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/common/src/main/java/com/sumory/gru/common/domain/StatObject.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.domain;
2 |
3 | import java.io.Serializable;
4 | import java.util.BitSet;
5 |
6 | /**
7 | * 群组状态统计
8 | *
9 | * @author sumory.wu
10 | * @date 2015年4月20日 下午3:11:18
11 | */
12 | public class StatObject implements Serializable {
13 | private static final long serialVersionUID = 1L;
14 |
15 | private boolean isEmpty;//bitSet是否为空,即是否有学生
16 | private long groupId;
17 | private int base;//bitset中所有值的基值,如base为5,bitset里存的是1,2,3,那么实际值则是6,7,8
18 | private String extra;//扩展数据
19 | private transient BitSet bitSet;//dubbo默认的hessian无法序列化BitSet,所以用bytes[]代替,直接不序列化该字段
20 | private byte[] bitSetBytes;//dubbo无法序列化BitSet,这里先转化为字节处理
21 |
22 | public StatObject() {
23 |
24 | }
25 |
26 | public StatObject(boolean isEmpty, long groupId, String extra) {
27 | this.isEmpty = isEmpty;
28 | this.groupId = groupId;
29 | this.extra = extra;
30 | }
31 |
32 | public StatObject(boolean isEmpty, long groupId, String extra, int base, BitSet bitSet,
33 | byte[] bitSetBytes) {
34 | this.isEmpty = isEmpty;
35 | this.groupId = groupId;
36 | this.base = base;
37 | this.bitSet = bitSet;
38 | this.extra = extra;
39 | this.bitSetBytes = bitSetBytes;
40 | }
41 |
42 | public boolean isEmpty() {
43 | return isEmpty;
44 | }
45 |
46 | public void setEmpty(boolean isEmpty) {
47 | this.isEmpty = isEmpty;
48 | }
49 |
50 | public int getBase() {
51 | return base;
52 | }
53 |
54 | public void setBase(int base) {
55 | this.base = base;
56 | }
57 |
58 | public BitSet getBitSet() {
59 | return bitSet;
60 | }
61 |
62 | public void setBitSet(BitSet bitSet) {
63 | this.bitSet = bitSet;
64 | }
65 |
66 | public String getExtra() {
67 | return extra;
68 | }
69 |
70 | public void setExtra(String extra) {
71 | this.extra = extra;
72 | }
73 |
74 | public long getGroupId() {
75 | return groupId;
76 | }
77 |
78 | public void setGroupId(long groupId) {
79 | this.groupId = groupId;
80 | }
81 |
82 | public byte[] getBitSetBytes() {
83 | return bitSetBytes;
84 | }
85 |
86 | public void setBitSetBytes(byte[] bitSetBytes) {
87 | this.bitSetBytes = bitSetBytes;
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/common/src/main/java/com/sumory/gru/common/utils/CollectionUtils.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.utils;
2 |
3 | import java.util.Collection;
4 | import java.util.Map;
5 |
6 | public class CollectionUtils {
7 | public static boolean isEmpty(Collection> collection) {
8 | return (collection == null || collection.isEmpty());
9 | }
10 |
11 | public static boolean isEmpty(Map, ?> map) {
12 | return (map == null || map.isEmpty());
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/common/src/main/java/com/sumory/gru/common/utils/DataCodec.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.utils;
2 |
3 | import in.srain.binpack.BinPack;
4 |
5 | import java.util.Map;
6 |
7 | /**
8 | * 编解码工具
9 | *
10 | * @author sumory.wu
11 | * @date 2015年1月18日 下午3:52:07
12 | */
13 | public class DataCodec {
14 |
15 | public static byte[] encode(Map obj) {
16 | return BinPack.encode(obj, "UTF-8");
17 | }
18 |
19 | @SuppressWarnings("unchecked")
20 | public static Map decode(byte[] obj) {
21 | return (Map) BinPack.decode(obj, "UTF-8");
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/common/src/main/java/com/sumory/gru/common/utils/DateUtil.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.utils;
2 |
3 | import java.text.ParseException;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Date;
6 |
7 | public class DateUtil {
8 |
9 | private static final ThreadLocal DATE_FORMAT = new ThreadLocal() {
10 | @Override
11 | protected SimpleDateFormat initialValue() {
12 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
13 | //format.setTimeZone(TimeZone.getTimeZone("GMT"));
14 | return format;
15 | }
16 | };
17 |
18 | public static String toDateTimeString(Date datetime) {
19 | return DATE_FORMAT.get().format(datetime);
20 | }
21 |
22 | public static Date parseDateTimeFromString(String datetime) {
23 | Date date = tryToParse(datetime, DATE_FORMAT.get());
24 | return date;
25 | }
26 |
27 | public static long parseDateTimeToMilliseconds(String datetime) {
28 | Date date = parseDateTimeFromString(datetime);
29 | if (date != null) {
30 | return date.getTime();
31 | }
32 | return 0;
33 | }
34 |
35 | private static Date tryToParse(String datetime, SimpleDateFormat format) {
36 | try {
37 | return format.parse(datetime);
38 | }
39 | catch (ParseException e) {
40 | return null;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/common/src/main/java/com/sumory/gru/common/utils/IdUtil.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.utils;
2 |
3 | import com.sumory.gru.common.id.IdWorker;
4 |
5 | public class IdUtil {
6 | private static IdWorker messageIdWorker = new IdWorker(0);
7 | private static IdWorker clientIdWorker = new IdWorker(1);
8 |
9 | public static long generateMsgId() {
10 | try {
11 | return messageIdWorker.nextId();
12 | }
13 | catch (Exception e) {
14 | e.printStackTrace();
15 | return -1;
16 | }
17 | }
18 |
19 | public static long generateClientId() {
20 | try {
21 | return clientIdWorker.nextId();
22 | }
23 | catch (Exception e) {
24 | e.printStackTrace();
25 | return -1;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/common/src/main/java/com/sumory/gru/common/utils/TokenUtil.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.utils;
2 |
3 | import java.security.MessageDigest;
4 |
5 | import org.apache.commons.lang3.StringUtils;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | public class TokenUtil {
10 | private final static Logger logger = LoggerFactory.getLogger(TokenUtil.class);
11 |
12 | public static String genToken(String source, String salt) {
13 | if (StringUtils.isBlank(source) || StringUtils.isBlank(salt))
14 | return "";
15 |
16 | String str = source + "_" + salt;
17 | byte[] buf = str.getBytes();
18 | MessageDigest md5;
19 | try {
20 | md5 = MessageDigest.getInstance("MD5");
21 | md5.update(buf);
22 | byte[] hash = md5.digest();
23 | String d = "";
24 | int usbyte = 0; // unsigned byte
25 | for (int i = 0; i < hash.length; i += 2) { // format with 2-byte words with spaces.
26 | usbyte = hash[i] & 0xFF;
27 | if (usbyte < 16)
28 | d += "0" + Integer.toHexString(usbyte);
29 | else
30 | d += Integer.toHexString(usbyte);
31 | usbyte = hash[i + 1] & 0xFF;
32 | if (usbyte < 16)
33 | d += "0" + Integer.toHexString(usbyte);
34 | else
35 | d += Integer.toHexString(usbyte); // + " ";
36 | }
37 | return d.trim().toLowerCase();
38 | }
39 | catch (Exception e) {
40 | logger.error("生成token出错", e);
41 | return "";
42 | }
43 | }
44 |
45 | public static void main(String[] args) {
46 | long t1 = System.currentTimeMillis();
47 | for (int i = 0; i < 1; i++) {
48 | System.out.println(genToken("123456", "salt"));//397a79aca9daa9aa6b06dfd834e23c81
49 | System.out.println(genToken("123456我是谁", "salt"));//05449c97faaf7df7efe43cefc6c37631
50 | }
51 | long t2 = System.currentTimeMillis();
52 | System.out.println(t2 - t1);
53 |
54 | System.out.println(genToken("652_mock_student_652", "token_gen_for_ticket@sumory.com"));
55 | System.out.println(genToken("652_mock_student_652_01500389b27446edc335fa6a68281cdb",
56 | "token_gen_for_spear@sumory.com"));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/common/src/main/java/com/sumory/gru/common/zk/NotifyListener.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.zk;
2 |
3 | import org.apache.zookeeper.ZooKeeper;
4 |
5 | public interface NotifyListener {
6 |
7 | public void action(ZooKeeper zookeeper);
8 |
9 | }
--------------------------------------------------------------------------------
/common/src/main/java/com/sumory/gru/common/zk/ZkNode.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.zk;
2 |
3 | import org.apache.zookeeper.CreateMode;
4 |
5 | public class ZkNode {
6 | private String path;
7 | private byte[] data;
8 | private CreateMode createMode;
9 |
10 | public ZkNode(String path, byte[] data, CreateMode createMode) {
11 | this.path = path;
12 | this.data = data;
13 | this.createMode = createMode;
14 | }
15 |
16 | public String getPath() {
17 | return path;
18 | }
19 |
20 | public void setPath(String path) {
21 | this.path = path;
22 | }
23 |
24 | public byte[] getData() {
25 | return data;
26 | }
27 |
28 | public void setData(byte[] data) {
29 | this.data = data;
30 | }
31 |
32 | public CreateMode getCreateMode() {
33 | return createMode;
34 | }
35 |
36 | public void setCreateMode(CreateMode createMode) {
37 | this.createMode = createMode;
38 | }
39 |
40 | public String toString() {
41 | return String.format("{path:%s, data:%s, createMode:%s}", path, new String(data),
42 | createMode.toString());
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/common/src/test/java/com/sumory/gru/common/utils/DataCodecTest.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.utils;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 |
7 | import org.junit.Test;
8 |
9 | import com.sumory.gru.common.utils.DataCodec;
10 |
11 | public class DataCodecTest {
12 |
13 | @Test
14 | public void testCodec() throws UnsupportedEncodingException {
15 | String strToBs = "All men are created equal.";
16 | Map m = new HashMap();
17 | m.put("abc", 32);
18 | m.put("hello", "You jump, I jump");
19 | m.put("world", strToBs.getBytes("UTF-8"));
20 | m.put("float", 12345.6789E123);
21 |
22 | byte[] tmp = DataCodec.encode(m);
23 | Map obj = (Map) DataCodec.decode(tmp);
24 | byte[] bs = (byte[]) obj.get("world");
25 | System.out.println(m);
26 | System.out.println(obj);
27 | System.out.println(new String(bs, "UTF-8").equals(strToBs));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/common/src/test/java/com/sumory/gru/common/utils/DateUtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.utils;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.util.Date;
5 |
6 | import org.junit.Test;
7 |
8 | import com.sumory.gru.common.utils.DateUtil;
9 |
10 | public class DateUtilsTest {
11 |
12 | @Test
13 | public void testCodec() throws UnsupportedEncodingException {
14 |
15 | System.out.println(DateUtil.parseDateTimeFromString("2015-01-02 10:10:10"));
16 | System.out.println(DateUtil.parseDateTimeToMilliseconds("2015-01-02 10:10:10"));
17 | System.out.println(DateUtil.toDateTimeString(new Date()));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/common/src/test/java/com/sumory/gru/common/utils/IdUtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.common.utils;
2 |
3 | import junit.framework.Assert;
4 |
5 | import org.junit.Test;
6 |
7 | import com.sumory.gru.common.utils.IdUtil;
8 |
9 | public class IdUtilsTest {
10 |
11 | @Test
12 | public void test() throws Exception {
13 | for (int i = 0; i < 100; i++) {
14 | long id = IdUtil.generateMsgId();
15 | System.out.println(id);
16 | Assert.assertNotNull(id);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/docs/dashboard-client.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/docs/dashboard-client.png
--------------------------------------------------------------------------------
/docs/dashboard-monitor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/docs/dashboard-monitor.png
--------------------------------------------------------------------------------
/docs/架构草图0.2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/docs/架构草图0.2.jpg
--------------------------------------------------------------------------------
/docs/登陆策略.md:
--------------------------------------------------------------------------------
1 | 参考资料:
2 |
3 | 以下摘自 https://github.com/cloudwu/skynet/wiki/MsgServer
4 |
5 | 消息服务器 msgserver (M) 必须和 LoginServer (L) 一起使用。用户 C 的业务流程通常是这样的:
6 |
7 | C 向 L 表明想登陆 M。
8 | L 验证 C 的身份,交换 secret ,并转告 M 说 C 想登陆。
9 | M 确认 C 的 secret ,做好登陆准备,生成一个 subid (subid 用于多重登陆)。
10 | L 构造当次登陆用的用户名(用 uid 和 subid 联合生成)返回给 C 向 C 确认可以登陆。
11 | C 以这个用户名加上一个自增序列号(用于断线重连),利用 secret 签名和 M 握手接入。
12 | M 检查签名是否正确,以及序列号是否使用过(一次有效)。然后等待 M 的请求,并在当前连接上回应。
13 | 如果 C 和 M 断开连接,并不意味着用户从系统登出,这时 C 可以自增序列号,重新和 M 握手。这称为修复连接,C 不需要重复进行和 L 的登陆认证流程。
--------------------------------------------------------------------------------
/idgen/src/main/assembly/package-idgen-jar.xml:
--------------------------------------------------------------------------------
1 |
5 | dist
6 |
7 | jar
8 |
9 | false
10 |
11 |
12 | ${project.build.directory}/classes
13 |
14 | com/sumory/gru/idgen/service/*.class
15 |
16 | /
17 |
18 |
19 |
20 |
27 |
28 |
--------------------------------------------------------------------------------
/idgen/src/main/assembly/shared-jar-pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | gru
6 | idgen
7 | gru-idgen-shared-jar
8 | 0.0.1-SNAPSHOT
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/idgen/src/main/java/com/sumory/gru/idgen/id/IdUtils.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.idgen.id;
2 |
3 | import com.sumory.gru.common.config.Config;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | /**
8 | * id生成工具
9 | *
10 | * @author sumory.wu
11 | * @date 2015年3月13日 下午7:15:32
12 | */
13 | public class IdUtils {
14 | private static final Logger logger = LoggerFactory.getLogger(IdUtils.class);
15 |
16 | static IdWorker msgIdWorker = new IdWorker(Integer.parseInt(Config.get("id.generator.worker")));
17 |
18 | public static long genMsgId() throws Exception {
19 | try {
20 | long genId = msgIdWorker.nextId();
21 | logger.info("当前workerId为:" + msgIdWorker.getWorkerId() + " 生成id:" + genId);
22 | return genId;
23 | }
24 | catch (Exception e) {
25 | logger.error("生成msg id发生异常", e);
26 | throw e;
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/idgen/src/main/java/com/sumory/gru/idgen/service/IdService.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.idgen.service;
2 |
3 | public interface IdService {
4 | public String getServiceVersion();
5 |
6 | public long getMsgId();
7 | }
8 |
--------------------------------------------------------------------------------
/idgen/src/main/java/com/sumory/gru/idgen/service/impl/IdServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.idgen.service.impl;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.stereotype.Service;
7 |
8 | import com.sumory.gru.idgen.id.IdUtils;
9 | import com.sumory.gru.idgen.service.IdService;
10 |
11 | /**
12 | * id生成服务
13 | *
14 | * @author sumory.wu
15 | * @date 2015年3月15日 上午11:16:14
16 | */
17 | @Service(value = "idService")
18 | public class IdServiceImpl implements IdService {
19 | private static final Logger logger = LoggerFactory.getLogger(IdServiceImpl.class);
20 |
21 | @Value("${dubbo.gru.idgen.version}")
22 | private String version;
23 |
24 | @Override
25 | public String getServiceVersion() {
26 | return version;
27 | }
28 |
29 | /**
30 | * 生成消息id
31 | */
32 | @Override
33 | public long getMsgId() {
34 | try {
35 | return IdUtils.genMsgId();
36 | }
37 | catch (Exception e) {
38 | logger.error("id生成出现严重异常", e);
39 | return Long.MIN_VALUE;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/idgen/src/main/resources/bin/dump.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | source /etc/profile
3 |
4 | cd `dirname $0`
5 | BIN_DIR=`pwd`
6 | cd ..
7 | DEPLOY_DIR=`pwd`
8 | CONF_DIR=$DEPLOY_DIR/conf
9 |
10 | SERVER_NAME=`sed '/dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
11 | LOGS_FILE=`sed '/dubbo.log4j.file/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
12 |
13 | if [ -z "$SERVER_NAME" ]; then
14 | SERVER_NAME=`hostname`
15 | fi
16 |
17 | PIDS=`ps --no-heading -C java -f --width 1000 | grep "$CONF_DIR" |awk '{print $2}'`
18 | if [ -z "$PIDS" ]; then
19 | echo "ERROR: The $SERVER_NAME does not started!"
20 | exit 1
21 | fi
22 |
23 | LOGS_DIR=""
24 | if [ -n "$LOGS_FILE" ]; then
25 | LOGS_DIR=`dirname $LOGS_FILE`
26 | else
27 | LOGS_DIR=/data/logs/dubbo
28 | fi
29 | if [ ! -d $LOGS_DIR ]; then
30 | mkdir $LOGS_DIR
31 | fi
32 | DUMP_DIR=$LOGS_DIR/dump
33 | if [ ! -d $DUMP_DIR ]; then
34 | mkdir $DUMP_DIR
35 | fi
36 | DUMP_DATE=`date +%Y%m%d%H%M%S`
37 | DATE_DIR=$DUMP_DIR/$DUMP_DATE
38 | if [ ! -d $DATE_DIR ]; then
39 | mkdir $DATE_DIR
40 | fi
41 |
42 | echo -e "Dumping the $SERVER_NAME ...\c"
43 | for PID in $PIDS ; do
44 | jstack $PID > $DATE_DIR/jstack-$PID.dump 2>&1
45 | echo -e ".\c"
46 | jinfo $PID > $DATE_DIR/jinfo-$PID.dump 2>&1
47 | echo -e ".\c"
48 | jstat -gcutil $PID > $DATE_DIR/jstat-gcutil-$PID.dump 2>&1
49 | echo -e ".\c"
50 | jstat -gccapacity $PID > $DATE_DIR/jstat-gccapacity-$PID.dump 2>&1
51 | echo -e ".\c"
52 | jmap $PID > $DATE_DIR/jmap-$PID.dump 2>&1
53 | echo -e ".\c"
54 | jmap -heap $PID > $DATE_DIR/jmap-heap-$PID.dump 2>&1
55 | echo -e ".\c"
56 | jmap -histo $PID > $DATE_DIR/jmap-histo-$PID.dump 2>&1
57 | echo -e ".\c"
58 | if [ -r /usr/sbin/lsof ]; then
59 | /usr/sbin/lsof -p $PID > $DATE_DIR/lsof-$PID.dump
60 | echo -e ".\c"
61 | fi
62 | done
63 | if [ -r /bin/netstat ]; then
64 | /bin/netstat -an > $DATE_DIR/netstat.dump 2>&1
65 | echo -e ".\c"
66 | fi
67 | if [ -r /usr/bin/iostat ]; then
68 | /usr/bin/iostat > $DATE_DIR/iostat.dump 2>&1
69 | echo -e ".\c"
70 | fi
71 | if [ -r /usr/bin/mpstat ]; then
72 | /usr/bin/mpstat > $DATE_DIR/mpstat.dump 2>&1
73 | echo -e ".\c"
74 | fi
75 | if [ -r /usr/bin/vmstat ]; then
76 | /usr/bin/vmstat > $DATE_DIR/vmstat.dump 2>&1
77 | echo -e ".\c"
78 | fi
79 | if [ -r /usr/bin/free ]; then
80 | /usr/bin/free -t > $DATE_DIR/free.dump 2>&1
81 | echo -e ".\c"
82 | fi
83 | if [ -r /usr/bin/sar ]; then
84 | /usr/bin/sar > $DATE_DIR/sar.dump 2>&1
85 | echo -e ".\c"
86 | fi
87 | if [ -r /usr/bin/uptime ]; then
88 | /usr/bin/uptime > $DATE_DIR/uptime.dump 2>&1
89 | echo -e ".\c"
90 | fi
91 | echo "OK!"
92 | echo "DUMP: $DATE_DIR"
93 |
--------------------------------------------------------------------------------
/idgen/src/main/resources/bin/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd `dirname $0`
4 | BIN_DIR=`pwd`
5 | cd ..
6 | DEPLOY_DIR=`pwd`
7 | CONF_DIR=$DEPLOY_DIR/conf
8 |
9 | echo "curr dir is $DEPLOY_DIR"
10 |
11 |
12 | SERVER_NAME=gru_idgen
13 |
14 | STDOUT_FILE=$DEPLOY_DIR/stdout.log
15 |
16 | LIB_DIR=$DEPLOY_DIR/lib
17 | LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'|tr "\n" ":"`
18 |
19 | JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
20 |
21 |
22 | #RMI_SERVER_HOSTNAME=10.60.0.67
23 | #JMX_REMOTE_PORT=10900
24 | #JAVA_JMX_OPTS=" -Djava.rmi.server.hostname="$RMI_SERVER_HOSTNAME" -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port="$JMX_REMOTE_PORT" -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false "
25 | JAVA_JMX_OPTS=""
26 |
27 |
28 | JAVA_MEM_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "
29 |
30 |
31 | echo -e "Starting the $SERVER_NAME ...\c"
32 | nohup java $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_JMX_OPTS -classpath $CONF_DIR:$LIB_JARS com.sumory.gru.idgen.main.IdGenMain > $STDOUT_FILE 2>&1 &
33 |
34 |
35 | echo "OK!"
36 | PIDS=`ps --no-heading -C java -f --width 1000 | grep "$DEPLOY_DIR" | awk '{print $2}'`
37 | echo "PID: $PIDS"
38 | echo $PIDS>pid
39 | echo "STDOUT: $STDOUT_FILE"
40 |
--------------------------------------------------------------------------------
/idgen/src/main/resources/bin/stop.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cd `dirname $0`
3 | BIN_DIR=`pwd`
4 | cd ..
5 | DEPLOY_DIR=`pwd`
6 | CONF_DIR=$DEPLOY_DIR/conf
7 |
8 | SERVER_NAME=`sed '/dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
9 |
10 | if [ -z "$SERVER_NAME" ]; then
11 | SERVER_NAME=`hostname`
12 | fi
13 |
14 | PIDS=`ps --no-heading -C java -f --width 1000 | grep "$CONF_DIR" |awk '{print $2}'`
15 | if [ -z "$PIDS" ]; then
16 | echo "ERROR: The $SERVER_NAME does not started!"
17 | exit 1
18 | fi
19 |
20 | if [ "$1" != "skip" ]; then
21 | $BIN_DIR/dump.sh
22 | fi
23 |
24 | echo -e "Stopping the $SERVER_NAME ...\c"
25 | for PID in $PIDS ; do
26 | kill $PID > /dev/null 2>&1
27 | done
28 |
29 | COUNT=0
30 | while [ $COUNT -lt 1 ]; do
31 | echo -e ".\c"
32 | sleep 1
33 | COUNT=1
34 | for PID in $PIDS ; do
35 | PID_EXIST=`ps --no-heading -p $PID`
36 | if [ -n "$PID_EXIST" ]; then
37 | COUNT=0
38 | break
39 | fi
40 | done
41 | done
42 | echo "OK!"
43 | echo "PID: $PIDS"
44 |
--------------------------------------------------------------------------------
/idgen/src/main/resources/profiles/dev/dubbo.properties:
--------------------------------------------------------------------------------
1 | dubbo.container=spring
2 | dubbo.application.name=gru-provider-idgen-service
3 | dubbo.spring.config=classpath*:applicationContext*.xml
4 | dubbo.protocol.name=dubbo
5 | dubbo.service.loadbalance=roundrobin
6 |
7 | #dubbo.registry.client=curator
8 | dubbo.registry.address=zookeeper://192.168.100.183:2181
9 | dubbo.registry.file=/data/logs/idgen/registry.cache
10 | dubbo.protocol.port=33000
11 |
12 | ###### dubbo service api version ######
13 | dubbo.gru.idgen.version=dev
--------------------------------------------------------------------------------
/idgen/src/main/resources/profiles/dev/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /data/logs/idgen
5 | [%d{MM-dd HH:mm:ss.SSS}] [%p] %l - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/idgen/src/main/resources/profiles/dev/system.properties:
--------------------------------------------------------------------------------
1 | ############################## id generator worker config#############################
2 | id.generator.worker=0
--------------------------------------------------------------------------------
/idgen/src/main/resources/profiles/test1/dubbo.properties:
--------------------------------------------------------------------------------
1 | dubbo.container=spring
2 | dubbo.application.name=gru-provider-idgen-service
3 | dubbo.spring.config=classpath*:applicationContext*.xml
4 | dubbo.protocol.name=dubbo
5 | dubbo.service.loadbalance=roundrobin
6 |
7 | dubbo.registry.client=curator
8 | dubbo.registry.address=zookeeper://192.168.100.183:2181
9 | dubbo.registry.file=/data/logs/idgen/registry.cache
10 | dubbo.protocol.port=33000
11 |
12 | ###### dubbo service api version ######
13 | dubbo.gru.idgen.version=1.0
14 |
15 |
--------------------------------------------------------------------------------
/idgen/src/main/resources/profiles/test1/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /data/logs/idgen
5 | [%d{MM-dd HH:mm:ss.SSS}] [%p] %l - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/idgen/src/main/resources/profiles/test1/system.properties:
--------------------------------------------------------------------------------
1 | ############################## id generator worker config#############################
2 | id.generator.worker=1
--------------------------------------------------------------------------------
/idgen/src/main/resources/spring/applicationContext-provider-idgen.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/idgen/src/main/resources/spring/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/minions/.gitignore:
--------------------------------------------------------------------------------
1 | /bin
2 |
--------------------------------------------------------------------------------
/minions/Makefile:
--------------------------------------------------------------------------------
1 | TESTS = test/*.test.js
2 | TIMEOUT = 200000
3 | REPORTER = spec
4 | NODE_ENV = dev
5 |
6 | test:
7 | @NODE_ENV=${NODE_ENV} ./node_modules/mocha/bin/mocha \
8 | --timeout $(TIMEOUT) \
9 | --reporter ${REPORTER} \
10 | $(TESTS)
11 |
12 |
13 | echo:
14 | echo "hello.."\
15 | "this is Makefile"
16 |
17 | .PHONY: test echo
18 |
--------------------------------------------------------------------------------
/minions/README.md:
--------------------------------------------------------------------------------
1 | ### introduction
2 |
3 | Gru监控
--------------------------------------------------------------------------------
/minions/config/dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | module.exports = {
4 | port: 40000,
5 | viewEngine: 'ejs',
6 |
7 | views: path.resolve(__dirname, '..', 'views'),
8 | staticPath: path.resolve(__dirname, '..', 'public'),
9 |
10 | env: 'dev',
11 | logfile: path.resolve(__dirname, '..', 'logs/access.log'),
12 |
13 | sessionSecret: 'session_secret_random_seed',
14 |
15 | //redis config
16 | redis: {"address": "192.168.100.185", "port": "6379", "passwd": ""},
17 |
18 | //不需要过滤是否登陆状态的白名单
19 | whitelist: [
20 | "/",
21 | "/auth/login",
22 | "/version"
23 | ],
24 |
25 | zk: {
26 | addr: "192.168.100.183:2181",
27 | spears: "/spearnodes_dev"
28 | },
29 |
30 | statServer: "http://192.168.100.122:50000"
31 |
32 | };
--------------------------------------------------------------------------------
/minions/config/index.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | // 通过NODE_ENV设置环境变量,默认为dev环境
4 | var env = process.env.NODE_ENV || 'dev';
5 | env = env.toLowerCase();
6 |
7 | var file = path.resolve(__dirname, env);
8 | try {
9 | var config = module.exports = require(file);
10 | console.log('Load config: [%s] %s', env, file);
11 | } catch (err) {
12 | console.error('Error when load config: [%s] %s', env, file);
13 | throw err;
14 | }
--------------------------------------------------------------------------------
/minions/config/prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | module.exports = {
4 | port: 40000,
5 | viewEngine: 'ejs',
6 |
7 | views: path.resolve(__dirname, '..', 'views'),
8 | staticPath: path.resolve(__dirname, '..', 'public'),
9 |
10 | env: 'dev',
11 | logfile: path.resolve(__dirname, '..', 'logs/access.log'),
12 |
13 | sessionSecret: 'session_secret_random_seed',
14 |
15 | //redis config
16 | "redis": {"address": "172.18.4.174", "port": "6379", "passwd": ""},
17 |
18 | //不需要过滤是否登陆状态的白名单
19 | "whitelist": [
20 | "/",
21 | "/auth/login",
22 | "/version"
23 | ],
24 |
25 | zk: {
26 | addr: "172.18.4.227:2181,172.18.4.162:2181,172.18.4.166:2181",
27 | spears: "/spearnodes_prod"
28 | },
29 |
30 | statServer: "http://172.18.4.172:50000"
31 | };
--------------------------------------------------------------------------------
/minions/config/test.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | module.exports = {
4 | port: 40000,
5 | viewEngine: 'ejs',
6 |
7 | views: path.resolve(__dirname, '..', 'views'),
8 | staticPath: path.resolve(__dirname, '..', 'public'),
9 |
10 | env: 'dev',
11 | logfile: path.resolve(__dirname, '..', 'logs/access.log'),
12 |
13 | sessionSecret: 'session_secret_random_seed',
14 |
15 | //redis config
16 | "redis": {"address": "192.168.100.185", "port": "6379", "passwd": ""},
17 |
18 | //不需要过滤是否登陆状态的白名单
19 | "whitelist": [
20 | "/",
21 | "/auth/login",
22 | "/version"
23 | ],
24 |
25 | zk: {
26 | addr: "192.168.100.183:2181",
27 | spears: "/spearnodes_test"
28 | },
29 |
30 | statServer: "http://192.168.100.122:50000"
31 |
32 | };
--------------------------------------------------------------------------------
/minions/lib/log.js:
--------------------------------------------------------------------------------
1 | var log4js = require('log4js');
2 | var config = require('../config');
3 |
4 | log4js.configure({
5 | appenders: [{
6 | type: 'console'
7 | }, //控制台输出
8 | {
9 | type: 'file', //文件输出
10 | filename: config.logfile,
11 | //maxLogSize: 20480, //当超过maxLogSize大小时,会自动生成一个新文件
12 | //backups: 3
13 | pattern: "-yyyy-MM-dd",
14 | alwaysIncludePattern: true
15 | }
16 | ],
17 | replaceConsole: true //增加replaceConsole配置,让所有console输出到日志中,以[INFO] console代替console默认样式。
18 | });
19 |
20 |
21 | exports.logger = function(name,level){
22 | var logger = log4js.getLogger(name);
23 | logger.setLevel(level || 'INFO');
24 | return logger;
25 | }
--------------------------------------------------------------------------------
/minions/lib/redisUtils.js:
--------------------------------------------------------------------------------
1 | var redis = require("redis");
2 | var config = require('../config');
3 |
4 | var client = redis.createClient(config.redis.port, config.redis.address);
5 | //client.auth(config.redis.passwd, function(){console.log("redis authRouter ok, redisUtils initializes ok")});
6 |
7 | /**
8 | * keys命令
9 | */
10 | exports.keys = function (regex, callback) {
11 | if(regex){
12 | client.keys(regex,function (err, reply) {
13 | if (err)
14 | console.log('redisUtils keys ' + regex + ' error: ' + err);
15 | //console.log('redisUtils keys: [' + regex + '] reply is:' + reply);
16 | callback && callback(err, reply);
17 | });
18 | }
19 | };
20 |
21 |
22 | /**
23 | * 设置一个不过期的值
24 | *
25 | * @param key
26 | * @param value
27 | * @param callback
28 | */
29 | exports.set = function (key, value, callback) {
30 | if(key){
31 | client.set(key, value,function (err, reply) {
32 | if (err)
33 | console.log('redisUtils set ' + key + ' error: ' + err);
34 | console.log('redisUtils set: [' + key + ']:[' + value + '] reply is:' + reply);
35 | callback && callback(err, reply);
36 | });
37 | }
38 | };
39 |
40 | /**
41 | * 设置带过期时间的值
42 | *
43 | * @param key
44 | * @param value
45 | * @param seconds 多少秒后过期
46 | * @param callback
47 | */
48 | exports.setWithExpire = function(key, value, seconds, callback){
49 | if(key && seconds){
50 | client.setex(key, seconds, value,function (err, reply) {
51 | if (err)
52 | console.log('redisUtils setex ' + key + ' error: ' + err);
53 | console.log('redisUtils setex: [' + key + ']:[' + value + '] reply is:' + reply);
54 | callback && callback(err, reply);
55 | });
56 | }
57 | };
58 |
59 | /**
60 | * 获取值
61 | *
62 | * @param key
63 | * @param callback
64 | */
65 | exports.get = function (key, callback) {
66 | client.get(key, function (err, reply) {
67 | if (err)
68 | console.log('redisUtils get ' + key + ' error:' + err);
69 | callback && callback(err, reply);
70 | });
71 | };
72 |
--------------------------------------------------------------------------------
/minions/lib/zkUtils.js:
--------------------------------------------------------------------------------
1 | var zookeeper = require('node-zookeeper-client');
2 | var config = require("../config");
3 |
4 | var client = zookeeper.createClient(config.zk.addr);
5 |
6 | client.once('connected', function () {
7 | console.log('Connected to the zookeeper server.');
8 | });
9 |
10 | client.connect();
11 |
12 |
13 | exports.getSpears = function (callback) {
14 | var path = config.zk.spears;
15 | client.removeAllListeners();
16 | client.getChildren(path,
17 | function (error, children, stat) {
18 | if (error) {
19 | console.log(
20 | 'Failed to list children of %s due to: %s.',
21 | path,
22 | error
23 | );
24 | callback(error);
25 | }
26 | console.log('Children of %s are: %j.', path, children);
27 | callback(null, children);
28 | }
29 | );
30 | }
--------------------------------------------------------------------------------
/minions/models/adminModel.js:
--------------------------------------------------------------------------------
1 | var logger = require("../lib/log.js").logger("adminModel");
2 | var commonUtils = require("../lib/utils.js");
3 |
4 |
--------------------------------------------------------------------------------
/minions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"minions",
3 | "version": "0.0.1",
4 | "private": true,
5 | "engines": {
6 | "node": ">=0.10.x"
7 | },
8 | "author": {
9 |
10 | },
11 | "scripts": {
12 | "start": "node ./app.js",
13 | "test": "make test"
14 | },
15 | "dependencies": {
16 | "express": "~4.0.0",
17 | "static-favicon": "~1.0.0",
18 | "cookie": "0.1.0",
19 | "cookie-parser": "~1.0.1",
20 | "body-parser": "~1.0.0",
21 | "debug": "~0.7.4",
22 | "ejs": "~0.8.5",
23 | "method-override": "1.0.0",
24 | "express-session": "1.0.2",
25 | "multiparty": "3.2.4",
26 | "redis": "0.10.1",
27 | "node-zookeeper-client":"*",
28 | "mysql": "2.4.2",
29 | "log4js": "0.6.16",
30 | "request":"2.53.0"
31 | },
32 | "devDependencies": {
33 | "should": "*",
34 | "mocha": "*"
35 | }
36 | }
--------------------------------------------------------------------------------
/minions/public/css/minions.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 0 30px 20px 30px;
3 | }
4 |
5 | #header-navigate{
6 | margin-left:30px;
7 | }
8 |
9 | #header-navigate a{
10 | margin-top:-12px;
11 | }
12 |
13 | .ui.avatar.image.monitor_logo{
14 | margin-right:20px;
15 | margin-top:-8px;
16 | }
17 |
18 | #console {
19 | min-height: 300px;
20 | max-height: 300px;
21 | overflow: auto;
22 | }
23 |
24 | .username-msg {
25 | color: orange;
26 | }
27 |
28 | .connect-msg {
29 | color: green;
30 | }
31 |
32 | .disconnect-msg {
33 | color: red;
34 | }
35 |
36 | .send-msg {
37 | color: #888
38 | }
39 |
40 | .tip_text{
41 | color:#999;
42 | }
43 |
44 |
45 |
46 | #navigate {
47 | margin-bottom: 40px;
48 | }
49 |
50 | #btnKickGroup {
51 | width: 220px;
52 | margin-top: 4px;
53 | margin-left: 10px;
54 |
55 | }
56 |
57 | #btnKickUser {
58 | width: 220px;
59 | margin-top: 4px;
60 | margin-left: 20px;
61 | padding-left: 25px;
62 | border-left: 1px #DDD solid;
63 | }
64 |
65 | #listenGroup {
66 | width: 280px;
67 | margin-top: 4px;
68 | margin-left: 20px;
69 | padding-left: 25px;
70 | border-left: 1px #DDD solid;
71 | }
72 |
73 | .ui.list .item a {
74 | color: #fff;
75 | }
76 |
77 | #groups_area {
78 | min-height: 30px;
79 | max-height: 513px;
80 | overflow-y: scroll;
81 | -ms-overflow-y: scroll;
82 | }
83 |
84 | #groups_area a {
85 | margin-top: 5px;
86 | width: 100px;
87 | }
88 |
89 | #group_area a {
90 | margin: 2px;
91 | min-width: 80px;
92 | text-align: center;
93 | }
94 |
95 | #spears_area a {
96 | padding: 0.8em !important;
97 | margin-bottom: 5px;
98 | }
99 |
100 | #spear_stat {
101 | margin-top: -20px;
102 | }
103 |
104 | #spear_stat .statistic {
105 | margin-right: 30px;
106 | }
107 |
108 | #spear_stat .statistic .value .icon {
109 | width: 40px;
110 | display: inline-block;
111 | }
112 |
113 | #spear_stat .statistic .value span {
114 | width: 30px;
115 | }
116 |
117 | #spear_stat i.small.icon {
118 | font-size: 0.5em
119 | }
120 |
121 | #spear_stat span {
122 | font-size: 0.4em
123 | }
124 |
125 | .custom_section{
126 | margin-bottom:50px;
127 | }
--------------------------------------------------------------------------------
/minions/public/img/gru.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/public/img/gru.png
--------------------------------------------------------------------------------
/minions/public/semantic/components/ad.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Ad
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2013 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.ui.ad{display:block;overflow:hidden;margin:1em 0}.ui.ad:first-child,.ui.ad:last-child{margin:0}.ui.ad iframe{margin:0;padding:0;border:none;overflow:hidden}.ui.leaderboard.ad{width:728px;height:90px}.ui[class*="medium rectangle"].ad{width:300px;height:250px}.ui[class*="large rectangle"].ad{width:336px;height:280px}.ui[class*="half page"].ad{width:300px;height:600px}.ui.square.ad{width:250px;height:250px}.ui[class*="small square"].ad{width:200px;height:200px}.ui[class*="small rectangle"].ad{width:180px;height:150px}.ui[class*="vertical rectangle"].ad{width:240px;height:400px}.ui.button.ad{width:120px;height:90px}.ui[class*="square button"].ad{width:125px;height:125px}.ui[class*="small button"].ad{width:120px;height:60px}.ui.skyscraper.ad{width:120px;height:600px}.ui[class*="wide skyscraper"].ad{width:160px}.ui.banner.ad{width:468px;height:60px}.ui[class*="vertical banner"].ad{width:120px;height:240px}.ui[class*="top banner"].ad{width:930px;height:180px}.ui[class*="half banner"].ad{width:234px;height:60px}.ui[class*="large leaderboard"].ad{width:970px;height:90px}.ui.billboard.ad{width:970px;height:250px}.ui.panorama.ad{width:980px;height:120px}.ui.netboard.ad{width:580px;height:400px}.ui[class*="large mobile banner"].ad{width:320px;height:100px}.ui[class*="mobile leaderboard"].ad{width:320px;height:50px}.ui.mobile.ad{display:none}@media only screen and (max-width:767px){.ui.mobile.ad{display:block}}.ui.centered.ad{margin-left:auto;margin-right:auto}.ui.test.ad{position:relative;background:#333}.ui.test.ad:after{position:absolute;top:50%;left:50%;width:100%;text-align:center;-webkit-transform:translateX(-50%)translateY(-50%);-ms-transform:translateX(-50%)translateY(-50%);transform:translateX(-50%)translateY(-50%);content:'Ad';color:#fff;font-size:1em;font-weight:700}.ui.mobile.test.ad:after{font-size:.85714em}.ui.test.ad[data-text]:after{content:attr(data-text)}
--------------------------------------------------------------------------------
/minions/public/semantic/components/breadcrumb.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Breadcrumb
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */
11 |
12 |
13 | /*******************************
14 | Breadcrumb
15 | *******************************/
16 |
17 | .ui.breadcrumb {
18 | margin: 1em 0em;
19 | display: inline-block;
20 | vertical-align: middle;
21 | }
22 | .ui.breadcrumb:first-child {
23 | margin-top: 0em;
24 | }
25 | .ui.breadcrumb:last-child {
26 | margin-bottom: 0em;
27 | }
28 |
29 |
30 | /*******************************
31 | Content
32 | *******************************/
33 |
34 |
35 | /* Divider */
36 | .ui.breadcrumb .divider {
37 | display: inline-block;
38 | opacity: 0.5;
39 | margin: 0em 0.2rem 0em;
40 | font-size: 0.9em;
41 | color: rgba(0, 0, 0, 0.4);
42 | vertical-align: baseline;
43 | }
44 |
45 | /* Link */
46 | .ui.breadcrumb a {
47 | color: #009fda;
48 | }
49 | .ui.breadcrumb a:hover {
50 | color: #00b2f3;
51 | }
52 |
53 | /* Icon Divider */
54 | .ui.breadcrumb .icon.divider {
55 | font-size: 0.85714286em;
56 | vertical-align: baseline;
57 | }
58 |
59 | /* Section */
60 | .ui.breadcrumb a.section {
61 | cursor: pointer;
62 | }
63 | .ui.breadcrumb .section {
64 | display: inline-block;
65 | margin: 0em;
66 | padding: 0em;
67 | }
68 |
69 | /* Loose Coupling */
70 | .ui.breadcrumb.segment {
71 | display: inline-block;
72 | padding: 0.5em 1em;
73 | }
74 |
75 |
76 | /*******************************
77 | States
78 | *******************************/
79 |
80 | .ui.breadcrumb .active.section {
81 | font-weight: bold;
82 | }
83 |
84 |
85 | /*******************************
86 | Variations
87 | *******************************/
88 |
89 | .ui.mini.breadcrumb {
90 | font-size: 0.65em;
91 | }
92 | .ui.tiny.breadcrumb {
93 | font-size: 0.7em;
94 | }
95 | .ui.small.breadcrumb {
96 | font-size: 0.75em;
97 | }
98 | .ui.breadcrumb {
99 | font-size: 1em;
100 | }
101 | .ui.large.breadcrumb {
102 | font-size: 1.1em;
103 | }
104 | .ui.big.breadcrumb {
105 | font-size: 1.05em;
106 | }
107 | .ui.huge.breadcrumb {
108 | font-size: 1.3em;
109 | }
110 | .ui.massive.breadcrumb {
111 | font-size: 1.5em;
112 | }
113 |
114 |
115 | /*******************************
116 | Theme Overrides
117 | *******************************/
118 |
119 |
120 |
121 | /*******************************
122 | Site Overrides
123 | *******************************/
124 |
125 |
--------------------------------------------------------------------------------
/minions/public/semantic/components/breadcrumb.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Breadcrumb
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.ui.breadcrumb{margin:1em 0;display:inline-block;vertical-align:middle}.ui.breadcrumb:first-child{margin-top:0}.ui.breadcrumb:last-child{margin-bottom:0}.ui.breadcrumb .divider{display:inline-block;opacity:.5;margin:0 .2rem;font-size:.9em;color:rgba(0,0,0,.4);vertical-align:baseline}.ui.breadcrumb a{color:#009fda}.ui.breadcrumb a:hover{color:#00b2f3}.ui.breadcrumb .icon.divider{font-size:.85714286em;vertical-align:baseline}.ui.breadcrumb a.section{cursor:pointer}.ui.breadcrumb .section{display:inline-block;margin:0;padding:0}.ui.breadcrumb.segment{display:inline-block;padding:.5em 1em}.ui.breadcrumb .active.section{font-weight:700}.ui.mini.breadcrumb{font-size:.65em}.ui.tiny.breadcrumb{font-size:.7em}.ui.small.breadcrumb{font-size:.75em}.ui.breadcrumb{font-size:1em}.ui.large.breadcrumb{font-size:1.1em}.ui.big.breadcrumb{font-size:1.05em}.ui.huge.breadcrumb{font-size:1.3em}.ui.massive.breadcrumb{font-size:1.5em}
--------------------------------------------------------------------------------
/minions/public/semantic/components/comment.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Comment
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributorss
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.ui.comments .comment,.ui.comments .comment .comments .comment{border:none;border-top:none;background:0 0}.ui.comments{margin:1.5em 0;max-width:650px}.ui.comments:first-child{margin-top:0}.ui.comments:last-child{margin-bottom:0}.ui.comments .comment{position:relative;margin:.5em 0 0;padding:.5em 0 0;line-height:1.2}.ui.comments .comment:first-child{margin-top:0;padding-top:0}.ui.comments .comment .comments{margin:0 0 .5em .5em;padding:1em 0 1em 1em}.ui.comments .comment .comments:before{position:absolute;top:0;left:0}.ui.comments .comment .avatar{display:block;width:2.5em;height:auto;float:left;margin:.2em 0 0}.ui.comments .comment .avatar img,.ui.comments .comment img.avatar{display:block;margin:0 auto;width:100%;height:100%;border-radius:.25rem}.ui.comments .comment>.content{display:block}.ui.comments .comment>.avatar~.content{margin-left:3.5em}.ui.comments .comment .author{font-size:1em;color:rgba(0,0,0,.8);font-weight:700}.ui.comments .comment a.author{cursor:pointer}.ui.comments .comment a.author:hover{color:#00b2f3}.ui.comments .comment .metadata{display:inline-block;margin-left:.5em;color:rgba(0,0,0,.4);font-size:.875em}.ui.comments .comment .metadata>*{display:inline-block;margin:0 .5em 0 0}.ui.comments .comment .metadata>:last-child{margin-right:0}.ui.comments .comment .text{margin:.25em 0 .5em;font-size:1em;word-wrap:break-word;color:rgba(0,0,0,.8);line-height:1.3}.ui.comments .comment .actions{font-size:.875em}.ui.comments .comment .actions a{cursor:pointer;display:inline-block;margin:0 .75em 0 0;color:rgba(0,0,0,.4)}.ui.comments .comment .actions a:last-child{margin-right:0}.ui.comments .comment .actions a.active,.ui.comments .comment .actions a:hover{color:rgba(0,0,0,.8)}.ui.comments>.reply.form{margin-top:1em}.ui.comments .comment .reply.form{width:100%;margin-top:1em}.ui.comments .reply.form textarea{font-size:1em;height:12em}.ui.collapsed.comments,.ui.comments .collapsed.comment,.ui.comments .collapsed.comments{display:none}.ui.threaded.comments .comment .comments{margin:-1.5em 0 -1em 1.25em;padding:3em 0 2em 2.25em;box-shadow:-1px 0 0 rgba(39,41,43,.15)}.ui.minimal.comments .comment .actions{opacity:0;position:absolute;top:0;right:0;left:auto;-webkit-transition:opacity .2s ease;transition:opacity .2s ease;-webkit-transition-delay:.1s;transition-delay:.1s}.ui.minimal.comments .comment>.content:hover>.actions{opacity:1}.ui.small.comments{font-size:.9em}.ui.comments{font-size:1em}.ui.large.comments{font-size:1.1em}.ui.huge.comments{font-size:1.2em}
--------------------------------------------------------------------------------
/minions/public/semantic/components/dimmer.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Dimmer
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.dimmable{position:relative}.ui.dimmer{display:none;position:absolute;top:0!important;left:0!important;width:100%;height:100%;text-align:center;vertical-align:middle;background:rgba(0,0,0,.85);opacity:0;line-height:1;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-transition:background-color .5s linear;transition:background-color .5s linear;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;will-change:opacity;z-index:1000}.dimmed.dimmable>.ui.animating.dimmer,.dimmed.dimmable>.ui.visible.dimmer,.ui.active.dimmer,.ui.simple.dimmer{display:block;opacity:1}.ui.dimmer>.content{width:100%;height:100%;display:table;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ui.dimmer>.content>div{display:table-cell;vertical-align:middle;color:#fff}.ui.segment>.ui.dimmer{border-radius:inherit!important}.animating.dimmable:not(body),.dimmed.dimmable:not(body){overflow:hidden}.ui.disabled.dimmer{width:0!important;height:0!important}.ui.page.dimmer{position:fixed;-webkit-transform-style:'';transform-style:'';-webkit-perspective:2000px;perspective:2000px;-webkit-transform-origin:center center;-ms-transform-origin:center center;transform-origin:center center}body.animating.in.dimmable,body.dimmed.dimmable{overflow:hidden}body.dimmable>.dimmer{position:fixed}.ui.dimmer>.top.aligned.content>*{vertical-align:top}.ui.dimmer>.bottom.aligned.content>*{vertical-align:bottom}.ui.inverted.dimmer{background:rgba(255,255,255,.85)}.ui.inverted.dimmer>.content>*{color:#fff}.ui.simple.dimmer{overflow:hidden;width:0;height:0;z-index:-100;background-color:transparent}.dimmed.dimmable>.ui.simple.dimmer{overflow:visible;opacity:1;width:100%;height:100%;background:rgba(0,0,0,.85);z-index:1}.ui.simple.inverted.dimmer{background:rgba(255,255,255,0)}.dimmed.dimmable>.ui.simple.inverted.dimmer{background:rgba(255,255,255,.85)}
--------------------------------------------------------------------------------
/minions/public/semantic/components/nag.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Nag
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.ui.nag{display:none;opacity:.95;position:relative;top:0;left:0;z-index:999;min-height:0;width:100%;margin:0;padding:.75em 1em;background:#555;box-shadow:0 1px 2px 0 rgba(0,0,0,.2);font-size:1rem;text-align:center;color:rgba(0,0,0,.8);border-radius:0 0 .2857rem .2857rem;-webkit-transition:.2s background ease;transition:.2s background ease}a.ui.nag{cursor:pointer}.ui.nag>.title{display:inline-block;margin:0 .5em;color:#fff}.ui.nag>.close.icon{cursor:pointer;opacity:.4;position:absolute;top:50%;right:1em;font-size:1em;margin:-.5em 0 0;color:#fff;-webkit-transition:opacity .2s ease;transition:opacity .2s ease}.ui.nag:hover{background:#555;opacity:1}.ui.nag .close:hover{opacity:1}.ui.overlay.nag{position:absolute;display:block}.ui.fixed.nag{position:fixed}.ui.bottom.nag,.ui.bottom.nags{border-radius:.2857rem .2857rem 0 0;top:auto;bottom:0}.ui.inverted.nag,.ui.inverted.nags .nag{background-color:#f0f0f0;color:rgba(0,0,0,.85)}.ui.inverted.nag .close,.ui.inverted.nag .title,.ui.inverted.nags .nag .close,.ui.inverted.nags .nag .title{color:rgba(0,0,0,.4)}.ui.nags .nag{border-radius:0!important}.ui.nags .nag:last-child{border-radius:0 0 .2857rem .2857rem}.ui.bottom.nags .nag:last-child{border-radius:.2857rem .2857rem 0 0}
--------------------------------------------------------------------------------
/minions/public/semantic/components/popup.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Popup
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.ui.popup{display:none;position:absolute;top:0;right:0;min-width:-moz-max-content;z-index:1900;border:1px solid #ccc;max-width:250px;background-color:#fff;padding:.833em 1em;font-weight:400;font-style:normal;color:rgba(0,0,0,.8);border-radius:.2857rem;box-shadow:0 2px 4px rgba(0,0,0,.1);margin:0}.ui.bottom.center.popup:before,.ui.bottom.left.popup:before,.ui.bottom.right.popup:before{box-shadow:-1px -1px 0 0 #b3b3b3;top:-.325em}.ui.popup>.header{padding:0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1.125em;line-height:1.2;font-weight:700}.ui.popup>.header+.content{padding-top:.5em}.ui.popup:before{position:absolute;content:'';width:.75em;height:.75em;background:#fff;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);z-index:2;box-shadow:1px 1px 0 0 #b3b3b3}.ui.popup.bottom{margin:.75em 0 0}.ui.popup.top{margin:0 0 .75em}.ui.popup.left.center{margin:0 .75em 0 0}.ui.popup.right.center{margin:0 0 0 .75em}.ui.bottom.right.popup,.ui.top.right.popup{margin-right:0}.ui.bottom.center.popup:before{margin-left:-.325em;left:50%;right:auto;bottom:auto}.ui.bottom.left.popup{margin-left:0}.ui.bottom.left.popup:before{left:1em;right:auto;bottom:auto;margin-left:0}.ui.bottom.right.popup:before{right:1em;bottom:auto;left:auto;margin-left:0}.ui.top.center.popup:before{top:auto;right:auto;bottom:-.325em;left:50%;margin-left:-.325em}.ui.top.left.popup{margin-left:0}.ui.top.left.popup:before{bottom:-.325em;left:1em;top:auto;right:auto;margin-left:0}.ui.top.right.popup:before{bottom:-.325em;right:1em;top:auto;left:auto;margin-left:0}.ui.left.center.popup:before{top:50%;right:-.325em;bottom:auto;left:auto;margin-top:-.325em;box-shadow:1px -1px 0 0 #b3b3b3}.ui.right.center.popup:before{top:50%;left:-.325em;bottom:auto;right:auto;margin-top:-.325em;box-shadow:-1px 1px 0 0 #b3b3b3}.ui.popup>.ui.grid:not(.padded){width:-webkit-calc(100% + 1.75rem);width:calc(100% + 1.75rem);margin:-.7rem -.875rem}.ui.loading.popup{display:block;visibility:hidden;z-index:-1}.ui.animating.popup,.ui.visible.popup{display:block}.ui.basic.popup:before{display:none}.ui.wide.popup{max-width:350px}.ui[class*="very wide"].popup{max-width:550px}.ui.fluid.popup{width:100%;max-width:none}.ui.inverted.popup{background:#1b1c1d;color:#fff;border:none;box-shadow:none}.ui.inverted.popup .header{background-color:none;color:#fff}.ui.inverted.popup:before{background-color:#1b1c1d;box-shadow:none!important}.ui.flowing.popup{max-width:none}.ui.small.popup{font-size:.785714rem}.ui.popup{font-size:.85714rem}.ui.large.popup{font-size:1rem}.ui.huge.popup{font-size:1.14285rem}
--------------------------------------------------------------------------------
/minions/public/semantic/components/rail.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Rail
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */
11 |
12 |
13 | /*******************************
14 | Rails
15 | *******************************/
16 |
17 | .ui.rail {
18 | position: absolute;
19 | top: 0%;
20 | width: 300px;
21 | height: 100%;
22 | box-sizing: content-box;
23 | }
24 | .ui.left.rail {
25 | left: auto;
26 | right: 100%;
27 | padding: 0em 2rem 0em 0em;
28 | margin: 0em 2rem 0em 0em;
29 | }
30 | .ui.right.rail {
31 | left: 100%;
32 | right: auto;
33 | padding: 0em 0em 0em 2rem;
34 | margin: 0em 0em 0em 2rem;
35 | }
36 |
37 |
38 | /*******************************
39 | Variations
40 | *******************************/
41 |
42 |
43 | /*--------------
44 | Internal
45 | ---------------*/
46 |
47 | .ui.left.internal.rail {
48 | left: 0%;
49 | right: auto;
50 | padding: 0em 0em 0em 2rem;
51 | margin: 0em 0em 0em 2rem;
52 | }
53 | .ui.right.internal.rail {
54 | left: auto;
55 | right: 0%;
56 | padding: 0em 2rem 0em 0em;
57 | margin: 0em 2rem 0em 0em;
58 | }
59 |
60 | /*--------------
61 | Divided
62 | ---------------*/
63 |
64 | .ui.left.dividing.rail {
65 | padding: 0em 2.5rem 0em 0em;
66 | margin: 0em 2.5rem 0em 0em;
67 | border-right: 1px solid rgba(39, 41, 43, 0.15);
68 | }
69 | .ui.right.dividing.rail {
70 | border-left: 1px solid rgba(39, 41, 43, 0.15);
71 | padding: 0em 0em 0em 2.5rem;
72 | margin: 0em 0em 0em 2.5rem;
73 | }
74 |
75 | /*--------------
76 | Distance
77 | ---------------*/
78 |
79 | .ui.close.left.rail {
80 | padding: 0em 1em 0em 0em;
81 | margin: 0em 1em 0em 0em;
82 | }
83 | .ui.close.right.rail {
84 | padding: 0em 0em 0em 1em;
85 | margin: 0em 0em 0em 1em;
86 | }
87 | .ui.very.close.left.rail {
88 | padding: 0em 0.5em 0em 0em;
89 | margin: 0em 0.5em 0em 0em;
90 | }
91 | .ui.very.close.right.rail {
92 | padding: 0em 0em 0em 0.5em;
93 | margin: 0em 0em 0em 0.5em;
94 | }
95 |
96 | /*--------------
97 | Attached
98 | ---------------*/
99 |
100 | .ui.attached.left.rail,
101 | .ui.attached.right.rail {
102 | padding: 0em;
103 | margin: 0em;
104 | }
105 |
106 | /*--------------
107 | Sizing
108 | ---------------*/
109 |
110 | .ui.rail {
111 | font-size: 1em;
112 | }
113 |
114 |
115 | /*******************************
116 | Theme Overrides
117 | *******************************/
118 |
119 |
120 |
121 | /*******************************
122 | Site Overrides
123 | *******************************/
124 |
125 |
--------------------------------------------------------------------------------
/minions/public/semantic/components/rail.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Rail
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.ui.rail{position:absolute;top:0;width:300px;height:100%;box-sizing:content-box;font-size:1em}.ui.left.rail{left:auto;right:100%;padding:0 2rem 0 0;margin:0 2rem 0 0}.ui.left.internal.rail,.ui.right.rail{right:auto;padding:0 0 0 2rem;margin:0 0 0 2rem}.ui.right.rail{left:100%}.ui.left.internal.rail{left:0}.ui.right.internal.rail{left:auto;right:0;padding:0 2rem 0 0;margin:0 2rem 0 0}.ui.left.dividing.rail{padding:0 2.5rem 0 0;margin:0 2.5rem 0 0;border-right:1px solid rgba(39,41,43,.15)}.ui.right.dividing.rail{border-left:1px solid rgba(39,41,43,.15);padding:0 0 0 2.5rem;margin:0 0 0 2.5rem}.ui.close.left.rail{padding:0 1em 0 0;margin:0 1em 0 0}.ui.close.right.rail{padding:0 0 0 1em;margin:0 0 0 1em}.ui.very.close.left.rail{padding:0 .5em 0 0;margin:0 .5em 0 0}.ui.very.close.right.rail{padding:0 0 0 .5em;margin:0 0 0 .5em}.ui.attached.left.rail,.ui.attached.right.rail{padding:0;margin:0}
--------------------------------------------------------------------------------
/minions/public/semantic/components/reset.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Reset
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */img,legend{border:0}legend,td,th{padding:0}*,:after,:before{box-sizing:inherit}html{box-sizing:border-box;font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}input[type=text],input[type=email],input[type=search],input[type=password]{-webkit-appearance:none;-moz-appearance:none}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,optgroup,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre,textarea{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}table{border-collapse:collapse;border-spacing:0}
--------------------------------------------------------------------------------
/minions/public/semantic/components/shape.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Shape
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.ui.shape.animating,.ui.shape.animating .sides{-webkit-transition:all .6s ease-in-out;transition:all .6s ease-in-out}.ui.shape{position:relative;display:inline-block;-webkit-perspective:2000px;perspective:2000px}.ui.shape .sides{-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.ui.shape .side{opacity:1;width:100%;margin:0!important;-webkit-backface-visibility:hidden;backface-visibility:hidden;display:none}.ui.shape .side>*{-webkit-backface-visibility:visible!important;backface-visibility:visible!important}.ui.cube.shape .side{min-width:15em;height:15em;padding:2em;background-color:#e6e6e6;color:rgba(0,0,0,.8);box-shadow:0 0 2px rgba(0,0,0,.3)}.ui.cube.shape .side>.content{width:100%;height:100%;display:table;text-align:center;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ui.cube.shape .side>.content>div{display:table-cell;vertical-align:middle;font-size:2em}.ui.text.shape.animating .sides{position:static}.ui.text.shape .side{white-space:nowrap}.ui.text.shape .side>*{white-space:normal}.ui.loading.shape{position:absolute;top:-9999px;left:-9999px}.ui.shape .animating.side{position:absolute;top:0;left:0;z-index:100}.ui.shape .hidden.side{opacity:.4}.ui.shape.animating .sides{position:absolute}.ui.shape.animating .side{-webkit-transition:opacity .6s ease-in-out;transition:opacity .6s ease-in-out}.ui.shape .active.side{display:block}
--------------------------------------------------------------------------------
/minions/public/semantic/components/site.min.css:
--------------------------------------------------------------------------------
1 | body,html{font-size:14px}body,h1,h2,h3,h4,h5{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif}body,p{line-height:1.33}::selection,body{color:rgba(0,0,0,.8)}@import 'https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic&subset=latin';/*!
2 | * # Semantic UI - Site
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */body,html{height:100%}body{margin:0;padding:0;min-width:278px;background:#f7f7f7;font-smoothing:antialiased}h1,h2,h3,h4,h5{line-height:1.33em;margin:-webkit-calc(2rem - .165em)0 1rem;margin:calc(2rem - .165em)0 1rem;font-weight:700;padding:0}h1{min-height:1rem;font-size:2rem}h2{font-size:1.714rem}h3{font-size:1.28rem}h4{font-size:1.071rem}h5{font-size:1rem}p{margin:0 0 1em}p:first-child{margin-top:0}p:last-child{margin-bottom:0}a{color:#009fda;text-decoration:none}a:hover{color:#00b2f3}::-webkit-selection{background-color:#cce2ff;color:rgba(0,0,0,.8)}::-moz-selection{background-color:#cce2ff;color:rgba(0,0,0,.8)}::selection{background-color:#cce2ff}input::-webkit-selection,textarea::-webkit-selection{background-color:rgba(100,100,100,.4);color:rgba(0,0,0,.8)}input::-moz-selection,textarea::-moz-selection{background-color:rgba(100,100,100,.4);color:rgba(0,0,0,.8)}input::selection,textarea::selection{background-color:rgba(100,100,100,.4);color:rgba(0,0,0,.8)}
--------------------------------------------------------------------------------
/minions/public/semantic/components/sticky.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Sticky
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */
11 |
12 |
13 | /*******************************
14 | Sticky
15 | *******************************/
16 |
17 | .ui.sticky {
18 | position: static;
19 | -webkit-transition: width 0.2s ease, height 0.2s ease, top 0.2s ease, bottom 0.2s ease;
20 | transition: width 0.2s ease, height 0.2s ease, top 0.2s ease, bottom 0.2s ease;
21 | z-index: 800;
22 | }
23 |
24 |
25 | /*******************************
26 | States
27 | *******************************/
28 |
29 |
30 | /* Bound */
31 | .ui.sticky.bound {
32 | position: absolute;
33 | left: auto;
34 | right: auto;
35 | }
36 |
37 | /* Fixed */
38 | .ui.sticky.fixed {
39 | position: fixed;
40 | left: auto;
41 | right: auto;
42 | }
43 |
44 | /* Bound/Fixed Position */
45 | .ui.sticky.bound.top,
46 | .ui.sticky.fixed.top {
47 | top: 0px;
48 | bottom: auto;
49 | }
50 | .ui.sticky.bound.bottom,
51 | .ui.sticky.fixed.bottom {
52 | top: auto;
53 | bottom: 0px;
54 | }
55 |
56 |
57 | /*******************************
58 | Types
59 | *******************************/
60 |
61 | .ui.native.sticky {
62 | position: -webkit-sticky;
63 | position: -moz-sticky;
64 | position: -ms-sticky;
65 | position: -o-sticky;
66 | position: sticky;
67 | }
68 |
69 |
70 | /*******************************
71 | Theme Overrides
72 | *******************************/
73 |
74 |
75 |
76 | /*******************************
77 | Site Overrides
78 | *******************************/
79 |
80 |
--------------------------------------------------------------------------------
/minions/public/semantic/components/sticky.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Sticky
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.ui.sticky{position:static;-webkit-transition:width .2s ease,height .2s ease,top .2s ease,bottom .2s ease;transition:width .2s ease,height .2s ease,top .2s ease,bottom .2s ease;z-index:800}.ui.sticky.bound{position:absolute;left:auto;right:auto}.ui.sticky.fixed{position:fixed;left:auto;right:auto}.ui.sticky.bound.top,.ui.sticky.fixed.top{top:0;bottom:auto}.ui.sticky.bound.bottom,.ui.sticky.fixed.bottom{top:auto;bottom:0}.ui.native.sticky{position:-webkit-sticky;position:-moz-sticky;position:-ms-sticky;position:-o-sticky;position:sticky}
--------------------------------------------------------------------------------
/minions/public/semantic/components/tab.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Tab
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */
11 |
12 |
13 | /*******************************
14 | UI Tabs
15 | *******************************/
16 |
17 | .ui.tab {
18 | display: none;
19 | }
20 |
21 |
22 | /*******************************
23 | States
24 | *******************************/
25 |
26 |
27 | /*--------------------
28 | Active
29 | ---------------------*/
30 |
31 | .ui.tab.active,
32 | .ui.tab.open {
33 | display: block;
34 | }
35 |
36 | /*--------------------
37 | Loading
38 | ---------------------*/
39 |
40 | .ui.tab.loading {
41 | position: relative;
42 | overflow: hidden;
43 | display: block;
44 | min-height: 250px;
45 | }
46 | .ui.tab.loading * {
47 | position: relative !important;
48 | left: -10000px !important;
49 | }
50 | .ui.tab.loading:before,
51 | .ui.tab.loading.segment:before {
52 | position: absolute;
53 | content: '';
54 | top: 100px;
55 | left: 50%;
56 | margin: -1.25em 0em 0em -1.25em;
57 | width: 2.5em;
58 | height: 2.5em;
59 | border-radius: 500rem;
60 | border: 0.2em solid rgba(0, 0, 0, 0.1);
61 | }
62 | .ui.tab.loading:after,
63 | .ui.tab.loading.segment:after {
64 | position: absolute;
65 | content: '';
66 | top: 100px;
67 | left: 50%;
68 | margin: -1.25em 0em 0em -1.25em;
69 | width: 2.5em;
70 | height: 2.5em;
71 | -webkit-animation: button-spin 0.6s linear;
72 | animation: button-spin 0.6s linear;
73 | -webkit-animation-iteration-count: infinite;
74 | animation-iteration-count: infinite;
75 | border-radius: 500rem;
76 | border-color: #aaaaaa transparent transparent;
77 | border-style: solid;
78 | border-width: 0.2em;
79 | box-shadow: 0px 0px 0px 1px transparent;
80 | }
81 |
82 |
83 | /*******************************
84 | Tab Overrides
85 | *******************************/
86 |
87 |
88 |
89 | /*******************************
90 | User Overrides
91 | *******************************/
92 |
93 |
--------------------------------------------------------------------------------
/minions/public/semantic/components/tab.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Tab
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.ui.tab.loading.segment:after,.ui.tab.loading.segment:before,.ui.tab.loading:after,.ui.tab.loading:before{position:absolute;left:50%;content:'';top:100px;margin:-1.25em 0 0 -1.25em;width:2.5em}.ui.tab{display:none}.ui.tab.active,.ui.tab.open{display:block}.ui.tab.loading{position:relative;overflow:hidden;display:block;min-height:250px}.ui.tab.loading *{position:relative!important;left:-10000px!important}.ui.tab.loading.segment:before,.ui.tab.loading:before{height:2.5em;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.tab.loading.segment:after,.ui.tab.loading:after{height:2.5em;-webkit-animation:button-spin .6s linear;animation:button-spin .6s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;border-radius:500rem;border-color:#aaa transparent transparent;border-style:solid;border-width:.2em;box-shadow:0 0 0 1px transparent}
--------------------------------------------------------------------------------
/minions/public/semantic/components/video.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Video
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */
11 |
12 |
13 | /*******************************
14 | Video
15 | *******************************/
16 |
17 | .ui.video {
18 | background-color: #dddddd;
19 | position: relative;
20 | max-width: 100%;
21 | padding-bottom: 56.25%;
22 | height: 0px;
23 | overflow: hidden;
24 | }
25 |
26 | /*--------------
27 | Content
28 | ---------------*/
29 |
30 |
31 | /* Placeholder Image */
32 | .ui.video .placeholder {
33 | background-color: #333333;
34 | }
35 |
36 | /* Play Icon Overlay */
37 | .ui.video .play {
38 | cursor: pointer;
39 | position: absolute;
40 | top: 0px;
41 | left: 0px;
42 | z-index: 10;
43 | width: 100%;
44 | height: 100%;
45 | opacity: 0.8;
46 | -webkit-transition: opacity 0.3s;
47 | transition: opacity 0.3s;
48 | }
49 | .ui.video .play.icon:before {
50 | position: absolute;
51 | top: 50%;
52 | left: 50%;
53 | z-index: 11;
54 | background: rgba(0, 0, 0, 0.3);
55 | width: 8rem;
56 | height: 8rem;
57 | line-height: 8rem;
58 | border-radius: 500rem;
59 | color: #ffffff;
60 | font-size: 8rem;
61 | text-shadow: none;
62 | -webkit-transform: translateX(-50%) translateY(-50%);
63 | -ms-transform: translateX(-50%) translateY(-50%);
64 | transform: translateX(-50%) translateY(-50%);
65 | }
66 | .ui.video .placeholder {
67 | position: absolute;
68 | top: 0px;
69 | left: 0px;
70 | display: block;
71 | width: 100%;
72 | height: 100%;
73 | }
74 |
75 | /* IFrame Embed */
76 | .ui.video .embed iframe,
77 | .ui.video .embed embed,
78 | .ui.video .embed object {
79 | position: absolute;
80 | border: none;
81 | width: 100%;
82 | height: 100%;
83 | top: 0px;
84 | left: 0px;
85 | margin: 0em;
86 | padding: 0em;
87 | }
88 |
89 |
90 | /*******************************
91 | States
92 | *******************************/
93 |
94 |
95 | /*--------------
96 | Hover
97 | ---------------*/
98 |
99 | .ui.video .play:hover {
100 | opacity: 1;
101 | }
102 |
103 | /*--------------
104 | Active
105 | ---------------*/
106 |
107 | .ui.video.active .play,
108 | .ui.video.active .placeholder {
109 | display: none;
110 | }
111 | .ui.video.active .embed {
112 | display: inline;
113 | }
114 |
115 |
116 | /*******************************
117 | Video Overrides
118 | *******************************/
119 |
120 |
121 |
122 | /*******************************
123 | Site Overrides
124 | *******************************/
125 |
126 |
--------------------------------------------------------------------------------
/minions/public/semantic/components/video.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * # Semantic UI 1.11.1 - Video
3 | * http://github.com/semantic-org/semantic-ui/
4 | *
5 | *
6 | * Copyright 2014 Contributors
7 | * Released under the MIT license
8 | * http://opensource.org/licenses/MIT
9 | *
10 | */.ui.video{background-color:#ddd;position:relative;max-width:100%;padding-bottom:56.25%;height:0;overflow:hidden}.ui.video .placeholder{background-color:#333}.ui.video .play{cursor:pointer;position:absolute;top:0;left:0;z-index:10;width:100%;height:100%;opacity:.8;-webkit-transition:opacity .3s;transition:opacity .3s}.ui.video .play.icon:before{position:absolute;top:50%;left:50%;z-index:11;background:rgba(0,0,0,.3);width:8rem;height:8rem;line-height:8rem;border-radius:500rem;color:#fff;font-size:8rem;text-shadow:none;-webkit-transform:translateX(-50%)translateY(-50%);-ms-transform:translateX(-50%)translateY(-50%);transform:translateX(-50%)translateY(-50%)}.ui.video .placeholder{position:absolute;top:0;left:0;display:block;width:100%;height:100%}.ui.video .embed embed,.ui.video .embed iframe,.ui.video .embed object{position:absolute;border:none;width:100%;height:100%;top:0;left:0;margin:0;padding:0}.ui.video .play:hover{opacity:1}.ui.video.active .placeholder,.ui.video.active .play{display:none}.ui.video.active .embed{display:inline}
--------------------------------------------------------------------------------
/minions/public/semantic/themes/basic/assets/fonts/icons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/public/semantic/themes/basic/assets/fonts/icons.eot
--------------------------------------------------------------------------------
/minions/public/semantic/themes/basic/assets/fonts/icons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/public/semantic/themes/basic/assets/fonts/icons.ttf
--------------------------------------------------------------------------------
/minions/public/semantic/themes/basic/assets/fonts/icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/public/semantic/themes/basic/assets/fonts/icons.woff
--------------------------------------------------------------------------------
/minions/public/semantic/themes/default/assets/fonts/icons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/public/semantic/themes/default/assets/fonts/icons.eot
--------------------------------------------------------------------------------
/minions/public/semantic/themes/default/assets/fonts/icons.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/public/semantic/themes/default/assets/fonts/icons.otf
--------------------------------------------------------------------------------
/minions/public/semantic/themes/default/assets/fonts/icons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/public/semantic/themes/default/assets/fonts/icons.ttf
--------------------------------------------------------------------------------
/minions/public/semantic/themes/default/assets/fonts/icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/public/semantic/themes/default/assets/fonts/icons.woff
--------------------------------------------------------------------------------
/minions/public/semantic/themes/default/assets/fonts/icons.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/public/semantic/themes/default/assets/fonts/icons.woff2
--------------------------------------------------------------------------------
/minions/public/semantic/themes/default/assets/images/flags.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/public/semantic/themes/default/assets/images/flags.png
--------------------------------------------------------------------------------
/minions/route.js:
--------------------------------------------------------------------------------
1 | var mockRouter = require('./routes/mockRouter.js');
2 | var monitorRouter = require('./routes/monitorRouter.js');
3 |
4 | module.exports = function (app) {
5 | app.use('/mock', mockRouter);//
6 | app.use('/m', monitorRouter);
7 |
8 |
9 | app.get("/", function (req, res, next) {
10 | res.render("index");
11 | });
12 |
13 |
14 | };
--------------------------------------------------------------------------------
/minions/routes/mockRouter.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var commonUtils = require("../lib/utils.js");
3 | var logger = require("../lib/log.js").logger("mockRouter");
4 | var config = require("../config");
5 | var router = express.Router();
6 |
7 |
8 | //生成token
9 | router.get("/genToken", function (req, res, next) {
10 | var userId = req.query.userId;
11 | var userName = req.query.userName;
12 | var appType = req.query.appType;
13 |
14 | console.log("params:", userId, userName, appType);
15 |
16 | var token1 = commonUtils.md5(userId+"_"+userName+"_"+appType, "token_gen_for_ticket@sumory.com");
17 | var token2 = commonUtils.md5(userId+"_"+userName+"_"+appType+"_"+token1, "token_gen_for_spear@sumory.com");
18 |
19 | res.json({
20 | success:true,
21 | token1: token1,
22 | token2: token2
23 | })
24 | });
25 |
26 |
27 | module.exports = router;
28 |
29 |
--------------------------------------------------------------------------------
/minions/routes/monitorRouter.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var request = require("request");
3 |
4 | var commonUtils = require("../lib/utils.js");
5 | var redisUtils = require("../lib/redisUtils");
6 | var zkUtils = require("../lib/zkUtils.js");
7 | var logger = require("../lib/log.js").logger("monitorRouter");
8 | var config = require("../config");
9 | var router = express.Router();
10 |
11 |
12 | router.get("/", function (req, res, next) {
13 | res.render("monitor");
14 | });
15 |
16 | //获取长连接服务节点
17 | router.get("/spears", function (req, res, next) {
18 | zkUtils.getSpears(function (err, spears) {
19 | if (err) {
20 | res.json([]);
21 | }
22 | else {
23 | res.json(spears);
24 | }
25 | });
26 | });
27 |
28 | //获取某个长连接服务节点下连接上的所有群组
29 | router.get("/:spear/groups", function (req, res, next) {
30 | var spear = req.params['spear'];
31 | redisUtils.keys("gru_stat:" + spear + "_*", function (err, result) {
32 | //console.log(result);
33 | res.json(result);
34 | });
35 | });
36 |
37 | //查询某个spear下的所有在线人数
38 | router.get("/:spear/usercount", function (req, res, next) {
39 | var spear = req.params['spear'];
40 | redisUtils.get("user_count:" + spear , function (err, result) {
41 | res.json(result);
42 | });
43 | });
44 |
45 | //获取整个长连接集群下所有的 【节点-群组】 对
46 | router.get("/groups", function (req, res, next) {
47 | redisUtils.keys("gru_stat:*", function (err, result) {
48 | console.log("redis上群组数目",result.length);
49 | res.json(result);
50 | });
51 | });
52 |
53 |
54 | //获取某个节点下某个群组的所有人
55 | router.get("/group/:id", function (req, res, next) {
56 | var id = req.params['id'];
57 | console.log("取组", id, "的统计信息");
58 |
59 | var groupId = id.split("_")[1];
60 | request.get({url:config.statServer+"/getGroupStat?groupId="+groupId}, function (error, response, body) {
61 | logger.info("请求statServer结果:", groupId, error, body);
62 | if(!error){
63 | body = JSON.parse(body);
64 | if(body){
65 | logger.info("请求statServer success");
66 | res.json({
67 | success:true,
68 | data:body
69 | });
70 | }else{
71 | logger.error("请求statServer结果出错");
72 | res.json({
73 | success:false
74 | });
75 | }
76 | }else{
77 | logger.error("请求statServer错误", error);
78 | res.json({
79 | success:false
80 | });
81 | }
82 | });
83 | });
84 |
85 |
86 |
87 | module.exports = router;
88 |
89 |
--------------------------------------------------------------------------------
/minions/start_prod.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # 'dev' or 'prod', check the folder config for more detail
4 | export NODE_ENV=prod
5 |
6 | echo 'Please be sure you have install all needed modules'
7 |
8 |
9 | echo 'start @prod mode'
10 | pm2 stop minions -f
11 | pm2 start app.js -n minions
12 |
13 |
14 |
--------------------------------------------------------------------------------
/minions/start_test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # 'dev' or 'prod', check the folder config for more detail
4 | export NODE_ENV=test
5 |
6 | echo 'Please be sure you have install all needed modules'
7 |
8 |
9 | echo 'start @test mode'
10 | pm2 stop minions -f
11 | pm2 start app.js -n minions
12 |
13 |
14 |
--------------------------------------------------------------------------------
/minions/test/redis.js:
--------------------------------------------------------------------------------
1 | var redis = require('redis');
2 | var async = require('async');
3 |
4 | function test() {
5 | var client = redis.createClient();
6 | client.on("error", function(err) {
7 | console.log("Error " + err);
8 | });
9 |
10 | client.on("ready", function() {
11 |
12 | var index = 0;
13 | var max = 1000000;
14 | var step = 30000;
15 |
16 | var interval = setInterval(function() {
17 | if (index >= max) {
18 | clearInterval(interval);
19 | }
20 | batchSome('test_key', index, index + step, max, function(c) {
21 | //console.log(c);
22 | });
23 | index = index + step;
24 |
25 | }, 100);
26 | //最大11897115,0到11897114
27 | });
28 | }
29 |
30 |
31 | process.on('uncaughtException', function(err) {
32 | console.log('Caught exception: ' + err);
33 | });
34 |
35 | function batchSome(key, start, stop, max, callback) {
36 | var count = 0;
37 | for (var i = start; i < stop; i++) {
38 | client.hset(key, i, i, function(err, result) {
39 | if (err) console.log(result);
40 | count++;
41 | if (count % 10000 == 0) {
42 | console.log('current count:', count, ' start:', start, ' stop:', stop, ' i:', i);
43 | callback(count);
44 | }
45 | if (count == max) {
46 | console.log('finish: ', count);
47 | console.timeEnd('init');
48 | }
49 | });
50 | }
51 | }
--------------------------------------------------------------------------------
/minions/test/utils.js:
--------------------------------------------------------------------------------
1 | var commonUtils = require("../lib/utils.js");
2 | var crypto=require("crypto");
3 |
4 |
5 | var s = commonUtils.md5("123456","-www");
6 | console.log(s);
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/minions/views/common/header.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Gru Dashboard
4 |
5 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/minions/views/common/meta.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/minions/views/common/sidebar.ejs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sumory/gru/89ff67b5941c3baf7c8cbf8a76e6293daaa8818c/minions/views/common/sidebar.ejs
--------------------------------------------------------------------------------
/minions/views/error.ejs:
--------------------------------------------------------------------------------
1 | <%= msg %>
2 |
--------------------------------------------------------------------------------
/spear-client/docs/nett-socket.io.bench.txt:
--------------------------------------------------------------------------------
1 | Customer feedback in 2012:
2 | CentOS, 1 CPU, 4GB RAM runned on VM: CPU 10%, Memory 15%
3 | 6000 xhr-long polling sessions or 15000 websockets sessions
4 | 4000 messages per second
5 |
6 | Customer feedback in 2014:
7 | 30 000 simultaneous websocket clients
8 | 140 000 messages per second
9 | less than 1 second average delay
--------------------------------------------------------------------------------
/spear-client/src/main/java/com/sumory/gru/spear/client/pojo/ChatObject.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.client.pojo;
2 |
3 | import java.io.Serializable;
4 |
5 | public class ChatObject implements Serializable {
6 |
7 | /**
8 | *
9 | */
10 | private static final long serialVersionUID = 1L;
11 | private String userName;
12 | private String message;
13 |
14 | public ChatObject() {
15 | }
16 |
17 | public ChatObject(String userName, String message) {
18 | super();
19 | this.userName = userName;
20 | this.message = message;
21 | }
22 |
23 | public String getUserName() {
24 | return userName;
25 | }
26 |
27 | public void setUserName(String userName) {
28 | this.userName = userName;
29 | }
30 |
31 | public String getMessage() {
32 | return message;
33 | }
34 |
35 | public void setMessage(String message) {
36 | this.message = message;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/spear-client/src/main/java/com/sumory/gru/spear/client/pojo/User.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.client.pojo;
2 |
3 | import java.io.Serializable;
4 |
5 | import com.alibaba.fastjson.JSONObject;
6 |
7 | public class User implements Serializable {
8 | private static final long serialVersionUID = 1L;
9 |
10 | private long id;
11 | private long classId;
12 | private int type;
13 | private String name;
14 |
15 | public long getId() {
16 | return id;
17 | }
18 |
19 | public void setId(long id) {
20 | this.id = id;
21 | }
22 |
23 | public long getClassId() {
24 | return classId;
25 | }
26 |
27 | public void setClassId(long classId) {
28 | this.classId = classId;
29 | }
30 |
31 | public int getType() {
32 | return type;
33 | }
34 |
35 | public void setType(int type) {
36 | this.type = type;
37 | }
38 |
39 | public String getName() {
40 | return name;
41 | }
42 |
43 | public void setName(String name) {
44 | this.name = name;
45 | }
46 |
47 | @Override
48 | public String toString() {
49 | return JSONObject.toJSONString(this);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/spear-client/src/main/resources/bin/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cd `dirname $0`
3 | BIN_DIR=`pwd`
4 | cd ..
5 | DEPLOY_DIR=`pwd`
6 | CONF_DIR=$DEPLOY_DIR/conf
7 |
8 | echo "curr dir is $DEPLOY_DIR"
9 |
10 |
11 | STDOUT_FILE=$DEPLOY_DIR/$1.log
12 |
13 | LIB_DIR=$DEPLOY_DIR/lib
14 | LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'|tr "\n" ":"`
15 |
16 | JAVA_MEM_OPTS=""
17 |
18 | JAVA_MEM_OPTS=" -server -Xmx3g -Xms2g -Xmn1536m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "
19 |
20 |
21 | echo -e "Starting the spear client ...\c"
22 | nohup java $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS -classpath $CONF_DIR:$LIB_JARS com.sumory.gru.spear.client.Main $1 $2 $3 $4 $5 > $STDOUT_FILE 2>&1 &
23 |
24 | echo "OK!"
25 | PIDS=`ps --no-heading -C java -f --width 1000 | grep "$DEPLOY_DIR" | awk '{print $2}'`
26 | echo "PID: $PIDS"
27 | echo $PIDS>pid""$4
28 | echo "STDOUT: $STDOUT_FILE"
29 |
--------------------------------------------------------------------------------
/spear-client/src/main/resources/bin/start_msg.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cd `dirname $0`
3 | BIN_DIR=`pwd`
4 | cd ..
5 | DEPLOY_DIR=`pwd`
6 | CONF_DIR=$DEPLOY_DIR/conf
7 |
8 | echo "curr dir is $DEPLOY_DIR"
9 |
10 |
11 | STDOUT_FILE=$DEPLOY_DIR/$1.log
12 |
13 | LIB_DIR=$DEPLOY_DIR/lib
14 | LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'|tr "\n" ":"`
15 |
16 | JAVA_MEM_OPTS=""
17 |
18 | JAVA_MEM_OPTS=" -server -Xmx3g -Xms2g -Xmn1536m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "
19 |
20 |
21 | echo -e "Starting the spear client ...\c"
22 | nohup java $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS -classpath $CONF_DIR:$LIB_JARS com.sumory.gru.spear.client.msg.MsgMain $1 $2 $3 > $STDOUT_FILE 2>&1 &
23 |
24 | echo "OK!"
25 | PIDS=`ps --no-heading -C java -f --width 1000 | grep "$DEPLOY_DIR" | awk '{print $2}'`
26 | echo "PID: $PIDS"
27 | echo $PIDS>pid""$4
28 | echo "STDOUT: $STDOUT_FILE"
29 |
--------------------------------------------------------------------------------
/spear-client/src/main/resources/profiles/test/log4j.properties:
--------------------------------------------------------------------------------
1 | # Root logger option
2 | log4j.rootLogger=INFO, stdout, file
3 |
4 | # Direct log messages to stdout
5 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
6 | log4j.appender.stdout.Target=System.out
7 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
8 | log4j.appender.stdout.layout.ConversionPattern=[%d{dd HH:mm:ss,SSS\} %-5p] %-20c - %m%n
9 |
10 | log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
11 | log4j.appender.file.File = logs/log.log
12 | log4j.appender.file.Append = true
13 | log4j.appender.file.Threshold = INFO
14 | log4j.appender.file.layout = org.apache.log4j.PatternLayout
15 | log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L - %m%n
--------------------------------------------------------------------------------
/spear-client/src/test/java/com/sumory/gru/spear/client/Test.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.client;
2 |
3 |
4 | public class Test {
5 | public static void main(String[] args) {
6 |
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/spear/docs/nett-socket.io.bench.txt:
--------------------------------------------------------------------------------
1 | Customer feedback in 2012:
2 | CentOS, 1 CPU, 4GB RAM runned on VM: CPU 10%, Memory 15%
3 | 6000 xhr-long polling sessions or 15000 websockets sessions
4 | 4000 messages per second
5 |
6 | Customer feedback in 2014:
7 | 30 000 simultaneous websocket clients
8 | 140 000 messages per second
9 | less than 1 second average delay
--------------------------------------------------------------------------------
/spear/docs/performance.txt:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | - 追求目标:支持横向扩展scale out
4 |
5 |
6 | ### 调优点
7 |
8 | - 集中互连系的client,智能路由到其他spear,这一点与无状态相悖,应在策略和代码上有所取舍
9 | - 用户退出时删除逻辑时间复杂度太高,每次都需要遍历,可更改为退出时单独标记不可用client,然后起另一个线程遍历删除client
10 | 其它用到遍历的地方也可考虑这个方案,比如分为runq,timeoutq,errorq等
11 | - 目前一个群退出的时候并没有清理整个map,考虑到分布处理了,很难判断班里的人是否全部退出,所以可以考虑采用超时机制
12 | - 杀掉服务端进程的时候,应该做善后处理,将连接释放掉
13 |
14 | ### 监控
15 |
16 | - 监控每个slot
17 | - 开启调优策略后监控slot变化
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/spear/docs/选型.md:
--------------------------------------------------------------------------------
1 | ### router、消息转发层选型
2 |
3 | nsq
4 | http://nsq.io/
5 |
6 | nats
7 | https://nats.io/
8 | java客户端 https://github.com/tyagihas/java_nats
9 |
10 | 内存方案
11 | netty-pipe
12 |
13 | rocketmq
14 |
15 |
16 | 以上方案的统一proxy,用于不同类型的项目部署,最小化安装等。
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/domain/AuthObject.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.domain;
2 |
3 | import com.alibaba.fastjson.JSON;
4 |
5 | /**
6 | * 用于鉴权的object
7 | *
8 | * @author sumory.wu
9 | * @date 2015年3月12日 下午7:29:52
10 | */
11 | public class AuthObject {
12 | private long id;
13 | private String name;//名称
14 | private String appType;//客户端标识
15 | private String token1;//鉴权用,由业务系统产生
16 | private String token2;//鉴权用,由ticket产生
17 |
18 | public long getId() {
19 | return id;
20 | }
21 |
22 | public void setId(long id) {
23 | this.id = id;
24 | }
25 |
26 | public String getName() {
27 | return name;
28 | }
29 |
30 | public void setName(String name) {
31 | this.name = name;
32 | }
33 |
34 | public String getToken1() {
35 | return token1;
36 | }
37 |
38 | public void setToken1(String token1) {
39 | this.token1 = token1;
40 | }
41 |
42 | public String getToken2() {
43 | return token2;
44 | }
45 |
46 | public void setToken2(String token2) {
47 | this.token2 = token2;
48 | }
49 |
50 | public String getAppType() {
51 | return appType;
52 | }
53 |
54 | public void setAppType(String appType) {
55 | this.appType = appType;
56 | }
57 |
58 | @Override
59 | public String toString() {
60 | return JSON.toJSONString(this);
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/domain/CommonResult.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.domain;
2 |
3 | import java.util.Map;
4 |
5 | import com.alibaba.fastjson.JSON;
6 |
7 | /**
8 | * 返回结果通用数据结构
9 | *
10 | * @author sumory.wu
11 | * @date 2015年3月11日 下午4:17:43
12 | */
13 | public class CommonResult {
14 | private boolean success;//是否成功
15 | private int errorCode;//错误状态码,一般情况下0表示正常
16 | private String msg;//描述信息
17 | private Map data;//返回数据,扩展字段
18 |
19 | public CommonResult(boolean success) {
20 | this(success, success ? ResultCode.SUCCESS : ResultCode.FAIL, "", null);
21 | }
22 |
23 |
24 | public CommonResult(boolean success, String msg) {
25 | this(success, success ? ResultCode.SUCCESS : ResultCode.FAIL, msg, null);
26 | }
27 |
28 | public CommonResult(boolean success, String msg, Map data) {
29 | this(success, success ? ResultCode.SUCCESS : ResultCode.FAIL, msg, data);
30 | }
31 |
32 | public CommonResult(boolean success, int errorCode, String msg) {
33 | this(success, errorCode, msg, null);
34 | }
35 |
36 | public CommonResult(boolean success, int errorCode, String msg, Map data) {
37 | super();
38 | this.success = success;
39 | this.errorCode = errorCode;
40 | this.msg = (msg == null ? "" : msg);
41 | this.data = data;
42 | }
43 |
44 | public boolean isSuccess() {
45 | return success;
46 | }
47 |
48 | public void setSuccess(boolean success) {
49 | this.success = success;
50 | }
51 |
52 | public int getErrorCode() {
53 | return errorCode;
54 | }
55 |
56 | public void setErrorCode(int errorCode) {
57 | this.errorCode = errorCode;
58 | }
59 |
60 | public Map getData() {
61 | return data;
62 | }
63 |
64 | public void setData(Map data) {
65 | this.data = data;
66 | }
67 |
68 | public String getMsg() {
69 | return msg;
70 | }
71 |
72 | public void setMsg(String msg) {
73 | this.msg = msg;
74 | }
75 |
76 | @Override
77 | public String toString() {
78 | return JSON.toJSONString(this);
79 | }
80 |
81 | public static void main(String[] args) {
82 | CommonResult cr = new CommonResult(true);
83 | System.out.println(cr);
84 | }
85 |
86 | }
87 |
88 |
89 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/domain/MsgObject.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.domain;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import com.alibaba.fastjson.JSON;
7 |
8 | /**
9 | * 传输的消息
10 | *
11 | *
12 | * {
13 | * type: 1, //1 广播,0 单播
14 | * target: { // 给指定target
15 | * id: 10, //单播时指目标用户id,广播时指群组id
16 | * type: 1 //扩展字段,暂时无用
17 | * },
18 | * content: "字符串"//消息内容
19 | * }
20 | *
21 | *
22 | *
23 | * @author sumory.wu
24 | * @date 2015年3月18日 下午5:40:50
25 | */
26 | public class MsgObject {
27 |
28 | private long fromId;//发送者id
29 | private int type;//类型: 1 广播,0 单播给指定target
30 | private Map target;
31 | private String content;
32 |
33 | public static MsgType UNICAST = MsgType.UNICAST;
34 | public static MsgType BRAODCAST = MsgType.BRAODCAST;
35 | public static MsgType MULTICAST = MsgType.MULTICAST;
36 |
37 | public long getFromId() {
38 | return fromId;
39 | }
40 |
41 | public void setFromId(long fromId) {
42 | this.fromId = fromId;
43 | }
44 |
45 | public int getType() {
46 | return type;
47 | }
48 |
49 | public void setType(int type) {
50 | this.type = type;
51 | }
52 |
53 | public Map getTarget() {
54 | return target;
55 | }
56 |
57 | public void setTarget(Map target) {
58 | this.target = target;
59 | }
60 |
61 | public String getContent() {
62 | return content;
63 | }
64 |
65 | public void setContent(String content) {
66 | this.content = content;
67 | }
68 |
69 | @Override
70 | public String toString() {
71 | return JSON.toJSONString(this);
72 | }
73 |
74 | public static void main(String[] args) {
75 | MsgObject o = new MsgObject();
76 | Map target = new HashMap();
77 | target.put("id", 10);
78 | target.put("type", 1);
79 | o.setType(0);
80 | o.setContent("send");
81 | o.setTarget(target);
82 |
83 | System.out.println(o);
84 |
85 | System.out.println(MsgObject.BRAODCAST);
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/domain/MsgType.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.domain;
2 |
3 | public enum MsgType {
4 | BRAODCAST(1), UNICAST(0), MULTICAST(2);
5 |
6 | private int value;
7 |
8 | MsgType(int v) {
9 | this.value = v;
10 | }
11 |
12 | public int getValue() {
13 | return this.value;
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/domain/ResultCode.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.domain;
2 |
3 | public class ResultCode {
4 | public final static int SUCCESS = 0;//成功
5 | public final static int FAIL = -1;//失败
6 | public final static int DEFAULT_ERROR = 1;//默认错误
7 |
8 | public final static int SYSTEM_ERROR = 1001;//系统错误
9 | public final static int LOGIC_ERROR = 1002;//逻辑错误
10 | public final static int PARAMS_ERROR = 1003;//参数错误
11 | }
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/domain/SubscribeObject.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.domain;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 |
6 | import com.alibaba.fastjson.JSON;
7 |
8 | /**
9 | * 用户订阅群组的object
10 | *
11 | * @author sumory.wu
12 | * @date 2015年10月18日 下午3:36:00
13 | */
14 | public class SubscribeObject {
15 | private long userId;
16 | private List subscribeGroups;
17 |
18 | public long getUserId() {
19 | return userId;
20 | }
21 |
22 | public void setUserId(long userId) {
23 | this.userId = userId;
24 | }
25 |
26 | public List getSubscribeGroups() {
27 | return subscribeGroups;
28 | }
29 |
30 | public void setSubscribeGroups(List subscribeGroups) {
31 | this.subscribeGroups = subscribeGroups;
32 | }
33 |
34 | @Override
35 | public String toString() {
36 | return JSON.toJSONString(this);
37 | }
38 |
39 | public class SubscribeGroup {
40 | private long id;
41 | private String name;
42 | private Map extra;
43 |
44 | public SubscribeGroup() {
45 | }
46 |
47 | public SubscribeGroup(long id, String name) {
48 | this.id = id;
49 | this.name = name;
50 | }
51 |
52 | public long getId() {
53 | return id;
54 | }
55 |
56 | public void setId(long id) {
57 | this.id = id;
58 | }
59 |
60 | public String getName() {
61 | return name;
62 | }
63 |
64 | public void setName(String name) {
65 | this.name = name;
66 | }
67 |
68 | public Map getExtra() {
69 | return extra;
70 | }
71 |
72 | public void setExtra(Map extra) {
73 | this.extra = extra;
74 | }
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/extention/IAck.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.extention;
2 |
3 | public interface IAck {
4 | public void ack(T d);
5 | }
6 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/extention/LogAck.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.extention;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 |
7 | public class LogAck implements IAck {
8 | private final static Logger logger = LoggerFactory.getLogger(LogAck.class);
9 |
10 | @Override
11 | public void ack(String d) {
12 | logger.info("Log Ack:{}", d);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/extention/RabbitMQAck.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.extention;
2 |
3 | import com.rabbitmq.client.Channel;
4 | import com.rabbitmq.client.QueueingConsumer;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | public class RabbitMQAck implements IAck {
9 | private final static Logger logger = LoggerFactory.getLogger(RabbitMQAck.class);
10 |
11 | private Channel channel;
12 |
13 | public RabbitMQAck(Channel ch) {
14 | this.channel = ch;
15 | }
16 |
17 | @Override
18 | public void ack(T d) {
19 | try {
20 | this.channel.basicAck(((QueueingConsumer.Delivery) d).getEnvelope().getDeliveryTag(), false);
21 | } catch (Exception e) {
22 | logger.error("RabbitMQ ack错误", e);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/extention/ReceiverDataListener.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.extention;
2 |
3 | import com.corundumstudio.socketio.listener.DataListener;
4 | import com.sumory.gru.spear.transport.IReceiver;
5 |
6 | public abstract class ReceiverDataListener implements DataListener {
7 |
8 | private IReceiver receiver;
9 |
10 | public ReceiverDataListener(IReceiver receiver) {
11 | this.receiver = receiver;
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/extention/SenderDataListener.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.extention;
2 |
3 | import com.corundumstudio.socketio.listener.DataListener;
4 | import com.sumory.gru.spear.transport.ISender;
5 |
6 | public abstract class SenderDataListener implements DataListener {
7 |
8 | private ISender sender;
9 |
10 | public SenderDataListener(ISender sender) {
11 | this.sender = sender;
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/message/BaseMessage.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.message;
2 |
3 |
4 | /**
5 | * 基础消息类,可自行扩展
6 | * 后期考虑尽量减小消息体长度
7 | *
8 | * @author sumory.wu
9 | * @date 2015年2月4日 下午6:41:26
10 | */
11 | public abstract class BaseMessage {
12 | public long id;
13 | public long createTime;//产生时间
14 |
15 | public transient long expireTime;//过期时间(绝对时间,非间隔)
16 |
17 | public BaseMessage(long id) {
18 | this.id = id;
19 | }
20 |
21 | public long getId() {
22 | return id;
23 | }
24 |
25 | public void setId(long id) {
26 | this.id = id;
27 | }
28 |
29 | public long getCreateTime() {
30 | return createTime;
31 | }
32 |
33 | public void setCreateTime(long createTime) {
34 | this.createTime = createTime;
35 | }
36 |
37 | public long getExpireTime() {
38 | return expireTime;
39 | }
40 |
41 | public void setExpireTime(long expireTime) {
42 | this.expireTime = expireTime;
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/message/StringMessage.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.message;
2 |
3 | import java.util.Map;
4 |
5 | import com.alibaba.fastjson.JSONObject;
6 |
7 | // 字符串消息
8 | public class StringMessage extends BaseMessage {
9 | private long fromId;//来自谁
10 | private int msgType;//类型: 1 广播,0 单播给指定target
11 | private Map target;
12 | private String content;
13 |
14 | public StringMessage(long id, long fromId, int msgType, Map target,
15 | String content) {
16 | super(id);
17 | this.createTime = System.currentTimeMillis();
18 |
19 | this.fromId = fromId;
20 | this.msgType = msgType;
21 | this.target = target;
22 | this.content = content;
23 | }
24 |
25 | public long getFromId() {
26 | return fromId;
27 | }
28 |
29 | public void setFromId(long fromId) {
30 | this.fromId = fromId;
31 | }
32 |
33 | public int getMsgType() {
34 | return msgType;
35 | }
36 |
37 | public void setMsgType(int msgType) {
38 | this.msgType = msgType;
39 | }
40 |
41 | public Map getTarget() {
42 | return target;
43 | }
44 |
45 | public void setTarget(Map target) {
46 | this.target = target;
47 | }
48 |
49 | public String getContent() {
50 | return content;
51 | }
52 |
53 | public void setContent(String content) {
54 | this.content = content;
55 | }
56 |
57 | @Override
58 | public String toString() {
59 | return JSONObject.toJSONString(this);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/server/SpearServer.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.server;
2 |
3 | import com.corundumstudio.socketio.Configuration;
4 | import com.corundumstudio.socketio.SocketConfig;
5 | import com.corundumstudio.socketio.SocketIOServer;
6 | import com.sumory.gru.spear.SpearContext;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | /**
11 | * 长连接接入server
12 | *
13 | * @author sumory.wu
14 | * @date 2015年10月17日 下午4:15:20
15 | */
16 | public class SpearServer {
17 | private final static Logger logger = LoggerFactory.getLogger(SpearServer.class);
18 |
19 | private final SpearContext context;
20 | private SocketIOServer server;
21 |
22 | public SpearServer(final SpearContext context) {
23 | this.context = context;
24 | }
25 |
26 | public void run() {
27 | Configuration configuration = new Configuration();
28 | String[] addrArray = this.context.getConfig().get("spear.addr").split(":");
29 | configuration.setHostname(addrArray[0]);
30 | configuration.setPort(Integer.parseInt(addrArray[1]));
31 |
32 | SocketConfig socketConfig = new SocketConfig();
33 | socketConfig.setTcpKeepAlive(true);
34 | socketConfig.setAcceptBackLog(1024);//accept队列长度,min(net.core.somaxconn, backlog)
35 | configuration.setSocketConfig(socketConfig);
36 | // setHeartbeatInterval Heartbeat interval (in seconds), defaults to 25
37 | // setHeartbeatTimeout Heartbeat timeout (in seconds), defaults to 60. Use 0 to disable it
38 | // setCloseTimeout Channel close timeout (in seconds) due to inactivity, defaults to 60
39 |
40 | server = new SocketIOServer(configuration);
41 | ActionListener actionListener = new ActionListener(context);
42 | server.addListeners(actionListener);
43 | server.start();
44 | logger.info("SpearServer is running...");
45 |
46 | //Configuration sConfiguration = server.getConfiguration();
47 | //logger.info("bossThreads:{}, workerThreads:{}, socketConfig:{}",
48 | // sConfiguration.getBossThreads(), sConfiguration.getWorkerThreads(),
49 | // JSON.toJSONString(sConfiguration.getSocketConfig(), true));
50 |
51 | }
52 |
53 | public void stop() {
54 | if (server != null) {
55 | server.stop();
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/service/InnerIdService.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.service;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import com.sumory.gru.idgen.id.IdUtils;
7 | import com.sumory.gru.idgen.service.IdService;
8 |
9 | /**
10 | * 最简化部署single模式时使用的本地id生成服务
11 | *
12 | * @author sumory.wu
13 | * @date 2015年10月25日 下午8:38:09
14 | */
15 | public class InnerIdService implements IdService {
16 | private final static Logger logger = LoggerFactory.getLogger(InnerIdService.class);
17 |
18 | @Override
19 | public String getServiceVersion() {
20 | return "inner";
21 | }
22 |
23 | @Override
24 | public long getMsgId() {
25 | try {
26 | return IdUtils.genMsgId();
27 | }
28 | catch (Exception e) {
29 | logger.error("inner id service generate id error", e);
30 | return Long.MIN_VALUE;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/service/InnerStatService.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.service;
2 |
3 | import java.util.List;
4 | import java.util.Set;
5 |
6 | import com.sumory.gru.common.domain.StatObject;
7 | import com.sumory.gru.stat.service.StatService;
8 |
9 | /**
10 | * 由于最简化部署时不提供stat功能,所以这里没有实现stat服务
11 | *
12 | * @author sumory.wu
13 | * @date 2015年10月25日 下午8:37:35
14 | */
15 | public class InnerStatService implements StatService {
16 |
17 | @Override
18 | public String getServiceVersion() {
19 | return "inner";
20 | }
21 |
22 | @Override
23 | public Set getGroupStat(long groupId) {
24 | return null;
25 | }
26 |
27 | @Override
28 | public List getGroupStatObjectList(long groupId) {
29 | return null;
30 | }
31 |
32 | @Override
33 | public void setGroupStatOfNode(String node, long groupId, String data, int seconds) {
34 |
35 | }
36 |
37 | @Override
38 | public void setGroupStatOfNode(List groupStats, int seconds) {
39 |
40 | }
41 |
42 | @Override
43 | public void setGroupStatObjectOfNode(String node, List stats, int seconds) {
44 |
45 | }
46 |
47 | @Override
48 | public void setUserCountOfNode(String node, String data, int seconds) {
49 |
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/thread/ExecutesManager.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.thread;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 | import java.util.concurrent.BlockingQueue;
6 | import java.util.concurrent.Executor;
7 | import java.util.concurrent.LinkedBlockingQueue;
8 | import java.util.concurrent.ThreadPoolExecutor;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | /**
12 | *
13 | */
14 | public class ExecutesManager {
15 | private ThreadPoolExecutor defaultExecutor = null;
16 | private final Map servicePoolCache = new HashMap();
17 |
18 | //
19 | public ExecutesManager(int minCorePoolSize, int maxCorePoolSize, int queueSize,
20 | long keepAliveTime) {
21 | final BlockingQueue inWorkQueue = new LinkedBlockingQueue(queueSize);
22 | this.defaultExecutor = new ThreadPoolExecutor(minCorePoolSize, maxCorePoolSize,//
23 | keepAliveTime, TimeUnit.SECONDS, inWorkQueue,//
24 | new NameThreadFactory("GRU-SPEAR-%s"), new ThreadPoolExecutor.AbortPolicy());
25 | }
26 |
27 | //
28 | public Executor getExecute(String serviceUniqueName) {
29 | if (this.servicePoolCache.isEmpty() == false) {
30 | ThreadPoolExecutor executor = this.servicePoolCache.get(serviceUniqueName);
31 | if (executor != null) {
32 | return executor;
33 | }
34 | }
35 | return this.defaultExecutor;
36 | }
37 | }
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/thread/NameThreadFactory.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.thread;
2 |
3 | import java.util.concurrent.ThreadFactory;
4 |
5 | /**
6 | *
7 | */
8 | public class NameThreadFactory implements ThreadFactory {
9 | private String nameSample = "Thread-%s";
10 | private int index = 1;
11 |
12 | public NameThreadFactory(String nameSample) {
13 | this.nameSample = nameSample;
14 | }
15 |
16 | public Thread newThread(Runnable run) {
17 | Thread t = new Thread(run);
18 | t.setName(String.format(nameSample, index++));
19 | t.setDaemon(true);
20 | return t;
21 | }
22 | }
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/thread/NamedThreadFactory.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.thread;
2 |
3 | import java.util.concurrent.ThreadFactory;
4 | import java.util.concurrent.atomic.AtomicInteger;
5 |
6 | /*
7 | * 线程池工厂类
8 | */
9 | public class NamedThreadFactory implements ThreadFactory {
10 |
11 | static final AtomicInteger poolNumber = new AtomicInteger(1);
12 |
13 | final AtomicInteger threadNumber = new AtomicInteger(1);
14 | final ThreadGroup group;
15 | final String namePrefix;
16 | final boolean isDaemon;
17 |
18 | public NamedThreadFactory() {
19 | this("gru_named_thread_factory");
20 | }
21 |
22 | public NamedThreadFactory(String name) {
23 | this(name, false);
24 | }
25 |
26 | @Override
27 | public Thread newThread(Runnable r) {
28 | Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
29 | t.setDaemon(isDaemon);
30 | if (t.getPriority() != Thread.NORM_PRIORITY) {
31 | t.setPriority(Thread.NORM_PRIORITY);
32 | }
33 | return t;
34 | }
35 |
36 | public NamedThreadFactory(String preffix, boolean daemon) {
37 | SecurityManager s = System.getSecurityManager();
38 | group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
39 | namePrefix = preffix + "-" + poolNumber.getAndIncrement() + "-thread-";
40 | isDaemon = daemon;
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/transport/IReceiver.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.transport;
2 |
3 | public interface IReceiver {
4 | public void subscribe(final String topic);
5 | }
6 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/transport/ISender.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.transport;
2 |
3 | import com.sumory.gru.spear.domain.MsgObject;
4 |
5 | public interface ISender {
6 |
7 | public void run() throws Exception;
8 |
9 | public void send(final String topic, MsgObject msg);
10 |
11 | public void send(String topic, long tags, String body);
12 | }
13 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/transport/inner/InnerSender.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.transport.inner;
2 |
3 | import java.util.concurrent.BlockingQueue;
4 | import java.util.concurrent.ExecutorService;
5 | import java.util.concurrent.Executors;
6 |
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import com.sumory.gru.spear.SpearContext;
11 | import com.sumory.gru.spear.domain.MsgObject;
12 | import com.sumory.gru.spear.transport.ISender;
13 |
14 | /**
15 | * 单节点时发送器
16 | *
17 | * @author sumory.wu
18 | * @date 2015年10月15日 下午10:46:41
19 | */
20 | public class InnerSender implements ISender {
21 | private final static Logger logger = LoggerFactory.getLogger(InnerSender.class);
22 |
23 | private SpearContext context;
24 | private BlockingQueue msgQueue;//存放消息的队列
25 | private ExecutorService executor;
26 |
27 | public InnerSender(final SpearContext context) {
28 | this.context = context;
29 | this.msgQueue = this.context.getMsgQueue();
30 | this.executor = Executors.newFixedThreadPool(1);
31 |
32 | }
33 |
34 | @Override
35 | public void send(String topic, long tags, String body) {
36 | this.executor.execute(new Runnable() {
37 | @Override
38 | public void run() {
39 | try {
40 | MsgObject msg = new MsgObject();
41 |
42 | InnerSender.this.msgQueue.put(msg);//put方法放入一个msg,若queue满了,等到queue有位置
43 | }
44 | catch (Exception e) {
45 | logger.error("往内部消息队列传入消息发生异常", e);
46 | }
47 | }
48 | });
49 | }
50 |
51 | @Override
52 | public void send(final String topic, final MsgObject msg) {
53 | this.executor.execute(new Runnable() {
54 | @Override
55 | public void run() {
56 | try {
57 | InnerSender.this.msgQueue.put(msg);//put方法放入一个msg,若queue满了,等到queue有位置
58 | }
59 | catch (Exception e) {
60 | logger.error("往内部消息队列传入消息发生异常", e);
61 | }
62 | }
63 | });
64 | }
65 |
66 | @Override
67 | public void run() throws Exception {
68 |
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/transport/redis/RedisSender.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.transport.redis;
2 |
3 | import java.util.concurrent.BlockingQueue;
4 | import java.util.concurrent.ExecutorService;
5 | import java.util.concurrent.Executors;
6 |
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import com.sumory.gru.spear.SpearContext;
11 | import com.sumory.gru.spear.domain.MsgObject;
12 | import com.sumory.gru.spear.transport.ISender;
13 |
14 | /**
15 | * 单节点时发送器
16 | *
17 | * @author sumory.wu
18 | * @date 2015年10月15日 下午10:46:41
19 | */
20 | public class RedisSender implements ISender {
21 | private final static Logger logger = LoggerFactory.getLogger(RedisSender.class);
22 |
23 | private SpearContext context;
24 | private BlockingQueue msgQueue;//存放消息的队列
25 | private ExecutorService executor;
26 |
27 | public RedisSender(final SpearContext context) {
28 | this.context = context;
29 | this.msgQueue = this.context.getMsgQueue();
30 | this.executor = Executors.newScheduledThreadPool(1);
31 |
32 | }
33 |
34 | @Override
35 | public void send(String topic, long tags, String body) {
36 |
37 | }
38 |
39 | @Override
40 | public void send(final String topic, final MsgObject msg) {
41 | this.executor.execute(new Runnable() {
42 | @Override
43 | public void run() {
44 | try {
45 | RedisListener.getInstance().publish(topic, msg);
46 | }
47 | catch (Exception e) {
48 | logger.error("往redis消息队列{}传入消息发生异常", topic, e);
49 | }
50 | }
51 | });
52 | }
53 |
54 | @Override
55 | public void run() throws Exception {
56 |
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/transport/rocketmq/RocketMQSender.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.transport.rocketmq;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import com.alibaba.rocketmq.client.producer.DefaultMQProducer;
7 | import com.alibaba.rocketmq.client.producer.SendResult;
8 | import com.alibaba.rocketmq.common.message.Message;
9 | import com.sumory.gru.idgen.service.IdService;
10 | import com.sumory.gru.spear.SpearContext;
11 | import com.sumory.gru.spear.domain.MsgObject;
12 | import com.sumory.gru.spear.transport.ISender;
13 |
14 | /**
15 | * mq消息发送器
16 | *
17 | * @author sumory.wu
18 | * @date 2015年3月19日 下午3:04:23
19 | */
20 | public class RocketMQSender implements ISender {
21 | private final static Logger logger = LoggerFactory.getLogger(RocketMQSender.class);
22 |
23 | private SpearContext context;
24 | private DefaultMQProducer producer;
25 | private IdService idService;
26 |
27 | public RocketMQSender(final SpearContext context) {
28 | this.context = context;
29 | this.idService = context.getIdService();
30 | //创建mq producer
31 | String mqNamesrvAddr = context.getConfig().get("mq.server.addr");
32 | this.producer = new DefaultMQProducer("SpearProducer");
33 | producer.setNamesrvAddr(mqNamesrvAddr);
34 | }
35 |
36 | public void run() throws Exception {
37 | this.producer.start();
38 | }
39 |
40 | //使用groupId当tags
41 | public void send(String topic, long tags, String body) {
42 | long msgId = 0;
43 | try {
44 | msgId = this.idService.getMsgId();
45 | }
46 | catch (Exception e) {
47 | logger.error("生成消息id异常", e);
48 | }
49 |
50 | try {
51 | Message msg = new Message(topic, tags + "", msgId + "", body.getBytes());
52 | SendResult result = this.producer.send(msg);
53 | logger.debug("{} 发送消息到队列, topic:{} tags:{}, 返回 id:{} result:{}", Thread.currentThread()
54 | .getName(), topic, tags, result.getMsgId(), result.getSendStatus());
55 | }
56 | catch (Exception e) {
57 | logger.error("Sender发送异常", e);
58 | }
59 | }
60 |
61 | @Override
62 | public void send(final String topic, final MsgObject msg) {
63 | //TODO
64 | }
65 | }
66 |
67 | //Message msg = new Message("PushTopic", "push", "1", "Just for test.".getBytes());
68 | //SendResult result = producer.send(msg);
69 | //logger.debug("id:" + result.getMsgId() + " result:" + result.getSendStatus());
70 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/zk/SpearNotifyListener.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.zk;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | import org.apache.zookeeper.ZooDefs;
8 | import org.apache.zookeeper.ZooKeeper;
9 | import org.apache.zookeeper.data.Stat;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import com.sumory.gru.common.zk.NotifyListener;
14 | import com.sumory.gru.common.zk.ZkNode;
15 |
16 | public class SpearNotifyListener implements NotifyListener {
17 | private static final Logger logger = LoggerFactory.getLogger(SpearNotifyListener.class);
18 | private List zkNodes;
19 |
20 | public SpearNotifyListener(ZkNode... zkNodes) {
21 | if (zkNodes != null && zkNodes.length >= 1)
22 | this.zkNodes = Arrays.asList(zkNodes);
23 | else
24 | this.zkNodes = new ArrayList();
25 | }
26 |
27 | @Deprecated
28 | private void addZkNode(ZkNode node) {
29 | this.zkNodes.add(node);
30 | }
31 |
32 | private boolean checkExists(ZooKeeper zk, String node) {
33 | try {
34 | Stat s = zk.exists(node, true);
35 | boolean exists = (s != null ? true : false);
36 | if (exists)
37 | logger.debug("{} 存在", node);
38 | else
39 | logger.debug("{} 不存在", node);
40 |
41 | return exists;
42 | }
43 | catch (Exception e) {
44 | logger.warn("checkExists异常 " + node, e);
45 | return false;
46 | }
47 | }
48 |
49 | /**
50 | * 在zk首次连接,或者其他情况导致重连(这个时候EPHEMERAL节点已消失)后,重新执行该操作
51 | */
52 | @Override
53 | public void action(ZooKeeper zk) {
54 | synchronized (zk) {
55 | for (ZkNode n : zkNodes) {
56 | try {
57 | logger.info("检查节点:{}", n.getPath());
58 | if (!checkExists(zk, n.getPath())) {
59 | logger.info("节点:{} 不存在,尝试创建{}", n.getPath(), n.toString());
60 | zk.create(n.getPath(), n.getData(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
61 | n.getCreateMode());
62 | logger.info("尝试创建后检查节点{} 是否存在:{}", n.getPath(),
63 | checkExists(zk, n.getPath()));
64 | }
65 | }
66 | catch (Exception e) {
67 | logger.error("检查节点,不存在则创建,过程中出错", e);
68 | }
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/zk/SpearZkClientWatcher.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.zk;
2 |
3 | import java.io.IOException;
4 |
5 | import com.sumory.gru.common.zk.NotifyListener;
6 | import com.sumory.gru.common.zk.ZkClientWatcher;
7 |
8 | /**
9 | * 作为spear节点,需要与zk交互的功能并不多,只要保证节点正常启动时在zk始终保持一个临时节点即可
10 | * 如需要扩展,可在这里自定义对应事件,ps:注意zk的watch问题
11 | *
12 | * @author sumory.wu
13 | * @date 2015年3月13日 下午3:04:31
14 | */
15 | public class SpearZkClientWatcher extends ZkClientWatcher {
16 |
17 | public SpearZkClientWatcher(String zkHost, String baseNode, int sessionTimeout, int retryTimes,
18 | NotifyListener listener) throws IOException {
19 | super(zkHost, baseNode, sessionTimeout, retryTimes, listener);
20 | }
21 |
22 | @Override
23 | protected void processDataChanged(String path) {
24 | // TODO Auto-generated method stub
25 |
26 | }
27 |
28 | @Override
29 | protected void processNodeChildrenChanged(String path) {
30 | // TODO Auto-generated method stub
31 |
32 | }
33 |
34 | @Override
35 | protected void processNodeCreated(String path) {
36 | // TODO Auto-generated method stub
37 |
38 | }
39 |
40 | @Override
41 | protected void processNodeDeleted(String path) {
42 | // TODO Auto-generated method stub
43 |
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/spear/src/main/java/com/sumory/gru/spear/zk/ZkUtil.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear.zk;
2 |
3 | import java.io.IOException;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import org.apache.zookeeper.CreateMode;
8 | import org.apache.zookeeper.KeeperException;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import com.sumory.gru.common.zk.NotifyListener;
13 | import com.sumory.gru.common.zk.ZkClientWatcher;
14 | import com.sumory.gru.common.zk.ZkNode;
15 |
16 | public class ZkUtil {
17 | private static final Logger logger = LoggerFactory.getLogger(ZkUtil.class);
18 |
19 | public static void getAllSpearNodes(String baseNode, ZkClientWatcher zkClientWatcher)
20 | throws KeeperException, InterruptedException, IOException {
21 | List nodes = zkClientWatcher.getZooKeeper().getChildren(baseNode, false);
22 | if (nodes != null) {
23 | for (String s : nodes) {
24 | System.out.println("子节点:" + baseNode + "/" + s);
25 | String node = new String(zkClientWatcher.getData(zkClientWatcher, baseNode + "/"
26 | + s));
27 | System.out.println("子节点值:" + node);
28 | }
29 | }
30 | }
31 |
32 | public static void initNode(String zkHost, String baseNode, String outAddr, String spearId,
33 | int sessionTimeout, int retryTimes) throws Exception {
34 |
35 | //初始化listener
36 | String spearNode = baseNode + "/spear" + spearId;
37 | List zkNodes = new ArrayList();
38 | zkNodes.add(new ZkNode(baseNode, "spear cluster nodes".getBytes(), CreateMode.PERSISTENT));
39 |
40 | String spearNodeData = ("spear" + spearId) + "#http://" + outAddr;//例如:spear8#10.60.0.43:31001,严格遵守该格式,省去序列化麻烦
41 | zkNodes.add(new ZkNode(spearNode, spearNodeData.getBytes(), CreateMode.EPHEMERAL));
42 | NotifyListener listener = new SpearNotifyListener(
43 | zkNodes.toArray(new ZkNode[zkNodes.size()]));
44 |
45 | //创建zookeeper
46 | ZkClientWatcher zkClientWatcher = new SpearZkClientWatcher(zkHost, baseNode,
47 | sessionTimeout, retryTimes, listener);
48 |
49 | getAllSpearNodes(baseNode, zkClientWatcher);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/spear/src/main/resources/bin/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd `dirname $0`
4 | BIN_DIR=`pwd`
5 | cd ..
6 | DEPLOY_DIR=`pwd`
7 | CONF_DIR=$DEPLOY_DIR/conf
8 |
9 | echo "curr dir is $DEPLOY_DIR"
10 |
11 |
12 | SERVER_NAME=gru_spear
13 |
14 | STDOUT_FILE=$DEPLOY_DIR/stdout.log
15 |
16 | LIB_DIR=$DEPLOY_DIR/lib
17 | LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'|tr "\n" ":"`
18 |
19 | JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
20 |
21 |
22 | #RMI_SERVER_HOSTNAME=10.60.0.67
23 | #JMX_REMOTE_PORT=10900
24 | #JAVA_JMX_OPTS=" -Djava.rmi.server.hostname="$RMI_SERVER_HOSTNAME" -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port="$JMX_REMOTE_PORT" -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false "
25 | JAVA_JMX_OPTS=""
26 |
27 |
28 | JAVA_MEM_OPTS=" -server -Xmx5g -Xms5g -Xmn2048m -XX:PermSize=128m -Xss256k -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "
29 |
30 |
31 | echo -e "Starting the $SERVER_NAME ...\c"
32 | nohup java $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_JMX_OPTS -classpath $CONF_DIR:$LIB_JARS com.sumory.gru.spear.main.SpearMain > $STDOUT_FILE 2>&1 &
33 |
34 |
35 | echo "OK!"
36 | PIDS=`ps --no-heading -C java -f --width 1000 | grep "$DEPLOY_DIR" | awk '{print $2}'`
37 | echo "PID: $PIDS"
38 | echo $PIDS>pid
39 | echo "STDOUT: $STDOUT_FILE"
40 |
--------------------------------------------------------------------------------
/spear/src/main/resources/profiles/dev/dubbo.properties:
--------------------------------------------------------------------------------
1 | #################### dubbo ####################################
2 | dubbo.registry.client=curator
3 | dubbo.registry.address=zookeeper://192.168.100.183:2181
4 | dubbo.registry.file=/data/logs/spear/registry.cache
5 |
6 |
7 | ###### dubbo service api version ######
8 | dubbo.gru.idgen.version=dev
9 | dubbo.gru.stat.version=dev
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/spear/src/main/resources/profiles/dev/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /data/logs/spear
5 | [%d{MM-dd HH:mm:ss.SSS}] [%p] %l - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/spear/src/main/resources/profiles/dev/system.properties:
--------------------------------------------------------------------------------
1 | #################### mode ####################################
2 | ###指的是节点间的通讯方式,有single、rocketmq、redis可选
3 | #single指单点模式,也是最小化安装模式,只需要一个spear节点提供服务,不需要idgen、stat、ticket等服务和zookeeper、redis、mq等中间件
4 | #redis指各个spear长连接服务节点间的消息传递通过redis实现
5 | #rocketmq指各个spear长连接节点间的消息传递通过rocketmq实现,rocketmq有很好的消息积压和传递性能
6 | mode=single
7 |
8 | #################### mq配置,在mode选择roketmq时应配置该值 ###################################
9 | mq.server.addr=10.60.0.47:9876
10 |
11 | #################### redis配置,在mode选择redis时需要通过redis转发消息,应配置该值 ###################################
12 | #最大分配的对象数
13 | redis.pool.maxTotal=200
14 | #最大能够保持idel状态的对象数
15 | redis.pool.maxIdle=30
16 | #当池内没有返回对象时,最大等待时间 ,单位毫秒
17 | redis.pool.maxWaitMillis=5000
18 | #向调用者输出“链接”资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。设为true,一个挂都不能用
19 | redis.pool.testOnBorrow=false
20 | #向连接池“归还”链接时,是否检测“链接”对象的有效性。
21 | redis.pool.testOnReturn=true
22 | #IP and Port
23 | redis.ip=192.168.100.185
24 | redis.port=6379
25 |
26 |
27 |
28 | #################### spear ####################################
29 | #长连接接入服务配置,集群中不同的spear节点,id都不能冲突,同机器上的port不能冲突
30 | spear.addr=0.0.0.0:31001
31 | spear.id=111
32 | #该节点对应的外部访问地址,需要上报到zk,由ticket模块负责对外暴露
33 | out.addr=0.0.0.0:31001
34 |
35 | #################### monitor ###################################
36 | #是否开启监控server, true启动,false不启动,监控的端口应该只对内网开放
37 | monitor.start=true
38 | monitor.addr=0.0.0.0:31002
39 |
40 |
41 | #################### ZooKeeper Properties ######################
42 | zk.addr=192.168.100.183:2181
43 | zk.spear.cluster=/spearnodes_dev
44 |
45 |
46 | #################### stat 统计服务配置 ####################################
47 | #是否开启统计服务
48 | stat.start=true
49 |
50 | #################### hash盐,用于鉴权 ############################
51 | auth.open=false
52 | salt.toticket=token_gen_for_ticket@sumory.com
53 | salt.tospear=token_gen_for_spear@sumory.com
54 |
55 | #################### 发出的消息的ack模式,即如何处理消息回执,目前支持LogAck #############
56 | ack=log
57 |
58 |
--------------------------------------------------------------------------------
/spear/src/main/resources/profiles/test1/dubbo.properties:
--------------------------------------------------------------------------------
1 | #################### dubbo ####################################
2 | dubbo.registry.client=curator
3 | dubbo.registry.address=zookeeper://192.168.100.183:2181
4 | dubbo.registry.file=/data/logs/spear1/registry.cache
5 |
6 | ###### dubbo service api version ######
7 | dubbo.gru.stat.version=1.0
8 | dubbo.gru.idgen.version=1.0
--------------------------------------------------------------------------------
/spear/src/main/resources/profiles/test1/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /data/logs/spear
5 | [%d{MM-dd HH:mm:ss.SSS}] [%p] %l - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/spear/src/main/resources/profiles/test1/system.properties:
--------------------------------------------------------------------------------
1 | #################### mode ####################################
2 | ###指的是节点间的通讯方式,有single、rocketmq、redis可选
3 | #single指单点模式,也是最小化安装模式,只需要一个spear节点提供服务,不需要idgen、stat、ticket等服务和zookeeper、redis、mq等中间件
4 | #redis指各个spear长连接服务节点间的消息传递通过redis实现
5 | #rocketmq指各个spear长连接节点间的消息传递通过rocketmq实现,rocketmq有很好的消息积压和传递性能
6 | mode=redis
7 |
8 | #################### mq配置,在mode选择roketmq时应配置该值 ###################################
9 | mq.server.addr=10.60.0.47:9876
10 |
11 | #################### redis配置,在mode选择redis时需要通过redis转发消息,应配置该值 ###################################
12 | #最大分配的对象数
13 | redis.pool.maxTotal=200
14 | #最大能够保持idel状态的对象数
15 | redis.pool.maxIdle=30
16 | #当池内没有返回对象时,最大等待时间 ,单位毫秒
17 | redis.pool.maxWaitMillis=5000
18 | #向调用者输出“链接”资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。设为true,一个挂都不能用
19 | redis.pool.testOnBorrow=false
20 | #向连接池“归还”链接时,是否检测“链接”对象的有效性。
21 | redis.pool.testOnReturn=true
22 | #IP and Port
23 | redis.ip=192.168.100.185
24 | redis.port=6379
25 |
26 | #################### spear ####################################
27 | #长连接接入服务配置,集群中不同的spear节点,id都不能冲突,同机器上的port不能冲突
28 | spear.addr=192.168.100.122:31001
29 | spear.id=1
30 | #该节点对应的外部访问地址,需要上报到zk,由ticket模块负责对外暴露
31 | out.addr=192.168.100.122:31001
32 |
33 | #################### monitor ###################################
34 | #是否开启监控server, true启动,false不启动,监控的端口应该只对内网开放
35 | monitor.start=true
36 | monitor.addr=192.168.100.122:31002
37 |
38 |
39 | #################### ZooKeeper Properties ######################
40 | zk.addr=192.168.100.183:2181
41 | zk.spear.cluster=/spearnodes_test
42 |
43 |
44 | #################### stat 统计服务配置 ####################################
45 | #是否开启统计服务
46 | stat.start=false
47 |
48 | #################### hash盐,用于鉴权 ############################
49 | auth.open=true
50 | salt.toticket=token_gen_for_ticket@sumory.com
51 | salt.tospear=token_gen_for_spear@sumory.com
52 |
53 | #################### 发出的消息的ack模式,即如何处理消息回执 #############
54 | ack=log
--------------------------------------------------------------------------------
/spear/src/main/resources/profiles/test2/dubbo.properties:
--------------------------------------------------------------------------------
1 | #################### dubbo ####################################
2 | dubbo.registry.client=curator
3 | dubbo.registry.address=zookeeper://192.168.100.183:2181
4 | dubbo.registry.file=/data/logs/spear2/registry.cache
5 |
6 |
7 | ###### dubbo service api version ######
8 | dubbo.gru.stat.version=1.0
9 | dubbo.gru.idgen.version=1.0
--------------------------------------------------------------------------------
/spear/src/main/resources/profiles/test2/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /data/logs/spear
5 | [%d{MM-dd HH:mm:ss.SSS}] [%p] %l - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/spear/src/main/resources/profiles/test2/system.properties:
--------------------------------------------------------------------------------
1 | #################### mode ####################################
2 | ###指的是节点间的通讯方式,有single、rocketmq、redis可选
3 | #single指单点模式,也是最小化安装模式,只需要一个spear节点提供服务,不需要idgen、stat、ticket等服务和zookeeper、redis、mq等中间件
4 | #redis指各个spear长连接服务节点间的消息传递通过redis实现
5 | #rocketmq指各个spear长连接节点间的消息传递通过rocketmq实现,rocketmq有很好的消息积压和传递性能
6 | mode=redis
7 |
8 | #################### mq配置,在mode选择roketmq时应配置该值 ###################################
9 | mq.server.addr=10.60.0.47:9876
10 |
11 | #################### redis配置,在mode选择redis时需要通过redis转发消息,应配置该值 ###################################
12 | #最大分配的对象数
13 | redis.pool.maxTotal=200
14 | #最大能够保持idel状态的对象数
15 | redis.pool.maxIdle=30
16 | #当池内没有返回对象时,最大等待时间 ,单位毫秒
17 | redis.pool.maxWaitMillis=5000
18 | #向调用者输出“链接”资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。设为true,一个挂都不能用
19 | redis.pool.testOnBorrow=false
20 | #向连接池“归还”链接时,是否检测“链接”对象的有效性。
21 | redis.pool.testOnReturn=true
22 | #IP and Port
23 | redis.ip=192.168.100.185
24 | redis.port=6379
25 |
26 | #################### spear ####################################
27 | #长连接接入服务配置,集群中不同的spear节点,id都不能冲突,同机器上的port不能冲突
28 | spear.addr=192.168.100.122:31003
29 | spear.id=2
30 | #该节点对应的外部访问地址,需要上报到zk,由ticket模块负责对外暴露
31 | out.addr=192.168.100.122:31003
32 |
33 | #################### monitor ###################################
34 | #是否开启监控server, true启动,false不启动,监控的端口应该只对内网开放
35 | monitor.start=true
36 | monitor.addr=192.168.100.122:31004
37 |
38 |
39 | #################### ZooKeeper Properties ######################
40 | zk.addr=192.168.100.183:2181
41 | zk.spear.cluster=/spearnodes_test
42 |
43 |
44 | #################### stat 统计服务配置 ####################################
45 | #是否开启统计服务
46 | stat.start=false
47 |
48 | #################### hash盐,用于鉴权 ############################
49 | auth.open=true
50 | salt.toticket=token_gen_for_ticket@sumory.com
51 | salt.tospear=token_gen_for_spear@sumory.com
52 |
53 | #################### 发出的消息的ack模式,即如何处理消息回执 #############
54 | ack=log
55 |
--------------------------------------------------------------------------------
/spear/src/main/resources/spring/applicationContext-consumer-idgen.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
12 |
--------------------------------------------------------------------------------
/spear/src/main/resources/spring/applicationContext-consumer-stat.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
12 |
--------------------------------------------------------------------------------
/spear/src/main/resources/spring/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/spear/src/test/java/com/sumory/gru/spear/Test.java:
--------------------------------------------------------------------------------
1 | package com.sumory.gru.spear;
2 |
3 | import java.util.Arrays;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | /**
8 | *
9 | *
10 | * @author sumory.wu
11 | * @date 2015年1月14日 下午8:25:27
12 | */
13 | public class Test {
14 | public static void main(String[] args) {
15 |
16 | Integer[] abc = new Integer[] { 1, 2, 3, 4, 99, 7 };
17 | List list = Arrays.asList(abc);
18 | Collections.sort(list);
19 | System.out.println(list.get(list.size() - 1));
20 |
21 | List