zoukankan      html  css  js  c++  java
  • ToolBox Analysis & Design

     

    话说小菜过做已近3年,虽出身PHP后项目大多涉及.net,系统也做得比较繁杂,从常见的CMS,企业OA,ERP,也涉及到电商系统的开发定制,爬虫,工具不一而足,其中web系统居多。

    由于表现良好,时常会被经理叫去:“小菜,人是需要成长的啊,不如下个项目你负责吧,锻炼一下吧。”可是小菜对这种负责的职位是相当的不感冒,小菜只想快乐的编码,小菜的最终成为soho一族陪着美女环游世界。都知道事情一涉及到管理,负责就不那么纯洁了。并且小菜一直认为:将帅无能,累死三军。于是小菜每次都以“更对编码感兴趣” 为由拒绝。然而小菜知道丑媳妇早晚要见公婆,时间久了,这一步总是要过的,于是小菜也每每在项目之前,之后尝试以自己的理解,去搭一个架子,以备将来负责时不时之需。

    于是“小菜白话搭架子”系列便出现了,会结合一些项目进行分析与记录,促进成长,水平有限,不喜勿喷哦。

    闲话少绪,小菜给大家道一下整个的项目背景。

    背景:此项目是为欧美某信息服务公司A加工其世界范围内所合作的的“信息提供商”提供的信息。也就是为A设计开发一个数据处理管道使用其不同的合作商,使不同合作商的数据最终为A所用。

    由于小菜公司与A公司长久合作关系,大部分的合作商数据处理工具以开发完成,目前小菜们所要做的架子主要是将这些工具组合在一起,使开发过程减少人工干扰,减少人工成本。同时可适用于未来的需求扩展。

    开发完成的Tool,及步骤:

    1. DownloadTool:从信息提供商下载数据。
    2. Diff:为数据提供商本身数据去重。
    3. Caculate:整合新版本数据与库中数据,生成新数据。
    4. View:生成用户可用数据。
    5. Backup:数据备份。

    因为小菜认为做开发解决方案一定要在技术选型之前,如果先做技术选型的话,可能会我们的思考产生束缚,

    小菜第一次获取以上信息时,由于这么多的Tool,将来还可能扩展,本能的想到了一个模型,就是ToolBox。

    这样的话,将来那些新开发的Tool或已有的Tool之间就不需要产生本质的结合,将来我们就需要将他们扔进一个叫做“Toolbox"的文件夹中,MainServer会检索这个文件夹,将新增的Tool加载进系统运行程序集,有些类似于插件开发。由于经理对于Wcf有个人的青睐,要求使用wcf做选型的分布式信息架构,说实话小菜不喜欢wcf,因为小菜一直认为这又是微软的一个学院派产品,配置麻烦也不酷。不过用就用吧。

    于是小菜认为,既然头脑中有了模型,技术有了选型,开始最喜欢的代码吧:

    小菜的结构与分层大概是这样的:

    小菜为每一个Tool都做了一个接口,MainServer和Tool之间通过wcf进行通信,mainserver通过检测Tool的心跳获知其生活状态,Tool内有一个线程定时回调方法去Mainserver查找有没有分配自己的Task,Tool获取Task之后执行,返回给客户端:

    代码如下:

    server.contract:

    复制代码
    [ServiceContract]
    
    public interface IView {
    
    [OperationContract]
    
    Int32 CreateWork(UpstreamViewsWF work, Int32? seriesID);
    
    [OperationContract]
    
    Boolean TryTakeWork(out GeneralWork<UpstreamViewsWF> work);
    
    [OperationContract]
    
    void UpdateWork(GeneralWork<UpstreamViewsWF> work);
    
    [OperationContract]
    
    void DeleteWork(Int32 id);
    
    [OperationContract]
    
    void ReportProcess(WorkProgress progress);
    
    [OperationContract]
    
    void ReportCompletion(GeneralWork<UpstreamViewsWF> work);
    
    }
    复制代码

    service:

    复制代码
    public class View: ApplicationService, IView {
    private Logger _logger;
    private IGeneralJobRepository _generalRepo;
    private IJobStatusHistoryRepository _historyRepo;
    
    public ViewsService(
    IRepositoryContext context,
    IGeneralJobRepository generalJobRepo,
    IJobStatusHistoryRepository historyRepo)
    : base(context) {
    _generalRepo = generalJobRepo;
    _historyRepo = historyRepo;
    _logger = LogManager.GetCurrentClassLogger();
    }
    
    public Int32 CreateWork(Views work, Int32? seriesID) {
    _logger.Info(String.Empty);
    GeneralJob job = new GeneralJob {
    Category = (Byte)WorkCategory.Views,
    Status = (Byte)WorkStatus.Created,
    };
    
    if (seriesID.HasValue && seriesID.Value > 0) {
    job.SeriesID = seriesID.Value;
    }
    
    work.TypeName = WorkCategory.Views.ToString();
    
    job.VARS = JsonConvert.SerializeObject(work);
    try {
    Context.BeginTrans();
    _generalRepo.Create(job);
    _historyRepo.Create(CreateJobStatus(job));
    
    Context.Commit();
    return job.ID;
    }
    catch (Exception ex) {
    _logger.ErrorException(String.Empty, ex);
    throw FaultData.CreateFaultException(ex);
    }
    }
    
    public GeneralWork<Views> RetriveWork(int id) {
    _logger.Info(String.Empty);
    try {
    GeneralJob job = _generalRepo.RetriveByKey(id);
    return job.Map<Views>();
    }
    
    catch (Exception ex) {
    
    _logger.ErrorException(String.Empty, ex);
    
    throw FaultData.CreateFaultException(ex);
    
    }
    
    }
    
    public void UpdateWork(GeneralWork<Views> work) {
    
    _logger.Info(String.Empty);
    
    try {
    
    GeneralJob job = _generalRepo.RetriveByKey(work.ID);
    
    if (job.Status != (Byte)WorkStatus.Created) {
    
    Exception ex = new Exception("Only new work could be modified");
    
    }
    
    work.VARS.TypeName = WorkCategory.UpstreamViewsWF.ToString();
    
    job.SeriesID = work.SeriesID;
    
    job.VARS = JsonConvert.SerializeObject(work.VARS);
    
    Context.BeginTrans();
    
    _generalRepo.Update(job);
    
    _historyRepo.Create(CreateJobStatus(job));
    
    Context.Commit();
    
    }
    
    catch (Exception ex) {
    
    _logger.ErrorException(String.Empty, ex);
    
    throw FaultData.CreateFaultException(ex);
    
    }
    
    }
    
    public void DeleteWork(Int32 id) {
    
    _logger.Info(String.Empty);
    
    try {
    
    GeneralJob job = _generalRepo.RetriveByKey(id);
    
    if (job.Status != (Byte)WorkStatus.Created) {
    
    Exception ex = new Exception("Only new work could be modified");
    
    }
    
    job.Status = (Byte)WorkStatus.Abandoned;
    
    Context.BeginTrans();
    
    _generalRepo.Delete(job);
    
    _historyRepo.Create(CreateJobStatus(job));
    
    Context.Commit();
    
    }
    
    catch (Exception ex) {
    
    _logger.ErrorException(String.Empty, ex);
    
    throw FaultData.CreateFaultException(ex);
    
    }
    
    }
    
    public void ReportProcess(WorkProgress progress) {
    
    _logger.Info(String.Empty);
    
    }
    
    public void ReportCompletion(GeneralWork<Views> work) {
    
    _logger.Info(String.Empty);
    
    try {
    
    GeneralJob job1 = work.Map<Views>();
    
    job1.Status = (Byte)WorkStatus.Finished;
    
    Context.BeginTrans();
    
    _generalRepo.Update(job1);
    
    _historyRepo.Create(CreateJobStatus(job1));
    
    Context.Commit();
    
    }
    
    catch (Exception ex) {
    
    _logger.ErrorException(String.Empty, ex);
    
    throw FaultData.CreateFaultException(ex);
    
    }
    
    }
    
    public GeneralWork<Views>[] ReadWorkQueue(int currentPage, int itemsPerPage, WorkStatus? status, string key, outPageClause pageClause) {
    
    _logger.Info(String.Empty);
    
    try {
    
    List<Expression<Func<GeneralJob, Boolean>>> filters = new List<Expression<Func<GeneralJob, bool>>>();
    
    filters.Add(e => e.Category == (Byte)WorkCategory.UpstreamViewsWF);
    
    if (status.HasValue) {
    
    filters.Add(e => e.Status == (Byte)status);
    
    }
    
    if (!String.IsNullOrWhiteSpace(key)) {
    
    filters.Add(e => e.VARS.Contains(key.Trim()));
    
    }
    
    PageClause<GeneralJob> jobs = _generalRepo.RetriveAll(currentPage, itemsPerPage, true
    
    , p => p.ID, filters.ToArray());
    
    pageClause = new PageClause(jobs);
    
    return jobs.Map<UpstreamViewsWF>().ToArray();
    
    }
    
    catch (Exception ex) {
    
    _logger.ErrorException(String.Empty, ex);
    
    throw FaultData.CreateFaultException(ex);
    
    }
    
    }
    
    }
    复制代码

    对Tool的封装:

    复制代码
    class Program
    
    
     
    
    {
    
    static lWork<View> currentWork;
    
    
    private readonly static string exePath = Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "app.exe" );
    
    
    static void Main( string[] args ) {
    
    
    Trace.Listeners.Add( new ConsoleTraceListener() );
    
    
    try {
    
    
    if( TryTakeWork() ) {
    
    
    Trace.WriteLine( "Take a work" );
    
     
    
    Work();
    
    ProcessWork();
    
    ReportCompletion();
    
    }
    
    } catch( Exception ex ) {
    
    
    Console.WriteLine( ex );
    
     
    
    }
    
    }
    
    static void Work() {
    
    
    using( ServiceFactory factory = new ServiceFactory() ) {
    
    
    try {
    
    
    ISeriesWorkService service = factory.CreateChanel<ISeriesWorkService>();
    
    
    SeriesWork seriesWork = service.RetriveWork( currentWork.SeriesID );
    
    currentWork.VARS.TypeName = ConfigurationManager.AppSettings[ "TypeName" ];
    
    
    Trace.WriteLine( "TypeName:" + currentWork.VARS.TypeName );
    
    
    
    currentWork.VARS.Code = seriesWork.egion;
    
    Trace.WriteLine( "Code:" + Code );
    
    
    CopyHelper.CopyDir( Path.GetFullPath( @"....Application" ), AppDomain.CurrentDomain.BaseDirectory );
    
    
    } catch( Exception ex ) {
    
    
    Console.WriteLine( ex );
    
     
    
    }
    
    }
    
    }
    
    static Boolean TakeWork() {
    
    
    Trace.WriteLine( "TryTakeWork" );
    
    
    using( ServiceFactory factory = new ServiceFactory() ) {
    
    
    IUpstreamViewsWFService service = factory.CreateChanel<IUpstreamViewsWFService>();
    
    
    return service.TakeWork( out currentWork );
    
     
    
    }
    
    }
    
    static void ProcessWork() {
    
    
    Trace.WriteLine( "ProcessWork..." );
    
    
    Process process = null;
    
    
    try {
    
    
    ProcessStartInfo info = new ProcessStartInfo();
    
    
    info.CreateNoWindow = false;
    
    
    info.FileName = exePath; //as u need
    
    
    process = Process.Start( info );
    
    
    process.EnableRaisingEvents = true;
    
     
    
    process.WaitForExit();
    
    Trace.WriteLine( "ProcessWork succeed" );
    
    
    } catch( Exception EX ) {
    
    
    Trace.WriteLine( "Exception:" + EX );
    
    
    if( process != null ) {
    
     
    
    process.Kill();
    
    }
    
    }
    
    }
    
    static void ReportCompletion() {
    
    
    Trace.WriteLine( "ReportCompletion" );
    
    
    using( ServiceFactory factory = new ServiceFactory() ) {
    
    
    IView service = factory.CreateChanel<IView>();
    
     
    
    service.ReportCompletion( currentWork );
    
    }
    
    }
    
    
    }
    复制代码

    话说一天过去了,小菜把基本的功能完成了,小菜想反正也有时间于是小菜把他知道的东西都用上了IOC(unit),Log4,Orm(ef)。慢慢的小菜开始将接口丰富了越来越多的功能,最后小菜对经理说了自己的想法,说了用的技术,经理说好"下午部署上吧“,小菜心想我的接口已经有了很丰富的功能,我还提供了很多其他功能的接口,肯定没问题于是小菜欣然的答应了。然而新的挑战就这样悄然的反生了。

    他找到每个Tool的开发人员讲解自己的接口怎样用,能做什么,需要与MainServer以怎样的方式进行通信,不同异常机制信息的提供的不同。小菜发现他对每一开发者讲解的时候,大家更多的是关心自己的Tool应该更少的关注Mainserver,同时大家也更多的是忙于自己的事情,没有多少时间去协同维护你的接口,大家的一致决定是你先写一个Tool对接Mainserver的Demo我们照着改就可以了,你先写吧。

    就这样小菜心想,我已经写的这么好了的接口,为什么大家不喜欢用不喜欢听呢。下班之后小菜一直不思其解,于是拨通了同校大两级也是做开发的学长”老鸟“的电话,简单介绍了事情的经过之后,老鸟呵呵一笑说:“小菜, 你的想法不错,可是就是因为你提供的东西反而太多了,大师不是只懂加法也要会做减法,搭架子要有所为有所不为”。

    未完待续。。。

     
     
  • 相关阅读:
    软件测试从业者,试用期生存指南(完整版)
    学测试,看视频?NONONO,除非这种情况
    vi / vim 字符替换详解
    Centos 下 mysql 安装过程
    Web测试到底是在测什么(资料合集)
    我花了两个小时,写了这份年终总结 。
    拒绝无效加班 !
    你离月薪30K还差哪些?
    从业十年分享:你应该知道的一些测试职业事实!
    别再TM跟我说找不到满意的工作!
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3500474.html
Copyright © 2011-2022 走看看