HttpVerticle.java
package com.xiaoniu.im.rest;
import com.xiaoniu.im.utils.Runner;
import com.xiaoniu.im.utils.Utils;
import io.netty.util.internal.StringUtil;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.shareddata.LocalMap;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mapdb.HTreeMap;
import org.mapdb.Serializer;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* http 服務處理除聊天以外的業務
* Created by sweet on 2017/9/26.
*/
public class HttpVerticle extends AbstractVerticle {
private static final Logger log = LoggerFactory.getLogger(HttpVerticle.class);
public static void main(String[] args) {
Runner.runExample(HttpVerticle.class);
}
private static final String filePath = "C:\\xiaoniu_doc\\vertx\\sweet-im\\";
private static final Integer port = 8080;
private DB db;
private HTreeMap<String, String> userMap; // 儲存註冊使用者資訊
private LocalMap<String, Object> userMapCache; // 儲存註冊使用者資訊(記憶體共享版)
private HTreeMap<String, String> userNameAndIdMap; // 使用者id - 使用者名稱 方便快速查詢
private LocalMap<String, Object> userNameAndIdMapCache; // 使用者id - 使用者名稱 方便快速查詢 (記憶體版)
private HTreeMap<String, String> friendMap; // 儲存好友關係
private LocalMap<String, Object> friendMapCache; // 儲存好友關係(記憶體共享版)
private HTreeMap<String, String> onlineMap; // 當前登入使用者
private LocalMap<String, Object> onlineMapCache; // 當前登入使用者 (記憶體共享版)
private LocalMap<String, Object> messageMapCache; // 儲存使用者私密聊天記錄 (記憶體共享版)
private LocalMap<String, Object> messageGroupMapCache; // 儲存群組聊天記錄 (記憶體共享版)
private HTreeMap<String, String> groupMap; // 群組關係
private LocalMap<String, Object> groupMapCache; // 群組關係 (記憶體共享版)
private EventBus eventBus;
@Override
public void start(Future<Void> future) throws Exception {
eventBus = vertx.eventBus();
initDB().setHandler(init -> {
if (init.succeeded() && init.result()) {
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
// 使用者註冊
router.post("/register").consumes("application/json").handler(this::register);
// 使用者登入
router.post("/login").consumes("application/json").handler(this::login);
// 使用者退出 (暫定)
router.get("/logout").handler(this::logout);
// 新增好友
router.post("/api/friend").consumes("application/json").handler(this::addFriend);
// 查詢 使用者的好友列表
router.get("/api/friends").handler(this::queryFriends);
// 查詢 私密使用者聊天記錄
router.get("/api/message").handler(this::queryMessage);
// 查詢當前登入使用者數
router.get("/api/online").handler(this::queryUsersCount);
// 查詢所有註冊使用者
router.get("/api/users").handler(this::queryUsers);
// 新建群組
router.post("/api/group").consumes("application/json").handler(this::createGroup);
// 查詢所有 Map (測試方便觀察資料)
router.get("/maps").handler(this::queryAll);
vertx.createHttpServer().requestHandler(router::accept).listen(port, server -> {
if (server.succeeded()){
log.info("Http服務啟動成功");
future.complete();
} else {
server.cause().printStackTrace();
log.error("Http服務啟動失敗," + server.cause().getMessage());
future.fail(server.cause());
}
});
vertx.exceptionHandler(ex -> {
ex.printStackTrace();
log.error("異常處理器: " + ex.getMessage());
});
} else {
future.fail(init.cause());
}
});
}
private void queryAll(RoutingContext ctx) {
JsonObject j = new JsonObject();
j.put("userMapCache", userMapCache);
j.put("userNameAndIdMapCache", userNameAndIdMapCache);
j.put("friendMapCache", friendMapCache);
j.put("onlineMapCache", onlineMapCache);
j.put("messageMapCache", messageMapCache);
j.put("groupMapCache", groupMapCache);
j.put("messageGroupMapCache", messageGroupMapCache);
resp(ctx.request(), j);
}
private void createGroup(RoutingContext ctx) {
JsonObject bodyAsJson = ctx.getBodyAsJson();
String groupName = bodyAsJson.getString("groupName");
String creater = bodyAsJson.getString("creater");
JsonArray members = bodyAsJson.getJsonArray("members");
if (StringUtil.isNullOrEmpty(groupName)
|| StringUtil.isNullOrEmpty(creater)
|| members == null
|| members.size() < 2) {
respError(ctx.request(), 404, null);
return;
}
Object o = userNameAndIdMapCache.get(creater);
long count = members.stream()
.filter(memberId -> userNameAndIdMapCache.get(memberId) != null)
.count();
boolean contains = members.contains(creater); // 要求成員中必須包含建立人
if (o == null || count != members.size() || !contains) {
respError(ctx.request(), 404, null);
return;
}
// TODO 檢查群組中的人和建立人是否是好友關係
// TODO 建立人是否線上及細節群組中的人不線上如何處理,線上如何處理
String groupId = Utils.uuid(); // 群組ID
JsonObject body = new JsonObject();
body.put("id", groupId);
body.put("groupName", groupName);
body.put("creater", creater);
body.put("createTime", Utils.time());
body.put("members", members);
groupMap.put(groupId, body.encode());
groupMapCache.put(groupId, body);
resp(ctx.request(), body);
}
private void queryMessage(RoutingContext ctx) {
String senderName = ctx.request().getParam("sender");
String receiverName = ctx.request().getParam("receiver");
if (StringUtil.isNullOrEmpty(senderName) || StringUtil.isNullOrEmpty(receiverName)) {
respError(ctx.request(), 404, null);
return;
}
Object o = onlineMapCache.get(senderName);
if (o == null) {
respError(ctx.request(), 500, "請登入後使用");
return;
}
Object o1 = userMapCache.get(receiverName);
if (o1 == null) {
respError(ctx.request(), 500, "不存在");
return;
}
JsonObject senderJson = (JsonObject) userMapCache.get(senderName);
JsonObject receiverJson = (JsonObject) o1;
Object o2 = friendMapCache.get(senderJson.getString("id"));
if (o2 == null) {
respError(ctx.request(), 500, "不存在");
return;
}
JsonArray friends = (JsonArray) o2;
if (friends.contains(receiverJson.getString("id"))) {
String senderId = senderJson.getString("id");
String receiverId = receiverJson.getString("id");
String msgMapKey = senderId.compareTo(receiverId) < 0 ? senderId+"-"+receiverId : receiverId+"-"+senderId;
Object msgList = messageMapCache.get(msgMapKey);
if (msgList == null) {
resp(ctx.request(), new JsonObject().put("msg", new JsonArray()));
} else {
JsonArray msgArr = (JsonArray) msgList;
List<Object> sortMsgs = msgArr.stream().sorted().collect(Collectors.toList());
resp(ctx.request(), new JsonObject().put("msg", sortMsgs));
}
} else {
respError(ctx.request(), 500, "不是好友關係");
}
}
private void queryFriends(RoutingContext context) {
String login = context.request().getParam("login");
if (StringUtil.isNullOrEmpty(login)) {
respError(context.request(), 404, "使用者不存在");
return;
}
Object o = onlineMapCache.get(login);
if (o == null) {
respError(context.request(), 500, "請先登入");
return;
}
JsonObject userJson = (JsonObject) userMapCache.get(login);
Object friendsObj = friendMapCache.get(userJson.getString("id"));
if (friendsObj == null) {
respError(context.request(), 500, "請至少新增一個好友");
return;
}
log.info("friendsObj --> " + friendsObj.getClass().getName());
resp(context.request(), new JsonObject().put(login, friendsObj));
}
private void queryUsers(RoutingContext ctx) {
JsonObject data = new JsonObject();
userMapCache.forEach((k, v) -> {
data.put(k, v);
});
resp(ctx.request(), data);
}
private void queryUsersCount(RoutingContext ctx) {
long count = onlineMapCache.keySet().parallelStream().count();
resp(ctx.request(), new JsonObject().put("count", count));
}
private void logout(RoutingContext ctx) {
String login = ctx.request().getParam("login");
if (StringUtil.isNullOrEmpty(login)){
respError(ctx.request(), 404, null);
return;
}
Object o = userMapCache.get(login);
if (o == null) {
respError(ctx.request(), 404, null);
return;
}
Object o1 = onlineMapCache.get(login);
if (o1 == null) {
resp(ctx.request(), new JsonObject().put("msg", "成功退出"));
return;
} else {
onlineMapCache.remove(login);
onlineMap.remove(login);
JsonObject sendJson = new JsonObject();
sendJson.put("loginName", login);
eventBus.send("sweet-logout", sendJson, res -> {
if (res.succeeded()) {
JsonObject body = (JsonObject) res.result().body();
String code = body.getString("code");
if (code.equals("1")) {
resp(ctx.request(), new JsonObject().put("msg", "成功退出"));
} else {
resp(ctx.request(), new JsonObject().put("msg", "沒有資料"));
}
} else {
respError(ctx.request(), 500, res.cause().getMessage());
}
});
}
}
private void login(RoutingContext ctx) {
JsonObject bodyAsJson = ctx.getBodyAsJson();
String login = bodyAsJson.getString("login");
String passwd = bodyAsJson.getString("passwd");
if (StringUtil.isNullOrEmpty(login) || StringUtil.isNullOrEmpty(passwd)) {
respError(ctx.request(), 404, null);
return;
}
Object userValue = userMapCache.get(login);
if (userValue == null) {
respError(ctx.request(), 404, "使用者名稱或密碼錯誤");
return;
}
JsonObject userJson = (JsonObject) userValue;
if (userJson.getString("passwd").equals(passwd)) {
String time = Utils.time();
onlineMap.put(login, time);
onlineMapCache.put(login, time);
resp(ctx.request(), new JsonObject().put("msg", "登入成功"));
} else {
respError(ctx.request(), 404, "使用者名稱或密碼錯誤");
}
}
private void addFriend(RoutingContext ctx) {
JsonObject bodyAsJson = ctx.getBodyAsJson();
String login = bodyAsJson.getString("login"); // 登入使用者
String addName = bodyAsJson.getString("add"); // 要新增的好友
if (StringUtil.isNullOrEmpty(login) || StringUtil.isNullOrEmpty(addName)) {
respError(ctx.request(), 404, "找不到該使用者");
return;
}
Object o = userMapCache.get(login);
Object o1 = userMapCache.get(addName);
if (o == null || o1 == null) {
respError(ctx.request(), 404, "找不到該使用者");
return;
}
Object o2 = onlineMapCache.get(addName); // 檢查要新增的好友是否線上
if (o2 == null) {
respError(ctx.request(), 404, "該使用者不線上");
return;
}
JsonObject loginUser = (JsonObject) o;
JsonObject addUser = (JsonObject) o1;
String id = loginUser.getString("id");
String addUserId = addUser.getString("id");
Object o3 = friendMapCache.get(id);
if (o3 == null) {
JsonArray arr = new JsonArray();
arr.add(addUserId);
friendMap.put(id, arr.encode());
friendMapCache.put(id, arr);
} else {
JsonArray arr = (JsonArray) o3;
if (arr.contains(addUserId)) {
respError(ctx.request(), 500, "已新增過好友");
return;
}
arr.add(addUserId);
friendMap.put(id, arr.encode());
friendMapCache.put(id, arr);
}
resp(ctx.request(), new JsonObject().put("msg", "新增成功"));
}
private void register(RoutingContext routingContext) {
JsonObject bodyAsJson = routingContext.getBodyAsJson();
log.info(bodyAsJson);
String login = bodyAsJson.getString("login");
String name = bodyAsJson.getString("name");
String passwd = bodyAsJson.getString("passwd");
if (StringUtil.isNullOrEmpty(login)
|| StringUtil.isNullOrEmpty(name)
|| StringUtil.isNullOrEmpty(passwd)) {
respError(routingContext.request(), 404,null);
return;
}
Object v = userMapCache.get(login);
if (v != null) {
respError(routingContext.request(), 405, null);
return;
}
String uuid = Utils.uuid();
JsonObject obj = new JsonObject()
.put("id", uuid)
.put("name", name)
.put("login", login)
.put("passwd", passwd)
.put("createTime", Utils.time());
userMap.put(login, obj.encode());
userMapCache.put(login, obj);
userNameAndIdMap.put(uuid, login);
userNameAndIdMapCache.put(uuid, login);
JsonObject copy = obj.copy();
copy.remove("passwd");
resp(routingContext.request(), copy);
}
private Future<Boolean> initDB() {
Future<Boolean> initDBFuture = Future.future();
try {
db = DBMaker.fileDB(filePath +"sweet-im.db").closeOnJvmShutdown().make();
// 儲存註冊使用者資訊
userMap = db.hashMap("user-db", Serializer.STRING, Serializer.STRING).createOrOpen();
userMapCache = vertx.sharedData().getLocalMap("user-db-cache");
copyJsonObj(userMap, userMapCache); // 把檔案中的使用者資料快取到 記憶體中
// 儲存好友關係
friendMap = db.hashMap("friend-db", Serializer.STRING, Serializer.STRING).createOrOpen();
friendMapCache = vertx.sharedData().getLocalMap("friend-db-cache");
copyJsonArray(friendMap, friendMapCache);
// 當前登入使用者
onlineMap = db.hashMap("online-db", Serializer.STRING, Serializer.STRING).createOrOpen();
onlineMapCache = vertx.sharedData().getLocalMap("online-db-cache");
copyString(onlineMap, onlineMapCache);
// 私密聊天的訊息記錄
messageMapCache = vertx.sharedData().getLocalMap("message-db-cache");
// 儲存群組聊天記錄
messageGroupMapCache = vertx.sharedData().getLocalMap("message-group-db-cache");
// 群組關係
groupMap = db.hashMap("group-db", Serializer.STRING, Serializer.STRING).createOrOpen();
groupMapCache = vertx.sharedData().getLocalMap("group-db-cache");
copyJsonObj(groupMap, groupMapCache);
// 使用者名稱 - 使用者id
userNameAndIdMap = db.hashMap("username-id-db", Serializer.STRING, Serializer.STRING).createOrOpen();
userNameAndIdMapCache = vertx.sharedData().getLocalMap("username-id-db-cache");
copyString(userNameAndIdMap, userNameAndIdMapCache);
initDBFuture.complete(true);
} catch (Exception e) {
e.printStackTrace();
initDBFuture.fail(e.getCause());
}
return initDBFuture;
}
private void copyJsonObj(Map<String, String> sourceMap, LocalMap<String, Object> targetMap) {
sourceMap.forEach((k, v) -> targetMap.put(k, new JsonObject(v)));
}
private void copyJsonArray(Map<String, String> sourceMap, LocalMap<String, Object> targetMap) {
sourceMap.forEach((k, v) -> targetMap.put(k, new JsonArray(v)));
}
private void copyString(Map<String, String> sourceMap, LocalMap<String, Object> targetMap) {
sourceMap.forEach((k, v) -> targetMap.put(k, v));
}
private static void resp(HttpServerRequest request, JsonObject ret) {
request.response()
.putHeader("content-type", "application/json;charset=utf-8")
.putHeader("Access-Control-Allow-Origin", "*")
.putHeader("Access-Control-Allow-Credentials", "true")
.putHeader("Content-Disposition", "attachment")
.end(Json.encodePrettily(ret));
}
private static void respError(HttpServerRequest request, int code, String error) {
request.response()
.putHeader("content-type", "application/json;charset=utf-8")
.putHeader("Access-Control-Allow-Origin", "*")
.putHeader("Access-Control-Allow-Credentials", "true")
.putHeader("Content-Disposition", "attachment")
.setStatusCode(code)
.end(Json.encodePrettily(new JsonObject().put("error", error)));
}
@Override
public void stop(Future<Void> stopFuture) throws Exception {
userMap.close();
friendMap.close();
onlineMap.close();
groupMap.close();
userNameAndIdMap.close();
if (!db.isClosed()) {
db.commit();
db.close();
}
stopFuture.complete();
}
}
複製程式碼
SocketVerticle.java
package com.xiaoniu.im.socket;
import com.xiaoniu.im.utils.Utils;
import io.netty.util.internal.StringUtil;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.shareddata.LocalMap;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mapdb.HTreeMap;
import org.mapdb.Serializer;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* socket 通訊模組 主要處理聊天
* Created by sweet on 2017/9/26.
*/
public class SocketVerticle extends AbstractVerticle {
private static final Logger log = LoggerFactory.getLogger(SocketVerticle.class);
private static final String filePath = "C:\\xiaoniu_doc\\vertx\\sweet-im\\";
private static final Integer port = 8081;
private DB db;
private HTreeMap<String, String> messageMap; // 儲存使用者私密聊天記錄
private LocalMap<String, Object> messageMapCache; // 儲存使用者私密聊天記錄 (記憶體共享版)
private HTreeMap<String, String> messageGroupMap; // 儲存群組聊天記錄
private LocalMap<String, Object> messageGroupMapCache; // 儲存群組聊天記錄 (記憶體共享版)
private LocalMap<String, Object> userMapCache; // 儲存註冊使用者資訊(記憶體共享版)
private LocalMap<String, Object> friendMapCache; // 儲存好友關係(記憶體共享版)
private LocalMap<String, Object> onlineMapCache; // 當前登入使用者 (記憶體共享版)
private LocalMap<String, Object> userNameAndIdMapCache; // 使用者id - 使用者名稱 方便快速查詢 (記憶體版)
private LocalMap<String, Object> groupMapCache; // 群組關係 (記憶體共享版)
private Map<String, ServerWebSocket> socketMap; // 每個使用者對應一個 socket連線
private EventBus eventBus; // 處理使用者退出,關閉並刪除 socket 引用
@Override
public void start(Future<Void> future) throws Exception {
eventBus = vertx.eventBus();
initDB().setHandler(res -> {
vertx.createHttpServer().websocketHandler(serverWebSocket -> {
String path = serverWebSocket.path();
String query = serverWebSocket.query();
log.info("path: " + path + ", socket id: " + serverWebSocket.textHandlerID());
log.info("query: " + query);
if (!"/socket".equals(path) || StringUtil.isNullOrEmpty(query) || !query.startsWith("id=")) {
serverWebSocket.reject();
serverWebSocket.close();
return;
}
String userId = query.substring(3, query.length()); // 當前登入使用者的ID
if (StringUtil.isNullOrEmpty(userId)) {
serverWebSocket.reject();
serverWebSocket.close();
return;
}
Object o = userNameAndIdMapCache.get(userId); // 判斷使用者是否是註冊使用者 o = 使用者name
if (o == null || StringUtil.isNullOrEmpty(o.toString())) {
serverWebSocket.reject();
serverWebSocket.close();
return;
}
Object o1 = onlineMapCache.get(o.toString()); // 判斷使用者是否線上
if (o1 == null) {
serverWebSocket.reject();
serverWebSocket.close();
return;
}
// TODO 使用者重新整理如何處理 Socket連線 ?
put(socketMap, userId, serverWebSocket);
serverWebSocket.handler(buffer -> {
System.out.println("-------------Message------------");
System.out.println("收到的buffer : " + buffer);
System.out.println("-------------Message------------");
JsonObject jsonObject = buffer.toJsonObject();
// 好友之間聊天 所需欄位
String to = jsonObject.getString("to"); // 接受人的名字
String from = jsonObject.getString("from"); // 傳送人的名字
String msg = jsonObject.getString("msg");
// 群組之間聊天 所需欄位
String groupId = jsonObject.getString("groupId");
String fromForGroup = jsonObject.getString("from");
if (StringUtil.isNullOrEmpty(from) || !onlineMapCache.containsKey(from) || !from.equals(o)) {
serverWebSocket.writeTextMessage("欄位不能為空"); // 缺少欄位 和 傳送人沒有登入
return;
}
// 好友之間聊天 start ===========================================
// 好友之間聊天 TODO 全部暫定只能傳送線上使用者
if (!StringUtil.isNullOrEmpty(to) && msg != null && onlineMapCache.containsKey(to)) {
Object o4 = friendMapCache.get(userId);
if (o4 == null) {
serverWebSocket.writeTextMessage("你還沒有好友"); // 缺少欄位 和 傳送人沒有登入
return;
}
JsonArray fromFriends = (JsonArray) o4; // 傳送人的好友
Object o2 = userMapCache.get(to);
JsonObject toUserJson = (JsonObject) o2;
Object o5 = friendMapCache.get(toUserJson.getString("id"));
if (o5 == null) {
serverWebSocket.writeTextMessage("你倆不是好友關係"); // 缺少欄位 和 傳送人沒有登入
return;
}
JsonArray toFriends = (JsonArray) o5;
if (fromFriends.contains(toUserJson.getString("id")) && toFriends.contains(userId)) { // 確定雙方好友關係
String toUserId = toUserJson.getString("id"); // 接收人ID
ServerWebSocket toUserServerWebSocket = socketMap.get(toUserId); // TODO 暫時不做判斷 是否線上,不線上如何處理
String msgMapKey = toUserId.compareTo(userId) < 0 ? toUserId+"-"+userId : userId+"-"+toUserId;
String msgValue = Utils.time()+"-"+from+"-"+msg;
if (messageMapCache.containsKey(msgMapKey)) {
Object o3 = messageMapCache.get(msgMapKey);
JsonArray msgArr = (JsonArray) o3;
msgArr.add(msgValue);
messageMap.put(msgMapKey, msgArr.encode());
messageMapCache.put(msgMapKey, msgArr);
} else {
JsonArray jsonArray = new JsonArray();
jsonArray.add(msgValue);
messageMap.put(msgMapKey, jsonArray.encode());
messageMapCache.put(msgMapKey, jsonArray);
}
toUserServerWebSocket.writeTextMessage(msg);
return;
} else {
serverWebSocket.writeTextMessage("你倆不是好友關係2"); // 缺少欄位 和 傳送人沒有登入
return;
}// 好友之間聊天 end ===========================================
// 群組之間聊天 start ===========================================
} else if (!StringUtil.isNullOrEmpty(groupId)
&& !StringUtil.isNullOrEmpty(fromForGroup)
&& msg != null && groupMapCache.get(groupId) != null) {
log.info("==================群組聊天==================");
Object o2 = groupMapCache.get(groupId);
JsonObject groupJsonObj = (JsonObject) o2;
if (onlineMapCache.containsKey(fromForGroup)) {
JsonArray members = groupJsonObj.getJsonArray("members"); // 群組成員
JsonObject fromUserJson = (JsonObject) userMapCache.get(fromForGroup);
if (members.contains(fromUserJson.getString("id"))) {
sendGroupMessage(groupId, fromUserJson.getString("login"),
fromUserJson.getString("id"), members, msg); // 給全部群組成員傳送訊息
} else {
serverWebSocket.writeTextMessage("你不是群組成員");
}
return;
} else {
serverWebSocket.writeTextMessage("請先登入");
return;
} // 群組之間聊天 end ===========================================
} else {
// 其他不符合情況
serverWebSocket.writeTextMessage("欄位不能為空!!!");
return;
}
});
// 異常處理
serverWebSocket.exceptionHandler(ex -> {
ex.printStackTrace();
log.error(ex.getMessage());
});
}).listen(port, server -> {
if (server.succeeded()) {
log.info("socket server 啟動成功 埠8081");
future.complete();
} else {
log.error(server.cause().getMessage());
server.cause().printStackTrace();
future.fail(server.cause());
}
});
vertx.setPeriodic(10000, timer -> {
System.out.println("------------Socket Map Start---------");
socketMap.forEach((k, v) -> System.out.println("k: " + k + ", v: " + v));
System.out.println("------------Socket Map End ---------");
});
////
// vertx.setPeriodic(10000, timer -> {
// System.out.println("----------Message Map Start-----------");
// messageMapCache.forEach((k, v) -> System.out.println("k: " + k + ", v: " + v));
// System.out.println("----------Message Map End -----------");
// });
// 接受使用者退出訊息,然後處理 SocketMap中的引用
eventBus.consumer("sweet-logout", msg -> {
JsonObject body = (JsonObject) msg.body();
String loginName = body.getString("loginName");
ServerWebSocket serverWebSocket = socketMap.get(loginName);
JsonObject replyMsg = new JsonObject();
if (serverWebSocket != null) {
serverWebSocket.close();
socketMap.remove(loginName);
replyMsg.put("code", "1"); // code 1 退出成功
msg.reply(replyMsg);
} else {
replyMsg.put("code", "0"); // code 0 沒有資料
msg.reply(replyMsg);
}
});
});
}
/**
* @param groupId 群組id
* @param fromUserLogin 傳送人login
* @param fromUserId 傳送人Id
* @param members 群組成員 id
* @param msg 訊息
*/
private void sendGroupMessage(String groupId, String fromUserLogin, String fromUserId,
JsonArray members, String msg) {
String msgValue = Utils.time()+"-"+fromUserLogin+"-"+msg; // 要儲存的聊天記錄
JsonArray copy = members.copy();
copy.remove(fromUserId);
if (messageGroupMapCache.containsKey(groupId)) {
Object o = messageGroupMapCache.get(groupId);
JsonArray messageGroupArr = (JsonArray) o; // 群組聊天記錄
messageGroupArr.add(msgValue);
messageGroupMap.put(groupId, messageGroupArr.encode());
messageGroupMapCache.put(groupId, messageGroupArr);
} else {
JsonArray msgArr = new JsonArray();
msgArr.add(msgValue);
messageGroupMap.put(groupId, msgArr.encode());
messageGroupMapCache.put(groupId, msgArr);
}
copy.forEach(memberId -> {
System.out.println("群發訊息 " + Utils.time());
ServerWebSocket serverWebSocket = socketMap.get(memberId); // TODO 沒有處理不線上的情況,方便測試預設都線上
serverWebSocket.writeTextMessage(msg);
});
}
private void put(Map<String, ServerWebSocket> socketMap, String userId, ServerWebSocket serverWebSocket) {
try {
if (socketMap.containsKey(userId)) {
log.error(" ********* 連線存在,清除舊連線 ********* ");
ServerWebSocket serverWebSocket1 = socketMap.get(userId);
serverWebSocket1.close();
socketMap.remove(userId);
} else {
socketMap.put(userId, serverWebSocket);
}
} catch (Exception e) {
log.error("異常捕獲, " + e.getMessage());
socketMap.put(userId, serverWebSocket);
}
}
private Future<Boolean> initDB() {
Future<Boolean> initDBFuture = Future.future();
try {
// 儲存使用者私密聊天記錄
db = DBMaker.fileDB(filePath + "sweet-msg-im.db").closeOnJvmShutdown().make();
messageMap = db.hashMap("message-db", Serializer.STRING, Serializer.STRING).createOrOpen();
messageMapCache = vertx.sharedData().getLocalMap("message-db-cache");
copyJsonArray(messageMap, messageMapCache);
// 儲存群組聊天記錄
messageGroupMap = db.hashMap("message-group-db", Serializer.STRING, Serializer.STRING).createOrOpen();
messageGroupMapCache = vertx.sharedData().getLocalMap("message-group-db-cache");
copyJsonArray(messageGroupMap, messageGroupMapCache);
// 儲存註冊使用者資訊
userMapCache = vertx.sharedData().getLocalMap("user-db-cache");
// 儲存好友關係
friendMapCache = vertx.sharedData().getLocalMap("friend-db-cache");
// 當前登入使用者
onlineMapCache = vertx.sharedData().getLocalMap("online-db-cache");
// 使用者名稱 - 使用者id
userNameAndIdMapCache = vertx.sharedData().getLocalMap("username-id-db-cache");
// 群組關係
groupMapCache = vertx.sharedData().getLocalMap("group-db-cache");
socketMap = new ConcurrentHashMap<>();
initDBFuture.complete(true);
} catch (Exception e) {
e.printStackTrace();
initDBFuture.fail(e.getCause());
}
return initDBFuture;
}
@Override
public void stop(Future<Void> stopFuture) throws Exception {
messageMap.close();
messageGroupMap.close();
if (!db.isClosed()) {
db.commit();
db.close();
}
stopFuture.complete();
}
private void copyJsonArray(Map<String, String> sourceMap, LocalMap<String, Object> targetMap) {
sourceMap.forEach((k, v) -> targetMap.put(k, new JsonArray(v)));
}
}
複製程式碼
BootVerticle.java
package com.xiaoniu.im;
import com.xiaoniu.im.rest.HttpVerticle;
import com.xiaoniu.im.socket.SocketVerticle;
import com.xiaoniu.im.utils.Runner;
import io.vertx.core.*;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
/**
* Verticle 啟動類
* Created by sweet on 2017/9/26.
*/
public class BootVerticle extends AbstractVerticle{
private static final Logger log = LoggerFactory.getLogger(BootVerticle.class);
public static void main(String[] args) {
Runner.runExample(BootVerticle.class);
}
@Override
public void start(Future<Void> startFuture) throws Exception {
Future<String> future = Future.future();
Future<String> future1 = Future.future();
vertx.deployVerticle(new HttpVerticle(), future1);
future1.setHandler(res -> {
if (res.succeeded()) {
vertx.deployVerticle(new SocketVerticle(), future);
} else {
startFuture.fail(res.cause());
return;
}
});
future.setHandler(handler -> {
if (handler.succeeded()) {
startFuture.complete();
log.info("全部部署 OK");
} else {
startFuture.fail(handler.cause());
}
});
}
}
複製程式碼
使用MapDB儲存,需要修改程式碼裡的檔案儲存路徑,程式碼寫的沒那麼精緻,沒有使用配置檔案,原始碼裡有readme,寫了如何使用 原始碼地址 連結:http://pan.baidu.com/s/1o8ysUQQ 密碼:5crm