本博客猫叔的博客,转载请申明出处
阅读本文约 “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版本下的一些基本功能,欢迎大家测试,并在 这里 提出你们的看法或者问题。
<dependency> <groupId>com.github.UncleCatMySelf</groupId> <artifactId>InChat</artifactId> <version>1.1.4</version> </dependency>
由于在停更期间,很多朋友都问到InChat在SpringBoot等web框架下的使用问题及想法,所以这个Demo是完全在SpringBoot环境下搭建的。
下载地址: InChat-SpringBoot-Demo ,项目中demo-inchat-4.zip文件
这里建议大家可以直接敲一次,看看有什么问题。
首先,我的项目是SpringBoot-Web,数据库是MySQL,没有使用Redis
<?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.
可以按照以上移除对应的日志组件。
@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; }
public class User { /**id*/ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String username; private String password; }
这里就简单用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就是一个常规启动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的几个配置类
它是初始化Netty的基本配置,你可以根据你的需要的修改配置
public class MyInit extends InitNetty { @Override public int getWebport() { return 8070; } //分布式 @Override public Boolean getDistributed() { return false; } //加密 @Override public boolean isSsl() { return false; }
这个与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; } }
异步数据获取,就是聊天数据获取的类,在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); } }
你需要给这个类加一个静态变量,方便后续初始化后,做数据操作
这个类中的两个方法,一个是用户登录校验,一个是根据群聊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); } }
我这里就简单意识一下
正常运行,由于数据存储,我定时器只存储一条,大家记得修改下
关于加密,大家可以构建自己的加密文件,或者使用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将继续发展。
现架构设计(码农)兼创业技术顾问,不羁平庸,热爱开源,杂谈程序人生与不定期干货。
Previous
一个异步无限发送的Netty实例