基于Mina的配置中心(二)
首先来看看配置中心数据库的设计。
表结构:
CREATE TABLE `message` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '消息体id',
`project_name` varchar(200) DEFAULT NULL COMMENT '项目名称',
`env_value` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'local' COMMENT '环境,dev日常,gray灰度,online线上,local本地',
`property_value` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'preperties 中的value',
`config_value` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '配置中心配置的需要注入的值',
`remote_address` varchar(100) DEFAULT NULL COMMENT '客户端 session key',
`creator` bigint(20) DEFAULT NULL COMMENT '创建人',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`modifier` bigint(20) DEFAULT NULL COMMENT '修改人',
`gmt_modify` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`config_version` int(11) NOT NULL DEFAULT '1' COMMENT '乐观锁,版本',
`is_deleted` int(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除,0未删除,1已删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='消息体';
project_name
:项目名称,不同的项目可能有名字相同的配置,所以用来区分。env_value
:当前环境,我觉得既然是配置中心,就应该接管所有配置,不需要用户再写三个application.properties
文件来区分。这个极大的简化了多环境项目。property_value
:这个东西其实就是application.properties
里面配置的值,举个栗子🌰,比如你在application.properties
写了一个这个配置mina.config.name=data1
,那么data1
就是property_value
。config_value
:就是你真正要配置的值,就是用来替换data1
的值。remote_address
:这个是客户端的地址,主要是当服务端更改了配置项,来主动向客户端推送的。是的,这个配置中心既有推的模式,也有拉的模式。
然后用我们非常熟悉的Mybatis-Plus
代码生成器,生成一下代码。
生成后的效果:
首先要把 Message
这个类移动到Base
模块中,因为客户端发送消息,也需要使用这个类。
最后效果:
MinaServerProperty
因为使用了SpringBoot
,接下来我们使用配置类的方式来整合Mina
。
因为有很多配置属性,我不想用户全都写到application.properties
中,而且如果用户在application.properties
中写配置属性的时候,能出现提示就更好了。
所以首先要创建一个类:MinaServerProperty
package com.lww.mina.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author lww
* @date 2020-07-05 16:13
*/
@ConfigurationProperties(prefix = "mina.server")
public class MinaServerProperty {
/**
* 服务器监听端口,默认 9123
*/
private Integer port = 9123;
/**
* 服务器ip地址,默认 127.0.0.1
*/
private String address = "127.0.0.1";
/**
* 缓冲区大小,默认2048
*/
private Integer readBufferSize = 2048;
/**
* 空闲时间,单位秒 默认 5 秒没操作就进入空闲状态
*/
private Integer idelTimeOut = 5;
/**
* 初始化线程池大小,默认10
*/
private Integer corePoolSize = 10;
/**
* 最大线程数,默认20
*/
private Integer maximumPoolSize = 20;
/**
* 初始化用户名
*/
private String username;
/**
* 初始化密码
*/
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getReadBufferSize() {
return readBufferSize;
}
public void setReadBufferSize(Integer readBufferSize) {
this.readBufferSize = readBufferSize;
}
public Integer getIdelTimeOut() {
return idelTimeOut;
}
public void setIdelTimeOut(Integer idelTimeOut) {
this.idelTimeOut = idelTimeOut;
}
public Integer getCorePoolSize() {
return corePoolSize;
}
public void setCorePoolSize(Integer corePoolSize) {
this.corePoolSize = corePoolSize;
}
public Integer getMaximumPoolSize() {
return maximumPoolSize;
}
public void setMaximumPoolSize(Integer maximumPoolSize) {
this.maximumPoolSize = maximumPoolSize;
}
}
@ConfigurationProperties
这个注解配合@EnableConfigurationProperties
这个一起使用,可以在项目里生成如下的文件:
/META-INF/spring-configuration-metadata.json
注意:要使用Maven的编译命令,或者打包命令才会在
target
或者jar
包中创建/META-INF/spring-configuration-metadata.json
这个文件,还有这里不要用lombok
,老老实实的写getter
和setter
,不然是没有提示的。
{
"groups": [
{
"name": "mina.server",
"type": "com.lww.mina.config.MinaServerProperty",
"sourceType": "com.lww.mina.config.MinaServerProperty"
}
],
"properties": [
{
"name": "mina.server.address",
"type": "java.lang.String",
"description": "服务器ip地址,默认 127.0.0.1",
"sourceType": "com.lww.mina.config.MinaServerProperty",
"defaultValue": "127.0.0.1"
},
{
"name": "mina.server.core-pool-size",
"type": "java.lang.Integer",
"description": "初始化线程池大小,默认10",
"sourceType": "com.lww.mina.config.MinaServerProperty",
"defaultValue": 10
},
{
"name": "mina.server.idel-time-out",
"type": "java.lang.Integer",
"description": "空闲时间,单位秒 默认 5 秒没操作就进入空闲状态",
"sourceType": "com.lww.mina.config.MinaServerProperty",
"defaultValue": 5
},
{
"name": "mina.server.maximum-pool-size",
"type": "java.lang.Integer",
"description": "最大线程数,默认20",
"sourceType": "com.lww.mina.config.MinaServerProperty",
"defaultValue": 20
},
{
"name": "mina.server.password",
"type": "java.lang.String",
"description": "初始化密码",
"sourceType": "com.lww.mina.config.MinaServerProperty"
},
{
"name": "mina.server.port",
"type": "java.lang.Integer",
"description": "服务器监听端口,默认 9123",
"sourceType": "com.lww.mina.config.MinaServerProperty",
"defaultValue": 9123
},
{
"name": "mina.server.read-buffer-size",
"type": "java.lang.Integer",
"description": "缓冲区大小,默认2048",
"sourceType": "com.lww.mina.config.MinaServerProperty",
"defaultValue": 2048
},
{
"name": "mina.server.username",
"type": "java.lang.String",
"description": "初始化用户名",
"sourceType": "com.lww.mina.config.MinaServerProperty"
}
],
"hints": []
}
当用户在application.properties
里配置属性时,会有提示:
application.properties
里名称是mina.server
前缀加上属性的名称,如果是驼峰命名的,则用-
分隔开,如mina.server.read-buffer-size
。- 属性的默认值就是,我们声明时创建的值。
- 描述就是
javadoc
,在application.properties
里的提示就是这些东西。
MinaServerConfig
package com.lww.mina.config;
import javax.annotation.Resource;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.logging.LoggingFilter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author lww
* @date 2020-07-05 16:13
*/
@Configuration
@EnableConfigurationProperties(MinaServerProperty.class)
public class MinaServerConfig {
@Resource
private MinaServerProperty config;
/**
* 配置mina的多线程过滤器
*/
@Bean
public ExecutorFilter executorFilter() {
//设置初始化线程数,最大线程数
return new ExecutorFilter(config.getCorePoolSize(), config.getMaximumPoolSize());
}
/**
* 配置mina的日志过滤器
*/
@Bean
public LoggingFilter loggingFilter() {
return new LoggingFilter();
}
}
当然配置类不会这么简单,我们现在能配的东西不多。现在有很多东西没有写,等写好之后再把配置补上。
自定义协议
在客户端与服务端的通讯中,在底层其实是二进制数据,Mina提供了TextLineCodecFactory
,根据换行符的编码器,虽然一般场景够用,但是还是不满足我们的需求,为了更好的解决半包和粘包,我们需要自定义协议。
- 半包:客户端发送的数据,在服务器端读取到的其实是二进制数据,服务器不知道读取多少是完整的。所以读取到的可能是不完整的数据包。
- 粘包:同上,客户端一次发送了好几个数据包,服务器一次读取了两个或多个包的数据。
解决半包粘包的方式有很多种,我们采用添加消息头的方式,消息头中包含消息的长度,还有类型。
package com.lww.mina.protocol;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
/**
* @author lww
* @date 2020-07-05 17:47
*/
@Data
public class MessagePack {
/**
* 数据总长度
*/
private int len;
/**
* 模块代码
*/
private int module;
/**
* 包体 Message json格式
*/
private String body;
/**
* 包头长度
*/
public static final int PACK_HEAD_LEN = 8;
/**
* 最大长度
*/
public static final int MAX_LEN = 9999;
public MessagePack(int module, String body) {
this.module = module;
this.body = body;
// 总长度
this.len = PACK_HEAD_LEN + (StringUtils.isBlank(body) ? 0 : body.getBytes().length);
}
}
一个 int
类型占4个字节,所以长度加上模块就占8个byte
,包头长度就是8。而Message
则转为Json存储在body
中。
总结
第二章先写到这里,我们自定义了消息协议和Mina的配置类,用户在application.properties
中配置时,会有提示。
第三章会创建自定义的编码解码器,还有心跳检测,在第三章我们基本可以完成Mina的配置类,敬请期待。
对啦,欢迎大家关注我的公众号,共同学习,一起进步。加油🤣
本文使用 mdnice 排版