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。

  1. using Framework.Mapping.Base;
  2. using G.Client.Data.Entities.Base;
  3. using G.Client.Data.Wrapper;
  4. using G.Client.Model.DeployManage;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Data.Entity;
  8. using System.Linq;
  9. using System.Linq.Expressions;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12.  
  13. namespace G.Client.Infrastructure.Data.Pipeline.Database
  14. {
  15. public class SingleTableEngineer<TEntity, TKey, TListModel, TCreateModel, TEditModel>
  16. where TEntity : EntityBase<TKey>
  17. where TListModel : class
  18. where TCreateModel : class
  19. where TEditModel : class, IEditViewModel<TKey>
  20. where TKey: IEquatable<TKey>
  21. {
  22. public virtual async Task<List<TListModel>> GetList()
  23. {
  24. using (var dbContext = GDbContext.Create())
  25. {
  26. var query = from entity in GDbContext.GetDbSet<TEntity, TKey>(dbContext).Where(entity => !entity.IsDelete)
  27. select entity;
  28.  
  29. var mapper = new MapperBase<TListModel, TEntity>();
  30.  
  31. return mapper.GetModelList(await query.ToListAsync());
  32. }
  33. }
  34.  
  35. public virtual async Task<TKey> Create(TCreateModel model)
  36. {
  37. using (var dbContext = GDbContext.Create())
  38. {
  39. var mapper = new MapperBase<TCreateModel, TEntity>();
  40. var entity = mapper.GetEntity(model);
  41.  
  42. GDbContext.GetDbSet<TEntity, TKey>(dbContext).Add(entity);
  43.  
  44. await dbContext.SaveChangesAsync();
  45.  
  46. return entity.ID;
  47. }
  48. }
  49.  
  50. public async Task<TEditModel> GetByID(TKey id)
  51. {
  52. using (var dbContext = GDbContext.Create())
  53. {
  54. var entity = await GDbContext.GetDbSet<TEntity, TKey>(dbContext).SingleAsync(e => e.ID.Equals(id));
  55. var mapper = new MapperBase<TEditModel, TEntity>();
  56.  
  57. return mapper.GetModel(entity);
  58. }
  59. }
  60.  
  61. public virtual async Task<TEditModel> Update(TEditModel model)
  62. {
  63. using (var dbContext = GDbContext.Create())
  64. {
  65. var entity = await GDbContext.GetDbSet<TEntity, TKey>(dbContext).SingleAsync(e => e.ID.Equals(model.ID));
  66.  
  67. entity = new MapperBase<TEditModel, TEntity>().GetEntity(model, entity);
  68. entity.SetUpdateInfo();
  69.  
  70. GDbContext.GetDbSet<TEntity, TKey>(dbContext).Attach(entity);
  71. dbContext.Entry(entity).State = EntityState.Modified;
  72.  
  73. await dbContext.SaveChangesAsync();
  74.  
  75. return model;
  76. }
  77. }
  78. }
  79. }

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应该会处理这样的数据,毕竟从很多人的设计角度上来说是应该有这样的功能的。

于是出现了下面这段代码

  1. namespace G.Client.Model.DeployManage.DeployProjectModels
  2. {
  3. public class CreateDeployProjectViewModel
  4. {
  5. [Required, StringLength(, MinimumLength = )]
  6. public string Name { get; set; }
  7.  
  8. public int DeployProjectGroupID { get; set; }
  9.  
  10. public DeployProjectType Type { get; set; }
  11.  
  12. public DeployHostType Host { get; set; }
  13.  
  14. [Required(AllowEmptyStrings = true), StringLength(, MinimumLength = )]
  15. [DisplayFormat(ConvertEmptyStringToNull = false, NullDisplayText = "")]
  16. public string LoadBalanceIdentity { get; set; }
  17. }
  18. }
  1. [Required(AllowEmptyStrings = true), StringLength(50, MinimumLength = 0)]
  2. [DisplayFormat(ConvertEmptyStringToNull = false, NullDisplayText = "")]
  3.  
  4. 这两行是关键,首先 Required 是必填字段,这里按理说为空字符串也是应该报错的,但 AllowEmptyStrings=True 就保证了空字符串可以通过校验。
    其次 ConvertEmptyStringToNull = false,这里就阻止了把空字符串转换为null,同时 NullDisplayText = "" 也保证了如果真的是null也会被纠正为空字符串。
    从此我就告别了 ??

