开源一个跨平台运行的服务插件 - TaskCore.MainForm
本次将要很大家分享的是一个跨平台运行的服务插件 - TaskCore.MainForm,此框架是使用.netcore来写的,现在netcore已经支持很多系统平台运行了,所以将以前的Task.MainForm改良成跨平台的服务共大家使用和相互交流;本来这篇应该分享的是nginx+iis+redis+Task.MainForm构建分布式架构后续文章,但使用.netcore来定义一个服务插件和跨平台测试经过让我既兴奋又头痛,不说了下次再分享分布式后续文章吧;那么开始今天的分享内容,希望各位多多支持:
. 框架结构介绍及运行效果图
. 如何生成nuget包和使用开源框架TaskCore.MainForm
. win7和ubuntu16.04运行TaskCore.MainForm的两种方式和测试截图(也可认为.netcore项目在win7和ubuntu系统运行的两种方式)
. 框架代码解读及感悟
下面一步一个脚印的来分享:
. 框架结构介绍及运行效果图
首先,咋们先来认识下项目源码的工程目录结构如图:
结构看上去应该够清晰了,源码文件量也很少,不过的确实现了动态加载程序集dll来执行任务,后面会给出相应的执行截图,我们再来看下TaskCore.MainForm项目通过vs2015生成之后Bin文件夹中的文件如图:
如果安装了.netcore sdk的话在windows上您只需要上面截图的文件就能运行插件了;再来我们在已经安装过core sdk的win7系统上执行一下如下命令 dotnet TaskCore.MainForm.dll 就能看到如图的效果:
没错这就是插件运行起来的效果图,因为.netcore现目前暂时没有提供类似于winform那样的皮肤界面效果,所有只能通过命令行来做跨平台运行程序
. 如何生成nuget包和使用TaskCore.MainForm
首先,我们要明确服务是由两部分构成(TaskCore.MainForm和TaskCore.Plugin);TaskCore.MainForm主要用来运行程序,TaskCore.Plugin用来作为子任务需要继承的父类插件;当我们下载TaskCore.MainForm运行包之后(如图2结构),我们还需要做的就是继承TaskCore.Plugin.dl中的TPlugin这个类,来重写自己的任务内容,因此我们新建一个项目取名为:TaskCore.Test,再来我们通过vs2015引用功能直接添加TaskCore.MainForm运行包中的TaskCore.Plugin.dl引用,这个时候会提示一个错误:
错误意思是没法加载netcore版本的dll,因此这种直接在vs项目中引用方式添加dll依赖不行,需要通过nuget来添加依赖包(.netcore对类库的引用目前只能通过nuget来安装依赖,这个需要注意),所以我这里把TaskCore.Plugin项目通过 dotnet pack 命令来生成nuget包,然后以便我TaskCore.Test项目中来使用;
如何生成nuget包(win7系统dotnet命令来生成包的过程和下载):
直接在vs中右键您需要打包的类库项目-》选择"在资源管理器中打开文件夹"-》这样就到了您类库的根目录,然后退到类库根目录的上一级文件夹中-》按住键盘”shift"键并同时鼠标右键要打包类库的项目文件件(我这里是TaskCore.Plugin文件夹)-》选择“在此处打开命令窗体” 这个时候就进入了cmd命令窗体,当然有些朋友直接喜欢直接先cmd再来找对应的磁盘,反正我觉得第一种更快点(不同人不同选择吧),下面看下操作截图:
由上图可以看到,通过命令生成了nupkg文件,这文件就是咋们需要在项目中下载安装的TaskCore.Plugin插件包;接下来我们来在TaskCore.Test项目中安装这个插件如下步奏,右键TaskCore.Plugin项目中的“引用”-》选择“管理nuget程序包”-》然后选择右上角的这个图标
-》选择“Nuget包管理器”-》“程序包源”-》
-》然后选择新创建的包源,下面设置如图
这里的“源(S)”指定的本地路径就是刚才我们生成的nupkg文件文件的磁盘(当然我这里是吧刚才pack命令生成的文件复制到了MyNugPackage文件夹中方便测试)-》然后点击”确定“按钮-》然后在返回到
选择我们的包源MyNugPackage-》再浏览这里就能看到我们创建的nuget包了
-》选择并安装-》好安装好后我们可以查看“引用”下面有了如下变化
并且project.json文件中自动添加了依赖项:
好了到我们的TaskCore.Test项目中就可以使用Task.Plugin包里面的方法很类等信息了;
使用TaskCore.MainForm:
由上面的操作TaskCore.Test项目已经安装了Task.Plugin包,那么我们在项目中分别创建3个子类并继承自Task.Plugin包的父类TPlugin并重写方法TPlugin_Load(),对应文件名称分别为:BlogsObj.cs,BlogsObj01.cs,BlogsObj02.cs,分别添加入下代码内容:
BlogsObj.cs:
namespace TaskCore.Test
{
// This project can output the Class library as a NuGet Package.
// To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
public class BlogsObj : TPlugin
{
public BlogsObj()
{ } public override void TPlugin_Load()
{
var sbLog = new StringBuilder(string.Empty);
try
{
sbLog.Append($"这里是BlogsObj,获取配置文件:{this.XmlConfig.Name}"); //代码块
// new WriteLog()._WriteLog($"{DateTime.Now}测试引用nuget包"); }
catch (Exception ex)
{
sbLog.Append($"异常信息:{ex.Message}");
}
finally
{ PublicClass._WriteLog(sbLog.ToString(), this.XmlConfig.Name);
}
}
}
}
BlogsObj01.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TaskCore.Plugin; namespace TaskCore.Test
{
// This project can output the Class library as a NuGet Package.
// To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
public class BlogsObj01 : TPlugin
{
public BlogsObj01()
{ } public override void TPlugin_Load()
{
var sbLog = new StringBuilder(string.Empty);
try
{
sbLog.Append($"这里是BlogsObj01,获取配置文件:{this.XmlConfig.Name}"); //代码块
//
}
catch (Exception ex)
{
sbLog.Append($"异常信息:{ex.Message}");
}
finally
{
//Console.WriteLine($"这里是Blogs,获取配置文件:{this.XmlConfig.Name}");
PublicClass._WriteLog(sbLog.ToString(), this.XmlConfig.Name);
}
}
}
}
BlogsObj02.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TaskCore.Plugin; namespace TaskCore.Test
{
// This project can output the Class library as a NuGet Package.
// To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
public class BlogsObj02 : TPlugin
{
public BlogsObj02()
{ } public override void TPlugin_Load()
{
//Console.WriteLine($"这里是Blogs,获取配置文件:{this.XmlConfig.Name}");
PublicClass._WriteLog($"这里是BlogsObj02,获取配置文件:{this.XmlConfig.Name}", this.XmlConfig.Name);
}
}
}
好了测试代码写完,咋们生成一下,然后把这个TaskCore.Test.dll拷贝到TaskCore.MainForm运行包中,还需要在PluginXml文件夹中分别创建继承TPlugin的子类的配置文件对应的截图如(注:这里xml配置文件名称必须和子类名称一样):
配置文件内容如BlogsObj.xml:
<!--
.xml配置模板
.utf-8文件
.复制到程序根目录下面PluginXml文件夹下
.每个任务建议创建和程序dll名称相同xml配置文件
-->
<TaskMain>
<!--固定:执行任务时间计时器(分钟)-->
<Timer></Timer>
<!--固定:任务名称-->
<Name>获取博客信息</Name>
<!--固定:账号-->
<UserName></UserName>
<!--固定:密码-->
<UserPwd></UserPwd>
<!--固定:key-->
<ApiKey></ApiKey>
<!--固定:key-->
<ApiUrl></ApiUrl>
<!--固定:是否关闭任务 :是 :否-->
<CloseTask></CloseTask> <!--固定:描述-->
<Des>获取博客信息</Des>
<!--自定义:其他配置信息-->
<Other>
<ShenNiuBuXing3>神牛步行3</ShenNiuBuXing3>
</Other>
</TaskMain>
最后在CrossFiles.xml配置文件中添加TaskCore.Test.dll文件名称如:
<!--
CrossFiles指定对应任务的dll文件,必须存在的文件
-->
<TaskMain>
<File>TaskCore.Test.dll</File>
</TaskMain>
好了完成了,我们在windows开发环境上运行看下效果图:
到这里我们来简单总结下怎么使用TaskCore.MainForm插件,在自己项目中安装TaskCore.Plugin的nuget包-》重写父类TPlugin的TPlugin_Load()方法-》生成自己的项目,拷贝项目的dll到TaskCore.MainForm运行包中-》在运行包中PluginXml文件夹中增加任务子类相同类名的xml文件并配置如上的配置信息-》增加CrossFiles.xml中的任务dll文件配置-》使用命令 dotnet TaskCore.MainForm.dll 运行服务插件,挺简单的吧
. win7和ubuntu16.04运行TaskCore.MainForm的两种方式和测试截图(也可认为.netcore项目在win7和ubuntu系统运行的两种方式)
由于环境影响,这里跨平台运行测试我只测试win7和ubuntu16.04,其他系统的发布测试希望朋友们在使用过程中得到结果告知我下谢谢;先来讲在win7中运行:
1. 安装了netcore sdk环境的发布运行
安装了sdk后直接可以在cmd命令中 dotnet TaskCore.MainForm.dll 来运行服务,开篇上面讲解的示例都是在安装了sdk后的截图,服务运行所需要的文件如图:
就只有这些文件(当然程序需要的某些平台依赖项使用的是安装的sdk中的文件,所以看起来很少实际应该包含netcore sdk里面的文件才能运行),通过命令运行的效果图:
2. 未安装netcore sdk环境的发布运行
在为安装sdk平台上系统上运行才是重点,这样才可以说是跨平台;首先,为了更好看效果我们复制一份如图的两个文件到TaskCore.MainForm01文件夹中:
没错,只需要这两个文件,然后我们需要修改project.json文件的内容如下注释的地方:
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true
}, "dependencies": {
"Microsoft.NETCore.App": {
//"type": "platform", 跨平台发布需要注释
"version": "1.0.0"
},
"System.IO.FileSystem": "4.0.1",
"System.Reflection": "4.1.0",
"System.Text.Encoding.CodePages": "4.0.1",
"System.Threading.Timer": "4.0.1",
"System.Xml.XDocument": "4.0.11",
"TaskCore.Plugin": "1.0.0"
}, "frameworks": {
"netcoreapp1.0": {
"imports": "dnxcore50"
}
},
//跨平台发布需要添加如下节点
"runtimes": {
"ubuntu.16.04-x64": { }, //运行在ubuntu.16.04的64位系统
"win7-x64": { } //运行在win7的64位系统
}
}
然后使用cmd分别录入并回车运行指令 dotnet restore 此时TaskCore.MainForm01文件夹中会自动生成一个project.lock.json文件(大家注意看下),然后再录入指令 dotnet publish -r win7-x64 ,可以看到命令窗信息如:
就表示成功了,并且有返回生成运行文件的路径,我们按照路径找到生成的文件publish文件夹,里面没有PluginXml配置文件夹和配置文件和测试项目TaskCore.Test.dll,我们为了方便直接复制上面配置好的配置文件到publish文件夹中如下截图就是生成的发布文件:
没错TaskCore.MainForm01.exe这个就是自动生成的运行文件,然后我们双击运行效果图:
运行成功了,有朋友会问您之前系统不是安装了sdk么,这种测试能算么,我想说的是这个我通过QQ发给qq群里面的朋友@南宫亦琦(不要怪我贴出了您的昵称)测试过了,她没有安装过sdk的;
ubuntu16.04运行:
1. 安装了netcore sdk环境的发布运行
首先,我们需要把在win7上生成的项目复制到ubuntu系统磁盘中(我们使用上面配置好的TaskCore.MainForm包),我这里采用共享目录的方式把文件拷贝到ubuntu系统磁盘上,如图:
然后,鼠标右键点击空白地方,选择“在终端打开”,然后在窗体中录入如下指令 dotnet TaskCore.MainForm.dll ,能看到如下运行起的结果:
这个命令方式是不是很上面win7上的方式很相同,本来就是一样的,只要安装了.netcore sdk这种方式几乎可以共用
2. 未安装netcore sdk环境的发布运行
不安装sdk环境运行才是咋们需要关注的,下面就来看下怎么生成再ubuntu系统上能运行的文件;我们复制一份上面的TaskCore.MainForm01项目到TaskCore.MainForm02中来测试,由于之前我们在讲生成win7执行文件时候,执行了命令 dotnet restore 和 dotnet publish -r win7-x64 命令所以文件中有project.lock和bin文件夹,为了测试我们需要删除掉部分文件,只剩下如图文件,这里需要注意的是之前已经配置过project.xml我们无需在修改了:
然后,分别执行和生成win7运行文件几乎相同的命令 dotnet restore 和 dotnet publish -r ubuntu.16.04-x64 不同之处在于生成的文件存放的位置不同和使用的运行环境不同,运行结果如图:
publish文件夹中的文件就是在ubuntu系统执行的文件,然后我们需要把任务配置文件夹PluginXml和测试项目TaskCore.Test.dll复制到该目录中,再来咋们通过共享吧TaskCore.MainForm02目录中的publish复制到ubuntu中;然后我们需要把TaskCore.MainForm02可执行文件设置下权限,鼠标右键点击TaskCore.MainForm02可执行文件,选择“属性”,再选择“权限”选项卡,勾选“允许作为程序执行”,如图:
然后,我们右键文件夹中空白处,选择“在终端打开”,再执行如下命令 ./TaskCore.MainForm02 ,最后看运行效果图:
好了,发布在ubuntu系统上执行文件和运行的步奏就这些,几乎和win7上相同
. 框架代码解读及感悟
怎么使用跨平台TaskCore.MainForm任务框架讲解完了,下面来具体看下主要代码Program.cs文件中:
namespace TaskCore.MainForm
{
/// <summary>
/// author 神牛步行3
/// contact 841202396@qq.com
/// des TaskCore.MainForm跨平台插件由神牛步行3提供
/// </summary>
public class Program
{ private static Dictionary<string, MoAssembly> dicTasks = new Dictionary<string, MoAssembly>(); public static void Main(string[] args)
{
//注册编码,防止乱码
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); //初始化程序集文件
_Init(); //是否继续开启任务,默认没有待执行任务,不提示
if (dicTasks.Count <= ) { _LoopAlert("是否退出?(Y/N)"); return; }
_LoopAlert("是否开始执行任务?(Y / N)"); //执行任务
foreach (var item in dicTasks.Values)
{
//使用Task防止异常后相互影响
Task.Run(() =>
{
try
{ //创建任务对象
var tp = item.Asm.CreateInstance(item.FullName) as TPlugin;
if (!string.IsNullOrEmpty(tp.XmlConfig.TpError)) { _Alert($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm")}:{tp.XmlConfig.Name} - 异常信息:{tp.XmlConfig.TpError}"); }
else
{ //timer定时器
var timer = new Timer((param) =>
{
var msg = $"{DateTime.Now.ToString("yyyy/MM/dd HH:mm")}:{tp.XmlConfig.Name}";
try
{
var tpObj = param as TPlugin;
//是否关闭暂停任务
if (tpObj.XmlConfig.CloseTask) { return; }
_Alert($"{msg} - 开始执行...{tp.XmlConfig.Timer}分钟一次");
//任务入口
tpObj.TPlugin_Load();
}
catch (Exception ex) { _Alert($"{msg} - 异常信息:{ex.Message}"); }
}, tp, , * * tp.XmlConfig.Timer);
} }
catch (Exception ex)
{
_Alert($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm")}:{item.Name} - 异常信息:{ex.Message}");
}
});
}
_LoopAlert("正在监控执行的任务,是否退出?(Y / N)");
} /// <summary>
/// 初始化程序集文件
/// </summary>
private static void _Init()
{
try
{ _Alert("初始化任务中...");
//获取文件
var files = PublicClass._GetPluginFile("");
if (files.Length <= ) { _Alert("未能找到可用的程序集,请检查配置"); return; } //读取任务文件
_Alert("读取CrossFiles.xml配置中...");
var baseAddr = Path.Combine(Directory.GetCurrentDirectory(), "PluginXml", "CrossFiles.xml");
var doc = XDocument.Load(baseAddr);
var fileables = files.AsEnumerable();
var taskFiles = new List<FileInfo>();
foreach (var item in doc.Root.Nodes())
{
var crossFile = item.ToString().ToUpper();
var choiceFiles = fileables.Where(b => crossFile.Contains(b.Name.ToUpper()));
if (!choiceFiles.Any()) { continue; } taskFiles.AddRange(choiceFiles);
} //展示文件信息
_Alert($"待遍历{taskFiles.Count}个文件信息...");
foreach (var item in taskFiles.OrderBy(b => b.CreationTime))
{
var asmName = new AssemblyName($"{item.Name.Replace(".dll", "")}");
Assembly sm = Assembly.Load(asmName);
if (sm == null) { continue; }
var ts = sm.GetTypes();
//判断特定的任务类,加入任务dic
foreach (var t in ts.Where(b => b.Name != "TPlugin" && b.GetMethod("TPlugin_Load") != null))
{ dicTasks.Add(
t.FullName,
new MoAssembly
{
Asm = sm,
FullName = t.FullName,
Name = t.Name
});
}
}
_Alert($"获取待执行任务量:{dicTasks.Count}个");
}
catch (Exception ex)
{
_Alert($"异常信息:{ ex.Message}");
}
} /// <summary>
/// 消息提醒
/// </summary>
/// <param name="msg">提示信息</param>
/// <param name="isReadLine">是否需要用户输入指令</param>
/// <returns>用户录入的指令</returns>
private static string _Alert(string msg = "神牛步行3-消息提醒", bool isReadLine = false)
{
Console.WriteLine(msg);
if (isReadLine) { return Console.ReadLine(); }
return "";
} private static void _LoopAlert(string msg = "是否开始执行任务?(Y/N)")
{
do
{
var readKey = _Alert(msg, true);
if (readKey.ToUpper().Contains("Y")) { break; }
} while (true);
}
} public class MoAssembly
{
public Assembly Asm { get; set; }
public string FullName { get; set; } public string Name { get; set; }
}
}
TPlugin.cs文件中代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq; namespace TaskCore.Plugin
{
/// <summary>
/// 插件基类
/// </summary>
public class TPlugin : IDisposable
{ public TPlugin()
{ XmlConfig = _InitConfig();
} #region 初始化Xml配置文件 _InitConfig +XmlConfig /// <summary>
/// xml配置信息
/// </summary>
public XmlConfig XmlConfig; /// <summary>
/// 初始化配置信息
/// </summary>
/// <param name="configPath">配置文件对应路径</param>
/// <returns></returns>
public virtual XmlConfig _InitConfig(string configPath = "")
{
XmlConfig config = new XmlConfig();
config.Timer = ;
config.Name = this.GetType().Name;
try
{ if (string.IsNullOrEmpty(configPath))
{ //默认各个dllXml配置
var defaultConfigFolder = "PluginXml";
var baseAddr = Directory.GetCurrentDirectory();
configPath = Path.Combine(baseAddr, defaultConfigFolder, config.Name + ".xml");
} var doc = XDocument.Load(configPath);
config.doc = doc;
var taskMain = doc.Root; config.Timer = Convert.ToInt32(taskMain.Element(XName.Get("Timer", "")).Value);
config.Name = taskMain.Element(XName.Get("Name", "")).Value;
config.Des = taskMain.Element(XName.Get("Des", "")).Value; config.UserName = taskMain.Element(XName.Get("UserName", "")).Value;
config.UserPwd = taskMain.Element(XName.Get("UserPwd", "")).Value;
config.ApiKey = taskMain.Element(XName.Get("ApiKey", "")).Value;
config.ApiUrl = taskMain.Element(XName.Get("ApiUrl", "")).Value;
config.CloseTask = taskMain.Element(XName.Get("CloseTask", "")).Value == ""; }
catch (Exception ex)
{
config.TpError = ex.Message;
PublicClass._WriteLog($"{config.Name}初始化配置信息异常:{ex.Message}", "BaseLog");
throw new Exception(ex.Message);
}
return config;
}
#endregion #region 初始化-开始加载 _Load /// <summary>
/// 初始化-开始起
/// </summary>
public virtual void TPlugin_Load()
{ PublicClass._WriteLog("测试");
} #endregion #region 释放资源 public void Dispose()
{ GC.SuppressFinalize(this);//不需要再调用本对象的Finalize方法
} public virtual void Dispose(Action action)
{ action();
} #endregion
} #region 配置文件 XmlConfig public class XmlConfig
{
public XmlConfig()
{ } /// <summary>
/// 定制器时间(分钟)
/// </summary>
public int Timer { get; set; } /// <summary>
/// 运行名称
/// </summary>
public string Name { get; set; } /// <summary>
/// 描述(第一次获取dll描述,后面获取xml配置文件描述)
/// </summary>
public string Des { get; set; } /// <summary>
/// 接口账号
/// </summary>
public string UserName { get; set; } /// <summary>
/// 接口密码
/// </summary>
public string UserPwd { get; set; } /// <summary>
/// 接口秘钥
/// </summary>
public string ApiKey { get; set; } /// <summary>
/// 接口地址
/// </summary>
public string ApiUrl { get; set; } /// <summary>
/// 是否关闭任务
/// </summary>
public bool CloseTask { get; set; } /// <summary>
/// 插件中错误
/// </summary>
public string TpError { get; set; } /// <summary>
/// xml信息
/// </summary>
public XDocument doc { get; set; }
} #endregion
}
具体的说明和逻辑处理代码中都有注释,各位可以详细看下;这里要诉说的是该框架主要原理是动态加载任务dll来创建对象,netcore的程序集类Assembly不像framework里面的Assembly一样那么多可以用方法,我这里用的 Assembly.Load() ,netcore只能加载当前系统根目录的dll(这里我花了几个小时测试,有不同结果的朋友,请及时联系我谢谢),框架使用 Task.Run() 方法创建不同任务,实现并行执行的效果,并且各个容易出错地方使用try...catch来容错,避免了某个子任务异常后,导致框架全部中断运行,个人觉得其实还是不错的是吧;
下面给出几个不同环境下的压缩包,以供使用和参考:
TaskCore.MainForm-纯文件 TaskCore.MainForm01 git源码地址
开源一个跨平台运行的服务插件 - TaskCore.MainForm的更多相关文章
- 通过Dapr实现一个简单的基于.net的微服务电商系统(十三)——istio+dapr构建多运行时服务网格之生产环境部署
之前所有的演示都是在docker for windows上进行部署的,没有真正模拟生产环境,今天我们模拟真实环境在公有云上用linux操作如何实现istio+dapr+电商demo的部署. 目录:一. ...
- BootCDN 开源项目免费 CDN 加速服务,Jquery插件库
2017-11-17 19:38:32 免费好用的在线 css js 文件引用 BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务 Jquery插件库 .
- AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(四):开源的Silverlight运行容器的编译、配置
一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...
- [开源 .NET 跨平台 数据采集 爬虫框架: DotnetSpider] [一] 初衷与架构设计
[DotnetSpider 系列目录] 一.初衷与架构设计 二.基本使用 三.配置式爬虫 四.JSON数据解析与配置系统 为什么要造轮子 同学们可以去各大招聘网站查看一下爬虫工程师的要求,大多是招JA ...
- 微软良心之作——Visual Studio Code 开源免费跨平台代码编辑器
微软良心之作——Visual Studio Code 开源免费跨平台代码编辑器 在 Build 2015 大会上,微软除了发布了 Microsoft Edge 浏览器和新的 Windows 10 预览 ...
- Google FlatBuffers——开源、跨平台的新一代序列化工具
前段时间刚试用了一个序列化工具cereal,请看cereal:C++实现的开源序列化库,打算再总结下我对google proto buf序列化库的使用呢, 结果还没动手,大Google又出了一个新的. ...
- 编写一个go gRPC的服务
前置条件: 获取 gRPC-go 源码 $ go get google.golang.org/grpc 简单例子的源码位置: $ cd $GOPATH/src/google.golang.org/gr ...
- C 实现一个跨平台的定时器 论述
引言 今天我们要讲述和构建的是一个跨平台多线程C的定时器对象,粒度是毫秒级别.可以称之为简易的timer, sctimer.h 库. 首先看总的接口,看门见客. sctimer.h #ifndef _ ...
- MvvmCross[翻译] 使用Xamarin与MvvmCross完成一个跨平台App
总览 原文:https://github.com/MvvmCross/MvvmCross/wiki/Tip-Calc-A-first-app 我们所做的第一个Model-View-ViewModel( ...
随机推荐
- .NET面试题系列[8] - 泛型
“可变性是以一种类型安全的方式,将一个对象作为另一个对象来使用.“ - Jon Skeet .NET面试题系列目录 .NET面试题系列[1] - .NET框架基础知识(1) .NET面试题系列[2] ...
- 猫哥网络编程系列:HTTP PEM 万能调试法
注:本文内容较长且细节较多,建议先收藏再阅读,原文将在 Github 上维护与更新. 在 HTTP 接口开发与调试过程中,我们经常遇到以下类似的问题: 为什么本地环境接口可以调用成功,但放到手机上就跑 ...
- 【资源】.Net 入门@提高 - 逆天的高薪之路!
入门看视频,提高看书籍,飘升做项目.老练研开源,高手读外文,大牛讲低调~ 官方学习计划 http://www.cnblogs.com/dunitian/p/5667901.html ----- ...
- 虚拟dom与diff算法 分析
好文集合: 深入浅出React(四):虚拟DOM Diff算法解析 全面理解虚拟DOM,实现虚拟DOM
- nginx的使用
1.nginx的下载 解压后文件目录: 2.nginx的常用命令 nginx -s stop 强制关闭 nginx -s quit 安全关闭 nginx -s reload 改变配置文件的时候,重 ...
- Android——eclipse下运行android项目报错 Conversion to Dalvik format failed with error 1解决
在eclipse中导入android项目,项目正常没有任何错误,但是运行时候会报错,(clean什么的都没用了.....)如图: 百度大神大多说是jdk的问题,解决: 右键项目-Properties如 ...
- Android中AlarmManager使用示例(持续更新,已经更改)
现在普遍的手机都会有一个闹钟的功能,如果使用Android来实现一个闹钟可以使用AtarmManager来实现.AtarmManager提供了一种系统级的提示服务,允许你安排在将来的某个时间执行一个服 ...
- 在MySQL数据库中创建一个完整的表
1.登陆成功后,首先进入某一个数据库 (不是指数据库服务器) use t1; //t1是数据库名 如图所示: 2.在此数据库中建立数据库表 2.1 先建立表结构(可以理解为表的列名,也就是字段名)在实 ...
- 12个小技巧,让你高效使用Eclipse
集成开发环境(IDE)让应用开发更加容易.它们强调语法,让你知道是否你存在编译错误,在众多的其他事情中允许你单步调试代码.像所有的IDE一 样,Eclipse也有快捷键和小工具,这些会让您感觉轻松许多 ...
- 二叉树的递归实现(java)
这里演示的二叉树为3层. 递归实现,先构造出一个root节点,先判断左子节点是否为空,为空则构造左子节点,否则进入下一步判断右子节点是否为空,为空则构造右子节点. 利用层数控制迭代次数. 依次递归第二 ...