本博客 猫叔的博客,转载请申明出处
阅读本文约“8 分钟”
适读人群:Java 初级
前言
InChat = Iot Netty Chat
首先,感谢那些一直以来支持 InChat 的朋友们,你们可能是因为工作原因,或者自己的想法,或者自己的项目等等。
InChat 还不是一个合格的框架,它还存在很多弊端与问题,但是感谢你们的关注,也是你们让它学会成长。
重新声明一次,InChat:一个轻量级、高效率的支持多端(应用与硬件 Iot)的可分布式、异步网络应用通讯框架。
InChat 从 1 月 1.1.3 版本后,就停止了更新,期间由于个人原因(我后续也不敢保证它的连贯性),当时在 8 月 22 号,InChat 发布 1.1.4 版本,且在 9 月份预计也会继续发布 1.1.5 版本(由于 1.1.4 发现了一些核心问题)
接下来,我将详细介绍 1.1.4 版本下的一些基本功能,欢迎大家测试,并在这里提出你们的看法或者问题。
InChat 版本
<dependency>
<groupId>com.github.UncleCatMySelf</groupId>
<artifactId>InChat</artifactId>
<version>1.1.4</version>
</dependency>
构建 SpringBoot 的 web 项目
由于在停更期间,很多朋友都问到 InChat 在 SpringBoot 等 web 框架下的使用问题及想法,所以这个 Demo 是完全在 SpringBoot 环境下搭建的。
下载地址:InChat-SpringBoot-Demo,项目中 demo-inchat-4.zip 文件
这里建议大家可以直接敲一次,看看有什么问题。
首先,我的项目是 SpringBoot-Web,数据库是 MySQL,没有使用 Redis
pom 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo-inchat-4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-inchat-4</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.github.UncleCatMySelf</groupId>
<artifactId>InChat</artifactId>
<version>1.1.4</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
关于 pom 文件,大家需要注意的是 InChat 版本其实是自带 log4j,因此可能会和其他的日志组件有冲突,需要移除,这个在 1.1.5 版本也将移除。
如果大家在使用 InChat 期间报:
Exception in thread "main" java.lang.IllegalArgumentException: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath.
可以按照以上移除对应的日志组件。
账户登录
基本的数据关系
- Message 聊天消息类
@Entity
@Data
@DynamicUpdate
public class Message {
/**id, 自增 */
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
/** 消息时间 */
private Date time;
/** 消息类型(发送给自己、发送给朋友、发送给群组)*/
private String type;
/** 消息值(聊天内容)*/
private String value;
/** 用户标识(登录 token)*/
private String token;
/** 群聊 Id */
private String groudId;
/** 是否在线 - 个人(朋友是否在线)*/
private String online;
/** 是否在线 - 群聊(离线朋友)*/
private String onlineGroup;
/** 消息接收人标识(接收朋友 Token)*/
private String one;
}
- User 用户登录(简单模拟)
public class User {
/**id*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
}
JPA
这里就简单用 JPA 处理,喜欢用 MyBatis 的朋友也可以试试,欢迎贡献 Demo
启动类
为什么突然说到启动类呢?
因为,InChat(1.1.3 之前都是在启动类,启动的),这可能是一个误解,在本次 Demo 中咱们的启动类是这样的。
@SpringBootApplication
@EnableScheduling
public class DemoInchat4Application {public static void main(String[] args) {SpringApplication.run(DemoInchat4Application.class, args);
}
}
是的,就这样,在我的这个业务里,并没有打算一开始就启动 InChat,当然这还是要 看业务而定
Controller 层
这里的 Controller 就是一个常规启动 http 接口,启动默认 InChat 服务。
@RestController
public class UserController {
@Autowired
private UserRepository repository;
@GetMapping("/init")
public String init(){ConfigFactory.initNetty = new MyInit();
ConfigFactory.fromServerService = FromServerServiceImpl.TYPE2;
ConfigFactory.listenAsynData = new UserListenAsynData();
ConfigFactory.inChatVerifyService = new VerifyServiceImpl(repository);
InitServer.open();
return "success";
}
}
大家会发现,我通过一个 http 启动 InChat,同时将一个 UserRepository 注入到 InChat 的校验类里面。
启动。
2019-08-23 17:10:52.399 INFO 20136 --- [BOSS_1] c.g.u.bootstrap.NettyBootstrapServer : 服务端启动成功【192.168.1.121:8070】
接下来介绍下 InChat 的几个配置类
InChat 配置类——InitNetty
继承 InitNetty
它是初始化 Netty 的基本配置,你可以根据你的需要的修改配置
public class MyInit extends InitNetty {
@Override
public int getWebport() {return 8070;}
// 分布式
@Override
public Boolean getDistributed() {return false;}
// 加密
@Override
public boolean isSsl() {return false;}
InChat 配置类——FromServerService
实现 FromServerService
这个与 InChat1.1.3 版本没有差别,是一个服务器发送的系统通知,可以通过 Http 发送任务
public enum FromServerServiceImpl implements FromServerService {TYPE1(1,"【系统通知】您的账号存在异常,请注意安全保密信息。"),
TYPE2(2,"【系统通知】恭喜您连续登录超过 5 天,奖励 5 积分。");
private Integer code;
private String message;
FromServerServiceImpl(Integer code, String message){
this.code = code;
this.message = message;
}
public Integer getCode() {return code;}
public String findByCode(Object code) {Integer codes = (Integer)code;
for (FromServerServiceImpl item: FromServerServiceImpl.values()) {if (item.code == codes){return item.message;}
}
return null;
}
public void setCode(Integer code) {this.code = code;}
public String getMessage() {return message;}
public void setMessage(String message) {this.message = message;}
}
InChat 配置类——ListenAsynData
继承 ListenAsynData
异步数据获取,就是聊天数据获取的类,在 1.1.4 中,你需要自己写一个载体(Map 或者 list)来存储聊天数据,我在这个 Demo 中使用 Map,其实可以用 list,需要注意,InChat 中提供了一个将 Map 转为 InChatMessage 的工具类 MessageChangeUtil,
我希望我的业务不是时刻存储数据,所以我将聊天数据存储到 Map 中,使用定时器,定时存储到数据库中。
public class UserListenAsynData extends ListenAsynData {
@Override
public void asynData(Map<String, Object> maps) {InChatMessage inChatMessage = MessageChangeUtil.Change(maps);
CacheMap.add(inChatMessage);
}
}
InChat 配置类——InChatVerifyService
继承 InChatVerifyService
你需要给这个类加一个静态变量,方便后续初始化后,做数据操作
这个类中的两个方法,一个是用户登录校验,一个是根据群聊 ID 获取群聊成员数组
这两个数据我都默认通过数据库处理,群聊 ID 我是直接模拟,大家可以在数据库中存储一个对应的表试试
public class VerifyServiceImpl implements InChatVerifyService {
private UserRepository repository;
public VerifyServiceImpl(UserRepository repository){this.repository = repository;}
public boolean verifyToken(String token) {User user = repository.findByUsername(token);
if (user.getId() != null){return true;}
return false;
}
public JSONArray getArrayByGroupId(String groupId) {JSONArray jsonArray = JSONArray.parseArray("[\"1111\",\"2222\",\"3333\"]");
return jsonArray;
}
}
定时任务
我使用定时任务,定时存储聊天数据,这里需要注意,一定要清空存储过的内容
@Component
public class SchedulerTask {
@Autowired
private MessageRepository repository;
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
@Scheduled(fixedRate = 30000)
public void reportCurrentTime() {System.out.println("现在时间:" + dateFormat.format(new Date()));
Map<Integer,InChatMessage> Chatcache = CacheMap.copy();
InChatMessage inChatMessage = Chatcache.get(1);
Message message = new Message();
message.setOne(inChatMessage.getOne());
message.setGroudId(inChatMessage.getGroudId());
message.setOnline(inChatMessage.getOnline());
message.setOnlineGroup(inChatMessage.getOnlineGroup());
message.setToken(inChatMessage.getToken());
message.setType(inChatMessage.getType());
message.setValue(inChatMessage.getValue());
message.setTime(inChatMessage.getTime());
repository.save(message);
}
}
我这里就简单意识一下
功能测试
单机模式下,发送给自己、发送给别人、发送给群组(单机,不加密)
正常运行,由于数据存储,我定时器只存储一条,大家记得修改下
单机,ssl 加密,发送给自己、发送给别人、发送给群组(单机、加密)
关于加密,大家可以构建自己的加密文件,或者使用 inchat.jks
大家可以参考 InChatV1.1.3 版本使用说明
生成自己的 jks 加密文件,请在 InitNetty 类 的继承类中做对应的修改。
public abstract class InitNetty {
//...
/** 是否启动分布式 */
private Boolean isDistributed = false;
/** 是否启动加密 */
private boolean ssl = false;
private String jksFile = "inchat.jks";
private String jksStorePassword = "123456";
private String jksCertificatePassword = "123456";
//....
}
分布式,发送数据
本 Demo 暂不测试,大家有兴趣可是在 InChatV1.1.3 版本使用说明中学习了解。
因为分布式使用后,两个 Demo 项目都会存在数据存储,这个不在 InChat 的设计范围,所以目前推荐大家先使用单机版本
后续的分布式,会有一个数据存储的中间云组件,集中处理聊天的数据存储问题等
InChat 将继续发展。
公众号:Java 猫说
学习交流群:728698035
现架构设计(码农)兼创业技术顾问,不羁平庸,热爱开源,杂谈程序人生与不定期干货。