阅读本文约 "8 分钟"
适读人群: Java 初级
前言
InChat = Iot Netty Chat
首先, 感谢那些一直以来支持 https://github.com/AwakenCN/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 版本下的一些基本功能, 欢迎大家测试, 并在 这里 https://github.com/AwakenCN/InChat/issues 提出你们的看法或者问题.
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 https://github.com/AwakenCN/InChat , 项目中 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
现架构设计 (码农) 兼创业技术顾问, 不羁平庸, 热爱开源, 杂谈程序人生与不定期干货.
Previous
一个异步无限发送的 Netty 实例
来源: http://www.tuicool.com/articles/nYBN32f