zoukankan      html  css  js  c++  java
  • 项目演化系列--开启分布式(分离数据层)

    前言

      原本上一篇是打算写分离数据层的,但是在思考的过程当中发现分离数据层的时候,有一些操作是要依赖分布式锁的,因此先写了分布式锁。

      对于有些项目的数据层提供的是业务接口的(返回业务所需的数据),那么当数据层压力逐渐增大的时候,如需要使用缓存的时候,就需要开发人员去修改相应的数据接口使其使用缓存,缓存和各种数据查询接口交错在一起,整个数据层的代码变得非常混乱,连重构都无法进行,只能推倒重做。所以很多的文章中,在讲解数据层的时候,都是使用统一的数据接口,如:Find、Add、Save等,那么当需要缓存的时候,就可以在Find中直接扩展使其支持缓存,甚至可以引入缓存配置管理,对于不同的表之间进行可调度的缓存周期管理,使开发人员不需要知道缓存的存在,他们还是使用原先的Find,但是速度却更快了,又或者冷热数据管理、搜索引擎等,这便是大神们在经过多年开发总结下来的经验。

      将数据层从项目中分离出来成为一个独立的项目,并将其发布于独立的服务器,相对于单机系统而言,有如下几个优点:

      1、便于整合各方资源

      2、降低成本

      3、每个层的性能更加优秀且可伸缩性强,在不断增加的负荷下,可以便利的增加节点数量。

      4、某些次服务层出现错误的时候,不会导致项目的整体瘫痪。

      5、不同层次可用不同语言实现

      有优点就有缺点,最大的缺点就是较单机系统而言,分布式系统更加复杂,不仅系统本身结构会变得复杂,不同组件间的网络会引入影响因素,调试困难等等。

      虽然有不少的缺点,但是不去实践是不会发现另一番天地的,不去挑战看看永远都只是局限在单机系统上,也不用害怕会遇到哪些问题,毕竟只有发现问题之后,才能想办法解决,这是从书上无法学习到的,那么我们就开始今天的文章吧。

    基础CRUD

      数据层是提供于数据库进行操作的中间件,最基础的功能就是CRUD,单机系统当中,调用CRUD接口的时候,要么通过拼接SQL要么通过ORM直接连接数据库,而由于现在是分布式的,因此原先的调用方式就需要通过通信协议来实现了,假设原先的接口为:

    public class DbResult
    {
        public bool Error { get; set; }
    
        public object Data { get; set; }
    }
    
    public interface IDb
    {
        DbResult Find(Dictionary<string, object> query);
    
        DbResult Add(object entity);
    
        DbResult Save(object entity);
    
        DbResult Remove(object entity);
    }
    

      数据层那端的Find是以Query Object模式实现的,具体的格式可以查看此文章--《Query Object--查询对象模式(上)》、《Query Object--查询对象模式(下)》。

      这里的数据层项目,如果使用原先抽取的mvcHandler方式来实现,那么IService的派生类就需要使用HttpWebRequest来实现了,大致的实现思路就是将相应的方法最后转换成调用数据服务,Find实现代码如下:

    public DbResult Find(Dictionary<string, object> query)
    {
        var url = string.Format("{0}/{1}/{2}", ConnectionUri, this.table, "find");
        var req = (HttpWebRequest)HttpWebRequest.Create(url);
        req.Method = "Post";
        req.Accept = "text/plain, */*; q=0.01";
        req.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
        req.Timeout = 1000 * 30;
        req.KeepAlive = true;
        try
        {
            byte[] bytes = Encoding.UTF8.GetBytes(
                JsonConvert.SerializeObject(query));
            req.ContentLength = bytes.Length;
            using (var reqStream = req.GetRequestStream())
            {
                Stream requestStream = req.GetRequestStream();
                requestStream.Write(bytes, 0, bytes.Length);
            }
    
            var res = new DbResult();
            using (var resp = (HttpWebResponse)req.GetResponse())
            {
                using (var respStream = resp.GetResponseStream())
                {
                    using (var reader = new StreamReader(respStream, Encoding.UTF8))
                    {
                        string respContent = reader.ReadToEnd();
                        return JsonConvert.DeserializeObject(respContent);
                    }
                }
            }
        }
        catch
        {
            return new DbResult { Error = true };
        }
    }
    

    事务

      有了基础的CRUD以后,接下来就要实现事务了,如果参考单机系统来实现事务的话,那么开启事务以后,如果业务层出现问题导致主机重启或宕机,那么数据层的事务将会无法关闭,从而引发问题。

      因此需要使用其他的方式来实现,观察事务可以得出从事务开启到提交是一个整体,只有事务提交的时候才需要有状态来判定事务的执行情况,而事务开启时并可以不需要有执行结果的判定,因此可以将事务看成一个队列,而中间的每一个CUD操作可以看成是它的元素,而每一个操作需要知道对应的是哪个表、执行哪个操作以及相应的表数据即可,代码实现如下:

    public class TransactionAction
    {
        public string Table { get; set; }
    
        public string Name { get; set; }
    
        public object Data { get; set; }
    }
    
    //IDb
    public interface IDb
    {
        //其他省略
    
        void BeginTx();
    
        DbResult CommitTx();
    }
    
    //IDb实现
    private List actions;
    
    public void BeginTx()
    {
        this.actions = new List();
    }
    
    public DbResult CommitTx()
    {
        //http访问数据层并设置actions为null
    }
    
    public DbResult Add(object entity)
    {
        if (this.actions != null)
        {
            this.actions.Add(new TransactionAction
            {
                Name = "add",
                Table = this.table,
                Data = entity
            });
        }
        else
        {
            //单个增加
        }
    }
    

    结束语

      到这里数据层的分离基本上就完成了,如果想要更高效的数据获取,可以将访问方式修改成socket,之所以不直接使用WPF或者webService是因为这都是基于C#来开发的,而且如果直接使用现成的框架来用的话,能学到的东西就不多了。

      就到这里了,如果有什么问题或者错误的话,请给我留言,谢谢。

  • 相关阅读:
    java 基本数据类型的取值范围
    警惕自增的陷阱
    三元操作符的类型务必一致
    不要随便设置随机种子
    优先使用整形池
    IN、ANY、ALL与SOME
    第六章-序列:字符串、列表和元组 笔记
    第十二章-安全性
    第五章-数字 课后答案
    第十一章-约束、视图与事务
  • 原文地址:https://www.cnblogs.com/ahl5esoft/p/5111982.html
Copyright © 2011-2022 走看看