国际惯例

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

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

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

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

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

    做这个开源项目的意义是什么?(口水自问自答,不喜可略过) 从功能上来说,请参考 预告篇,因自知当时预告片没有任何含金量,所以并没有主动推送到首页,而是私下的给一些人发的. 从个人角度上来说,我希望.n ...

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

    G.系列导航 [G]开源的分布式部署解决方案 - 导航 序言 因各种原因,决定先写使用文档.也证明下项目没有太监.至于安装过程复杂,是因为还没有做一键安装,这个现阶段确实没精力. 项目进度 (点击图片 ...

  3. 【G】开源的分布式部署解决方案文档 - 使用手册

    G.系列导航 [G]开源的分布式部署解决方案 - 导航 已知问题 导航没有联动 因为权限只是做了基础的登录校验,考虑到后面导航要跟权限关联上暂时是写死的. 只有部分界面使用了Vue.js 因为刚开始没 ...

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

    G.系列导航 [G]开源的分布式部署解决方案 - 导航 微软官方部署方式 右键项目->发布 这个大家应该再熟悉不过,在部署前有个预览界面可以看本次更新到底更新哪些文件. 既然它可以预览部署结果, ...

  5. 【G】开源的分布式部署解决方案文档 - 部署Console & 控制负载均衡 & 跳转持续集成控制台

    G.系列导航 [G]开源的分布式部署解决方案 - 导航 设置项目部署流程 项目类型:选择Console,这个跟功能无关,只是做项目分类,后面会有后续功能 宿主:选择Console 部署方式:选择原始, ...

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

    为什么想到要做分布式部署解决方案? 当项目越做越大以后,你会发现部署变成一件极其头疼的事情.当然头疼的绝不仅仅在部署一个环节,比如新服务器环境搭建当中就许多坑要踩.各种重复性的工作,包括但不仅限于增加 ...

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

    分析目前项目结构 眼前出现这么一坨坨的文件夹,相信很多人已经看不下去了.是的,首先就是要把它给做掉. 按照这个项目文件夹的命名意图,大概可以划分如下: 1.Business:业务代码 2.Data:数 ...

  8. hyperledger fabric 1.0.5 分布式部署 (三)

    本篇博客主要是向读者介绍 fabric 在部署时的一些细节,还有作者自己学习过程中的心得. 初始化相关密钥的程序,实际上是一个shell脚本,并且结构特别简单 generateArtifacts.sh ...

  9. SpringCloudAlibaba分布式事务解决方案Seata实战与源码分析-上

    概述 定义 Spring Cloud Alibaba Seata 官网地址 https://seata.io/zh-cn/ 最新版本1.5.2 Spring Cloud Alibaba Seata 文 ...

随机推荐

  1. el5,6,7的ntpdate服务

    在el5里没有ntpdate服务 在el6里有ntpdate服务 在el7里有ntpdate服务

  2. Java的内存泄漏

    内存泄漏是指,无用对象(不再使用的对象)持续占用内存或者无用对象的内存得不到及时释放,从而造成的内存浪费 就说是有一块内存你不需要再用了,但是呢你还保留着它的指针,那么这块内存就不会被回收 举个例子 ...

  3. HDU 1255 覆盖的面积 ——(线段树+扫描线)

    又做了一题扫描线以后对节点的覆盖标记理解的更加深刻了. 代码如下: #include <stdio.h> #include <algorithm> #include <s ...

  4. Bzoj1479: [Nerrc1997]Puncher打孔机

    1479: [Nerrc1997]Puncher打孔机 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 22  Solved: 14[Submit][Sta ...

  5. 使用Python脚本操作MongoDB的教程

    Reference:  http://www.jb51.net/article/64225.htm

  6. Borda count

    波达计数法(Borda Count)是较为简单的排序投票法,每个选项借由选票上的排序来取得积分,积分最高者获胜.另一个类似的方法则是位置投票制. 投票人按喜好排列候选者.如果候选者在选票的排第一位,它 ...

  7. $(function(){})和$(document).ready(function(){}) 的区别

    document.ready和onload的区别——JavaScript文档加载完成事件 页面加载完成有两种事件 一是ready,表示文档结构已经加载完成(不包含图片等非文字媒体文件) 二是onloa ...

  8. Intel为什么做不好手机CPU?

    Intel大名鼎鼎,在CPU界无人不知无人不晓,然而在当前主流的手机CPU市场上却是远远落后日本的ARM公司,这到底是Intel技术不足,还是ARM过于强大呢,今天我们就来探讨一下. 故事要从2006 ...

  9. BZOJ2733 永无乡【splay启发式合并】

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  10. lepus3.7 天兔监控安装手册 CentOS6.5+mysql5.6

    lepus3.7 天兔监控安装配置手册 CentOS6.5+mysql5.6 整体环境 192.168.1.250为监控机 192.168.1.248为被监控机 安装LAMP环境 [root@HE3~ ...