通用接口开放平台设计与实现——(8)消息服务之消息格式的设计与实现

消息格式的选择

消息格式可以有多种实现方式,包括一些功能组件,如ProtoBuff、Thrift,可以实现对消息的高度压缩,提供更好的性能。

但设计不能单纯从技术角度出发,而是需要结合需求和背景。我们是一个企业应用而不是互联网应用,用户和并发相对而言不高,引入ProtoBuff、Thrift,反而会增加学习成本和复杂性,特别是对于对接方系统并不是那么友好,相关的开发人员需要系统地了解ProtoBuff该如何使用,有哪些注意事项。从我们要解决的问题出发考虑,json反而轻巧易用,是更好的选择。

消息的分类

我们为获取较大的吞吐量、较高的性能,采用异步机制。但对于企业应用而言,需要保证消息的可靠传递,因此采取请求响应模式,即将消息划分为两种类型,请求消息与响应消息。对于每条请求消息,均有一条响应消息与之对应,当发起消息请求方(可能是客户端也可能是服务端)未及时收到响应时,需要重发该请求;消息响应方则需要保持幂等性,通过消息唯一性标识进行去重。

例如,
客户端发起登录请求,服务端需要返回响应(登录成功或失败)
服务端向客户端发送一条业务事件消息,客户端需要返回一条确认该消息收到的响应。

消息类的设计

根据客户端和服务端消息交互设计,消息分为两类,一是请求消息,二是响应消息,并且这两个类可以抽取公共部分,形成父类。

很明显,这个三个类是公共基础,会作为传输的主体,消息客户端和服务端都要使用,因此放在公共工程message-common中

公用父类

/**
 * 消息基类
 * @author  wqliu
 * @date  2021-10-5 10:04
*/
@Data
public class BaseMessage
{

	/**
	 * 标识
	 */
	private String id;

	/**
	 * 主题
	 */
	private String topic;

	/**
	 * 内容
	 */
	private String content;

	/**
	 * 发布者
	 */
	private String publishAppCode;

	/**
	 * 发布时间
	 */
	private String publishTime;

	/**
	 * 消息类型
	 */
	private String messageType;



	public BaseMessage(){
		//采用雪片算法生成唯一性标识
		this.setId(IdWorker.getIdStr());
		//设置发布时间
		String publishTime =DateFormatUtils.format(new Date(),"yyyy-MM-dd HH:mm:ss");		
		this.setPublishTime(publishTime);

	}

}

这里有两个点进行了优化处理:
1.消息标识,之前用过UUID,不过UUID太长了,一方面影响效率和性能,在未达到海量数据的情况下,还好,另一方面主要是系统调试、联调、排查阶段,查看系统日志的时候,去找消息标识,很费眼神并且容易找错,因此采用了雪花算法,生成一串数字,既保证了唯一性,也很容易识别,比如1485521994310647809,直接找后四位7809就行了
2.发布时间,这里采用了字符串而不是时间类型,主要是根据个人经验进行的简化处理,因为日期格式……不同的开发语言中采用的格式差异比较大,在java/c#/xml/json中有多种方式,统一约定用yyyy-MM-dd HH:mm:ss格式的字符串,反而能准确表达。

请求消息

/**
 * 请求消息
 * @author  wqliu
 * @date  2021-10-5 10:30
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class RequestMessage extends BaseMessage implements Cloneable
{


	public RequestMessage(){
    
        //默认设置消息类型
		super.setMessageType(MessageType.REQUEST.name());


	}


	@Override
	public RequestMessage clone(){
		try{
			return (RequestMessage)super.clone();
		}catch (CloneNotSupportedException e){
			log.error("对象不支持复制");
			return null;
		}

	}

}

请求消息继承于消息基类,此外,实现了Cloneable接口,覆写了clone方法,当消息中心需要将一份消息拷贝多份,发送给多个消费者的时候使用,是原型设计模式的应用。

​在之前的设计中,还附加状态、发送次数、响应方编码这三个特有属性,实际是用来处理消息重发的,进行了重构优化,将重发职责完全剥离出去,请求消息更加简洁。

响应消息

/**
 * 响应消息
 * @author  wqliu
 * @date  2021-10-5 10:30
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class ResponseMessage extends BaseMessage
{

	/**
	 * 执行结果
	 */
	private String result;

	/**
	 * 错误编码
	 */
	private String errorCode;

	/**
	 * 错误信息
	 */
	private String errorMessage;

	/**
	 * 请求消息id
	 */
	private String requestMessageId;


	public ResponseMessage(){
		//默认设置消息类型
		super.setMessageType(MessageType.RESPONSE.name());
	}

}

响应消息则是附加了对请求的处理结果情况,包括是否成功、错误编码、错误信息和请求消息的标识。

需要注意的是,涉及到枚举类型的地方,全部使用字符串来处理,对于对接多系统多技术栈的情况,使用枚举类型,无论是初始化开发还是后期扩展,都会带来额外的麻烦。

你可能感兴趣的:(接口开放平台设计与实现,接口平台,netty,消息,实战,开放平台)