【G】开源的分布式部署解决方案(三) - 一期规划定稿与初步剖析

G.系列导航

【G】开源的分布式部署解决方案 - 预告篇

【G】开源的分布式部署解决方案(一) - 开篇

【G】开源的分布式部署解决方案(二) - 好项目是从烂项目基础上重构出来的

【G】开源的分布式部署解决方案(三) - 一期规划定稿与初步剖析

抱歉

首先我先说声抱歉,因为上一篇结尾预告第三篇本该是“部署项目管理”,那为什么变成本篇呢?

请容我解释一下,在预告篇到现在为止,经常会有人问我这个项目到底是干什么的。或许之前写的比较粗糙。那我相信目前定稿后的功能概览图应该会给大家一个比较清晰的认识。

另外也有不少人问我,更新进度这么慢会不会太监?

这个请放心,不会的,因为我们公司也要用到本项目,只是没列为紧急项目内,我也是抽空写写,毕竟我们已经有个精简中的精简版在运行着,也就是本项目的雏形。

PS:以下绝大多数内容没有明显说明的话仅针对一期有效。

一期功能概览图

(点击图片看大图)

先简单解释下图标的意思

相信已经有人知道了,是的,就是进度。

虽然纯黄色的比较多,但因为本身就定位PC+Mobile双端的情况下,确实对我这个不写前端的人有一点点挑战的。

好在目前进展还算顺利,发现的坑都填上了。

PS:这是终结吗?并不是,后面还会继续维护出新功能,比如项目组内各项目依赖部署、多人部署同一项目时的实时进度跟进、项目健康检查、灰度发布支持、更人性化的UI、更严谨的逻辑,部署本身就是个大工程,先走好当下的第一步。

适用场景

单机项目:

服务器数量:1

项目类型:网站

宿主:IIS

负载均衡:无

持续集成:无

可使用单机部署,或创建环境后只加入一台服务器,后续操作同多机项目。

多机项目

服务器数量:>1

项目类型:网站 (目前仅支持网站,后期会支持服务、Job程序,允许扩展)

宿主:IIS(目前仅支持IIS,后期会支持Windows服务,允许扩展)

负载均衡:有(目前仅支持阿里云,允许扩展)

持续集成:有(目前仅支持Jenkins,允许扩展)

项目部署、版本回滚、部署后预热、按单机部署或项目级部署、环境隔离、控制负载均衡阻断请求、防止正在处理的请求中断等。

至于详细的功能剖析,会在后面的博客中

名词解释

项目组

一个虚拟分组,用于将一些业务耦合度较高的项目捆绑在一起,后续或许会根据需要扩展支持按项目组部署,可根据依赖关系等条件部署。

项目

部署基准元素,可针对项目设置一些属性来决定部署流程的走向。

环境

一个虚拟的分组,与项目组不同的是,这个分组作用在服务器上。这里有一个比较特殊的概念叫环境标签,主要是用来区分环境分类的,比如开发、测试、仿真、生产等,作为一个一级分类来使用。

而环境这个概念本身,会在创建部署任务的时候,用于关联部署应该发生在哪些服务器上的。因为环境本身又包括了服务器。这样部署概念就完成了一个闭环。

当然这个流程就发生在刚才,我写环境介绍的时候突然想到的,我漏掉了关键的一个环节就是如何让部署、项目、服务器之间联系在一起,这的确是不该发生的事情。

服务器

部署单元,作为部署真正作用到的实体,这里有个小坑,就是目前暂时不支持服务器环境的初始化。比如安装个.net之类的就需要你手动来操作了。当然,服务器多的情况下,像阿里云本身就有快照之类的服务提供。

一键部署

一个自动化操作的动作,会根据之前项目配置阶段设置好的属性、流程等自动完成部署。因为部署本身是一个很复杂的过程,从项目构建、测试、打包、上传、覆盖、重启服务等等一系列操作。这当中人为的失误以及繁琐过程实在是让人抓狂至极。通过之前一步步的操作走到这里,以及后续博客篇幅以几个示例展开讲解会慢慢揭开自动化部署的神秘面纱。

