├── README.md
├── im-client
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── ai
│ │ │ └── yunxi
│ │ │ └── im
│ │ │ └── client
│ │ │ ├── IMClientApplication.java
│ │ │ ├── config
│ │ │ ├── BeanConfiguration.java
│ │ │ ├── InitConfiguration.java
│ │ │ └── SpringBeanFactory.java
│ │ │ ├── handle
│ │ │ └── IMClientHandle.java
│ │ │ ├── init
│ │ │ └── IMClientInit.java
│ │ │ └── scanner
│ │ │ └── Scan.java
│ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── ai
│ └── yunxi
│ └── im
│ └── client
│ └── AppTest.java
├── im-common
├── pom.xml
├── protobuf
│ └── MessageProto.proto
└── src
│ └── main
│ └── java
│ └── ai
│ └── yunxi
│ └── im
│ └── common
│ ├── constant
│ ├── BasicConstant.java
│ └── MessageConstant.java
│ ├── enums
│ └── StatusEnum.java
│ ├── pojo
│ ├── ChatInfo.java
│ ├── ServerInfo.java
│ └── UserInfo.java
│ ├── protocol
│ └── MessageProto.java
│ └── utils
│ └── StringUtil.java
├── im-route
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── ai
│ │ │ └── yunxi
│ │ │ └── im
│ │ │ └── route
│ │ │ ├── RouteApplication.java
│ │ │ ├── config
│ │ │ ├── BeanConfiguration.java
│ │ │ └── InitConfiguration.java
│ │ │ ├── controller
│ │ │ └── IMRouteController.java
│ │ │ ├── service
│ │ │ ├── RouteService.java
│ │ │ └── impl
│ │ │ │ └── RouteServiceImpl.java
│ │ │ └── zk
│ │ │ └── ZKUtil.java
│ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── ai
│ └── yunxi
│ └── im
│ └── route
│ └── AppTest.java
├── im-server
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── ai
│ │ └── yunxi
│ │ └── im
│ │ └── server
│ │ ├── IMServerApplication.java
│ │ ├── config
│ │ ├── BeanConfiguration.java
│ │ ├── InitConfiguration.java
│ │ └── SpringBeanFactory.java
│ │ ├── controller
│ │ └── IMServerController.java
│ │ ├── handle
│ │ ├── ChannelMap.java
│ │ ├── ClientProcessor.java
│ │ └── IMServerHandle.java
│ │ ├── init
│ │ └── IMServerInit.java
│ │ └── zk
│ │ ├── RegisterToZK.java
│ │ └── ZKUtil.java
│ └── resources
│ └── application.properties
├── pic
└── im.png
└── pom.xml
/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smallFive55/im/42c11418fc8d2e702f0ab0f0596ccc86bfe5e643/README.md
--------------------------------------------------------------------------------
/im-client/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | ai.yunxi
7 | im
8 | 0.0.1-SNAPSHOT
9 |
10 | ai.yunxi
11 | im-client
12 | 0.0.1-SNAPSHOT
13 | im-client
14 | http://maven.apache.org
15 |
16 | UTF-8
17 |
18 |
19 |
20 | org.springframework.boot
21 | spring-boot-starter-web
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-test
26 | test
27 |
28 |
29 | ai.yunxi
30 | im-common
31 | 0.0.1-SNAPSHOT
32 |
33 |
34 |
35 |
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-maven-plugin
40 |
41 |
42 |
43 | repackage
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/im-client/src/main/java/ai/yunxi/im/client/IMClientApplication.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.client;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.boot.CommandLineRunner;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 |
9 | import ai.yunxi.im.client.scanner.Scan;
10 |
11 | /**
12 | *
13 | * @author 小五老师-云析学院
14 | * @createTime 2019年2月26日 下午2:59:36
15 | *
16 | */
17 | @SpringBootApplication
18 | public class IMClientApplication implements CommandLineRunner {
19 |
20 | private final static Logger LOGGER = LoggerFactory.getLogger(IMClientApplication.class);
21 |
22 | public static void main(String[] args) {
23 | SpringApplication.run(IMClientApplication.class, args);
24 | LOGGER.info("启动 Client 服务成功");
25 | }
26 |
27 | @Override
28 | public void run(String... args) throws Exception {
29 | try {
30 | Thread th = new Thread(new Scan());
31 | th.setName("client-scanner-thread");
32 | th.start();
33 | } catch (Exception e) {
34 | e.printStackTrace();
35 | }
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/im-client/src/main/java/ai/yunxi/im/client/config/BeanConfiguration.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.client.config;
2 |
3 | import java.util.concurrent.TimeUnit;
4 |
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | import okhttp3.OkHttpClient;
9 |
10 | /**
11 | *
12 | * @author 小五老师-云析学院
13 | * @createTime 2019年2月26日 下午10:08:36
14 | *
15 | */
16 | @Configuration
17 | public class BeanConfiguration {
18 |
19 | /**
20 | * http client
21 | * @return okHttp
22 | */
23 | @Bean
24 | public OkHttpClient okHttpClient() {
25 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
26 | builder.connectTimeout(30, TimeUnit.SECONDS)
27 | .readTimeout(10, TimeUnit.SECONDS)
28 | .writeTimeout(10,TimeUnit.SECONDS)
29 | .retryOnConnectionFailure(true);
30 | return builder.build();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/im-client/src/main/java/ai/yunxi/im/client/config/InitConfiguration.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.client.config;
2 |
3 | import org.springframework.beans.factory.annotation.Value;
4 | import org.springframework.stereotype.Component;
5 |
6 | /**
7 | * @author 小五老师-云析学院
8 | * @createTime 2019年3月12日 下午8:56:48
9 | *
10 | */
11 | @Component
12 | public class InitConfiguration {
13 |
14 | @Value("${im.user.id}")
15 | private int userId;
16 | @Value("${im.user.userName}")
17 | private String userName;
18 | @Value("${im.route.login.url}")
19 | private String routeLoginUrl;
20 | @Value("${im.route.chat.url}")
21 | private String routeChatUrl;
22 | @Value("${im.route.logout.url}")
23 | private String routeLogoutUrl;
24 |
25 | public int getUserId() {
26 | return userId;
27 | }
28 | public void setUserId(int userId) {
29 | this.userId = userId;
30 | }
31 | public String getUserName() {
32 | return userName;
33 | }
34 | public void setUserName(String userName) {
35 | this.userName = userName;
36 | }
37 | public String getRouteLoginUrl() {
38 | return routeLoginUrl;
39 | }
40 | public void setRouteLoginUrl(String routeLoginUrl) {
41 | this.routeLoginUrl = routeLoginUrl;
42 | }
43 | public String getRouteChatUrl() {
44 | return routeChatUrl;
45 | }
46 | public void setRouteChatUrl(String routeChatUrl) {
47 | this.routeChatUrl = routeChatUrl;
48 | }
49 | public String getRouteLogoutUrl() {
50 | return routeLogoutUrl;
51 | }
52 | public void setRouteLogoutUrl(String routeLogoutUrl) {
53 | this.routeLogoutUrl = routeLogoutUrl;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/im-client/src/main/java/ai/yunxi/im/client/config/SpringBeanFactory.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.client.config;
2 |
3 | import org.springframework.beans.BeansException;
4 | import org.springframework.context.ApplicationContext;
5 | import org.springframework.context.ApplicationContextAware;
6 | import org.springframework.stereotype.Component;
7 |
8 | @Component
9 | public final class SpringBeanFactory implements ApplicationContextAware{
10 | private static ApplicationContext context;
11 |
12 | public static T getBean(Class c){
13 | return context.getBean(c);
14 | }
15 |
16 |
17 | public static T getBean(String name,Class clazz){
18 | return context.getBean(name,clazz);
19 | }
20 |
21 | @Override
22 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
23 | context = applicationContext;
24 | }
25 |
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/im-client/src/main/java/ai/yunxi/im/client/handle/IMClientHandle.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.client.handle;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import ai.yunxi.im.client.config.SpringBeanFactory;
7 | import ai.yunxi.im.client.init.IMClientInit;
8 | import ai.yunxi.im.common.protocol.MessageProto;
9 | import io.netty.channel.ChannelHandlerContext;
10 | import io.netty.channel.ChannelInboundHandlerAdapter;
11 |
12 | /**
13 | *
14 | * @author 小五老师-云析学院
15 | * @createTime 2019年2月26日 下午10:01:02
16 | *
17 | */
18 | public class IMClientHandle extends ChannelInboundHandlerAdapter {
19 |
20 | private final static Logger LOGGER = LoggerFactory.getLogger(IMClientHandle.class);
21 |
22 | @Override
23 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
24 |
25 | MessageProto.MessageProtocol message = (MessageProto.MessageProtocol) msg;
26 | LOGGER.info("客户端接收到消息:"+message.getContent());
27 | }
28 |
29 | /**
30 | * 当客户端发现服务端断线后,发起重连
31 | **/
32 | @Override
33 | public void channelInactive(ChannelHandlerContext ctx) throws Exception {
34 | IMClientInit client = SpringBeanFactory.getBean(IMClientInit.class);
35 | LOGGER.info("所连接的服务端断开了连接,发起重连请求...");
36 | try {
37 | client.restart();
38 | LOGGER.info("客户端重连成功!");
39 | } catch (Exception e) {
40 | LOGGER.info("客户端重连失败!");
41 | e.printStackTrace();
42 | }
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/im-client/src/main/java/ai/yunxi/im/client/init/IMClientInit.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.client.init;
2 |
3 | import java.io.IOException;
4 |
5 | import javax.annotation.PostConstruct;
6 |
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.stereotype.Component;
11 |
12 | import com.alibaba.fastjson.JSON;
13 | import com.alibaba.fastjson.JSONObject;
14 |
15 | import ai.yunxi.im.client.config.InitConfiguration;
16 | import ai.yunxi.im.client.handle.IMClientHandle;
17 | import ai.yunxi.im.common.constant.BasicConstant;
18 | import ai.yunxi.im.common.constant.MessageConstant;
19 | import ai.yunxi.im.common.pojo.ChatInfo;
20 | import ai.yunxi.im.common.pojo.ServerInfo;
21 | import ai.yunxi.im.common.protocol.MessageProto;
22 | import io.netty.bootstrap.Bootstrap;
23 | import io.netty.channel.Channel;
24 | import io.netty.channel.ChannelFuture;
25 | import io.netty.channel.ChannelInitializer;
26 | import io.netty.channel.ChannelPipeline;
27 | import io.netty.channel.EventLoopGroup;
28 | import io.netty.channel.nio.NioEventLoopGroup;
29 | import io.netty.channel.socket.SocketChannel;
30 | import io.netty.channel.socket.nio.NioSocketChannel;
31 | import io.netty.handler.codec.protobuf.ProtobufDecoder;
32 | import io.netty.handler.codec.protobuf.ProtobufEncoder;
33 | import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
34 | import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
35 | import io.netty.util.concurrent.DefaultThreadFactory;
36 | import okhttp3.OkHttpClient;
37 | import okhttp3.Request;
38 | import okhttp3.RequestBody;
39 | import okhttp3.Response;
40 | import okhttp3.ResponseBody;
41 |
42 | /**
43 | *
44 | * @author 小五老师-云析学院
45 | * @createTime 2019年2月26日 下午9:34:17
46 | * 客户端启动初始化:
47 | * 1、与服务端建立连接
48 | * 2、处理客户端输入
49 | */
50 | @Component
51 | public class IMClientInit {
52 |
53 | private final static Logger LOGGER = LoggerFactory.getLogger(IMClientInit.class);
54 | private ServerInfo server;
55 | public Channel channel;
56 | private EventLoopGroup group = new NioEventLoopGroup(0, new DefaultThreadFactory("im-client-work"));
57 |
58 | @Autowired
59 | private InitConfiguration conf;
60 | @Autowired
61 | private OkHttpClient okHttpClient;
62 |
63 | @PostConstruct
64 | public void start() throws Exception{
65 | if(server != null){
66 | LOGGER.info("---客户端当前已登录");
67 | return;
68 | }
69 | //1.获取服务端ip+port
70 | getServerInfo();
71 | //2.启动客户端
72 | startClient();
73 | //3.登录到服务端
74 | registerToServer();
75 |
76 | }
77 |
78 | /**
79 | * 与服务端通信
80 | */
81 | private void registerToServer() {
82 | MessageProto.MessageProtocol login = MessageProto.MessageProtocol.newBuilder()
83 | .setUserId(conf.getUserId())
84 | .setContent(conf.getUserName())
85 | .setCommand(MessageConstant.LOGIN)
86 | .setTime(System.currentTimeMillis())
87 | .build();
88 | channel.writeAndFlush(login);
89 | }
90 |
91 | /**
92 | * 向路由服务器获取服务端IP与端口
93 | */
94 | private void getServerInfo() {
95 | try {
96 | JSONObject jsonObject = new JSONObject();
97 | jsonObject.put("userId",conf.getUserId());
98 | jsonObject.put("userName",conf.getUserName());
99 | RequestBody requestBody = RequestBody.create(BasicConstant.MEDIA_TYPE,jsonObject.toString());
100 |
101 | Request request = new Request.Builder()
102 | .url(conf.getRouteLoginUrl())
103 | .post(requestBody)
104 | .build();
105 |
106 | Response response = okHttpClient.newCall(request).execute() ;
107 | if (!response.isSuccessful()){
108 | throw new IOException("Unexpected code " + response);
109 | }
110 | ResponseBody body = response.body();
111 | try {
112 | String json = body.string();
113 | server = JSON.parseObject(json, ServerInfo.class);
114 |
115 | }finally {
116 | body.close();
117 | }
118 | } catch (IOException e) {
119 | LOGGER.error("连接失败!");
120 | }
121 | }
122 |
123 | /**
124 | * 启动客户端,建立连接
125 | */
126 | public void startClient(){
127 | try {
128 | Bootstrap bootstrap = new Bootstrap();
129 | bootstrap.group(group)
130 | .channel(NioSocketChannel.class)
131 | .handler(new ChannelInitializer() {
132 | @Override
133 | protected void initChannel(SocketChannel ch) throws Exception {
134 | ChannelPipeline pipeline = ch.pipeline();
135 | // google Protobuf 编解码
136 | pipeline.addLast(new ProtobufVarint32FrameDecoder());
137 | pipeline.addLast(new ProtobufDecoder(MessageProto.MessageProtocol.getDefaultInstance()));
138 | pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
139 | pipeline.addLast(new ProtobufEncoder());
140 |
141 | pipeline.addLast(new IMClientHandle());
142 | }
143 | });
144 |
145 | ChannelFuture future = bootstrap.connect(server.getIp(), server.getNettyPort()).sync();
146 | if (future.isSuccess()) {
147 | LOGGER.info("---客户端启动成功[nettyport:"+8090+"]");
148 | }
149 | channel = future.channel();
150 | } catch (InterruptedException e) {
151 | LOGGER.error("---连接失败", e);
152 | } catch (Exception e) {
153 | e.printStackTrace();
154 | } finally {
155 | // group.shutdownGracefully();
156 | }
157 |
158 | }
159 |
160 | /**
161 | * 客户端发送消息
162 | **/
163 | public void sendMessage(ChatInfo chat){
164 | try {
165 | JSONObject jsonObject = new JSONObject();
166 | jsonObject.put("command",chat.getCommand());
167 | jsonObject.put("time",chat.getTime());
168 | jsonObject.put("userId",chat.getUserId());
169 | jsonObject.put("content",chat.getContent());
170 | RequestBody requestBody = RequestBody.create(BasicConstant.MEDIA_TYPE,jsonObject.toString());
171 |
172 | Request request = new Request.Builder()
173 | .url(conf.getRouteChatUrl())
174 | .post(requestBody)
175 | .build();
176 | Response response = okHttpClient.newCall(request).execute() ;
177 | if (!response.isSuccessful()){
178 | throw new IOException("Unexpected code " + response);
179 | }
180 | } catch (IOException e) {
181 | e.printStackTrace();
182 | }
183 | }
184 |
185 | /**
186 | * 清理客户端登录
187 | **/
188 | public void clear(){
189 | logoutRoute();
190 | logoutServer();
191 | server = null;
192 | }
193 |
194 | /**
195 | * 客户端登出命令-路由端处理
196 | **/
197 | private void logoutRoute(){
198 | try {
199 | JSONObject jsonObject = new JSONObject();
200 | jsonObject.put("userId",conf.getUserId());
201 | RequestBody requestBody = RequestBody.create(BasicConstant.MEDIA_TYPE,jsonObject.toString());
202 |
203 | Request request = new Request.Builder()
204 | .url(conf.getRouteLogoutUrl())
205 | .post(requestBody)
206 | .build();
207 |
208 | Response response = okHttpClient.newCall(request).execute() ;
209 | if (!response.isSuccessful()){
210 | throw new IOException("Unexpected code " + response);
211 | }
212 | } catch (IOException e) {
213 | e.printStackTrace();
214 | }
215 | }
216 |
217 | /**
218 | * 客户端登出命令-路由端处理
219 | **/
220 | private void logoutServer() {
221 | try {
222 | JSONObject jsonObject = new JSONObject();
223 | jsonObject.put("userId",conf.getUserId());
224 | RequestBody requestBody = RequestBody.create(BasicConstant.MEDIA_TYPE,jsonObject.toString());
225 |
226 | Request request = new Request.Builder()
227 | .url("http://"+server.getIp()+":"+server.getHttpPort()+"/clientLogout")
228 | .post(requestBody)
229 | .build();
230 |
231 | Response response = okHttpClient.newCall(request).execute() ;
232 | if (!response.isSuccessful()){
233 | throw new IOException("Unexpected code " + response);
234 | }
235 | } catch (IOException e) {
236 | e.printStackTrace();
237 | }
238 | }
239 |
240 | public void restart() throws Exception {
241 | //1.清理客户端信息(路由)
242 | logoutRoute();
243 | server = null;
244 | //2.start
245 | start();
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/im-client/src/main/java/ai/yunxi/im/client/scanner/Scan.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.client.scanner;
2 |
3 | import java.util.Scanner;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import ai.yunxi.im.client.config.InitConfiguration;
9 | import ai.yunxi.im.client.config.SpringBeanFactory;
10 | import ai.yunxi.im.client.init.IMClientInit;
11 | import ai.yunxi.im.common.constant.MessageConstant;
12 | import ai.yunxi.im.common.pojo.ChatInfo;
13 | import ai.yunxi.im.common.utils.StringUtil;
14 |
15 | public class Scan implements Runnable {
16 |
17 | private final static Logger LOGGER = LoggerFactory.getLogger(Scan.class);
18 |
19 | private IMClientInit client;
20 | private InitConfiguration conf;
21 |
22 | public Scan() {
23 | super();
24 | this.client = SpringBeanFactory.getBean(IMClientInit.class);
25 | this.conf = SpringBeanFactory.getBean(InitConfiguration.class);
26 | }
27 |
28 | @Override
29 | public void run() {
30 | Scanner scanner = new Scanner(System.in);
31 | try {
32 | while(true){
33 | String msg = scanner.nextLine();
34 |
35 | if(StringUtil.isEmpty(msg)){
36 | LOGGER.info("---不允许发送空消息!");
37 | continue;
38 | }
39 |
40 | //处理系统指令,如:LOGIN LOGOUT 等
41 | if(MessageConstant.LOGOUT.equals(msg)){
42 | //移除登录状态数据
43 | client.clear();
44 | LOGGER.info("---下线成功,如需加入聊天室,请重新登录");
45 | continue;
46 | } else if(MessageConstant.LOGIN.equals(msg)){
47 | client.start();
48 | LOGGER.info("---重新登录成功");
49 | continue;
50 | }
51 |
52 | //调用Route端API进行消息发送
53 | ChatInfo chat = new ChatInfo(MessageConstant.CHAT, System.currentTimeMillis(), conf.getUserId(), msg);
54 | client.sendMessage(chat);
55 | }
56 | } catch (Exception e) {
57 | e.printStackTrace();
58 | } finally {
59 | scanner.close();
60 | }
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/im-client/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=8070
2 |
3 | im.user.id=1001
4 | im.user.userName=Five
5 |
6 | im.route.login.url=http://localhost:8880/login
7 | im.route.chat.url=http://localhost:8880/chat
8 | im.route.logout.url=http://localhost:8880/logout
--------------------------------------------------------------------------------
/im-client/src/test/java/ai/yunxi/im/client/AppTest.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.client;
2 |
3 | import junit.framework.Test;
4 | import junit.framework.TestCase;
5 | import junit.framework.TestSuite;
6 |
7 | /**
8 | * Unit test for simple App.
9 | */
10 | public class AppTest
11 | extends TestCase
12 | {
13 | /**
14 | * Create the test case
15 | *
16 | * @param testName name of the test case
17 | */
18 | public AppTest( String testName )
19 | {
20 | super( testName );
21 | }
22 |
23 | /**
24 | * @return the suite of tests being tested
25 | */
26 | public static Test suite()
27 | {
28 | return new TestSuite( AppTest.class );
29 | }
30 |
31 | /**
32 | * Rigourous Test :-)
33 | */
34 | public void testApp()
35 | {
36 | assertTrue( true );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/im-common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | ai.yunxi
7 | im
8 | 0.0.1-SNAPSHOT
9 |
10 | im-common
11 | im-common
12 | http://maven.apache.org
13 |
14 | UTF-8
15 |
16 |
17 |
18 | org.msgpack
19 | msgpack
20 | 0.6.12
21 |
22 |
23 | com.google.protobuf
24 | protobuf-java
25 | 3.4.0
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/im-common/protobuf/MessageProto.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto2";
2 |
3 | package protocol;
4 |
5 | option java_package = "ai.yunxi.im.common.protocol";
6 | option java_outer_classname = "MessageProto";
7 |
8 | message MessageProtocol {
9 |
10 | required string command = 1;
11 | required int64 time = 2;
12 | required int32 userId = 3;
13 | required string content = 4;
14 |
15 | }
--------------------------------------------------------------------------------
/im-common/src/main/java/ai/yunxi/im/common/constant/BasicConstant.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.common.constant;
2 |
3 | import okhttp3.MediaType;
4 |
5 | /**
6 | * @author 小五老师-云析学院
7 | * @createTime 2019年3月12日 下午10:02:08
8 | *
9 | */
10 | public final class BasicConstant {
11 |
12 | /**
13 | * redis中客户端服务端映射前缀
14 | **/
15 | public static final String ROUTE_PREFIX = "im-route:";
16 |
17 | /**
18 | * 响应格式
19 | **/
20 | public static final MediaType MEDIA_TYPE = MediaType.parse("application/json");
21 | }
22 |
--------------------------------------------------------------------------------
/im-common/src/main/java/ai/yunxi/im/common/constant/MessageConstant.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.common.constant;
2 | /**
3 | *
4 | * @author Five老师
5 | * @createTime 2018年3月27日 下午8:23:31
6 | *
7 | */
8 | public class MessageConstant {
9 |
10 | public static final String LOGIN="LOGIN";
11 | public static final String LOGOUT="LOGOUT";
12 | public static final String CHAT="CHAT";
13 | public static final String SYSTEM="SYSTEM";
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/im-common/src/main/java/ai/yunxi/im/common/enums/StatusEnum.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.common.enums;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public enum StatusEnum {
7 |
8 | /** 成功 */
9 | SUCCESS("9000", "成功"),
10 | /** 成功 */
11 | FALLBACK("8000", "FALL_BACK"),
12 | /** 参数校验失败**/
13 | VALIDATION_FAIL("3000", "参数校验失败"),
14 | /** 失败 */
15 | FAIL("4000", "失败"),
16 |
17 | /** 重复登录 */
18 | REPEAT_LOGIN("5000", "账号重复登录,请退出一个账号!"),
19 |
20 | /** 账号不在线 */
21 | OFF_LINE("7000", "你选择的账号不在线,请重新选择!"),
22 |
23 | /** 登录信息不匹配 */
24 | ACCOUNT_NOT_MATCH("9100", "登录信息不匹配!"),
25 |
26 | /** 请求限流 */
27 | REQUEST_LIMIT("6000", "请求限流"),
28 | ;
29 |
30 |
31 | /** 枚举值码 */
32 | private final String code;
33 |
34 | /** 枚举描述 */
35 | private final String message;
36 |
37 | /**
38 | * 构建一个 StatusEnum 。
39 | * @param code 枚举值码。
40 | * @param message 枚举描述。
41 | */
42 | private StatusEnum(String code, String message) {
43 | this.code = code;
44 | this.message = message;
45 | }
46 |
47 | /**
48 | * 得到枚举值码。
49 | * @return 枚举值码。
50 | */
51 | public String getCode() {
52 | return code;
53 | }
54 |
55 | /**
56 | * 得到枚举描述。
57 | * @return 枚举描述。
58 | */
59 | public String getMessage() {
60 | return message;
61 | }
62 |
63 | /**
64 | * 得到枚举值码。
65 | * @return 枚举值码。
66 | */
67 | public String code() {
68 | return code;
69 | }
70 |
71 | /**
72 | * 得到枚举描述。
73 | * @return 枚举描述。
74 | */
75 | public String message() {
76 | return message;
77 | }
78 |
79 | /**
80 | * 通过枚举值码查找枚举值。
81 | * @param code 查找枚举值的枚举值码。
82 | * @return 枚举值码对应的枚举值。
83 | * @throws IllegalArgumentException 如果 code 没有对应的 StatusEnum 。
84 | */
85 | public static StatusEnum findStatus(String code) {
86 | for (StatusEnum status : values()) {
87 | if (status.getCode().equals(code)) {
88 | return status;
89 | }
90 | }
91 | throw new IllegalArgumentException("ResultInfo StatusEnum not legal:" + code);
92 | }
93 |
94 | /**
95 | * 获取全部枚举值。
96 | *
97 | * @return 全部枚举值。
98 | */
99 | public static List getAllStatus() {
100 | List list = new ArrayList();
101 | for (StatusEnum status : values()) {
102 | list.add(status);
103 | }
104 | return list;
105 | }
106 |
107 | /**
108 | * 获取全部枚举值码。
109 | *
110 | * @return 全部枚举值码。
111 | */
112 | public static List getAllStatusCode() {
113 | List list = new ArrayList();
114 | for (StatusEnum status : values()) {
115 | list.add(status.code());
116 | }
117 | return list;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/im-common/src/main/java/ai/yunxi/im/common/pojo/ChatInfo.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.common.pojo;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | *
7 | * @author 小五老师-云析学院
8 | * @createTime 2019年3月7日 下午5:27:21
9 | *
10 | */
11 | public class ChatInfo implements Serializable {
12 |
13 | /**
14 | *
15 | */
16 | private static final long serialVersionUID = 1360647504610967672L;
17 | private String command;
18 | private Long time;
19 | private Integer userId;
20 | private String content;
21 |
22 | public ChatInfo(String command, Long time, Integer userId, String content) {
23 | this.command = command;
24 | this.time = time;
25 | this.userId = userId;
26 | this.content = content;
27 | }
28 | public ChatInfo() {
29 | }
30 | public String getCommand() {
31 | return command;
32 | }
33 | public void setCommand(String command) {
34 | this.command = command;
35 | }
36 | public Long getTime() {
37 | return time;
38 | }
39 | public void setTime(Long time) {
40 | this.time = time;
41 | }
42 | public Integer getUserId() {
43 | return userId;
44 | }
45 | public void setUserId(Integer userId) {
46 | this.userId = userId;
47 | }
48 | public String getContent() {
49 | return content;
50 | }
51 | public void setContent(String content) {
52 | this.content = content;
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/im-common/src/main/java/ai/yunxi/im/common/pojo/ServerInfo.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.common.pojo;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | *
7 | * @author 小五老师-云析学院
8 | * @createTime 2019年2月26日 下午9:14:28
9 | * 服务端基本信息
10 | */
11 | public class ServerInfo implements Serializable {
12 |
13 | /**
14 | *
15 | */
16 | private static final long serialVersionUID = -2230742812761280401L;
17 | private String ip;
18 | private Integer nettyPort;
19 | private Integer httpPort;
20 |
21 | public ServerInfo() {
22 | }
23 | public ServerInfo(String ip, Integer nettyPort, Integer httpPort) {
24 | super();
25 | this.ip = ip;
26 | this.nettyPort = nettyPort;
27 | this.httpPort = httpPort;
28 | }
29 | public String getIp() {
30 | return ip;
31 | }
32 | public void setIp(String ip) {
33 | this.ip = ip;
34 | }
35 | public Integer getNettyPort() {
36 | return nettyPort;
37 | }
38 | public void setNettyPort(Integer nettyPort) {
39 | this.nettyPort = nettyPort;
40 | }
41 | public Integer getHttpPort() {
42 | return httpPort;
43 | }
44 | public void setHttpPort(Integer httpPort) {
45 | this.httpPort = httpPort;
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/im-common/src/main/java/ai/yunxi/im/common/pojo/UserInfo.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.common.pojo;
2 | /**
3 | *
4 | * @author 小五老师-云析学院
5 | * @createTime 2019年2月27日 下午3:29:38
6 | *
7 | */
8 |
9 | import java.io.Serializable;
10 |
11 | public class UserInfo implements Serializable {
12 |
13 | /**
14 | *
15 | */
16 | private static final long serialVersionUID = 4227244327808705724L;
17 | private Integer userId;
18 | private String userName;
19 | public Integer getUserId() {
20 | return userId;
21 | }
22 | public void setUserId(Integer userId) {
23 | this.userId = userId;
24 | }
25 | public String getUserName() {
26 | return userName;
27 | }
28 | public void setUserName(String userName) {
29 | this.userName = userName;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/im-common/src/main/java/ai/yunxi/im/common/protocol/MessageProto.java:
--------------------------------------------------------------------------------
1 | // Generated by the protocol buffer compiler. DO NOT EDIT!
2 | // source: MessageProto.proto
3 |
4 | package ai.yunxi.im.common.protocol;
5 |
6 | public final class MessageProto {
7 | private MessageProto() {}
8 | public static void registerAllExtensions(
9 | com.google.protobuf.ExtensionRegistryLite registry) {
10 | }
11 |
12 | public static void registerAllExtensions(
13 | com.google.protobuf.ExtensionRegistry registry) {
14 | registerAllExtensions(
15 | (com.google.protobuf.ExtensionRegistryLite) registry);
16 | }
17 | public interface MessageProtocolOrBuilder extends
18 | // @@protoc_insertion_point(interface_extends:protocol.MessageProtocol)
19 | com.google.protobuf.MessageOrBuilder {
20 |
21 | /**
22 | * required string command = 1;
23 | */
24 | boolean hasCommand();
25 | /**
26 | * required string command = 1;
27 | */
28 | java.lang.String getCommand();
29 | /**
30 | * required string command = 1;
31 | */
32 | com.google.protobuf.ByteString
33 | getCommandBytes();
34 |
35 | /**
36 | * required int64 time = 2;
37 | */
38 | boolean hasTime();
39 | /**
40 | * required int64 time = 2;
41 | */
42 | long getTime();
43 |
44 | /**
45 | * required int32 userId = 3;
46 | */
47 | boolean hasUserId();
48 | /**
49 | * required int32 userId = 3;
50 | */
51 | int getUserId();
52 |
53 | /**
54 | * required string content = 4;
55 | */
56 | boolean hasContent();
57 | /**
58 | * required string content = 4;
59 | */
60 | java.lang.String getContent();
61 | /**
62 | * required string content = 4;
63 | */
64 | com.google.protobuf.ByteString
65 | getContentBytes();
66 | }
67 | /**
68 | * Protobuf type {@code protocol.MessageProtocol}
69 | */
70 | public static final class MessageProtocol extends
71 | com.google.protobuf.GeneratedMessageV3 implements
72 | // @@protoc_insertion_point(message_implements:protocol.MessageProtocol)
73 | MessageProtocolOrBuilder {
74 | private static final long serialVersionUID = 0L;
75 | // Use MessageProtocol.newBuilder() to construct.
76 | private MessageProtocol(com.google.protobuf.GeneratedMessageV3.Builder> builder) {
77 | super(builder);
78 | }
79 | private MessageProtocol() {
80 | command_ = "";
81 | content_ = "";
82 | }
83 |
84 | @java.lang.Override
85 | public final com.google.protobuf.UnknownFieldSet
86 | getUnknownFields() {
87 | return this.unknownFields;
88 | }
89 | private MessageProtocol(
90 | com.google.protobuf.CodedInputStream input,
91 | com.google.protobuf.ExtensionRegistryLite extensionRegistry)
92 | throws com.google.protobuf.InvalidProtocolBufferException {
93 | this();
94 | if (extensionRegistry == null) {
95 | throw new java.lang.NullPointerException();
96 | }
97 | com.google.protobuf.UnknownFieldSet.Builder unknownFields =
98 | com.google.protobuf.UnknownFieldSet.newBuilder();
99 | try {
100 | boolean done = false;
101 | while (!done) {
102 | int tag = input.readTag();
103 | switch (tag) {
104 | case 0:
105 | done = true;
106 | break;
107 | case 10: {
108 | com.google.protobuf.ByteString bs = input.readBytes();
109 | bitField0_ |= 0x00000001;
110 | command_ = bs;
111 | break;
112 | }
113 | case 16: {
114 | bitField0_ |= 0x00000002;
115 | time_ = input.readInt64();
116 | break;
117 | }
118 | case 24: {
119 | bitField0_ |= 0x00000004;
120 | userId_ = input.readInt32();
121 | break;
122 | }
123 | case 34: {
124 | com.google.protobuf.ByteString bs = input.readBytes();
125 | bitField0_ |= 0x00000008;
126 | content_ = bs;
127 | break;
128 | }
129 | default: {
130 | if (!parseUnknownField(
131 | input, unknownFields, extensionRegistry, tag)) {
132 | done = true;
133 | }
134 | break;
135 | }
136 | }
137 | }
138 | } catch (com.google.protobuf.InvalidProtocolBufferException e) {
139 | throw e.setUnfinishedMessage(this);
140 | } catch (java.io.IOException e) {
141 | throw new com.google.protobuf.InvalidProtocolBufferException(
142 | e).setUnfinishedMessage(this);
143 | } finally {
144 | this.unknownFields = unknownFields.build();
145 | makeExtensionsImmutable();
146 | }
147 | }
148 | public static final com.google.protobuf.Descriptors.Descriptor
149 | getDescriptor() {
150 | return ai.yunxi.im.common.protocol.MessageProto.internal_static_protocol_MessageProtocol_descriptor;
151 | }
152 |
153 | @java.lang.Override
154 | protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
155 | internalGetFieldAccessorTable() {
156 | return ai.yunxi.im.common.protocol.MessageProto.internal_static_protocol_MessageProtocol_fieldAccessorTable
157 | .ensureFieldAccessorsInitialized(
158 | ai.yunxi.im.common.protocol.MessageProto.MessageProtocol.class, ai.yunxi.im.common.protocol.MessageProto.MessageProtocol.Builder.class);
159 | }
160 |
161 | private int bitField0_;
162 | public static final int COMMAND_FIELD_NUMBER = 1;
163 | private volatile java.lang.Object command_;
164 | /**
165 | * required string command = 1;
166 | */
167 | public boolean hasCommand() {
168 | return ((bitField0_ & 0x00000001) != 0);
169 | }
170 | /**
171 | * required string command = 1;
172 | */
173 | public java.lang.String getCommand() {
174 | java.lang.Object ref = command_;
175 | if (ref instanceof java.lang.String) {
176 | return (java.lang.String) ref;
177 | } else {
178 | com.google.protobuf.ByteString bs =
179 | (com.google.protobuf.ByteString) ref;
180 | java.lang.String s = bs.toStringUtf8();
181 | if (bs.isValidUtf8()) {
182 | command_ = s;
183 | }
184 | return s;
185 | }
186 | }
187 | /**
188 | * required string command = 1;
189 | */
190 | public com.google.protobuf.ByteString
191 | getCommandBytes() {
192 | java.lang.Object ref = command_;
193 | if (ref instanceof java.lang.String) {
194 | com.google.protobuf.ByteString b =
195 | com.google.protobuf.ByteString.copyFromUtf8(
196 | (java.lang.String) ref);
197 | command_ = b;
198 | return b;
199 | } else {
200 | return (com.google.protobuf.ByteString) ref;
201 | }
202 | }
203 |
204 | public static final int TIME_FIELD_NUMBER = 2;
205 | private long time_;
206 | /**
207 | * required int64 time = 2;
208 | */
209 | public boolean hasTime() {
210 | return ((bitField0_ & 0x00000002) != 0);
211 | }
212 | /**
213 | * required int64 time = 2;
214 | */
215 | public long getTime() {
216 | return time_;
217 | }
218 |
219 | public static final int USERID_FIELD_NUMBER = 3;
220 | private int userId_;
221 | /**
222 | * required int32 userId = 3;
223 | */
224 | public boolean hasUserId() {
225 | return ((bitField0_ & 0x00000004) != 0);
226 | }
227 | /**
228 | * required int32 userId = 3;
229 | */
230 | public int getUserId() {
231 | return userId_;
232 | }
233 |
234 | public static final int CONTENT_FIELD_NUMBER = 4;
235 | private volatile java.lang.Object content_;
236 | /**
237 | * required string content = 4;
238 | */
239 | public boolean hasContent() {
240 | return ((bitField0_ & 0x00000008) != 0);
241 | }
242 | /**
243 | * required string content = 4;
244 | */
245 | public java.lang.String getContent() {
246 | java.lang.Object ref = content_;
247 | if (ref instanceof java.lang.String) {
248 | return (java.lang.String) ref;
249 | } else {
250 | com.google.protobuf.ByteString bs =
251 | (com.google.protobuf.ByteString) ref;
252 | java.lang.String s = bs.toStringUtf8();
253 | if (bs.isValidUtf8()) {
254 | content_ = s;
255 | }
256 | return s;
257 | }
258 | }
259 | /**
260 | * required string content = 4;
261 | */
262 | public com.google.protobuf.ByteString
263 | getContentBytes() {
264 | java.lang.Object ref = content_;
265 | if (ref instanceof java.lang.String) {
266 | com.google.protobuf.ByteString b =
267 | com.google.protobuf.ByteString.copyFromUtf8(
268 | (java.lang.String) ref);
269 | content_ = b;
270 | return b;
271 | } else {
272 | return (com.google.protobuf.ByteString) ref;
273 | }
274 | }
275 |
276 | private byte memoizedIsInitialized = -1;
277 | @java.lang.Override
278 | public final boolean isInitialized() {
279 | byte isInitialized = memoizedIsInitialized;
280 | if (isInitialized == 1) return true;
281 | if (isInitialized == 0) return false;
282 |
283 | if (!hasCommand()) {
284 | memoizedIsInitialized = 0;
285 | return false;
286 | }
287 | if (!hasTime()) {
288 | memoizedIsInitialized = 0;
289 | return false;
290 | }
291 | if (!hasUserId()) {
292 | memoizedIsInitialized = 0;
293 | return false;
294 | }
295 | if (!hasContent()) {
296 | memoizedIsInitialized = 0;
297 | return false;
298 | }
299 | memoizedIsInitialized = 1;
300 | return true;
301 | }
302 |
303 | @java.lang.Override
304 | public void writeTo(com.google.protobuf.CodedOutputStream output)
305 | throws java.io.IOException {
306 | if (((bitField0_ & 0x00000001) != 0)) {
307 | com.google.protobuf.GeneratedMessageV3.writeString(output, 1, command_);
308 | }
309 | if (((bitField0_ & 0x00000002) != 0)) {
310 | output.writeInt64(2, time_);
311 | }
312 | if (((bitField0_ & 0x00000004) != 0)) {
313 | output.writeInt32(3, userId_);
314 | }
315 | if (((bitField0_ & 0x00000008) != 0)) {
316 | com.google.protobuf.GeneratedMessageV3.writeString(output, 4, content_);
317 | }
318 | unknownFields.writeTo(output);
319 | }
320 |
321 | @java.lang.Override
322 | public int getSerializedSize() {
323 | int size = memoizedSize;
324 | if (size != -1) return size;
325 |
326 | size = 0;
327 | if (((bitField0_ & 0x00000001) != 0)) {
328 | size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, command_);
329 | }
330 | if (((bitField0_ & 0x00000002) != 0)) {
331 | size += com.google.protobuf.CodedOutputStream
332 | .computeInt64Size(2, time_);
333 | }
334 | if (((bitField0_ & 0x00000004) != 0)) {
335 | size += com.google.protobuf.CodedOutputStream
336 | .computeInt32Size(3, userId_);
337 | }
338 | if (((bitField0_ & 0x00000008) != 0)) {
339 | size += com.google.protobuf.GeneratedMessageV3.computeStringSize(4, content_);
340 | }
341 | size += unknownFields.getSerializedSize();
342 | memoizedSize = size;
343 | return size;
344 | }
345 |
346 | @java.lang.Override
347 | public boolean equals(final java.lang.Object obj) {
348 | if (obj == this) {
349 | return true;
350 | }
351 | if (!(obj instanceof ai.yunxi.im.common.protocol.MessageProto.MessageProtocol)) {
352 | return super.equals(obj);
353 | }
354 | ai.yunxi.im.common.protocol.MessageProto.MessageProtocol other = (ai.yunxi.im.common.protocol.MessageProto.MessageProtocol) obj;
355 |
356 | if (hasCommand() != other.hasCommand()) return false;
357 | if (hasCommand()) {
358 | if (!getCommand()
359 | .equals(other.getCommand())) return false;
360 | }
361 | if (hasTime() != other.hasTime()) return false;
362 | if (hasTime()) {
363 | if (getTime()
364 | != other.getTime()) return false;
365 | }
366 | if (hasUserId() != other.hasUserId()) return false;
367 | if (hasUserId()) {
368 | if (getUserId()
369 | != other.getUserId()) return false;
370 | }
371 | if (hasContent() != other.hasContent()) return false;
372 | if (hasContent()) {
373 | if (!getContent()
374 | .equals(other.getContent())) return false;
375 | }
376 | if (!unknownFields.equals(other.unknownFields)) return false;
377 | return true;
378 | }
379 |
380 | @java.lang.Override
381 | public int hashCode() {
382 | if (memoizedHashCode != 0) {
383 | return memoizedHashCode;
384 | }
385 | int hash = 41;
386 | hash = (19 * hash) + getDescriptor().hashCode();
387 | if (hasCommand()) {
388 | hash = (37 * hash) + COMMAND_FIELD_NUMBER;
389 | hash = (53 * hash) + getCommand().hashCode();
390 | }
391 | if (hasTime()) {
392 | hash = (37 * hash) + TIME_FIELD_NUMBER;
393 | hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
394 | getTime());
395 | }
396 | if (hasUserId()) {
397 | hash = (37 * hash) + USERID_FIELD_NUMBER;
398 | hash = (53 * hash) + getUserId();
399 | }
400 | if (hasContent()) {
401 | hash = (37 * hash) + CONTENT_FIELD_NUMBER;
402 | hash = (53 * hash) + getContent().hashCode();
403 | }
404 | hash = (29 * hash) + unknownFields.hashCode();
405 | return hash;
406 | }
407 |
408 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseFrom(
409 | java.nio.ByteBuffer data)
410 | throws com.google.protobuf.InvalidProtocolBufferException {
411 | return PARSER.parseFrom(data);
412 | }
413 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseFrom(
414 | java.nio.ByteBuffer data,
415 | com.google.protobuf.ExtensionRegistryLite extensionRegistry)
416 | throws com.google.protobuf.InvalidProtocolBufferException {
417 | return PARSER.parseFrom(data, extensionRegistry);
418 | }
419 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseFrom(
420 | com.google.protobuf.ByteString data)
421 | throws com.google.protobuf.InvalidProtocolBufferException {
422 | return PARSER.parseFrom(data);
423 | }
424 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseFrom(
425 | com.google.protobuf.ByteString data,
426 | com.google.protobuf.ExtensionRegistryLite extensionRegistry)
427 | throws com.google.protobuf.InvalidProtocolBufferException {
428 | return PARSER.parseFrom(data, extensionRegistry);
429 | }
430 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseFrom(byte[] data)
431 | throws com.google.protobuf.InvalidProtocolBufferException {
432 | return PARSER.parseFrom(data);
433 | }
434 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseFrom(
435 | byte[] data,
436 | com.google.protobuf.ExtensionRegistryLite extensionRegistry)
437 | throws com.google.protobuf.InvalidProtocolBufferException {
438 | return PARSER.parseFrom(data, extensionRegistry);
439 | }
440 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseFrom(java.io.InputStream input)
441 | throws java.io.IOException {
442 | return com.google.protobuf.GeneratedMessageV3
443 | .parseWithIOException(PARSER, input);
444 | }
445 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseFrom(
446 | java.io.InputStream input,
447 | com.google.protobuf.ExtensionRegistryLite extensionRegistry)
448 | throws java.io.IOException {
449 | return com.google.protobuf.GeneratedMessageV3
450 | .parseWithIOException(PARSER, input, extensionRegistry);
451 | }
452 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseDelimitedFrom(java.io.InputStream input)
453 | throws java.io.IOException {
454 | return com.google.protobuf.GeneratedMessageV3
455 | .parseDelimitedWithIOException(PARSER, input);
456 | }
457 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseDelimitedFrom(
458 | java.io.InputStream input,
459 | com.google.protobuf.ExtensionRegistryLite extensionRegistry)
460 | throws java.io.IOException {
461 | return com.google.protobuf.GeneratedMessageV3
462 | .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
463 | }
464 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseFrom(
465 | com.google.protobuf.CodedInputStream input)
466 | throws java.io.IOException {
467 | return com.google.protobuf.GeneratedMessageV3
468 | .parseWithIOException(PARSER, input);
469 | }
470 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parseFrom(
471 | com.google.protobuf.CodedInputStream input,
472 | com.google.protobuf.ExtensionRegistryLite extensionRegistry)
473 | throws java.io.IOException {
474 | return com.google.protobuf.GeneratedMessageV3
475 | .parseWithIOException(PARSER, input, extensionRegistry);
476 | }
477 |
478 | @java.lang.Override
479 | public Builder newBuilderForType() { return newBuilder(); }
480 | public static Builder newBuilder() {
481 | return DEFAULT_INSTANCE.toBuilder();
482 | }
483 | public static Builder newBuilder(ai.yunxi.im.common.protocol.MessageProto.MessageProtocol prototype) {
484 | return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
485 | }
486 | @java.lang.Override
487 | public Builder toBuilder() {
488 | return this == DEFAULT_INSTANCE
489 | ? new Builder() : new Builder().mergeFrom(this);
490 | }
491 |
492 | @java.lang.Override
493 | protected Builder newBuilderForType(
494 | com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
495 | Builder builder = new Builder(parent);
496 | return builder;
497 | }
498 | /**
499 | * Protobuf type {@code protocol.MessageProtocol}
500 | */
501 | public static final class Builder extends
502 | com.google.protobuf.GeneratedMessageV3.Builder implements
503 | // @@protoc_insertion_point(builder_implements:protocol.MessageProtocol)
504 | ai.yunxi.im.common.protocol.MessageProto.MessageProtocolOrBuilder {
505 | public static final com.google.protobuf.Descriptors.Descriptor
506 | getDescriptor() {
507 | return ai.yunxi.im.common.protocol.MessageProto.internal_static_protocol_MessageProtocol_descriptor;
508 | }
509 |
510 | @java.lang.Override
511 | protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
512 | internalGetFieldAccessorTable() {
513 | return ai.yunxi.im.common.protocol.MessageProto.internal_static_protocol_MessageProtocol_fieldAccessorTable
514 | .ensureFieldAccessorsInitialized(
515 | ai.yunxi.im.common.protocol.MessageProto.MessageProtocol.class, ai.yunxi.im.common.protocol.MessageProto.MessageProtocol.Builder.class);
516 | }
517 |
518 | // Construct using ai.yunxi.im.common.protocol.MessageProto.MessageProtocol.newBuilder()
519 | private Builder() {
520 | maybeForceBuilderInitialization();
521 | }
522 |
523 | private Builder(
524 | com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
525 | super(parent);
526 | maybeForceBuilderInitialization();
527 | }
528 | private void maybeForceBuilderInitialization() {
529 | if (com.google.protobuf.GeneratedMessageV3
530 | .alwaysUseFieldBuilders) {
531 | }
532 | }
533 | @java.lang.Override
534 | public Builder clear() {
535 | super.clear();
536 | command_ = "";
537 | bitField0_ = (bitField0_ & ~0x00000001);
538 | time_ = 0L;
539 | bitField0_ = (bitField0_ & ~0x00000002);
540 | userId_ = 0;
541 | bitField0_ = (bitField0_ & ~0x00000004);
542 | content_ = "";
543 | bitField0_ = (bitField0_ & ~0x00000008);
544 | return this;
545 | }
546 |
547 | @java.lang.Override
548 | public com.google.protobuf.Descriptors.Descriptor
549 | getDescriptorForType() {
550 | return ai.yunxi.im.common.protocol.MessageProto.internal_static_protocol_MessageProtocol_descriptor;
551 | }
552 |
553 | @java.lang.Override
554 | public ai.yunxi.im.common.protocol.MessageProto.MessageProtocol getDefaultInstanceForType() {
555 | return ai.yunxi.im.common.protocol.MessageProto.MessageProtocol.getDefaultInstance();
556 | }
557 |
558 | @java.lang.Override
559 | public ai.yunxi.im.common.protocol.MessageProto.MessageProtocol build() {
560 | ai.yunxi.im.common.protocol.MessageProto.MessageProtocol result = buildPartial();
561 | if (!result.isInitialized()) {
562 | throw newUninitializedMessageException(result);
563 | }
564 | return result;
565 | }
566 |
567 | @java.lang.Override
568 | public ai.yunxi.im.common.protocol.MessageProto.MessageProtocol buildPartial() {
569 | ai.yunxi.im.common.protocol.MessageProto.MessageProtocol result = new ai.yunxi.im.common.protocol.MessageProto.MessageProtocol(this);
570 | int from_bitField0_ = bitField0_;
571 | int to_bitField0_ = 0;
572 | if (((from_bitField0_ & 0x00000001) != 0)) {
573 | to_bitField0_ |= 0x00000001;
574 | }
575 | result.command_ = command_;
576 | if (((from_bitField0_ & 0x00000002) != 0)) {
577 | result.time_ = time_;
578 | to_bitField0_ |= 0x00000002;
579 | }
580 | if (((from_bitField0_ & 0x00000004) != 0)) {
581 | result.userId_ = userId_;
582 | to_bitField0_ |= 0x00000004;
583 | }
584 | if (((from_bitField0_ & 0x00000008) != 0)) {
585 | to_bitField0_ |= 0x00000008;
586 | }
587 | result.content_ = content_;
588 | result.bitField0_ = to_bitField0_;
589 | onBuilt();
590 | return result;
591 | }
592 |
593 | @java.lang.Override
594 | public Builder clone() {
595 | return super.clone();
596 | }
597 | @java.lang.Override
598 | public Builder setField(
599 | com.google.protobuf.Descriptors.FieldDescriptor field,
600 | java.lang.Object value) {
601 | return super.setField(field, value);
602 | }
603 | @java.lang.Override
604 | public Builder clearField(
605 | com.google.protobuf.Descriptors.FieldDescriptor field) {
606 | return super.clearField(field);
607 | }
608 | @java.lang.Override
609 | public Builder clearOneof(
610 | com.google.protobuf.Descriptors.OneofDescriptor oneof) {
611 | return super.clearOneof(oneof);
612 | }
613 | @java.lang.Override
614 | public Builder setRepeatedField(
615 | com.google.protobuf.Descriptors.FieldDescriptor field,
616 | int index, java.lang.Object value) {
617 | return super.setRepeatedField(field, index, value);
618 | }
619 | @java.lang.Override
620 | public Builder addRepeatedField(
621 | com.google.protobuf.Descriptors.FieldDescriptor field,
622 | java.lang.Object value) {
623 | return super.addRepeatedField(field, value);
624 | }
625 | @java.lang.Override
626 | public Builder mergeFrom(com.google.protobuf.Message other) {
627 | if (other instanceof ai.yunxi.im.common.protocol.MessageProto.MessageProtocol) {
628 | return mergeFrom((ai.yunxi.im.common.protocol.MessageProto.MessageProtocol)other);
629 | } else {
630 | super.mergeFrom(other);
631 | return this;
632 | }
633 | }
634 |
635 | public Builder mergeFrom(ai.yunxi.im.common.protocol.MessageProto.MessageProtocol other) {
636 | if (other == ai.yunxi.im.common.protocol.MessageProto.MessageProtocol.getDefaultInstance()) return this;
637 | if (other.hasCommand()) {
638 | bitField0_ |= 0x00000001;
639 | command_ = other.command_;
640 | onChanged();
641 | }
642 | if (other.hasTime()) {
643 | setTime(other.getTime());
644 | }
645 | if (other.hasUserId()) {
646 | setUserId(other.getUserId());
647 | }
648 | if (other.hasContent()) {
649 | bitField0_ |= 0x00000008;
650 | content_ = other.content_;
651 | onChanged();
652 | }
653 | this.mergeUnknownFields(other.unknownFields);
654 | onChanged();
655 | return this;
656 | }
657 |
658 | @java.lang.Override
659 | public final boolean isInitialized() {
660 | if (!hasCommand()) {
661 | return false;
662 | }
663 | if (!hasTime()) {
664 | return false;
665 | }
666 | if (!hasUserId()) {
667 | return false;
668 | }
669 | if (!hasContent()) {
670 | return false;
671 | }
672 | return true;
673 | }
674 |
675 | @java.lang.Override
676 | public Builder mergeFrom(
677 | com.google.protobuf.CodedInputStream input,
678 | com.google.protobuf.ExtensionRegistryLite extensionRegistry)
679 | throws java.io.IOException {
680 | ai.yunxi.im.common.protocol.MessageProto.MessageProtocol parsedMessage = null;
681 | try {
682 | parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
683 | } catch (com.google.protobuf.InvalidProtocolBufferException e) {
684 | parsedMessage = (ai.yunxi.im.common.protocol.MessageProto.MessageProtocol) e.getUnfinishedMessage();
685 | throw e.unwrapIOException();
686 | } finally {
687 | if (parsedMessage != null) {
688 | mergeFrom(parsedMessage);
689 | }
690 | }
691 | return this;
692 | }
693 | private int bitField0_;
694 |
695 | private java.lang.Object command_ = "";
696 | /**
697 | * required string command = 1;
698 | */
699 | public boolean hasCommand() {
700 | return ((bitField0_ & 0x00000001) != 0);
701 | }
702 | /**
703 | * required string command = 1;
704 | */
705 | public java.lang.String getCommand() {
706 | java.lang.Object ref = command_;
707 | if (!(ref instanceof java.lang.String)) {
708 | com.google.protobuf.ByteString bs =
709 | (com.google.protobuf.ByteString) ref;
710 | java.lang.String s = bs.toStringUtf8();
711 | if (bs.isValidUtf8()) {
712 | command_ = s;
713 | }
714 | return s;
715 | } else {
716 | return (java.lang.String) ref;
717 | }
718 | }
719 | /**
720 | * required string command = 1;
721 | */
722 | public com.google.protobuf.ByteString
723 | getCommandBytes() {
724 | java.lang.Object ref = command_;
725 | if (ref instanceof String) {
726 | com.google.protobuf.ByteString b =
727 | com.google.protobuf.ByteString.copyFromUtf8(
728 | (java.lang.String) ref);
729 | command_ = b;
730 | return b;
731 | } else {
732 | return (com.google.protobuf.ByteString) ref;
733 | }
734 | }
735 | /**
736 | * required string command = 1;
737 | */
738 | public Builder setCommand(
739 | java.lang.String value) {
740 | if (value == null) {
741 | throw new NullPointerException();
742 | }
743 | bitField0_ |= 0x00000001;
744 | command_ = value;
745 | onChanged();
746 | return this;
747 | }
748 | /**
749 | * required string command = 1;
750 | */
751 | public Builder clearCommand() {
752 | bitField0_ = (bitField0_ & ~0x00000001);
753 | command_ = getDefaultInstance().getCommand();
754 | onChanged();
755 | return this;
756 | }
757 | /**
758 | * required string command = 1;
759 | */
760 | public Builder setCommandBytes(
761 | com.google.protobuf.ByteString value) {
762 | if (value == null) {
763 | throw new NullPointerException();
764 | }
765 | bitField0_ |= 0x00000001;
766 | command_ = value;
767 | onChanged();
768 | return this;
769 | }
770 |
771 | private long time_ ;
772 | /**
773 | * required int64 time = 2;
774 | */
775 | public boolean hasTime() {
776 | return ((bitField0_ & 0x00000002) != 0);
777 | }
778 | /**
779 | * required int64 time = 2;
780 | */
781 | public long getTime() {
782 | return time_;
783 | }
784 | /**
785 | * required int64 time = 2;
786 | */
787 | public Builder setTime(long value) {
788 | bitField0_ |= 0x00000002;
789 | time_ = value;
790 | onChanged();
791 | return this;
792 | }
793 | /**
794 | * required int64 time = 2;
795 | */
796 | public Builder clearTime() {
797 | bitField0_ = (bitField0_ & ~0x00000002);
798 | time_ = 0L;
799 | onChanged();
800 | return this;
801 | }
802 |
803 | private int userId_ ;
804 | /**
805 | * required int32 userId = 3;
806 | */
807 | public boolean hasUserId() {
808 | return ((bitField0_ & 0x00000004) != 0);
809 | }
810 | /**
811 | * required int32 userId = 3;
812 | */
813 | public int getUserId() {
814 | return userId_;
815 | }
816 | /**
817 | * required int32 userId = 3;
818 | */
819 | public Builder setUserId(int value) {
820 | bitField0_ |= 0x00000004;
821 | userId_ = value;
822 | onChanged();
823 | return this;
824 | }
825 | /**
826 | * required int32 userId = 3;
827 | */
828 | public Builder clearUserId() {
829 | bitField0_ = (bitField0_ & ~0x00000004);
830 | userId_ = 0;
831 | onChanged();
832 | return this;
833 | }
834 |
835 | private java.lang.Object content_ = "";
836 | /**
837 | * required string content = 4;
838 | */
839 | public boolean hasContent() {
840 | return ((bitField0_ & 0x00000008) != 0);
841 | }
842 | /**
843 | * required string content = 4;
844 | */
845 | public java.lang.String getContent() {
846 | java.lang.Object ref = content_;
847 | if (!(ref instanceof java.lang.String)) {
848 | com.google.protobuf.ByteString bs =
849 | (com.google.protobuf.ByteString) ref;
850 | java.lang.String s = bs.toStringUtf8();
851 | if (bs.isValidUtf8()) {
852 | content_ = s;
853 | }
854 | return s;
855 | } else {
856 | return (java.lang.String) ref;
857 | }
858 | }
859 | /**
860 | * required string content = 4;
861 | */
862 | public com.google.protobuf.ByteString
863 | getContentBytes() {
864 | java.lang.Object ref = content_;
865 | if (ref instanceof String) {
866 | com.google.protobuf.ByteString b =
867 | com.google.protobuf.ByteString.copyFromUtf8(
868 | (java.lang.String) ref);
869 | content_ = b;
870 | return b;
871 | } else {
872 | return (com.google.protobuf.ByteString) ref;
873 | }
874 | }
875 | /**
876 | * required string content = 4;
877 | */
878 | public Builder setContent(
879 | java.lang.String value) {
880 | if (value == null) {
881 | throw new NullPointerException();
882 | }
883 | bitField0_ |= 0x00000008;
884 | content_ = value;
885 | onChanged();
886 | return this;
887 | }
888 | /**
889 | * required string content = 4;
890 | */
891 | public Builder clearContent() {
892 | bitField0_ = (bitField0_ & ~0x00000008);
893 | content_ = getDefaultInstance().getContent();
894 | onChanged();
895 | return this;
896 | }
897 | /**
898 | * required string content = 4;
899 | */
900 | public Builder setContentBytes(
901 | com.google.protobuf.ByteString value) {
902 | if (value == null) {
903 | throw new NullPointerException();
904 | }
905 | bitField0_ |= 0x00000008;
906 | content_ = value;
907 | onChanged();
908 | return this;
909 | }
910 | @java.lang.Override
911 | public final Builder setUnknownFields(
912 | final com.google.protobuf.UnknownFieldSet unknownFields) {
913 | return super.setUnknownFields(unknownFields);
914 | }
915 |
916 | @java.lang.Override
917 | public final Builder mergeUnknownFields(
918 | final com.google.protobuf.UnknownFieldSet unknownFields) {
919 | return super.mergeUnknownFields(unknownFields);
920 | }
921 |
922 |
923 | // @@protoc_insertion_point(builder_scope:protocol.MessageProtocol)
924 | }
925 |
926 | // @@protoc_insertion_point(class_scope:protocol.MessageProtocol)
927 | private static final ai.yunxi.im.common.protocol.MessageProto.MessageProtocol DEFAULT_INSTANCE;
928 | static {
929 | DEFAULT_INSTANCE = new ai.yunxi.im.common.protocol.MessageProto.MessageProtocol();
930 | }
931 |
932 | public static ai.yunxi.im.common.protocol.MessageProto.MessageProtocol getDefaultInstance() {
933 | return DEFAULT_INSTANCE;
934 | }
935 |
936 | @java.lang.Deprecated public static final com.google.protobuf.Parser
937 | PARSER = new com.google.protobuf.AbstractParser() {
938 | @java.lang.Override
939 | public MessageProtocol parsePartialFrom(
940 | com.google.protobuf.CodedInputStream input,
941 | com.google.protobuf.ExtensionRegistryLite extensionRegistry)
942 | throws com.google.protobuf.InvalidProtocolBufferException {
943 | return new MessageProtocol(input, extensionRegistry);
944 | }
945 | };
946 |
947 | public static com.google.protobuf.Parser parser() {
948 | return PARSER;
949 | }
950 |
951 | @java.lang.Override
952 | public com.google.protobuf.Parser getParserForType() {
953 | return PARSER;
954 | }
955 |
956 | @java.lang.Override
957 | public ai.yunxi.im.common.protocol.MessageProto.MessageProtocol getDefaultInstanceForType() {
958 | return DEFAULT_INSTANCE;
959 | }
960 |
961 | }
962 |
963 | private static final com.google.protobuf.Descriptors.Descriptor
964 | internal_static_protocol_MessageProtocol_descriptor;
965 | private static final
966 | com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
967 | internal_static_protocol_MessageProtocol_fieldAccessorTable;
968 |
969 | public static com.google.protobuf.Descriptors.FileDescriptor
970 | getDescriptor() {
971 | return descriptor;
972 | }
973 | private static com.google.protobuf.Descriptors.FileDescriptor
974 | descriptor;
975 | static {
976 | java.lang.String[] descriptorData = {
977 | "\n\022MessageProto.proto\022\010protocol\"Q\n\017Messag" +
978 | "eProtocol\022\017\n\007command\030\001 \002(\t\022\014\n\004time\030\002 \002(\003" +
979 | "\022\016\n\006userId\030\003 \002(\005\022\017\n\007content\030\004 \002(\tB+\n\033ai." +
980 | "yunxi.im.common.protocolB\014MessageProto"
981 | };
982 | com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
983 | new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
984 | public com.google.protobuf.ExtensionRegistry assignDescriptors(
985 | com.google.protobuf.Descriptors.FileDescriptor root) {
986 | descriptor = root;
987 | return null;
988 | }
989 | };
990 | com.google.protobuf.Descriptors.FileDescriptor
991 | .internalBuildGeneratedFileFrom(descriptorData,
992 | new com.google.protobuf.Descriptors.FileDescriptor[] {
993 | }, assigner);
994 | internal_static_protocol_MessageProtocol_descriptor =
995 | getDescriptor().getMessageTypes().get(0);
996 | internal_static_protocol_MessageProtocol_fieldAccessorTable = new
997 | com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
998 | internal_static_protocol_MessageProtocol_descriptor,
999 | new java.lang.String[] { "Command", "Time", "UserId", "Content", });
1000 | }
1001 |
1002 | // @@protoc_insertion_point(outer_class_scope)
1003 | }
1004 |
--------------------------------------------------------------------------------
/im-common/src/main/java/ai/yunxi/im/common/utils/StringUtil.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.common.utils;
2 |
3 | public class StringUtil {
4 | public StringUtil() {
5 | }
6 |
7 | public static boolean isNullOrEmpty(String str) {
8 | return null == str || 0 == str.trim().length();
9 | }
10 |
11 | public static boolean isEmpty(String str) {
12 | return str == null || "".equals(str.trim());
13 | }
14 |
15 | public static boolean isNotEmpty(String str) {
16 | return str != null && !"".equals(str.trim());
17 | }
18 |
19 | public static String formatLike(String str) {
20 | return isNotEmpty(str)?"%" + str + "%":null;
21 | }
22 | }
--------------------------------------------------------------------------------
/im-route/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | ai.yunxi
7 | im
8 | 0.0.1-SNAPSHOT
9 |
10 | im-route
11 | im-route
12 | http://maven.apache.org
13 |
14 | UTF-8
15 |
16 |
17 |
18 | org.springframework.boot
19 | spring-boot-starter-web
20 |
21 |
22 | org.springframework.boot
23 | spring-boot-starter-data-redis
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-test
28 | test
29 |
30 |
31 | ai.yunxi
32 | im-common
33 | 0.0.1-SNAPSHOT
34 |
35 |
36 |
37 |
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-maven-plugin
42 |
43 |
44 |
45 | repackage
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/im-route/src/main/java/ai/yunxi/im/route/RouteApplication.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.route;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | *
8 | * @author 小五老师-云析学院
9 | * @createTime 2019年2月27日 下午4:20:02
10 | *
11 | */
12 | @SpringBootApplication
13 | public class RouteApplication {
14 |
15 | public static void main(String[] args) {
16 | SpringApplication.run(RouteApplication.class, args);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/im-route/src/main/java/ai/yunxi/im/route/config/BeanConfiguration.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.route.config;
2 |
3 | import java.util.List;
4 | import java.util.concurrent.TimeUnit;
5 |
6 | import org.I0Itec.zkclient.IZkChildListener;
7 | import org.I0Itec.zkclient.ZkClient;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.data.redis.connection.RedisConnectionFactory;
12 | import org.springframework.data.redis.core.RedisTemplate;
13 | import org.springframework.data.redis.core.StringRedisTemplate;
14 | import org.springframework.data.redis.serializer.StringRedisSerializer;
15 |
16 | import ai.yunxi.im.route.zk.ZKUtil;
17 | import okhttp3.OkHttpClient;
18 |
19 | /**
20 | *
21 | * @author 小五老师-云析学院
22 | * @createTime 2019年2月26日 下午10:08:36
23 | *
24 | */
25 | @Configuration
26 | public class BeanConfiguration {
27 | @Autowired
28 | private InitConfiguration conf;
29 | @Autowired
30 | private ZKUtil zkUtil;
31 |
32 | @Bean
33 | public ZkClient createZKClient(){
34 | ZkClient zk = new ZkClient(conf.getAddr());
35 |
36 | //监听/route节点下子节点的变化,实时更新server list
37 | zk.subscribeChildChanges(conf.getRoot(), new IZkChildListener() {
38 |
39 | @Override
40 | public void handleChildChange(String parentPath, List currentChilds) throws Exception {
41 | zkUtil.setAllNode(currentChilds);
42 | }
43 | });
44 | return zk;
45 | }
46 |
47 | /**
48 | * Redis bean
49 | * @param factory
50 | * @return
51 | */
52 | @Bean
53 | public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
54 | StringRedisTemplate redisTemplate = new StringRedisTemplate(factory);
55 | redisTemplate.setKeySerializer(new StringRedisSerializer());
56 | redisTemplate.setValueSerializer(new StringRedisSerializer());
57 | redisTemplate.afterPropertiesSet();
58 | return redisTemplate;
59 | }
60 |
61 | /**
62 | * http client
63 | * @return okHttp
64 | */
65 | @Bean
66 | public OkHttpClient okHttpClient() {
67 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
68 | builder.connectTimeout(30, TimeUnit.SECONDS)
69 | .readTimeout(10, TimeUnit.SECONDS)
70 | .writeTimeout(10,TimeUnit.SECONDS)
71 | .retryOnConnectionFailure(true);
72 | return builder.build();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/im-route/src/main/java/ai/yunxi/im/route/config/InitConfiguration.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.route.config;
2 |
3 | import org.springframework.beans.factory.annotation.Value;
4 | import org.springframework.stereotype.Component;
5 |
6 | @Component
7 | public class InitConfiguration {
8 |
9 | @Value("${im.zk.switch}")
10 | private boolean zkSwitch;
11 | @Value("${im.zk.root}")
12 | private String root;
13 | @Value("${im.zk.addr}")
14 | private String addr;
15 | public boolean isZkSwitch() {
16 | return zkSwitch;
17 | }
18 | public void setZkSwitch(boolean zkSwitch) {
19 | this.zkSwitch = zkSwitch;
20 | }
21 | public String getRoot() {
22 | return root;
23 | }
24 | public void setRoot(String root) {
25 | this.root = root;
26 | }
27 | public String getAddr() {
28 | return addr;
29 | }
30 | public void setAddr(String addr) {
31 | this.addr = addr;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/im-route/src/main/java/ai/yunxi/im/route/controller/IMRouteController.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.route.controller;
2 |
3 | import java.io.IOException;
4 | import java.util.List;
5 | import java.util.concurrent.atomic.AtomicLong;
6 |
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.data.redis.core.RedisTemplate;
11 | import org.springframework.web.bind.annotation.RequestBody;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.bind.annotation.RequestMethod;
14 | import org.springframework.web.bind.annotation.RestController;
15 |
16 | import ai.yunxi.im.common.constant.BasicConstant;
17 | import ai.yunxi.im.common.pojo.ChatInfo;
18 | import ai.yunxi.im.common.pojo.ServerInfo;
19 | import ai.yunxi.im.common.pojo.UserInfo;
20 | import ai.yunxi.im.common.utils.StringUtil;
21 | import ai.yunxi.im.route.service.RouteService;
22 | import ai.yunxi.im.route.zk.ZKUtil;
23 |
24 | /**
25 | *
26 | * @author 小五老师-云析学院
27 | * @createTime 2019年2月27日 下午3:11:53
28 | *
29 | */
30 | @RestController
31 | @RequestMapping("/")
32 | public class IMRouteController {
33 |
34 | private static final Logger LOGGER = LoggerFactory.getLogger(IMRouteController.class);
35 | private AtomicLong index = new AtomicLong();
36 |
37 | @Autowired
38 | private ZKUtil zk;
39 | @Autowired
40 | private RedisTemplate redisTemplate;
41 | @Autowired
42 | private RouteService routeService;
43 |
44 | /**
45 | * 客户端登录,发现可用服务端:
46 | * 1、获取所有zk上的节点;
47 | * 2、轮询法得到一个节点
48 | **/
49 | @RequestMapping(value="/login", method=RequestMethod.POST)
50 | public ServerInfo login(@RequestBody UserInfo userInfo){
51 | String server="";
52 | try {
53 | List all = zk.getAllNode();
54 | if(all.size()<=0){
55 | LOGGER.info("no server start...");
56 | return null;
57 | }
58 | Long position = index.incrementAndGet() % all.size();
59 | if (position < 0) {
60 | position = 0L;
61 | }
62 | server = all.get(position.intValue());
63 | redisTemplate.opsForValue().set(BasicConstant.ROUTE_PREFIX+userInfo.getUserId(), server);
64 | LOGGER.info("get server info :"+server);
65 | } catch (Exception e) {
66 | e.printStackTrace();
67 | }
68 | String[] serv = server.split("-");
69 | ServerInfo serviceInfo = new ServerInfo(serv[0], Integer.parseInt(serv[1]), Integer.parseInt(serv[2]));
70 | return serviceInfo;
71 | }
72 |
73 | /**
74 | * 分发消息
75 | **/
76 | @RequestMapping(value="/chat", method=RequestMethod.POST)
77 | public void chat(@RequestBody ChatInfo chat){
78 | //判断userId是否登录——从缓存取数据 ...
79 | String islogin = redisTemplate.opsForValue().get(BasicConstant.ROUTE_PREFIX + chat.getUserId());
80 | if(StringUtil.isEmpty(islogin)){
81 | LOGGER.info("该用户并未登录["+chat.getUserId()+"]");
82 | return;
83 | }
84 | try {
85 | //从ZK拿到所有节点,分发消息
86 | List all = zk.getAllNode();
87 | for (String server : all) {
88 | String[] serv = server.split("-");
89 | String ip = serv[0];
90 | int httpPort = Integer.parseInt(serv[2]);
91 | String url = "http://"+ip+":"+httpPort+"/pushMessage";
92 | routeService.sendMessage(url, chat);
93 | }
94 | } catch (NumberFormatException e) {
95 | e.printStackTrace();
96 | } catch (IOException e) {
97 | e.printStackTrace();
98 | } catch (Exception e) {
99 | e.printStackTrace();
100 | }
101 | }
102 |
103 | /**
104 | * 客户端下线,从缓存中删除客户端与服务端映射关系
105 | **/
106 | @RequestMapping(value="/logout", method=RequestMethod.POST)
107 | public void logout(@RequestBody UserInfo userInfo){
108 | redisTemplate.opsForValue().getOperations().delete(BasicConstant.ROUTE_PREFIX+userInfo.getUserId());
109 | LOGGER.info("路由端处理了用户下线逻辑:"+userInfo.getUserId());
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/im-route/src/main/java/ai/yunxi/im/route/service/RouteService.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.route.service;
2 |
3 | import java.io.IOException;
4 |
5 | import ai.yunxi.im.common.pojo.ChatInfo;
6 |
7 | /**
8 | *
9 | * @author 小五老师-云析学院
10 | * @createTime 2019年3月7日 下午8:38:44
11 | *
12 | */
13 | public interface RouteService {
14 |
15 | public void sendMessage(String url, ChatInfo chat) throws IOException;
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/im-route/src/main/java/ai/yunxi/im/route/service/impl/RouteServiceImpl.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.route.service.impl;
2 |
3 | import java.io.IOException;
4 |
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Service;
7 |
8 | import com.alibaba.fastjson.JSONObject;
9 |
10 | import ai.yunxi.im.common.pojo.ChatInfo;
11 | import ai.yunxi.im.route.service.RouteService;
12 | import okhttp3.MediaType;
13 | import okhttp3.OkHttpClient;
14 | import okhttp3.Request;
15 | import okhttp3.RequestBody;
16 | import okhttp3.Response;
17 |
18 | /**
19 | *
20 | * @author 小五老师-云析学院
21 | * @createTime 2019年3月7日 下午8:40:34
22 | *
23 | */
24 | @Service
25 | public class RouteServiceImpl implements RouteService {
26 |
27 | private MediaType mediaType = MediaType.parse("application/json");
28 | @Autowired
29 | private OkHttpClient okHttpClient;
30 |
31 | @Override
32 | public void sendMessage(String url, ChatInfo chat) throws IOException {
33 | JSONObject jsonObject = new JSONObject();
34 | jsonObject.put("command",chat.getCommand());
35 | jsonObject.put("time",chat.getTime());
36 | jsonObject.put("userId",chat.getUserId());
37 | jsonObject.put("content",chat.getContent());
38 |
39 | RequestBody requestBody = RequestBody.create(mediaType,jsonObject.toString());
40 |
41 | Request request = new Request.Builder()
42 | .url(url)
43 | .post(requestBody)
44 | .build();
45 |
46 | Response response = okHttpClient.newCall(request).execute() ;
47 | if (!response.isSuccessful()){
48 | throw new IOException("Unexpected code " + response);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/im-route/src/main/java/ai/yunxi/im/route/zk/ZKUtil.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.route.zk;
2 |
3 | import java.util.List;
4 |
5 | import org.I0Itec.zkclient.ZkClient;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.stereotype.Component;
10 |
11 | import ai.yunxi.im.route.config.InitConfiguration;
12 |
13 |
14 | @Component
15 | public class ZKUtil {
16 |
17 | private static Logger LOGGER = LoggerFactory.getLogger(ZKUtil.class);
18 |
19 | @Autowired
20 | private ZkClient zkClient;
21 |
22 | @Autowired
23 | private InitConfiguration conf ;
24 |
25 | List allNode;
26 |
27 | /**
28 | * 创建父级节点
29 | */
30 | public void createRootNode(){
31 | boolean exists = zkClient.exists(conf.getRoot());
32 | if (exists){
33 | return;
34 | }
35 | //创建 root
36 | zkClient.createPersistent(conf.getRoot()) ;
37 | }
38 |
39 | /**
40 | * 写入指定节点 临时目录
41 | */
42 | public void createNode(String path) {
43 | zkClient.createEphemeral(path);
44 | }
45 |
46 | /**
47 | * 获取所有注册节点
48 | * @return
49 | */
50 | public List getAllNode(){
51 | LOGGER.info("查询所有节点成功,节点数:"+allNode.size());
52 | return allNode;
53 | }
54 |
55 | /**
56 | * 更新server list
57 | */
58 | public void setAllNode(List allNode){
59 | LOGGER.info("server节点更新,节点数:"+allNode.size());
60 | this.allNode = allNode;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/im-route/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=8880
2 |
3 | im.zk.switch=true
4 | im.zk.root=/route
5 | im.zk.addr=localhost:2181
6 |
7 | spring.redis.host=localhost
8 | spring.redis.port=6379
9 | spring.redis.pool.max-active=100
10 | spring.redis.pool.max-idle=100
11 | spring.redis.pool.max-wait=1000
12 | spring.redis.pool.min-idle=10
--------------------------------------------------------------------------------
/im-route/src/test/java/ai/yunxi/im/route/AppTest.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.route;
2 |
3 | import junit.framework.Test;
4 | import junit.framework.TestCase;
5 | import junit.framework.TestSuite;
6 |
7 | /**
8 | * Unit test for simple App.
9 | */
10 | public class AppTest
11 | extends TestCase
12 | {
13 | /**
14 | * Create the test case
15 | *
16 | * @param testName name of the test case
17 | */
18 | public AppTest( String testName )
19 | {
20 | super( testName );
21 | }
22 |
23 | /**
24 | * @return the suite of tests being tested
25 | */
26 | public static Test suite()
27 | {
28 | return new TestSuite( AppTest.class );
29 | }
30 |
31 | /**
32 | * Rigourous Test :-)
33 | */
34 | public void testApp()
35 | {
36 | assertTrue( true );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/im-server/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | ai.yunxi
7 | im
8 | 0.0.1-SNAPSHOT
9 |
10 | im-server
11 | im-server
12 | http://maven.apache.org
13 |
14 | UTF-8
15 |
16 |
17 |
18 | org.springframework.boot
19 | spring-boot-starter-web
20 |
21 |
22 | org.springframework.boot
23 | spring-boot-starter-test
24 | test
25 |
26 |
27 | ai.yunxi
28 | im-common
29 | 0.0.1-SNAPSHOT
30 |
31 |
32 | org.msgpack
33 | msgpack
34 | 0.6.12
35 |
36 |
37 |
38 |
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-maven-plugin
43 |
44 |
45 |
46 | repackage
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/IMServerApplication.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.boot.CommandLineRunner;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 |
9 | import ai.yunxi.im.server.zk.RegisterToZK;
10 |
11 | /**
12 | *
13 | * @author 小五老师-云析学院
14 | * @createTime 2019年2月26日 下午2:59:36
15 | *
16 | */
17 | @SpringBootApplication
18 | public class IMServerApplication implements CommandLineRunner {
19 |
20 | private final static Logger LOGGER = LoggerFactory.getLogger(IMServerApplication.class);
21 |
22 | public static void main(String[] args) {
23 | SpringApplication.run(IMServerApplication.class, args);
24 | LOGGER.info("启动 Service 服务成功");
25 | }
26 |
27 | /**
28 | * 启动后,将节点注册在Zookeeper
29 | **/
30 | @Override
31 | public void run(String... args) throws Exception {
32 | try {
33 | Thread thread = new Thread(new RegisterToZK());
34 | thread.setName("im-server-register-thread");
35 | thread.start();
36 | } catch (Exception e) {
37 | e.printStackTrace();
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/config/BeanConfiguration.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server.config;
2 |
3 | import java.util.concurrent.TimeUnit;
4 |
5 | import org.I0Itec.zkclient.ZkClient;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 |
10 | import okhttp3.OkHttpClient;
11 |
12 | /**
13 | *
14 | * @author 小五老师-云析学院
15 | * @createTime 2019年2月26日 下午5:48:27
16 | *
17 | */
18 | @Configuration
19 | public class BeanConfiguration {
20 | @Autowired
21 | private InitConfiguration conf;
22 |
23 | @Bean
24 | public ZkClient createZKClient(){
25 | return new ZkClient(conf.getAddr());
26 | }
27 |
28 | /**
29 | * http client
30 | * @return okHttp
31 | */
32 | @Bean
33 | public OkHttpClient okHttpClient() {
34 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
35 | builder.connectTimeout(30, TimeUnit.SECONDS)
36 | .readTimeout(10, TimeUnit.SECONDS)
37 | .writeTimeout(10,TimeUnit.SECONDS)
38 | .retryOnConnectionFailure(true);
39 | return builder.build();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/config/InitConfiguration.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server.config;
2 |
3 | import org.springframework.beans.factory.annotation.Value;
4 | import org.springframework.stereotype.Component;
5 |
6 | /**
7 | * @author 小五老师-云析学院
8 | * @createTime 2019年3月12日 下午8:56:48
9 | *
10 | */
11 | @Component
12 | public class InitConfiguration {
13 |
14 | @Value("${server.port}")
15 | private int httpPort;
16 | @Value("${im.server.port}")
17 | private int nettyPort;
18 |
19 | @Value("${im.zk.switch}")
20 | private boolean zkSwitch;
21 | @Value("${im.zk.root}")
22 | private String root;
23 | @Value("${im.zk.addr}")
24 | private String addr;
25 |
26 | @Value("${im.route.logout.url}")
27 | private String routeLogoutUrl;
28 | public int getHttpPort() {
29 | return httpPort;
30 | }
31 | public void setHttpPort(int httpPort) {
32 | this.httpPort = httpPort;
33 | }
34 | public int getNettyPort() {
35 | return nettyPort;
36 | }
37 | public void setNettyPort(int nettyPort) {
38 | this.nettyPort = nettyPort;
39 | }
40 | public boolean isZkSwitch() {
41 | return zkSwitch;
42 | }
43 | public void setZkSwitch(boolean zkSwitch) {
44 | this.zkSwitch = zkSwitch;
45 | }
46 | public String getRoot() {
47 | return root;
48 | }
49 | public void setRoot(String root) {
50 | this.root = root;
51 | }
52 | public String getAddr() {
53 | return addr;
54 | }
55 | public void setAddr(String addr) {
56 | this.addr = addr;
57 | }
58 | public String getRouteLogoutUrl() {
59 | return routeLogoutUrl;
60 | }
61 | public void setRouteLogoutUrl(String routeLogoutUrl) {
62 | this.routeLogoutUrl = routeLogoutUrl;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/config/SpringBeanFactory.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server.config;
2 |
3 | import org.springframework.beans.BeansException;
4 | import org.springframework.context.ApplicationContext;
5 | import org.springframework.context.ApplicationContextAware;
6 | import org.springframework.stereotype.Component;
7 |
8 | @Component
9 | public final class SpringBeanFactory implements ApplicationContextAware{
10 | private static ApplicationContext context;
11 |
12 | public static T getBean(Class c){
13 | return context.getBean(c);
14 | }
15 |
16 |
17 | public static T getBean(String name,Class clazz){
18 | return context.getBean(name,clazz);
19 | }
20 |
21 | @Override
22 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
23 | context = applicationContext;
24 | }
25 |
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/controller/IMServerController.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server.controller;
2 |
3 | import java.util.Map.Entry;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.web.bind.annotation.RequestBody;
9 | import org.springframework.web.bind.annotation.RequestMapping;
10 | import org.springframework.web.bind.annotation.RequestMethod;
11 | import org.springframework.web.bind.annotation.RestController;
12 |
13 | import ai.yunxi.im.common.constant.MessageConstant;
14 | import ai.yunxi.im.common.pojo.ChatInfo;
15 | import ai.yunxi.im.common.protocol.MessageProto;
16 | import ai.yunxi.im.server.handle.ChannelMap;
17 | import io.netty.channel.Channel;
18 | import io.netty.util.AttributeKey;
19 |
20 | /**
21 | *
22 | * @author 小五老师-云析学院
23 | * @createTime 2019年2月26日 下午3:10:41
24 | * 服务端处理器
25 | */
26 | @RestController
27 | @RequestMapping("/")
28 | public class IMServerController {
29 |
30 | private static final Logger LOGGER = LoggerFactory.getLogger(IMServerController.class);
31 | private ChannelMap CHANNEL_MAP = ChannelMap.newInstance();
32 | private AttributeKey userId = AttributeKey.valueOf("userId");
33 | /**
34 | * 服务端接收消息,并推送到指定客户端
35 | **/
36 | @RequestMapping(value="/pushMessage", method=RequestMethod.POST)
37 | public void pushMessage(@RequestBody ChatInfo chat){
38 | //1.接收客户端封装好的消息对象
39 | MessageProto.MessageProtocol message = MessageProto.MessageProtocol.newBuilder()
40 | .setCommand(chat.getCommand())
41 | .setTime(chat.getTime())
42 | .setUserId(chat.getUserId())
43 | .setContent(chat.getContent()).build();
44 | //2.根据消息发送给指定客户端(群发)
45 | // 根据userID,从本地Map集合中得到对应的客户端Channel,发送消息
46 | if(MessageConstant.CHAT.equals(message.getCommand())){
47 | for (Entry entry : CHANNEL_MAP.getCHANNEL_MAP().entrySet()) {
48 | //过滤客户端本身
49 | if(entry.getKey() != message.getUserId()){
50 | LOGGER.info("----服务端向"+entry.getValue().attr(userId).get()+"发送了消息,来自userId="+message.getUserId()+", content="+message.getContent());
51 | entry.getValue().writeAndFlush(message);
52 | }
53 | }
54 | }
55 | }
56 |
57 | /**
58 | * 服务端处理客户端下线事件
59 | **/
60 | @RequestMapping(value="/clientLogout", method=RequestMethod.POST)
61 | public void clientLogout(@RequestBody ChatInfo chatinfo){
62 |
63 | CHANNEL_MAP.getCHANNEL_MAP().remove(chatinfo.getUserId());
64 | LOGGER.info("---客户端下线["+chatinfo.getUserId()+"]");
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/handle/ChannelMap.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server.handle;
2 |
3 | import java.util.Map;
4 | import java.util.concurrent.ConcurrentHashMap;
5 |
6 | import org.springframework.stereotype.Component;
7 |
8 | import io.netty.channel.Channel;
9 |
10 | /**
11 | *
12 | * @author 小五老师-云析学院
13 | * @createTime 2019年3月7日 下午9:12:59
14 | *
15 | */
16 | public class ChannelMap {
17 |
18 | private static ChannelMap instance;
19 | private final Map CHANNEL_MAP = new ConcurrentHashMap();
20 |
21 | private ChannelMap() {
22 | }
23 | public static ChannelMap newInstance(){
24 | if(instance == null){
25 | instance = new ChannelMap();
26 | }
27 | return instance;
28 | }
29 |
30 | public Map getCHANNEL_MAP() {
31 | return CHANNEL_MAP;
32 | }
33 |
34 | public void putClient(Integer userId, Channel channel){
35 | CHANNEL_MAP.put(userId, channel);
36 | }
37 |
38 | public Channel getClient(Integer userId){
39 | return CHANNEL_MAP.get(userId);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/handle/ClientProcessor.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server.handle;
2 |
3 | import java.io.IOException;
4 |
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Component;
7 |
8 | import com.alibaba.fastjson.JSONObject;
9 |
10 | import ai.yunxi.im.common.constant.BasicConstant;
11 | import ai.yunxi.im.server.config.InitConfiguration;
12 | import okhttp3.OkHttpClient;
13 | import okhttp3.Request;
14 | import okhttp3.RequestBody;
15 | import okhttp3.Response;
16 |
17 | /**
18 | *
19 | * @author 小五老师-云析学院
20 | * @createTime 2019年3月7日 下午10:38:47
21 | *
22 | */
23 | @Component
24 | public class ClientProcessor {
25 |
26 | @Autowired
27 | private OkHttpClient okHttpClient;
28 | @Autowired
29 | private InitConfiguration conf;
30 |
31 | public void down(Integer userId){
32 | try {
33 | JSONObject jsonObject = new JSONObject();
34 | jsonObject.put("userId",userId);
35 | RequestBody requestBody = RequestBody.create(BasicConstant.MEDIA_TYPE,jsonObject.toString());
36 |
37 | Request request = new Request.Builder()
38 | .url(conf.getRouteLogoutUrl())
39 | .post(requestBody)
40 | .build();
41 |
42 | Response response = okHttpClient.newCall(request).execute() ;
43 | if (!response.isSuccessful()){
44 | throw new IOException("Unexpected code " + response);
45 | }
46 | } catch (IOException e) {
47 | e.printStackTrace();
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/handle/IMServerHandle.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server.handle;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import ai.yunxi.im.common.constant.MessageConstant;
7 | import ai.yunxi.im.common.protocol.MessageProto;
8 | import ai.yunxi.im.server.config.SpringBeanFactory;
9 | import io.netty.channel.ChannelHandlerContext;
10 | import io.netty.channel.ChannelInboundHandlerAdapter;
11 | import io.netty.util.AttributeKey;
12 |
13 | /**
14 | *
15 | * @author 小五老师-云析学院
16 | * @createTime 2019年2月27日 下午2:02:42
17 | *
18 | */
19 | public class IMServerHandle extends ChannelInboundHandlerAdapter {
20 |
21 | private final static Logger LOGGER = LoggerFactory.getLogger(IMServerHandle.class);
22 |
23 | private AttributeKey userId = AttributeKey.valueOf("userId");
24 |
25 | private ChannelMap CHANNEL_MAP = ChannelMap.newInstance();
26 |
27 | private ClientProcessor clientProcessor;
28 |
29 |
30 | public IMServerHandle() {
31 | this.clientProcessor = SpringBeanFactory.getBean(ClientProcessor.class);
32 | }
33 |
34 | @Override
35 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
36 | MessageProto.MessageProtocol message = (MessageProto.MessageProtocol)msg;
37 |
38 | //处理客户端向服务端推送的消息
39 | if(MessageConstant.LOGIN.equals(message.getCommand())){
40 | //登录,保存Channel
41 | ctx.channel().attr(userId).set(message.getUserId());
42 | CHANNEL_MAP.putClient(message.getUserId(), ctx.channel());
43 | LOGGER.info("---客户端登录成功。userId:"+message.getUserId());
44 | }
45 | }
46 |
47 | @Override
48 | public void channelInactive(ChannelHandlerContext ctx) throws Exception {
49 | Integer uid = ctx.channel().attr(userId).get();
50 | //从Channel缓存删除客户端
51 | CHANNEL_MAP.getCHANNEL_MAP().remove(uid);
52 | clientProcessor.down(uid);
53 |
54 | LOGGER.info("----客户端强制下线。userId:"+uid);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/init/IMServerInit.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server.init;
2 |
3 | import javax.annotation.PostConstruct;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.stereotype.Component;
9 |
10 | import ai.yunxi.im.common.protocol.MessageProto;
11 | import ai.yunxi.im.server.config.InitConfiguration;
12 | import ai.yunxi.im.server.handle.IMServerHandle;
13 | import io.netty.bootstrap.ServerBootstrap;
14 | import io.netty.channel.ChannelFuture;
15 | import io.netty.channel.ChannelInitializer;
16 | import io.netty.channel.ChannelOption;
17 | import io.netty.channel.ChannelPipeline;
18 | import io.netty.channel.EventLoopGroup;
19 | import io.netty.channel.nio.NioEventLoopGroup;
20 | import io.netty.channel.socket.SocketChannel;
21 | import io.netty.channel.socket.nio.NioServerSocketChannel;
22 | import io.netty.handler.codec.protobuf.ProtobufDecoder;
23 | import io.netty.handler.codec.protobuf.ProtobufEncoder;
24 | import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
25 | import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
26 |
27 | /**
28 | *
29 | * @author 小五老师-云析学院
30 | * @createTime 2019年2月27日 下午1:35:52
31 | *
32 | */
33 | @Component
34 | public class IMServerInit {
35 |
36 | private final static Logger LOGGER = LoggerFactory.getLogger(IMServerInit.class);
37 |
38 | private EventLoopGroup acceptorGroup = new NioEventLoopGroup();
39 | private EventLoopGroup workerGroup = new NioEventLoopGroup();
40 |
41 | @Autowired
42 | private InitConfiguration conf;
43 |
44 | @PostConstruct
45 | public void start() throws Exception{
46 | try {
47 | //Netty用于启动NIO服务器的辅助启动类
48 | ServerBootstrap sb = new ServerBootstrap();
49 | //将两个NIO线程组传入辅助启动类中
50 | sb.group(acceptorGroup, workerGroup)
51 | //设置创建的Channel为NioServerSocketChannel类型
52 | .channel(NioServerSocketChannel.class)
53 | //保持长连接
54 | .childOption(ChannelOption.SO_KEEPALIVE, true)
55 | //设置绑定IO事件的处理类
56 | .childHandler(new ChannelInitializer() {
57 | @Override
58 | protected void initChannel(SocketChannel ch) throws Exception {
59 | ChannelPipeline pipeline = ch.pipeline();
60 | // google Protobuf 编解码
61 | pipeline.addLast(new ProtobufVarint32FrameDecoder());
62 | pipeline.addLast(new ProtobufDecoder(MessageProto.MessageProtocol.getDefaultInstance()));
63 | pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
64 | pipeline.addLast(new ProtobufEncoder());
65 |
66 | pipeline.addLast(new IMServerHandle());
67 | }
68 | });
69 | ChannelFuture conn = sb.bind(conf.getNettyPort()).sync();
70 | if(conn.isSuccess()){
71 | LOGGER.info("---服务端启动成功,端口["+conf.getNettyPort()+"]");
72 | }
73 | } finally {
74 | // acceptorGroup.shutdownGracefully();
75 | // workerGroup.shutdownGracefully();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/zk/RegisterToZK.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server.zk;
2 |
3 | import java.net.InetAddress;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import ai.yunxi.im.server.config.InitConfiguration;
9 | import ai.yunxi.im.server.config.SpringBeanFactory;
10 |
11 | public class RegisterToZK implements Runnable {
12 |
13 | private static Logger LOGGER = LoggerFactory.getLogger(RegisterToZK.class);
14 |
15 | private InitConfiguration conf;
16 | private ZKUtil zk;
17 |
18 | public RegisterToZK() {
19 | conf = SpringBeanFactory.getBean(InitConfiguration.class);
20 | zk = SpringBeanFactory.getBean(ZKUtil.class);
21 | }
22 |
23 | @Override
24 | public void run() {
25 | try {
26 | String ip = InetAddress.getLocalHost().getHostAddress();
27 | int httpPort = conf.getHttpPort();
28 | int nettyPort = conf.getNettyPort();
29 | LOGGER.info("---服务端注册到Zookeeper. ip:"+ip+"; httpPort:"+httpPort+"; nettyPort:"+nettyPort);
30 |
31 | //创建父节点
32 | zk.createRootNode();
33 | //判断是否需要注册到zk
34 | if(conf.isZkSwitch()){
35 | String path = conf.getRoot() + "/"+ip+"-"+conf.getNettyPort()+"-"+conf.getHttpPort();
36 | zk.createNode(path);
37 | LOGGER.info("---服务端注册到ZK成功,Path="+path);
38 | }
39 | } catch (Exception e) {
40 | e.printStackTrace();
41 | }
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/im-server/src/main/java/ai/yunxi/im/server/zk/ZKUtil.java:
--------------------------------------------------------------------------------
1 | package ai.yunxi.im.server.zk;
2 |
3 | import java.util.List;
4 |
5 | import org.I0Itec.zkclient.ZkClient;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.stereotype.Component;
10 |
11 | import ai.yunxi.im.server.config.InitConfiguration;
12 |
13 | @Component
14 | public class ZKUtil {
15 |
16 | private static Logger LOGGER = LoggerFactory.getLogger(ZKUtil.class);
17 |
18 | @Autowired
19 | private ZkClient zkClient;
20 |
21 | @Autowired
22 | private InitConfiguration conf ;
23 |
24 | /**
25 | * 创建父级节点
26 | */
27 | public void createRootNode(){
28 | boolean exists = zkClient.exists(conf.getRoot());
29 | if (exists){
30 | return;
31 | }
32 | //创建 root
33 | zkClient.createPersistent(conf.getRoot()) ;
34 | }
35 |
36 | /**
37 | * 写入指定节点 临时目录
38 | */
39 | public void createNode(String path) {
40 | zkClient.createEphemeral(path);
41 | }
42 |
43 | /**
44 | * 获取所有注册节点
45 | * @return
46 | */
47 | public List getAllNode(){
48 | List children = zkClient.getChildren("/route");
49 | LOGGER.info("查询所有节点成功,节点数:"+children.size());
50 | return children;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/im-server/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=8080
2 | im.server.port=8090
3 |
4 | im.zk.switch=true
5 | im.zk.root=/route
6 | im.zk.addr=localhost:2181
7 |
8 | im.route.logout.url=http://localhost:8880/logout
--------------------------------------------------------------------------------
/pic/im.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smallFive55/im/42c11418fc8d2e702f0ab0f0596ccc86bfe5e643/pic/im.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | ai.yunxi
4 | im
5 | pom
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 | org.springframework.boot
10 | spring-boot-parent
11 | 2.1.2.RELEASE
12 |
13 |
14 |
15 | com.101tec
16 | zkclient
17 | 0.10
18 |
19 |
20 | io.netty
21 | netty-all
22 | 4.1.5.Final
23 |
24 |
25 | com.squareup.okhttp3
26 | okhttp
27 | 3.3.1
28 |
29 |
30 | com.alibaba
31 | fastjson
32 | 1.2.31
33 |
34 |
35 |
36 | im-server
37 | im-route
38 | im-client
39 | im-common
40 |
41 |
--------------------------------------------------------------------------------