Bootstrap

MQTT&Dapper保证微服务数据一致性

MQTT&Dapper保证微服务数据一致性

1.MQTT简介

MQTT(消息队列遥测传输)是ISO标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议,为此,它需要一个消息中间件。
MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。通过MQTT协议,目前已经扩展出了数十个MQTT服务器端程序,可以通过PHP,JAVA,Python,C,C#等系统语言来向MQTT发送相关消息。
如果需要下载MQTT服务器端,可以直接去MQTT官方网站点击software进行下载MQTT协议衍生出来的各个不同版本。MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。

2.MQTT主要特性

MQTT协议工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议,它具有以下主要的几项特性:
(1)使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。
(2)对负载内容屏蔽的消息传输。
(3)使用TCP/IP提供网络连接。主流的MQTT是基于TCP连接进行数据推送的,但是同样有基于UDP的版本,叫做MQTT-SN。这两种版本由于基于不同的连接方式,优缺点自然也就各有不同了。
(4)有三种消息发布服务质量:
“至多一次”(Q0S=“0”),消息发布完全依赖底层TCP/IP网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。这一种方式主要普通APP的推送,倘若你的智能设备在消息推送时未联网,推送过去没收到,再次联网也就收不到了。
“至少一次”(Q0S=“1”),确保消息到达,但消息重复可能会发生。
“只有一次”(Q0S=“2”),确保消息到达一次。在一些要求比较严格的计费系统中,可以使用此级别。在计费系统中,消息重复或丢失会导致不正确的结果。这种最高质量的消息发布服务还可以用于即时通讯类的APP的推送,确保用户收到且只会收到一次。(我们应该是采用“只有一次”模式)

3.MQTT工作原理

3.1 MQTT协议实现方式

实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:
(1)Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);
(2)payload,可以理解为消息的内容,是指订阅者具体要使用的内容。

3.2 网络传输与应用消息

MQTT会构建底层网络传输:它将建立客户端到服务器的连接,提供两者之间的一个有序的、无损的、基于字节流的双向传输。
当应用数据通过MQTT网络发送时,MQTT会把与之相关的服务质量(QoS)和主题名(Topic)相关连。

3.3 MQTT客户端

一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。客户端可以:
(1)发布其他客户端可能会订阅的信息;
(2)订阅其它客户端发布的消息;
(3)退订或删除应用程序的消息;
(4)断开与服务器连接。

3.4 MQTT服务器

MQTT服务器以称为"消息代理"(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间,它可以:
(1)接受来自客户的网络连接;
(2)接受客户发布的应用信息;
(3)处理来自客户端的订阅和退订请求;
(4)向订阅的客户转发应用程序消息。

3.5 MQTT协议中的订阅、主题、会话

3.5.1订阅(Subscription)

订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每一个会话中的每个订阅都有一个不同的主题筛选器。

3.5.2会话(Session)

每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。

3.5.3主题名(Topic Name)

连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。

3.5.4主题筛选器(Topic Filter)

一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。

3.5.5负载(Payload)

消息订阅者所具体接收的内容。

3.6 MQTT协议中的方法

(1)Connect。等待与服务器建立连接。
(2)Disconnect。等待MQTT客户端完成所做的工作,并与服务器断开TCP/IP会话。
(3)Subscribe。等待完成订阅。
(4)UnSubscribe。等待服务器取消客户端的一个或多个topics订阅。
(5)Publish。MQTT客户端发送消息请求,发送完成后返回应用程序线程。

4.Dapper简介

数据库池化技术(数据库连接池技术),微型ORM(对象映射模型),可生成动态、静态物化视图(DTO、数据传输对象),如果在小的项目中,使用Entity Framework、NHibernate 来处理大数据访问及关系映射,未免有点杀鸡用牛刀。

5.任务目标

在实现微服务的过程中,划分数据库至各个微服务,从而保证微服务对其下数据的自治性十分重要。这是划分微服务的一个目的,使每个服务可维护性增强,因为数据库表结构的修改对服务的影响范围是可控的,即其所属的微服务;但数据库中不乏有一些表需要在其余的微服务中被查询和使用,为了保证微服务数据的最终一致性问题,我们将使用消息队列实现跨微服务的数据一致性问题。这里做一个Demo演示MQTT&Dapper保证两个数据库内数据一致。

6.具体步骤

6.1 搭建Dapper框架及实体类

6.1.1 实体类及帮助类

定义person实体类

namespace MQTT_Dapper
{
   
   public class person
   {
   
       public string id {
    get; set; }
       public string name {
    get; set;  }
   }
}

定义jsonhelper帮助类(帮助解析json为对象或List对象)

   public static class JsonHelper
    {
   
        public static object ToJson(this string Json)
        {
   
            return JsonConvert.DeserializeObject(Json);
        }
        public static string ToJson(this object obj)
        {
   
            return JsonConvert.SerializeObject(obj);
        }
        public static string ToJson(this object obj, string datetimeformats)
        {
   
            var timeConverter = new IsoDateTimeConverter {
    DateTimeFormat = datetimeformats };
            return JsonConvert.SerializeObject(obj, timeConverter);
        }
        public static T ToJson<T>(this string Json)
        {
   
            return JsonConvert.DeserializeObject<T>(Json);
        }
        public static DataTable JsonToDataTable(this string strJson)
        {
   
            #region
            DataTable tb = null;
            //获取数据  
            Regex rg = new Regex(@"(?<={)[^}]+(?=})");
            MatchCollection mc = rg.Matches(strJson);
            for (int i = 0; i < mc.Count; i++)
            {
   
                string strRow = mc[i].Value;
                string[] strRows = strRow.Split(',');
                //创建表  
                if (tb == null)
                {
   
                    tb = new DataTable();
                    tb.TableName = "Table";
                    foreach (string str in strRows)
                    {
   
                        DataColumn dc = new DataColumn();
                        string[] strCell = str.Split(':');
                        dc.DataType = typeof(String);
                        dc.ColumnName = strCell[0].ToString().Replace("\"", "").Trim();
                        tb.Columns.Add(dc);
                    }
                    tb.AcceptChanges();
          
;