UI

拿出几个有代表性的界面给大家先看看吧

项目列表界面-PC版

项目列表界面-手机版

项目组列表-PC版

项目组列表-手机版

抽取公共类的小波折

写这个类的时候其实是因为发现单表操作的功能还是不少的,如果提炼出来一个公共操作类的话就节约了我不少的时间。

在这个单表操作公共类里我目前希望有4个方法,因为这4个的操作概率比较高。Create、Update、GetList、GetByID。

方法不难找,只是过程有点反复,有几个比较关键的信息,比如底层操作数据库用的EF,又有Entity和Model的概念,自然就少不了AutoMapper。

using Framework.Mapping.Base;
using G.Client.Data.Entities.Base;
using G.Client.Data.Wrapper;
using G.Client.Model.DeployManage;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace G.Client.Infrastructure.Data.Pipeline.Database
{
    public class SingleTableEngineer<TEntity, TKey, TListModel, TCreateModel, TEditModel>
        where TEntity : EntityBase<TKey>
        where TListModel : class
        where TCreateModel : class
        where TEditModel : class, IEditViewModel<TKey>
        where TKey: IEquatable<TKey>
    {
        public virtual async Task<List<TListModel>> GetList()
        {
            using (var dbContext = GDbContext.Create())
            {
                var query = from entity in GDbContext.GetDbSet<TEntity, TKey>(dbContext).Where(entity => !entity.IsDelete)
                            select entity;

                var mapper = new MapperBase<TListModel, TEntity>();

                return mapper.GetModelList(await query.ToListAsync());
            }
        }

        public virtual async Task<TKey> Create(TCreateModel model)
        {
            using (var dbContext = GDbContext.Create())
            {
                var mapper = new MapperBase<TCreateModel, TEntity>();
                var entity = mapper.GetEntity(model);

                GDbContext.GetDbSet<TEntity, TKey>(dbContext).Add(entity);

                await dbContext.SaveChangesAsync();

                return entity.ID;
            }
        }

        public async Task<TEditModel> GetByID(TKey id)
        {
            using (var dbContext = GDbContext.Create())
            {
                var entity = await GDbContext.GetDbSet<TEntity, TKey>(dbContext).SingleAsync(e => e.ID.Equals(id));
                var mapper = new MapperBase<TEditModel, TEntity>();

                return mapper.GetModel(entity);
            }
        }

        public virtual async Task<TEditModel> Update(TEditModel model)
        {
            using (var dbContext = GDbContext.Create())
            {
                var entity = await GDbContext.GetDbSet<TEntity, TKey>(dbContext).SingleAsync(e => e.ID.Equals(model.ID));

                entity = new MapperBase<TEditModel, TEntity>().GetEntity(model, entity);
                entity.SetUpdateInfo();

                GDbContext.GetDbSet<TEntity, TKey>(dbContext).Attach(entity);
                dbContext.Entry(entity).State = EntityState.Modified;

                await dbContext.SaveChangesAsync();

                return model;
            }
        }
    }
}

Not Support Exception

整个类提炼过程并没有想象中那么复杂,就是遇到了个小坑。

刚开始的时候TKey是没有做约束的,这样再调用 SingleAsync的时候就报错了。

首先不做约束 == 就不能使用,只能用Equals,那对于SingleAsync这个方法来说就变成了 object.Equals(object) ,而EF也大大方方的抛出了一个 Not Support Exception。

后来加了一行代码 where TKey: IEquatable<TKey>  ,轻松搞定。

丑陋的GetDbSet

因为要根据泛型获取到DbSet,所以DbContext暴露了一个GetDbSet的方法。

然后就是操作公共类中出现的 DbContext.GetDbSet<TEntity, TKey>(dbContext) ,其实只不知道原由的话第一反应是上面的using已经Create了一个DbContext为什么下面还这么复杂的GetDbSet又把这个DbContext作为参数给扔了回去。

