Data Center

源码地址(未完善):https://github.com/jsen-joker/DC

场景分析:

现有多个业务系统,由于前期的规划和对业务的把握不足,导致出现多个类似的系统,现在需要在各个业务系统之间共享数据,由于前期业务复杂,耦合度高,很难一步到位,将系统微服务化,因此需要一个类似数据中心的系统来在各个业务系统(client)之间同步数据。

这里要完成的是对于client的CUD操作,都要同步到服务器上,存在的技术难点包括,id重复,外键关联在同步后失效,对相同记录(如同一个用户)的鉴定,对特殊字段的处理(如,同一个账户的余额需要相加,不是简单的忽略或者替换等),由此可以看到这是一个复杂的同步工程,并非最优方案,但相于对服务进行拆分而言,较为简单快捷。

方案:

这里构想,数据的同步完全由客户端发起,客户端需要配置需要同步的数据库,字段,同步规则等,在spring、client启动加载后,自动注册到zookeeper上,dc发现zookeeper状态变更后,检查zookeeper数据,在本地创建对应的数据表、字段等,同时客户端也需要自动生成一些数据中心的辅助字段如dc_id, dc_app_name等字段,完成注册后,客户端可以通过dubbo进行对数据中心的CUD同步,具体执行时,client将插入、更新、删除这些操作转换成特定批量的数据格式,在本地对column字段进行解析,这里包括将column替换为远程dc中对应的column名字,替换外键为对应的dc_id,完成这些后发到dc中心进行数据库修改,修改完成根据结果进行本地对应的处理CUD和对应记录辅助字段的修改。

网络问题:为保证数据一致性,必须考虑网络问题,这里大部分错误可以通过单机的事务解决,唯一的问题是当dc中心操作成功后,返回结果给client时,网络出现错误,这时dc已经完成操作,client认为操作失败,这时客户端会重试,这里考虑CUD三种情况:1、插入,从多个client插入数据到数据中心本来就会出现冲突(如应该表示为同一个账号),所以这里在column配置的时候,会指定对应的部分column为键,插入的时候回校验,如果根据键找到记录则执行的是更新而不是插入,因此插入式其实是不用管网络错误的;2、更新,由于同一条记录的多次更新不会产生任何影响,因此也可以忽略;3、删除,删除和更新操作全部使用的是dc_id,因此,第二次删除的时候只会删除0个记录,并不会对其他数据产生影响,因此删除的情况也不用考虑。

部分代码:

客户端配置文件和schema:

dc.xsd

<?xml version="1.0" encoding="utf-8"?>
<xsd:schema
        xmlns="https://raw.githubusercontent.com/jsen-joker/data-center/master"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:beans="http://www.springframework.org/schema/beans"
        targetNamespace="https://raw.githubusercontent.com/jsen-joker/data-center/master"
        elementFormDefault="qualified"
        attributeFormDefault="unqualified" >
    <xsd:import namespace="http://www.springframework.org/schema/beans" />

    <xsd:element name="app">
        <xsd:complexType>
            <xsd:attribute name="name" type="xsd:string">
                <xsd:annotation>
                    <xsd:documentation><![CDATA[ App name. ]]></xsd:documentation>
                </xsd:annotation>
            </xsd:attribute>
            <xsd:attribute name="type" type="xsd:string">
                <xsd:annotation>
                    <xsd:documentation><![CDATA[ App type eg. client,server. not used yet. ]]></xsd:documentation>
                </xsd:annotation>
            </xsd:attribute>
        </xsd:complexType>
    </xsd:element>
    <xsd:element name="table">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ Define a table mapper. ]]></xsd:documentation>
        </xsd:annotation>
        <xsd:complexType>
            <xsd:sequence maxOccurs="unbounded">
                <xsd:element name="column">
                    <xsd:annotation>
                        <xsd:documentation><![CDATA[ Define a column mapper. ]]></xsd:documentation>
                    </xsd:annotation>
                    <xsd:complexType>
                        <xsd:attribute name="local" type="xsd:string" use="required">
                            <xsd:annotation>
                                <xsd:documentation><![CDATA[ The column name in client table. ]]></xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                        <xsd:attribute name="remote" type="xsd:string" use="required">
                            <xsd:annotation>
                                <xsd:documentation><![CDATA[ The column name in datacenter table. ]]></xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                        <xsd:attribute name="key" type="xsd:boolean" use="optional" default="false">
                            <xsd:annotation>
                                <xsd:documentation><![CDATA[ If this column use as a business key. ]]></xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                        <xsd:attribute name="groupType" type="xsd:string" use="required">
                            <xsd:annotation>
                                <xsd:documentation><![CDATA[ When record be inserted, the replace rule, eg. replace, ignore, sum. ]]></xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                        <xsd:attribute name="group" type="xsd:string" use="optional" default="g1">
                            <xsd:annotation>
                                <xsd:documentation><![CDATA[ The column belong, not used yet. ]]></xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                        <xsd:attribute name="type" type="xsd:string" use="required">
                            <xsd:annotation>
                                <xsd:documentation><![CDATA[ The column type in mysql eg. int(11). ]]></xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                        <xsd:attribute name="fk" type="xsd:boolean" use="optional" default="false">
                            <xsd:annotation>
                                <xsd:documentation><![CDATA[ If the column is a foreign key. ]]></xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                        <xsd:attribute name="fkTable" type="xsd:string" use="optional" default="null">
                            <xsd:annotation>
                                <xsd:documentation><![CDATA[ The foreign table in client. ]]></xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                        <xsd:attribute name="fkColumn" type="xsd:string" use="optional" default="null">
                            <xsd:annotation>
                                <xsd:documentation><![CDATA[ If foreign column in client. ]]></xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                    </xsd:complexType>
                </xsd:element>
            </xsd:sequence>
            <xsd:attribute name="local" type="xsd:string" use="required" >
                <xsd:annotation>
                    <xsd:documentation><![CDATA[ The table name in client database. ]]></xsd:documentation>
                </xsd:annotation>
            </xsd:attribute>

            <xsd:attribute name="remote" type="xsd:string" use="required" >
                <xsd:annotation>
                    <xsd:documentation><![CDATA[ The table name in datacenter database. ]]></xsd:documentation>
                </xsd:annotation>
            </xsd:attribute>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

dc-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dc="https://raw.githubusercontent.com/jsen-joker/data-center/master"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                                http://www.springframework.org/schema/beans/spring-beans.xsd
                                https://raw.githubusercontent.com/jsen-joker/data-center/master
                                https://raw.githubusercontent.com/jsen-joker/data-center/master/dc.xsd">

    <dc:app type="client" name="testClient1" />

    <dc:table local="table0" remote="tableCombine">
        <dc:column local="id" remote="id" key="false" groupType="replace" group="g1" type="int(11)" />
        <dc:column local="name" remote="name" key="true" groupType="replace" group="g1" type="varchar(256)" />
    </dc:table>
</beans>

猜你喜欢

转载自blog.csdn.net/jsenht/article/details/84665699