上一篇博客中我们讲述了, 消息队列的相关知识, 这一篇博客,我们主要讲解消息队列中的实体类的实现
因为之前已经有博客写过SpringBoot的创建了, 这里就不过多叙述了
1: 先创建一个SpringBoot项目 ,
2: 依赖至少选择 SpringWeb 与MyBatis俩个依赖 (我里面使用了Lombok , 没有的也没关系)
3: 创建相关的交换机, 队列 , 绑定, 消息, 等实体类
package com.example.demo.mqServer.core;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
// 代表交换机的类
public class Exchange {
// 交换机名字, 唯一标识
private String name;
// 交换机的类型
private ExchangeType type = ExchangeType.direct;
//判断是否要持久化
private boolean durable = false;
//判断是否自动删除
private boolean autoDelete =false;
//交换机指定的额外参数
// 为了把argument存到数据库中, 要使用JSON转成字符串
private Map<String,Object> arguments = new HashMap<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ExchangeType getType() {
return type;
}
public void setType(ExchangeType type) {
this.type = type;
}
public boolean isDurable() {
return durable;
}
public void setDurable(boolean durable) {
this.durable = durable;
}
public boolean isAutoDelete() {
return autoDelete;
}
public void setAutoDelete(boolean autoDelete) {
this.autoDelete = autoDelete;
}
public String getArguments() {
// 将当前的getArguments从map, 转换成String
// SpringBoot 集成了JSON
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.writeValueAsString(arguments);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return "{}";
}
public void setArguments(String argumentsJson) {
ObjectMapper objectMapper = new ObjectMapper();
try {
this.arguments = objectMapper.readValue(argumentsJson, new TypeReference<HashMap<String, Object>>() {});
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
package com.example.demo.mqServer.core;
public enum ExchangeType {
direct(0),
fanout(1),
topic(2);
private final int type;
ExchangeType(int type) {
this.type = type;
}
}
package com.example.demo.mqServer.core;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
// 代表队列
public class MSGQueue {
// 队列名字, 唯一标识
private String name;
//判断是否要持久化
private boolean durable = false;
// 判断该队列是否被独占
private boolean exclusive = false;
//判断是否自动删除
private boolean autoDelete =false;
//队列指定的额外参数
private Map<String,Object> arguments = new HashMap<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isDurable() {
return durable;
}
public void setDurable(boolean durable) {
this.durable = durable;
}
public boolean isExclusive() {
return exclusive;
}
public void setExclusive(boolean exclusive) {
this.exclusive = exclusive;
}
public boolean isAutoDelete() {
return autoDelete;
}
public void setAutoDelete(boolean autoDelete) {
this.autoDelete = autoDelete;
}
public String getArguments() {
// 将当前的getArguments从map, 转换成String
// SpringBoot 集成了JSON
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.writeValueAsString(arguments);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return "{}";
}
public void setArguments(String argumentsJson) {
ObjectMapper objectMapper = new ObjectMapper();
try {
this.arguments = objectMapper.readValue(argumentsJson, new TypeReference<HashMap<String, Object>>() {});
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
package com.example.demo.mqServer.core;
import lombok.Data;
// 绑定
@Data
public class Binding {
// 交换机名称
private String exchangeName;
// 队列名称
private String msgQueueName;
// 交换机与队列绑定关键字
private String bindingKey;
}
package com.example.demo.mqServer.core;
import lombok.Data;
import java.io.Serializable;
import java.util.UUID;
// 使用标准库中的序列化和反序列化
// 消息
@Data
public class Message implements Serializable {
//消息的属性 - 防止拿到空
private BasicProperties basicProperties = new BasicProperties();
// 消息的正文
private byte[] body;
//偏移量 , beg,表示消息开头到文件开头有多少个字节 , End表示消息结尾到文件开头有多少个字节
// 前闭后开区间
// 这俩个属性不需要序列化, 因为不往硬盘中存储
private transient long offsetBeg;
private transient long offsetEnd;
// 用字节, 来表示是否逻辑删除 0x1 表示有效, 0x0表示无效
private byte isValid = 0x1;
// 创建一个工厂方法, 让工厂方法帮我们封装一下创建 Message 对象的过程.
// 这个方法中创建的 Message 对象, 会自动生成唯一的 MessageId
// 万一 routingKey 和 basicProperties 里的 routingKey 冲突, 以外面的为主.
public static Message createMessageWithId(String RoutingKey ,BasicProperties basicProperties,byte[] body){
Message message = new Message();
if (basicProperties != null) {
message.setBasicProperties(basicProperties);
}
// 此处生成的 MessageId 以 M- 作为前缀.
message.basicProperties.setMessageId("M-"+UUID.randomUUID().toString());
message.basicProperties.setRoutingKey(RoutingKey);
message.body = body;
// 此处是把 body 和 basicProperties 先设置出来. 他俩是 Message 的核心内容.
// 而 offsetBeg, offsetEnd, isValid, 则是消息持久化的时候才会用到. 在把消息写入文件之前再进行设定.
// 此处只是在内存中创建一个 Message 对象.
return message;
}
public void setMessageId(String messageId){
basicProperties.setMessageId(messageId);
}
public String getMessageId(){
return basicProperties.getMessageId();
}
public void setRoutingKey(String routingKey){
basicProperties.setRoutingKey(routingKey);
}
public String getRoutingKey(){
return basicProperties.getRoutingKey();
}
public void setDeliverMode(int deliverMode){
basicProperties.setDeliverMode(deliverMode);
}
public int getDeliverMode(){
return basicProperties.getDeliverMode();
}
}
package com.example.demo.mqServer.core;
import lombok.Data;
import java.io.Serializable;
@Data
public class BasicProperties implements Serializable {
// 唯一身份表示, 使用UUID
private String messageId;
// 与BindingKey 对暗号的
// 如果是 direct 则是转发的队列名
// 如果是 Topic 则是对应的Sting
// 如果是 fanout 则无意义
private String routingKey;
// 判断是否要持久化, 1代表持久化
private int deliverMode =1;
}
在创建完实体类之后, 需要配置数据库的链接信息, 这里数据库使用的是SQLite , 使用SQLite 而不使用MySQL的原因是, SQLite 更轻量, 因为我们要在数据库中存放一些相关的交换机,绑定, 队列等概念, MySQL我们都知道他是 客户端-服务器的模式, 拿取数据太慢了, 我们消息队列要求速度, 所以使用SQLite
在Java中想要使用SQLite十分简单, 引入SQLlite的依赖即可, 这里给大家放下依赖
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.41.0.1</version>
</dependency>
spring:
datasource:
url: jdbc:sqlite:./data/meta.db
username:
password:
driver-class-name: org.sqlite.JDBC
mybatis:
mapper-locations: classpath:mapper/**Mapper.xml
在resource中创建mapper包, 里面存放对应的注解 (也可以使用MyBatis新版的方式)
我们发现之前使用MySQL的时候, 都是直接写好创建表然后才启动服务器, 在整个项目中只部署一次, 但是我们希望用代码来创建表
package com.example.demo.mqServer.mapper;
public interface MateMapper {
void createExchangeTable();
void createQueueTable();
void createBindingTable();
}
xml还是不难写的, 只需要与实体类的属性对应上即可,
但是有一个问题被我们忽略了 , 那就是在 交换机和队列中 我们使用 一个属性 是用 哈希表来存储的, 而数据库中是没有哈希表的数据类型的, 我们还要将哈希表转换为, 数据库中的数据类型 , 怎么转换?
答: 我们先补充一个前置知识, 就是 MyBatis 在完成数据库操作的时候,会自动调用对象的get和set方法 , 我们利用这个规则, 将 哈希表 与 数据库数据类型 实现对应
我们使用JSON 来完成 哈希表 与 字符串 之间的相互转换, 在SpringBoot中内置了JSON , 所以我们直接创建 ObjectMapper objectMapper = new ObjectMapper(); 使用对应的方法就行了
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mqServer.mapper.MateMapper">
<update id="createExchangeTable">
create table if no exists exchange(
name varchar(50) primary key,
type int,
durable boolean,
autoDelete boolean,
arguments varchar(1024)
);
</update>
<update id="createQueueTable">
create table if no exists queue(
name varchar(50) primary key,
durable boolean,
exclusive boolean,
autoDelete boolean,
arguments varchar(1024)
);
</update>
<update id="createBindingTable">
create table if no exists binding(
exchangeName varchar(50),
msgQueueName varchar(50),
bindingKey varchar(256),
);
</update>
</mapper>