这我真的只能说,已经拖了很多进度就没多少心情纠结这个事情了。

小技巧

作为一个做C/S转B/S的人,深深的感觉到要踩的坑还有太多太多。比如一个创建页面,text不赋值的时候是null,而我的数据库设计是不允许为null的,但业务又允许为空,那我希望这个值是空字符串。

所以之前傻傻的先 a??"" 这样写了一下。后来越来越觉得不对劲,本着自己的经验,猜测DataAnnotation应该会处理这样的数据,毕竟从很多人的设计角度上来说是应该有这样的功能的。

于是出现了下面这段代码

namespace G.Client.Model.DeployManage.DeployProjectModels
{
    public class CreateDeployProjectViewModel
    {
        [Required, StringLength(50, MinimumLength = 2)]
        public string Name { get; set; }

        public int DeployProjectGroupID { get; set; }

        public DeployProjectType Type { get; set; }

        public DeployHostType Host { get; set; }

        [Required(AllowEmptyStrings = true), StringLength(50, MinimumLength = 0)]
        [DisplayFormat(ConvertEmptyStringToNull = false, NullDisplayText = "")]
        public string LoadBalanceIdentity { get; set; }
    }
}
[Required(AllowEmptyStrings = true), StringLength(50, MinimumLength = 0)]
[DisplayFormat(ConvertEmptyStringToNull = false, NullDisplayText = "")]

这两行是关键,首先 Required 是必填字段,这里按理说为空字符串也是应该报错的,但 AllowEmptyStrings=True 就保证了空字符串可以通过校验。其次 ConvertEmptyStringToNull = false,这里就阻止了把空字符串转换为null,同时 NullDisplayText = "" 也保证了如果真的是null也会被纠正为空字符串。从此我就告别了 ?? 

国际惯例

源码Git地址:http://git.oschina.net/doddgu/G

G.开源分布式部署 QQ群:601476986 (本群会实时更新进度,相比来说肯定比博客频繁得多)

下一篇预告:经过这次的事情,防止打脸,不敢预告了。

时间: 2024-11-09 23:59:08

【G】开源的分布式部署解决方案(三) - 一期规划定稿与初步剖析的相关文章

【G】开源的分布式部署解决方案(二) - 好项目是从烂项目基础上重构出来的

G.系列导航 [G]开源的分布式部署解决方案 - 预告篇 [G]开源的分布式部署解决方案(一) - 开篇 [G]开源的分布式部署解决方案(二) - 好项目是从烂项目基础上重构出来的 分析目前项目结构 眼前出现这么一坨坨的文件夹,相信很多人已经看不下去了.是的,首先就是要把它给做掉. 按照这个项目文件夹的命名意图,大概可以划分如下: 1.Business:业务代码 2.Data:数据访问 3.Helpers:辅助类(通用类库之类的) 4.Models:各种模型(包括视图模型) 5.theme:皮肤

【G】开源的分布式部署解决方案文档 - Web Deploy

G.系列导航 [G]开源的分布式部署解决方案 - 导航 微软官方部署方式 右键项目->发布 这个大家应该再熟悉不过,在部署前有个预览界面可以看本次更新到底更新哪些文件. 既然它可以预览部署结果,那其实它部署也不会完全覆盖,而是采取部分覆盖的方式,并提供了增.删.改的实际数量. 这都要依赖一个叫做 Web Deploy 的项目. 传送门 当然,web deploy功能绝不仅仅如此,包括打包.备份.还原.更新数据库等. 在此不多做普及,提到了它的优点,自然要支持它.所以本项目支持原始+Web Dep

【G】开源的分布式部署解决方案文档 - 手动安装

G.系列导航 [G]开源的分布式部署解决方案 - 导航 序言 因各种原因,决定先写使用文档.也证明下项目没有太监.至于安装过程复杂,是因为还没有做一键安装,这个现阶段确实没精力. 项目进度 (点击图片看大图) 必备工具 IDE:VS2015+ 运行环境: .Net Framework 4.6.1(已测可降4.5,其余没测) 宿主:IIS 下载源码 源码地址 http://git.oschina.net/doddgu/G/ ps:强烈希望顺手点下 star.watch.fork VS克隆源码 编译

