【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(, MinimumLength = )]
public string Name { get; set; } public int DeployProjectGroupID { get; set; } public DeployProjectType Type { get; set; } public DeployHostType Host { get; set; } [Required(AllowEmptyStrings = true), StringLength(, MinimumLength = )]
[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 (本群会实时更新进度,相比来说肯定比博客频繁得多)
下一篇预告:经过这次的事情,防止打脸,不敢预告了。
【G】开源的分布式部署解决方案(三) - 一期规划定稿与初步剖析的更多相关文章
- 【G】开源的分布式部署解决方案(一) - 开篇
做这个开源项目的意义是什么?(口水自问自答,不喜可略过) 从功能上来说,请参考 预告篇,因自知当时预告片没有任何含金量,所以并没有主动推送到首页,而是私下的给一些人发的. 从个人角度上来说,我希望.n ...
- 【G】开源的分布式部署解决方案文档 - 手动安装
G.系列导航 [G]开源的分布式部署解决方案 - 导航 序言 因各种原因,决定先写使用文档.也证明下项目没有太监.至于安装过程复杂,是因为还没有做一键安装,这个现阶段确实没精力. 项目进度 (点击图片 ...
- 【G】开源的分布式部署解决方案文档 - 使用手册
G.系列导航 [G]开源的分布式部署解决方案 - 导航 已知问题 导航没有联动 因为权限只是做了基础的登录校验,考虑到后面导航要跟权限关联上暂时是写死的. 只有部分界面使用了Vue.js 因为刚开始没 ...
- 【G】开源的分布式部署解决方案文档 - Web Deploy
G.系列导航 [G]开源的分布式部署解决方案 - 导航 微软官方部署方式 右键项目->发布 这个大家应该再熟悉不过,在部署前有个预览界面可以看本次更新到底更新哪些文件. 既然它可以预览部署结果, ...
- 【G】开源的分布式部署解决方案文档 - 部署Console & 控制负载均衡 & 跳转持续集成控制台
G.系列导航 [G]开源的分布式部署解决方案 - 导航 设置项目部署流程 项目类型:选择Console,这个跟功能无关,只是做项目分类,后面会有后续功能 宿主:选择Console 部署方式:选择原始, ...
- 【G】开源的分布式部署解决方案 - 预告篇
为什么想到要做分布式部署解决方案? 当项目越做越大以后,你会发现部署变成一件极其头疼的事情.当然头疼的绝不仅仅在部署一个环节,比如新服务器环境搭建当中就许多坑要踩.各种重复性的工作,包括但不仅限于增加 ...
- 【G】开源的分布式部署解决方案(二) - 好项目是从烂项目基础上重构出来的
分析目前项目结构 眼前出现这么一坨坨的文件夹,相信很多人已经看不下去了.是的,首先就是要把它给做掉. 按照这个项目文件夹的命名意图,大概可以划分如下: 1.Business:业务代码 2.Data:数 ...
- hyperledger fabric 1.0.5 分布式部署 (三)
本篇博客主要是向读者介绍 fabric 在部署时的一些细节,还有作者自己学习过程中的心得. 初始化相关密钥的程序,实际上是一个shell脚本,并且结构特别简单 generateArtifacts.sh ...
- SpringCloudAlibaba分布式事务解决方案Seata实战与源码分析-上
概述 定义 Spring Cloud Alibaba Seata 官网地址 https://seata.io/zh-cn/ 最新版本1.5.2 Spring Cloud Alibaba Seata 文 ...
随机推荐
- Post data using ajax in laravel 5
转自:http://www.tuicool.com/articles/2u2mmmu Post data using ajax in laravel 5 to controller If you ar ...
- EasyUI datagrid 的checkbox设置
参考url: http://blog.csdn.net/baronyang/article/details/9323463 我的需求: 抓取数据生成的日志,日志中有部分是抓取失败的,需要将失败的发送到 ...
- IOS中单例NSUserDefaults的使用(转)
一.了解NSUserDefaults以及它可以直接存储的类型 http://my.oschina.net/u/1245365/blog/294449 NSUserDefaults是一个单例,在整个程序 ...
- android 菜单的总结
安卓菜单有三种菜单. 选项菜单: 点击系统菜单按钮会触发 上下文菜单:长按屏幕触发 子菜单:某一个菜单的下一级菜单 具体的描叙:http://blog.csdn.net/zqiang_55/artic ...
- [Angular Tutorial] 3-Components
在先前的步骤中,我们看到了一个控制器和一个模板如何一起工作来将一个静态的HTML文件转化为动态页面(view).一般说来,这在单页应用中一种非常常见的模式(在Angular应用中尤其是这样): ·客户 ...
- 使用python爬取东方财富网机构调研数据
最近有一个需求,需要爬取东方财富网的机构调研数据.数据所在的网页地址为: 机构调研 网页如下所示: 可见数据共有8464页,此处不能直接使用scrapy爬虫进行爬取,因为点击下一页时,浏览器只是发起了 ...
- iOS 程序初始一个带导航栏的视图
@synthesize window = _window; @synthesize rootViewController = _rootViewController; - (BOOL)applicat ...
- 使用菜单(Menu)资源
前面已经介绍过Android的菜单支持,前面介绍菜单时分别介绍了如何使用Java代码来实现菜单和使用XML资源文件定义菜单. 实际上Android推荐使用XML资源文件来定义菜单,使用XML资源文件定 ...
- SecureCRT 使用
1. 连接linux 服务器 http://www.cnblogs.com/SimonGao/p/4959274.html
- Android3.0 以前的Fragment支持
Fragment非常实用,Android也为3.0以前的平台增加了Fragment支持,只是该Fragment不是继承android.app.Fragment,而是继承android.support. ...