Apache Spark探秘:三种分布式部署方式比较

目前Apache Spark支持三种分布式部署方式,分别是standalone.spark on mesos和 spark on YARN,其中,第一种类似于MapReduce 1.0所采用的模式,内部实现了容错性和资源管理,后两种则是未来发展的趋势,部分容错性和资源管理交由统一的资源管理系统完成:让Spark运行在一个通用的资源管理系统之上,这样可以与其他计算框架,比如MapReduce,公用一个集群资源,最大的好处是降低运维成本和提高资源利用率(资源按需分配).本文将介绍这三种部署方式,并比

Spark的三种分布式部署

目前Apache Spark支持三种分布式部署方式: 分别是standalone.spark on mesos和 spark on YARN, 其中,第一种类似于MapReduce 1.0所采用的模式,内部实现了容错性和资源管理,后两种则是未来发展的趋势, 部分容错性和资源管理交由统一的资源管理系统完成:让Spark运行在一个通用的资源管理系统之上,这样可以与 其他计算框架,比如MapReduce,公用一个集群资源,最大的好处是降低运维成本和提高资源利用率(资源按需 分配).本文将介绍这三种部署

阿里开源分布式事务解决方案 Fescar 全解析

广为人知的阿里分布式事务解决方案:GTS(Global Transaction Service),已正式推出开源版本,取名为"Fescar",希望帮助业界解决微服务架构下的分布式事务问题,今天我们一起来深入了解. FESCAR on GitHub https://github.com/alibaba/fescar 微服务倡导将复杂的单体应用拆分为若干个功能简单.松耦合的服务,这样可以降低开发难度.增强扩展性.便于敏捷开发.当前被越来越多的开发者推崇,系统微服务化后,一个看似简单的功能,

项目分布式部署那些事(1):ONS消息队列、基于Redis的Session共享,开源共享

因业务发展需要现在的系统不足以支撑现在的用户量,于是我们在一周之前着手项目的性能优化与分布式部署的相关动作. 概况 现在的系统是基于RabbitHub(一套开源的开发时框架)和Rabbit.WeiXin(开源的微信开发SDK)开发的一款微信应用类系统,主要业务是围绕当下流行的微信元素,如:微官网.微商城.微分销.营销活动.会员卡等. 关于RabbitHub详情请戳: .NET 平台下的插件化开发内核(Rabbit Kernel) RabbitHub开源情况及计划 关于Rabbit.WeiXin详

分布式事务解决方案---阅读--篇1--关于分布式系统的数据一致性问题

self: 这篇文章逻辑不算很清晰,但讲到的点还算是比较好的.自己总结一下可以做不错的参考: 1. 这边文章主要讲了两个方面,一方面是MQ的消息可靠性问题,另一方面是MQ可以被利用来做补偿机制的最终一致性分布式事务解决方案. 2. 关于MQ消息的问题大致有下面三个 2.1 如何保证A->M的消息,M一定接收到了,同样,如何保证M->A的消息,M一定接收到了 2.2 如果数据需要一致性更新,比如A发送了三条消息给M,M要么全部保存,要么全部不保存,不能够只保存其中的几条记录.我们假设更新的数据是

微服务架构下分布式事务解决方案——阿里云GTS

https://blog.csdn.net/jiangyu_gts/article/details/79470240 1 微服务的发展 微服务倡导将复杂的单体应用拆分为若干个功能简单.松耦合的服务,这样可以降低开发难度.增强扩展性.便于敏捷开发.当前被越来越多的开发者推崇,很多互联网行业巨头.开源社区等都开始了微服务的讨论和实践.Hailo有160个不同服务构成,NetFlix有大约600个服务.国内方面,阿里巴巴.腾讯.360.京东.58同城等很多互联网公司都进行了微服务化实践.当前微服务的开