C#总结(四)调用C++动态库

由于公司很多底层的SDK,都是C++开发,上层的应用软件却是C# Winform程序。在实际工作的过程中,就经常碰到了C# 程序调用C++ 动态库的问题。最近一直在和C++ 打交道,C# 怎么调用C++ 类库函数。也遇到了一些问题,所以就来总结总结C#程序调用C++动态库时的各种坑。

 

1. 可能遇到的问题:

C#在调用动态库的过程中我也遇到了以下一些问题:

1、C++中有指针,C#中需要使用指针吗?

由于C++中的动态库中有指针参数,因此我也是用.NET的不安全代码,使用了C#的指针,但是也还是出现了一些问题,如在C#中传入的参数是一个二维数组时就出现了问题,最后只能改C++函数传入参数的参数类型。

2、C#和C++中的类型如何转换呢?

虽然C#和C++比较类似,但是其给我们的参数类型我们要与C#的参数类型一一对应起来,具体看后续说明。

3、C++函数中的CallingConventionCharSet 怎么设置?

调用C++函数之前一定要先确认,否则可能出现函数调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配的问题。函数的CallingConvention和CharSet,可以查看动态库对应的 .h头文件。

4、如何反编译C++的dll的名称,端口?

可以通过Dependency Walker工具进行反编译查看别人写的动态库的信息

5、指针函数如何传参?

对于函数需要的指针函数,C# 调用时,可以定义委托来传入参数。

6、需要注意C++ dll 编译的平台是x86还是x64,是多字节的还是双字节的(Unicode)。

 

  2. 通过Dependency Walke查看dll的名称,端口

  下载Dependency 后将对应的C++ dll文件加载进去,就尅看到动态库的对应的信息,同时也可以通过.h 头文件查看。

 

 3. 如何调用

c#调用c++动态库一般我们这样写

    [DllImport(SDK, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    public static extern int IKSDK_Release();
 1. DllImport的第一个参数SDK是动态库dll的路径,此dll放在程序运行的根目录或者c:windows/sytem32下,建议在程序根目录创建一个子目录来放置相应的C++ 动态库文件,方便以后更新。

2. CallingConvention 参数是c#调用c++的方式 是个枚举 msdn解释如下:

Cdecl 调用方清理堆栈。这使您能够调用具有 varargs 的函数(如 Printf),使之可用于接受可变数目的参数的方法。
FastCal 不支持此调用约定。
StdCall 被调用方清理堆栈。这是使用平台 invoke 调用非托管函数的默认约定。
ThisCall 第一个参数是 this 指针,它存储在寄存器 ECX 中。其他参数被推送到堆栈上。此调用约定用于对从非托管 DLL 导出的类调用方法。
Winapi 此成员实际上不是调用约定,而是使用了默认平台调用约定。例如,在 Windows 上默认为 StdCall,在 Windows CE.NET 上默认为 Cdecl

3. CharSet参数是控制名称重整以及将字符串参数封送到函数中的方式。 默认值为 CharSet.Ansi。

4. entrypoint参数用于标识函数在 DLL 中的位置。在托管对象中,目标函数的原名或序号入口点将标识跨越交互操作边界的函数。此外,您可以将入口点映射到一个不同的名称,这实际上是将函数重命名。一般默认不设置此参数。

5. 其他参数,请查看MSDN对于 DllImportAttribute 的说明。

 

4. 其他说明

C# 调用C++ 动态库,还有一个特别麻烦的问题就是参数对于的问题。后续会结合网上的资料总结一份详细的对照表。

 

如何内网搭建NuGet服务器

  NuGet 是.NET程序员熟知的工具,它可以直接安装开源社区中的各个公用组件,可以说是非常方便。不过,有些时候,公司内部的公用的基础类库,各个项目都有引用,但又不能发布到公共的NuGet服务器上,所以我们需要自己架设一个私有服务,通过NuGet 来的各个项目中用到的公共组件进行管理。就非常的方便。也便于组件版本的管理。

 

一、NuGet网站构建

  1. 创建一个空的ASNET网站

   

   注意:将Framework 设置为4.6 以上,NuGet.Server库 要求.net framework 的版本是4.6 。

 

  2. 用NuGet为 NuGetServer 安装“NuGet.Server”库。
 
  安装成功后,你的Solution会变成这样一个结构。并且不需要写任何代码或更改任何配置,它已经可以运行了。运行此网站,如果看到下面的欢迎界面,就表示网站已经构建完成了。
   
   注意:Packages里只有一个readme.txt,这个目录是用来放NuGet包的,点击 here 连接,可以查看现有的包。

  3. 部署网站

  这也和部署任何一个ASP.NET网站完全一样。这里不细说。
 

二、把代码打包为NuGet包

  1. 去Nuget官网下载nuget command line tool,

   下载地址:https://www.nuget.org/downloads

 

  2. 用Nuget打包

   Nuget 有两种打包方式:

   A:直接通过工程文件打包dll:

     1 创建一个类库,然后编译下。把刚才下载的nuget.exe拷贝到工程文件所在的文件夹中:

       

    2. 通过命令行来创建nuget包:nuget.exe pack

     

 

  B:使用nuget的spec文件进行打包,。

    1. 同样把要打包的DLL跟NuGet.exe放在一个目录下,用nuget spec命令创建一个nuspec文件:

    

 

    2. 打开Package.nuspec文件,可自定义一些配置,。

     注意$description$使用AssemblyDescriptionAttribute的值进行替换,在产生package之前,一定要记得先编译项目,否则会提示$description$找不到的错误;

 

    3. 用 NuGet pack打包成Nuget包:

     

 

 

  至此打包完毕。

 

三、 把NuGet包上传到服务器

  将打包好的.nupkg 文件 拷贝到 第一步中架好的Nuget服务器 的Package目录下。可以Nuget 服务中自己写一个文件上传的功能,来降低拷贝的麻烦。

四、配置Visual Studio

  Nuget 服务已经架设成功,接下来就要在VS里配置NuGet 源,让其他的项目可以使用我们的NuGet服务器。
  选择工具-》Nuget包管理器-》程序包管理控制台,点击 配置图标:

   

添加,设置本地的NuGet源名字和地址即可。注意:source 最后是 /nuget

  完成后,我们的VS就可以从私服上安装NuGet包了。

五、在其他项目中使用 

  到目前为止NuGet服务器端已经配置完成,类库和DLL文件的打包也介绍完毕, 接下来我们该如何在项目中引用内网NuGet 服务器中的包呢,步骤如下:

  1、 新建一个NuGetTest 项目,然后 工具-》Nuget包管理器-》程序包管理控制台 ,在命令行:install-package IKSDK 

 

  2、最后看看效果啦:

 

至此,内网搭建NuGet服务器 的过程就介绍完了,,

 

 

电商总结(八)如何打造一个小而精的电商网站架构

前面写过一些电商网站相关的文章,这几天有时间,就把之前写得网站架构相关的文章,总结整理一下。把以前的一些内容就连贯起来,这样也能系统的知道,一个最小的电商平台是怎么一步步搭建起来的。对以前的文章感兴趣的朋友可以看这个,http://www.cnblogs.com/zhangweizhong/category/879056.html

本文大纲:

1. 小型电商网站的架构

2. 日志与监控系统的解决方案

3. 构建数据库的主从架构

4. 基于共享存储的图片服务器架构

5. 移动M站建设

6. 系统容量预估

7. 缓存系统

 

一、小型电商网站的架构

 

刚从传统软件行业进入到电商企业时,觉得电商网站没有什么技术含量,也没有什么门槛,都是一些现有的东西堆积木似的堆出来罢了。然而,真正进入到这个行业之后,才发现并非如此。有人说过,好的架构,是演化出来的,电商网站的架构也是如此。现在好的电商网站,看似很复杂,很牛逼,其实也是从很小的架构,也是从没什么技术含量开始的。所以,架构的演化过程,就是在技术团队不断追求极致的过程。

 

今天就来总结小型电商网站的架构演进。一套电商系统最初期的架构,往往会采用一个比较典型的LAMP架构,前端加上Apache/PHP,后端是MySQL。这个算是比较流行的。不过,目前还有一套.net的技术架构,可能大家很少提到。很不幸,我就是在一个.net平台为基础的电商公司。所以,今天也是要总结.net 平台的电商架构。

 

1技术架构

 

 

一般初期的电商网站,基本就几个业务子系统:网站前台、商家前台、系统管理后台、App、M站等。业务量也不是很大。所以,MVC + 缓存 + 数据库基本就搞定了。

 

单就开发效率而言,.net MVC 的技术架构不会比LAMP开发速度慢。所以,一些企业,为了快速推出自己的电商平台,也会采用.net 架构。

 

2基础架构

 

 

上图为基础架构层面。这是一个很简单的基础架构。

 

  • 前端网站和M站,考虑到访问量和系统的可用性,基本会采用分布式部署。通过代理服务器进行请求分发。
  • 其它的业务子系统,像商家前台和管理系统,基本上都是单机或是主从部署。
  • 各个DB ,Redis 服务和文件和图片服务,搜索引擎Solr服务等,采用主从部署。

 

3详细架构

 

 

整个系统架构里面,还有一个比较重要的组成部分,那就是监控系统。例如:流量监控、硬件监控、系统性能监控等, 还有就是对某个页面进行监控,设置页面的其中一块进行监控等。它是提高整个平台可用性的一个重要手段。多平台、多个维度的监控,能够确保系统的可用性。一旦出现异常,特别在硬件或者性能方面出现异常,监控系统也能立刻发出警告,这样也好防范于未然。

 

总而言之,一个好的系统架构应该从扩展性、安全性、性能和可靠性来考虑。罗马不是一天建成的,架构适合就行,可以先行之而后优。通过渐进演化的过程,逐步让系统越来越完善。

 

 

二、日志与监控系统的解决方案

 

监控系统主要用于服务器集群的资源和性能监控,以及应用异常、性能监控、日志管理等多维度的性能监控分析。一个完善的监控系统和日志系统对于一个系统的重要性不必多说。总之,只有实时了解各系统的状态,才能保证各系统的稳定。

 

 

如上图所示,监控平台监控的范围很广,从服务器性能及资源,到应用系统的监控。每个公司都有特定的平台统一监控的需求及解决方案,但监控平台的任务和作用基本是一致的。

 

1日志

 

日志是监视程序运行的一种重要的方式,主要有两个目的:1.bug的及时发现和定位;2.显示程序运行状态。

 

正确详细的日志记录能够快速的定位问题。同样,通过查看日志,可以看出程序正在做什么,是不是按预期的设计在执行,所以记录下程序的运行状态是必要的。这里将日志分为两种:1.异常日志;2.运行日志。

 

我们主要是使用log4net,将各个系统的日志,持久化记录到数据库或者文件中,以方便后续的系统异常监控和性能分析。如何集成log4net,这里不多说。

 

日志记录的几个原则:

  • 日志级别一定要区分清楚,哪些属于error、warning、info等。
  • 记录错误的位置。如果是分层系统,一定要在某个层统一处理,例如我们的MVC架构,都是在各个Action中Catch异常并处理,而业务层和数据库层这些地方的异常,都是Catch到异常后,往上一层抛。
  • 日志信息清晰准确有意义,日志尽量详细点,以方便处理。应该记录相关系统、模块、时间、操作人、堆栈信息等。方便后续处理。

 

2监控

 

监控系统是一个复杂的系统平台,目前有很多的开源产品和平台。不过我们平台小,监控任务和需求少,所以基本都是自己开发。主要有这五个方面:1.系统资源;2.服务器;3.服务;4.应用异常;5.应用性能。

 

具体的架构图如下:

 

 

1)系统资源监控

监控各种网络参数和各服务器相关资源(CPU、内存、磁盘读写、网络、访问请求等),保证服务器系统的安全运营,并提供异常通知机制以让系统管理员快速定位/解决存在的各种问题。目前比较流行的应该是Zabbix。

 

2)服务器监控

服务器的监控,主要是监控各个服务器、网络节点、网关等网络设备的请求响应是否正常。通过定时服务,定时去Ping各个网络节点设备,以确认各网络设备是否正常。如果哪个网络设备出现异常,则发出消息提醒。

 

3)服务监控

服务监控,指的是各个Web服务、图片服务、搜索引擎服务、缓存服务等平台系统的各项服务是否正常运行。可以通过定时服务,每隔一段时间,就去请求相关的服务,以确保平台的各项服务正常运行。

 

4)应用异常监控

目前我们平台所有系统的异常记录,都记录在数据库中。通过定时服务,统计分析一段时间之内的异常记录。如果发现有相关重要的模块的系统异常,比如支付、下单模块频繁发生异常,则立即通知相关人员处理,确保服务正常运行。

 

5)应用性能监控

在API接口和各应用的相关位置进行拦截和记录下程序性能(SQL性能,或是 程序执行效率)。相关重要模块提供性能预警,提前发现问题。 同时统计相关监控信息并显示给开发的人员,以方便后续的性能分析。

 

 

三、构建数据库的主从架构

 

发展到大型成熟的公司之后,主从架构可能就有点落伍了,取而代之的是更加复杂的数据库集群。但作为一个小型电商公司,数据库的主从架构应该是最基础的。任何大型的系统架构,都是不断演进的。主从架构便是数据库架构中最基础的架构。所以研究完主从架构,也就能看懂更加复杂的架构了。

 

首先为什么要读写分离?

 

对于一个小型网站,可能单台数据库服务器就能满足需求。但在一些大型的网站或者应用中,单台的数据库服务器可能难以支撑大的访问压力,升级服务器性能成本又太高,所以必须要横向扩展。还有就是,单库的话,读、写都是操作一个数据库。数据多了之后,对数据库的读、写性能就会有很大影响。同时对于数据安全性和系统的稳定性也是挑战。

 

数据库的读写分离的好处?

 

  • 将读操作和写操作分离到不同的数据库上,避免主服务器出现性能瓶颈;
  • 主服务器进行写操作时,不影响查询应用服务器的查询性能,降低阻塞,提高并发;
  • 数据拥有多个容灾副本,提高数据安全性,同时当主服务器故障时,可立即切换到其他服务器,提高系统可用性。

 

 

读写分离的基本原理就是让主数据库处理事务性增、改、删操作(Insert、Update、Delete)操作,而从数据库处理Select查询操作。数据库复制被用来把事务性操作导致的变更同步到其它从数据库。

 

以SQL为例,主库负责写数据、读数据。读库仅负责读数据。每次有写库操作,同步更新到读库。写库就一个,读库可以有多个,采用日志同步的方式实现主库和多个读库的数据同步。

 

1SQL Server 读写分离的配置

 

SQL Server提供了三种技术,可以用于主从架构之间的数据同步的实现:日志传送、事务复制和SQL 2012 中新增的功能Always On 技术。各自优劣,具体的大家自己去百度吧,这里提供网上的朋友的配置方式,仅供参考。

 

 

(图源:网络)

 

2C# 数据库读写操作

 

C#的请求数据库操作,单数据库和主从架构的数据库还是不一样的。主从架构的数据库,为了保证数据一致性,一般主库可读可写,从库只负责读,不负责写入。所以,实际C#在请求数据库时,要进行区别对待。

 

最简单的就是:配置两个数据库连接,然后在各个数据库调用的位置,区分读写请求相应的数据库服务器,如下图:

 

第二种解决方案就是判断SQL语句是写语句(Insert 、Update、Create、 Alter)还是读语句(Select)。

 

Demo下载:http://files.cnblogs.com/files/zhangweizhong/Weiz.DB.rar

 

(PS:此Demo为本人总结,跟实际生产中的DLL 不太相同,但原理是一样的,大家总结封装吧)

 

 

同时,增加相关的数据库配置

 

 

 

四、基于共享存储的图片服务器架构

 

在当前这个互联网的时代,不管何种网站,对图片的需求量越来越大。尤其是电商网站,几乎都会面临到海量图片资源的存储、访问等相关技术问题。在对图片服务器的架构、扩展、升级的过程中,肯定也会碰到各种各样的问题与需求。当然这并不代表,你就必须得弄一个特别NB的图片服务架构,只要简单、高效、稳定就行。这部分我们来总结一个特别简单、高效的图片服务架构:通过共享存储的方式来实现图片服务架构。

 

然而,也有一些人问我,现在大型网站的图片服务器的架构已经完全不是这样了,别人家的图片系统比你这个牛逼多了,为啥不直接写那个呢?

 

事实是:第一,大型牛逼的系统我也不会;第二, 再牛逼的系统也是从小的架构演化过去的,没有一步到位的。这里介绍图片服务器架构虽然比较简单,但也是经过了单机时代的演化了,基本上可以满足中小型分布式网站的需求。这种架构的搭建和学习成本都极低,符合目前“短平快”的开发模式。

 

通过共享目录的方式实现共享存储 ,在共享目录文件服务器上配置独立域名,这样可以将图片服务器和应用服务器进行分离,来实现独立图片服务器。

 

优点:

1. 将图片服务和应用服务分离,缓解应用服务器的I/O负载。

2. 通过共享目录的方式来进行读写操作,可以避免多服务器之间同步相关的问题。

3. 相对来讲很灵活,也支持扩容/扩展。支持配置成独立图片服务器和域名访问,方便日后的扩展和优化。

4. 相对于更加复杂的分布式的NFS系统,这种方式是性价比高,符合目前互联网的“短平快”的开发模式。

 

缺点 :

1. 共享目录配置有些繁琐。

2. 会造成一定的(读写和安全)性能损失。

3. 如果图片服务器出现问题,那所有的应用都会受到影响。同时也对存储服务器的性能要求特别高。

4. 图片上传操作,还是得经过Web服务器,这对Web服务器还是有巨大的压力。

 

架构非常简单,基本架构如下图所示:

 

 

 

在存储服务器上建立一个共享目录(具体方式,我就不去重复了,自己百度吧,注意共享目录的文件安全)。

 

各个应用直接通过共享目录(\\192.168.1.200),将图片上传到存储服务器上。

 

建立一个Web站点(i1.abc.com)将该共享目录通过Web站点发布出去。这样其它的应用就能访问到相关图片。

 

所以,各应用将文件上传到共享目录

 

//保存原图
//完整的地址:\\192.168.1.200\lib\2016\03\04\10\IMG\4ugvvt6m9gdu.jpg
relativePath = relativeDir + fileName + imageExtension;

var absolutePath = ConfigHelper.SharePath + relativePath;

fileData.SaveAs(absolutePath);

 

上传成功后,可直接通过web 的方式访问:

http://i1.abc.com/lib/2016/03/04/10/IMG/4ugvvt6m9gdu.jpg

 

 

五、移动M站建设

 

最近在一直在搞M站,也就是移动Web站点。由于是第一次,也遇到了很多问题,所以把最近了解到的东西总结一番。聊一聊什么是移动M站,以及它有什么作用和优势。

 

有人会问,M站和APP有什么不同?

 

  1. APP直接在用户的移动设备上,曝光率相对较高。 而M站需打开浏览器,输入地址才能访问,所以曝光率相对较低。
  2. M站的推广的渠道相比移动APP,渠道较多,方便追踪用户来源、流量入口等,方便以后的活动推广和数据分析。
  3. M站用户无需安装,输入URL即可访问,而APP需要下载安装。
  4. M站能够快速地通过数据分析,能快速得到用户的反馈,从而更容易根据统计数据分析和用户的需求来调整产品。
  5. APP对用户更具粘性及用户体验也更好。
  6. M站对于营销推广活动非常方便,转发分享方便快捷。
  7. M站更新迭代产品速度和响应产品调整非常快,随时发布,而APP需要审核时间。
  8. M站跨平台,无需开发安卓和iOS版,只需有浏览器即可。

 

所以, 我觉得,M站和客户端是相辅相成的。M站的及时性和快捷性,是APP无法比拟的。而APP的用户体验,则是M站无法做到的。目前来说两者是不可能被对方完全替代的,在互联网营销大行其道的今天,M站也越来越重要。营销活动大多以H5页面的形式展示和传播。通过M站的营销和推广,从而又促进APP的使用和推广。

 

目前,移动M站有倾向APP的趋势。M站会越来越像个APP,使得M站也越来越重要。而且,很多APP的展示效果,在原生代码无法实现的时候,嵌套移动H5页面也是一个很好的选择。

 

下面介绍几个移动M站建设的要点:

 

151Degree

 

51Degrees号称是目前最快、最准确的设备检测的解决方案。它是一个免费开源的.NET移动应用开发组件,可以用来检测移动设备和浏览器。甚至可以获取屏幕尺寸、输入法、加上制造商和型号信息等。从而可以选择性地被定向到为移动设备而设计的内容。由于拥有精确的移动设备的数据,所以几乎支持所有的智能手机,平板电脑等移动设备。

 

其实说白了,51Degree的作用就是识别客户端的设备。PC浏览器访问,就跳转到PC站,手机浏览器访问就跳转到M站。从而达到更好的用户体验。

 

如何将51Degree加入到现有网站?

 

2架构

 

移动Web和传统的Web其实并没有本质的区别。说白了还是一个Web站点,使用的技术都是Html+CSS+JS。不同的是,只不过目前在Html5的大趋势下,将Html5加入到了移动M站,使得M站更像个轻APP。

 

 

3Bootstrap

 

Bootstrap就不多说了,网上有很多Bootstrap的资料。它最大的优势应该就是非常流行,非常容易上手。如果缺少专业的设计或美工,那么Bootstrap是一个比较好的选择。他的用法极其简单,几乎没什么学习成本,绝对是快速开发的利器。

 

官网:http://getbootstrap.com/
Github:https://github.com/twbs/bootstrap/

 

4几点建议

 

  • 移动M站的URL要尽量和PC相同,这是可以避免同一URL在PC站可以显示,但是在手机上打开却是404;
  • M站写单独的TDK。

 

 

六、系统容量预估

 

电商公司的朋友,这样的场景是否似曾相识:

 

运营和产品神秘兮兮的跑过来问:我们晚上要做搞个促销,服务器能抗得住么?如果扛不住,需要加多少台机器?

 

于是,技术一脸懵逼。

 

其实这些都是系统容量预估的问题,容量预估是架构师必备的技能之一。所谓,容量预估其实说白了就是系统在Down掉之前,所能承受的最大流量。这个是技术人员对于系统性能了解的重要指标。常见的容量评估包括流量、并发量、带宽、CPU、内存 、磁盘等一系列内容。这部分来聊一聊容量预估的问题。

 

1几个重要参数

 

  • QPS:每秒钟处理的请求数。
  • 并发量: 系统同时处理的请求数。
  • 响应时间:  一般取平均响应时间。

 

很多人经常会把并发数和QPS给混淆了。其实只要理解了上面三个要素的意义之后,就能推算出它们之间的关系:QPS = 并发量 / 平均响应时间。

 

2容量评估的步骤与方法

 

1)预估总访问量

 

如何知道总访问量?对于一个运营活动的访问量评估,或者一个系统上线后PV的评估,有什么好方法?

 

最简单的办法就是:询问业务方,询问运营同学,询问产品同学,看产品和运营对此次活动的流量预估。

 

不过,业务方对于流量的预估,应该就PV和用户访问数这两个指标。技术人员需要根据这两个数据,计算其他相关指标,比如QPS等。

 

2)预估平均QPS

 

  • 总请求数=总PV*页面衍生连接数
  • 平均QPS = 总请求数/总时间

 

比如:活动落地页1小时内的总访问量是30w PV,该落地页的衍生连接数为30,那么落地页的平均QPS=(30w*30)/(60*60)=2500。

 

3)预估峰值QPS

 

系统容量规划时,不能只考虑平均QPS,还要考虑高峰的QPS,那么如何评估峰值QPS呢?

 

这个要根据实际的业务评估,通过以往的一些营销活动的PV等数据进行预估。一般情况下,峰值QPS大概是均值QPS的3-5倍,如果日均QPS为1000,于是评估出峰值QPS为5000。

 

不过,有一些业务会比较难评估业务访问量,例如“秒杀业务”,这类业务的容量评估暂时不在此讨论。

 

4)预估系统、单机极限QPS

 

如何预估一个业务,一个服务器单机的极限QPS呢?

 

这个性能指标是服务器最基本的指标之一,所以除了压力测试没有其他的办法。通过压力测试,算出服务器的单机极限QPS 。

 

在一个业务上线前,一般都需要进行压力测试(很多创业型公司,业务迭代很快的系统可能没有这一步,那就悲剧了),以APP推送某营销活动为例(预计日均QPS为1000,峰值QPS为5000),业务场景可能是这样的:

 

  • 通过APP推送一个活动消息;
  • 运营活动H5落地页是一个Web站点;
  • H5落地页由缓存Cache和数据库DB中的数据拼装而成。

 

通过压力测试发现,Web服务器单机只能抗住1200的QPS,Cache和数据库DB能抗住并发压力(一般来说,1%的流量到数据库,数据库120 QPS还是能轻松抗住的,Cache的话QPS能抗住,需要评估Cache的带宽,这里假设Cache不是瓶颈),这样,我们就得到了Web单机极限的QPS是1200。一般来说,生产系统不会跑满到极限的,这样容易影响服务器的寿命和性能,单机线上允许跑到QPS1200*0.8=960。

 

扩展说一句,通过压力测试,已经知道Web层是瓶颈,则可针对Web相关的方面做一些调整优化,以提高Web服务器的单机QPS 。

 

还有压力测试工作中,一般是以具体业务的角度进行压力测试,关心的是某个具体业务的并发量和QPS。

 

5)回答最开始那两个问题

需要的机器=峰值QPS/单机极限QPS

 

好了,上述已经得到了峰值QPS是5000,单机极限QPS是1000,线上部署了3台服务器:

  • 服务器能抗住么? -> 峰值5000,单机1000,线上3台,扛不住
  • 如果扛不住,需要加多少台机器? -> 需要额外2台,提前预留1台更好,给3台保险

 

3总结

 

需要注意的是,以上都是计算单个服务器或是单个集群的容量。实际生产环境是由Web、消息队列、缓存、数据库等等一系列组成的复杂集群。在分布式系统中,任何节点出现瓶颈,都有可能导致雪崩效应,最后导致整个集群垮掉 (“雪崩效应”指的是系统中一个小问题会逐渐扩大,最后造成整个集群宕机)。

 

所以,要了解规划整个平台的容量,就必须计算出每一个节点的容量。找出任何可能出现的瓶颈所在。

 

 

七、缓存系统

 

 

对于一个电商系统,缓存是重要组成部分,而提升系统性能的主要方式之一也是缓存。它可以挡掉大部分的数据库访问的冲击,如果没有它,系统很可能会因为数据库不可用导致整个系统崩溃。

 

但缓存带来了另外一些棘手的问题: 数据的一致性和实时性。例如,数据库中的数据状态已经改变,但在页面上看到的仍然是缓存的旧值,直到缓冲时间失效之后,才能重新更新缓存。这个问题怎么解决?

 

还有就是缓存数据如果没有失效的话,是会一直保持在内存中的,对服务器的内存也是负担,那么,什么数据可以放缓存,什么数据不可以,这是系统设计之初必须考虑的问题。

 

什么数据可以放缓存?

 

  • 不需要实时更新但是又极其消耗数据库的数据。比如网站首页的商品销售的排行榜,热搜商品等等,这些数据基本上都是一天统计一次,用户不会关注其是否是实时的。
  • 需要实时更新,但是数据更新的频率不高的数据。
  • 每次获取这些数据都经过复杂的处理逻辑,比如生成报表。

 

什么数据不应该使用缓存?

 

实际上,在电商系统中,大部分数据都是可以缓存的,不能使用缓存的数据很少。这类数据包括涉及到钱、密钥、业务关键性核心数据等。总之,如果你发现,系统里面的大部分数据都不能使用缓存,这说明架构本身出了问题。

 

如何解决一致性和实时性的问题?

 

保证一致性和实时性的办法就是:一旦数据库更新了,就必须把原来的缓存更新。

 

说一说我们的缓存方案:我们目前的缓存系统:Redis(主从)+ RabbitMQ + 缓存清理服务组成,具体如下图:

 

缓存清理作业订阅RabbitMQ消息队列,一有数据更新进入队列,就将数据重新更新到Redis缓存服务器。

 

当然,有些朋友的方案,是数据库更新完成之后,立马去更新相关缓存数据。这样就不需要MQ和缓存清理作业。不过,这同时也增加了系统的耦合性。具体得看自己的业务场景和平台大小。

 

以上均为个人经验分享,不足之处请大伙轻点拍砖,有更好的建议欢迎留言。

 

Redis总结(六)Redis配置文件全解

前面已经写了一些关于redis 的介绍,redis 的基本功能和用法,基本上都说了,有问题的可以去看看 http://www.cnblogs.com/zhangweizhong/category/771056.html。今天把redis 配置文件里面,用到的一些常用的配置项总结总结。

==基本配置

port 6379  # 监听端口号,默认为 6379,如果你设为 0 ,redis 将不在 socket 上监听任何客户端连接。

daemonize no #是否以后台进程启动

databases 16 #创建database的数量(默认选中的是database 0)

save 900 1 #刷新快照到硬盘中,必须满足两者要求才会触发,即900秒之后至少1个关键字发生变化。
save 300 10 #必须是300秒之后至少10个关键字发生变化。
save 60 10000 #必须是60秒之后至少10000个关键字发生变化。

stop-writes-on-bgsave-error yes #后台存储错误停止写。
rdbcompression yes #使用LZF压缩rdb文件。
rdbchecksum yes #存储和加载rdb文件时校验。
dbfilename dump.rdb #设置rdb文件名。
dir ./ #设置工作目录,rdb文件会写入该目录。

==主从配置
slaveof 设为某台机器的从服务器
masterauth 连接主服务器的密码
slave-serve-stale-data yes # 当主从断开或正在复制中,从服务器是否应答
slave-read-only yes #从服务器只读
repl-ping-slave-period 10 #从ping主的时间间隔,秒为单位
repl-timeout 60 #主从超时时间(超时认为断线了),要比period大
slave-priority 100 #如果master不能再正常工作,那么会在多个slave中,选择优先值最小的一个slave提升为master,优先值为0表示不能提升为master。

repl-disable-tcp-nodelay no #主端是否合并数据,大块发送给slave
slave-priority 100 从服务器的优先级,当主服挂了,会自动挑slave priority最小的为主服

===安全
requirepass foobared # 需要密码
rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 #如果公共环境,可以重命名部分敏感命令 如config

 

===限制
maxclients 10000 #最大连接数
maxmemory #最大使用内存

maxmemory-policy volatile-lru #内存到极限后的处理
volatile-lru -> LRU算法删除过期key
allkeys-lru -> LRU算法删除key(不区分过不过期)
volatile-random -> 随机删除过期key
allkeys-random -> 随机删除key(不区分过不过期)
volatile-ttl -> 删除快过期的key
noeviction -> 不删除,返回错误信息

#解释 LRU ttl都是近似算法,可以选N个,再比较最适宜T踢出的数据
maxmemory-samples 3

====日志模式
appendonly no #是否仅要日志
appendfsync no # 系统缓冲,统一写,速度快
appendfsync always # 系统不缓冲,直接写,慢,丢失数据少
appendfsync everysec #折衷,每秒写1次

no-appendfsync-on-rewrite no #为yes,则其他线程的数据放内存里,合并写入(速度快,容易丢失的多)
auto-AOF-rewrite-percentage 100 当前aof文件是上次重写是大N%时重写
auto-AOF-rewrite-min-size 64mb aof重写至少要达到的大小

====慢查询
slowlog-log-slower-than 10000 #记录响应时间大于10000微秒的慢查询
slowlog-max-len 128 # 最多记录128条

====服务端命令
time 返回时间戳+微秒
dbsize 返回key的数量
bgrewriteaof 重写aof
bgsave 后台开启子进程dump数据
save 阻塞进程dump数据
lastsave

slaveof host port 做host port的从服务器(数据清空,复制新主内容)
slaveof no one 变成主服务器(原数据不丢失,一般用于主服失败后)

flushdb 清空当前数据库的所有数据
flushall 清空所有数据库的所有数据(误用了怎么办?)

shutdown [save/nosave] 关闭服务器,保存数据,修改AOF(如果设置)

slowlog get 获取慢查询日志
slowlog len 获取慢查询日志条数
slowlog reset 清空慢查询

info []

config get 选项(支持*通配)
config set 选项 值
config rewrite 把值写到配置文件
config restart 更新info命令的信息

debug object key #调试选项,看一个key的情况
debug segfault #模拟段错误,让服务器崩溃
object key (refcount|encoding|idletime)
monitor #打开控制台,观察命令(调试用)
client list #列出所有连接
client kill #杀死某个连接 CLIENT KILL 127.0.0.1:43501
client getname #获取连接的名称 默认nil
client setname “名称” #设置连接名称,便于调试

 

====连接命令===
auth 密码 #密码登陆(如果有密码)
ping #测试服务器是否可用
echo “some content” #测试服务器是否正常交互
select 0/1/2… #选择数据库
quit #退出连接

 

Thrift总结(三)Thrift框架

1.数据类型

  • 基本类型:
    • bool:布尔值,true 或 false,对应 Java 的 boolean
    • byte:8 位有符号整数,对应 Java 的 byte
    • i16:16 位有符号整数,对应 Java 的 short
    • i32:32 位有符号整数,对应 Java 的 int
    • i64:64 位有符号整数,对应 Java 的 long
    • double:64 位浮点数,对应 Java 的 double
    • string:utf-8编码的字符串,对应 Java 的 String
  • 结构体类型:
    • struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean
  • 容器类型:
    • list:对应 Java 的 ArrayList
    • set:对应 Java 的 HashSet
    • map:对应 Java 的 HashMap
  • 异常类型:
    • exception:对应 Java 的 Exception
  • 服务类型:
    • service:对应服务的类

2.服务端编码基本步骤:

  • 实现服务处理接口impl
  • 创建TProcessor
  • 创建TServerTransport
  • 创建TProtocol
  • 创建TServer
  • 启动Server

3.客户端编码基本步骤:

  • 创建Transport
  • 创建TProtocol
  • 基于TTransport和TProtocol创建 Client
  • 调用Client的相应方法

4.数据传输协议

  • TBinaryProtocol : 二进制格式.
  • TCompactProtocol : 压缩格式
  • TJSONProtocol : JSON格式
  • TSimpleJSONProtocol : 提供JSON只写协议, 生成的文件很容易通过脚本语言解析

tips:客户端和服务端的协议要一致

 

5.Selector服务端实现模式

  • TSimpleServer  : 简单的单线程服务模型,常用于测试.
  • TThreadedServer : 多线程服务模型,使用阻塞式IO,每个请求创建一个线程。(java 不支持)
  • TThreadPoolServer : 多线程服务模型,使用标准的阻塞式IO,预先创建一组线程处理请求。
  • TThreadedSelectorServer : 允许你用多个线程来处理网络I/O。它维护了两个线程池,一个用来处理网络I/O,另一个用来进行请求的处理
  • TNonblockingServer : 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式),只有一个线程来处理消息
  • THsHaServer : 半同步半异步的服务模型,一个单独的线程用来处理网络I/O,一个worker线程池用来进行消息的处理

 

Thrift总结(二)创建RPC服务

前面介绍了thrift 基础的东西,怎么写thrift 语法规范编写脚本,如何生成相关的语言的接口。不清楚的可以看这个《Thrift总结(一)介绍》。做好之前的准备工作以后,下面就开始如何用Thrift写RPC接口。

如何用Thrift写RPC接口

1. 打开之前下载的thrift 源码,thrift-0.10.0\lib\csharp\src ,编译生成Thrift.dll 文件。

 

2. 新建一个空白解决方案命名为HelloThrift。在解决方案根目录下创建一个lib文件夹,将刚刚生成的Thrift.dll文件放入lib文件夹中。在解决方案分中建立两个控制台程序和一个类库,控制台程序分别命名为HelloThrift.Client 和 HelloThrift.Server,类库命名为Thrift.Interface。Client、Server和Interface分别引用lib文件夹中的Thrift.dll文件,将准备工作中生成的HelloService文件导入到Interface类库中。Client和Server分别引用Interface。具体结果如下图所示

 

3. 创建完相关的项目和引用之后,在服务端HelloThrift.Server 创建一个类命名为MyHelloService,实现HelloService.Iface接口,代码如下:

  清单1.MyHelloService

using System;
using HelloThrift.Interface;

namespace HelloThrift.Server
{

    public class MyHelloService : HelloService.Iface
    {
        /// 

        /// 只有一个参数返回值为字符串类型的方法
        ///

 

        /// string类型参数
        /// 返回值为string类型
        public string HelloString(string para)
        {
            System.Threading.Thread.Sleep(1 * 1000);

            Console.WriteLine("客户端调用了HelloString方法");

            return para;
        }

        /// 

        /// 只有一个参数,返回值为int类型的方法
        ///

 

        /// 
        /// 返回值为int类型
        public int HelloInt(int para)
        {
            System.Threading.Thread.Sleep(1 * 1000);

            Console.WriteLine("客户端调用了HelloInt方法");

            return para;
        }

        /// 

        /// 只有一个bool类型参数,返回值为bool类型的方法
        ///

 

        /// 
        /// 返回值为bool类型
        public bool HelloBoolean(bool para)
        {
            System.Threading.Thread.Sleep(1 * 1000);

            Console.WriteLine("客户端调用了HelloBoolean方法");

            return para;
        }


        /// 

        /// 返回执行为空的方法
        ///

 

        public void HelloVoid()
        {
            System.Threading.Thread.Sleep(1 * 1000);

            Console.WriteLine("客户端调用了HelloVoid方法");

            Console.WriteLine("HelloWorld");
        }

        /// 

        /// 无参数,返回值为null的方法
        ///

 

        /// 返回值为null
        public string HelloNull()
        {
            System.Threading.Thread.Sleep(1 * 1000);

            Console.WriteLine("客户端调用了HelloNull方法");

            return null;

        }

    }
}

 

4. 创建服务器端HelloThrift.Server 宿主的实现代码,在Program.cs 中添加如下代码,这样一个thrift 服务器就创建好了。

  清单2.HelloThrift.Server

using System;
using Thrift;
using Thrift.Protocol;
using Thrift.Server;
using Thrift.Transport;
using HelloThrift.Interface;

namespace HelloThrift.Server
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //设置服务端口为8080
                TServerSocket serverTransport = new TServerSocket(9081);

                //设置传输协议工厂
                TBinaryProtocol.Factory factory = new TBinaryProtocol.Factory();

                //关联处理器与服务的实现
                TProcessor processor = new HelloService.Processor(new MyHelloService());

                //创建服务端对象
                TServer server = new TThreadPoolServer(processor, serverTransport, new TTransportFactory(), factory);

                Console.WriteLine("服务端正在监听9081端口");

                server.Serve();
            }
            catch (TTransportException ex)//捕获异常信息
            {
                //打印异常信息
                Console.WriteLine(ex.Message);

            }

        }
    }
}

 

5. 创建完服务端之后,下面开始生成一个客户端实现代码,在HelloThrift.Client  的 Program.cs 中添加如下代码:

  清单3.HelloThrift.Client

using System;
using HelloThrift.Interface;
using Thrift.Protocol;
using Thrift.Transport;

namespace HelloThrift.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {

                //设置服务端端口号和地址
                TTransport transport = new TSocket("localhost", 9081);
                transport.Open();

                //设置传输协议为二进制传输协议
                TProtocol protocol = new TBinaryProtocol(transport);

                //创建客户端对象
                HelloService.Client client = new HelloService.Client(protocol);

                //调用服务端的方法
                Console.WriteLine(client.HelloString("HelloThrift"));

                Console.ReadKey();

            }
            catch (TTransportException e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}

上面的代码调用了服务端的HelloString方法,服务端也会返回传入的传输值,客户端将服务端返回的数据打印出来。好了完成了代码以后,将先启动Server,在启动Client。客户端调用结果 和 服务端请求显示。

 

说明

1. 关于使用Thrift 构建我们自己的rpc 的方法,这里基本讲完了。其他的方法本文就不再演示了,调用起来都是一样。

2. 后续会简单讨论一下Thrift 框架的通信原理。

3. 源代码下载,HelloThrift.rar

 

Thrift总结(一)介绍

这段时间,一直在整理公司的内部 rpc 服务接口,面临的一个问题就是:由于公司内部的系统由几个不同的语言编写的。C# ,java,node.js 等,如何实现这些内部系统之间的接口统一调用,确实是比较麻烦,本来考虑用webapi 但是感觉内部系统之间用webapi 效率不高。最终,我们还是考虑引入Thrift ,通过Thrift整合各个不同的RPC服务。下面就Thrift 如何使用,做个简单的介绍,本人也是初次接触。

  介绍

Thrift是一款由Fackbook开发的可伸缩、跨语言的服务开发框架,该框架已经开源并且加入的Apache项目。Thrift主要功能是:通过自定义的Interface Definition Language(IDL),可以创建基于RPC的客户端和服务端的服务代码。数据和服务代码的生成是通过Thrift内置的代码生成器来实现的。Thrift 的跨语言性体现在,它可以生成C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml , Delphi等语言的代码,且它们之间可以进行透明的通信。

Thrift 使得各个不同的语言的系统之间可以进行透明高效的通信。但是,Fackbook 的一贯作风就是只管发布,不管维护。所以,Thrift 目前还存在一些为解决的问题。大家在调研的时候,应该考虑清楚。

本文结合网络上的资源对从C#开发人员的角度简单介绍Thrift的使用,并且针对不同的传输协议和服务类型给出相应的C#实例,同时简单介绍Thrift异步客户端的实现。

 

Thrift代码生成器windows版下载地址

http://www.apache.org/dyn/closer.cgi?path=/thrift/0.10.0/thrift-0.10.0.exe

Thrift源码下载地址

http://www.apache.org/dyn/closer.cgi?path=/thrift/0.10.0/thrift-0.10.0.tar.gz

 

  Window 下安装配置

Thrift 不需要安装,只需下载windows版的 Thrift代码生成器即可,下载地址如上连接

注意:下载下来之后,必须把文件名字thrift-0.10.0.exe 改为  thrift.exe, 否则cmd会提示:thrift 不是内部命令的错误。

 

  创建thrift的语法规范编写脚本文件

根据thrift的语法规范编写脚本文件Hello.thrift,代码如下:

namespace csharp HelloThrift.Interface

service HelloService{

    string HelloString(1:string para)

    i32 HelloInt(1:i32 para)

    bool HelloBoolean(1:bool para)

    void HelloVoid()

    string HelloNull()

}

 

  生成Csharp 版的服务定义类

然后打开cmd切换到thrift代码生成工具的存放目录,在命令行中输入如下命令:thrift -gen csharp Hello.thrift

代码生成工具会自动在当前目录下把定义好的接口脚本生成C#代码,生成后的代码目录如下

接口脚本生成C#代码

/**
 * Autogenerated by Thrift Compiler (0.9.3)
 *
 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
 *  @generated
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Thrift;
using Thrift.Collections;
using System.Runtime.Serialization;
using Thrift.Protocol;
using Thrift.Transport;

namespace HelloThrift.Interface
{
  public partial class HelloService {
    public interface Iface {
      string HelloString(string para);
      #if SILVERLIGHT
      IAsyncResult Begin_HelloString(AsyncCallback callback, object state, string para);
      string End_HelloString(IAsyncResult asyncResult);
      #endif
      int HelloInt(int para);
      #if SILVERLIGHT
      IAsyncResult Begin_HelloInt(AsyncCallback callback, object state, int para);
      int End_HelloInt(IAsyncResult asyncResult);
      #endif
      bool HelloBoolean(bool para);
      #if SILVERLIGHT
      IAsyncResult Begin_HelloBoolean(AsyncCallback callback, object state, bool para);
      bool End_HelloBoolean(IAsyncResult asyncResult);
      #endif
      void HelloVoid();
      #if SILVERLIGHT
      IAsyncResult Begin_HelloVoid(AsyncCallback callback, object state);
      void End_HelloVoid(IAsyncResult asyncResult);
      #endif
      string HelloNull();
      #if SILVERLIGHT
      IAsyncResult Begin_HelloNull(AsyncCallback callback, object state);
      string End_HelloNull(IAsyncResult asyncResult);
      #endif
    }

    public class Client : IDisposable, Iface {
      public Client(TProtocol prot) : this(prot, prot)
      {
      }

      public Client(TProtocol iprot, TProtocol oprot)
      {
        iprot_ = iprot;
        oprot_ = oprot;
      }

      protected TProtocol iprot_;
      protected TProtocol oprot_;
      protected int seqid_;

      public TProtocol InputProtocol
      {
        get { return iprot_; }
      }
      public TProtocol OutputProtocol
      {
        get { return oprot_; }
      }


      #region " IDisposable Support "
      private bool _IsDisposed;

      // IDisposable
      public void Dispose()
      {
        Dispose(true);
      }
      

      protected virtual void Dispose(bool disposing)
      {
        if (!_IsDisposed)
        {
          if (disposing)
          {
            if (iprot_ != null)
            {
              ((IDisposable)iprot_).Dispose();
            }
            if (oprot_ != null)
            {
              ((IDisposable)oprot_).Dispose();
            }
          }
        }
        _IsDisposed = true;
      }
      #endregion


      
      #if SILVERLIGHT
      public IAsyncResult Begin_HelloString(AsyncCallback callback, object state, string para)
      {
        return send_HelloString(callback, state, para);
      }

      public string End_HelloString(IAsyncResult asyncResult)
      {
        oprot_.Transport.EndFlush(asyncResult);
        return recv_HelloString();
      }

      #endif

      public string HelloString(string para)
      {
        #if !SILVERLIGHT
        send_HelloString(para);
        return recv_HelloString();

        #else
        var asyncResult = Begin_HelloString(null, null, para);
        return End_HelloString(asyncResult);

        #endif
      }
      #if SILVERLIGHT
      public IAsyncResult send_HelloString(AsyncCallback callback, object state, string para)
      #else
      public void send_HelloString(string para)
      #endif
      {
        oprot_.WriteMessageBegin(new TMessage("HelloString", TMessageType.Call, seqid_));
        HelloString_args args = new HelloString_args();
        args.Para = para;
        args.Write(oprot_);
        oprot_.WriteMessageEnd();
        #if SILVERLIGHT
        return oprot_.Transport.BeginFlush(callback, state);
        #else
        oprot_.Transport.Flush();
        #endif
      }

      public string recv_HelloString()
      {
        TMessage msg = iprot_.ReadMessageBegin();
        if (msg.Type == TMessageType.Exception) {
          TApplicationException x = TApplicationException.Read(iprot_);
          iprot_.ReadMessageEnd();
          throw x;
        }
        HelloString_result result = new HelloString_result();
        result.Read(iprot_);
        iprot_.ReadMessageEnd();
        if (result.__isset.success) {
          return result.Success;
        }
        throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, "HelloString failed: unknown result");
      }

      
      #if SILVERLIGHT
      public IAsyncResult Begin_HelloInt(AsyncCallback callback, object state, int para)
      {
        return send_HelloInt(callback, state, para);
      }

      public int End_HelloInt(IAsyncResult asyncResult)
      {
        oprot_.Transport.EndFlush(asyncResult);
        return recv_HelloInt();
      }

      #endif

      public int HelloInt(int para)
      {
        #if !SILVERLIGHT
        send_HelloInt(para);
        return recv_HelloInt();

        #else
        var asyncResult = Begin_HelloInt(null, null, para);
        return End_HelloInt(asyncResult);

        #endif
      }
      #if SILVERLIGHT
      public IAsyncResult send_HelloInt(AsyncCallback callback, object state, int para)
      #else
      public void send_HelloInt(int para)
      #endif
      {
        oprot_.WriteMessageBegin(new TMessage("HelloInt", TMessageType.Call, seqid_));
        HelloInt_args args = new HelloInt_args();
        args.Para = para;
        args.Write(oprot_);
        oprot_.WriteMessageEnd();
        #if SILVERLIGHT
        return oprot_.Transport.BeginFlush(callback, state);
        #else
        oprot_.Transport.Flush();
        #endif
      }

      public int recv_HelloInt()
      {
        TMessage msg = iprot_.ReadMessageBegin();
        if (msg.Type == TMessageType.Exception) {
          TApplicationException x = TApplicationException.Read(iprot_);
          iprot_.ReadMessageEnd();
          throw x;
        }
        HelloInt_result result = new HelloInt_result();
        result.Read(iprot_);
        iprot_.ReadMessageEnd();
        if (result.__isset.success) {
          return result.Success;
        }
        throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, "HelloInt failed: unknown result");
      }

      
      #if SILVERLIGHT
      public IAsyncResult Begin_HelloBoolean(AsyncCallback callback, object state, bool para)
      {
        return send_HelloBoolean(callback, state, para);
      }

      public bool End_HelloBoolean(IAsyncResult asyncResult)
      {
        oprot_.Transport.EndFlush(asyncResult);
        return recv_HelloBoolean();
      }

      #endif

      public bool HelloBoolean(bool para)
      {
        #if !SILVERLIGHT
        send_HelloBoolean(para);
        return recv_HelloBoolean();

        #else
        var asyncResult = Begin_HelloBoolean(null, null, para);
        return End_HelloBoolean(asyncResult);

        #endif
      }
      #if SILVERLIGHT
      public IAsyncResult send_HelloBoolean(AsyncCallback callback, object state, bool para)
      #else
      public void send_HelloBoolean(bool para)
      #endif
      {
        oprot_.WriteMessageBegin(new TMessage("HelloBoolean", TMessageType.Call, seqid_));
        HelloBoolean_args args = new HelloBoolean_args();
        args.Para = para;
        args.Write(oprot_);
        oprot_.WriteMessageEnd();
        #if SILVERLIGHT
        return oprot_.Transport.BeginFlush(callback, state);
        #else
        oprot_.Transport.Flush();
        #endif
      }

      public bool recv_HelloBoolean()
      {
        TMessage msg = iprot_.ReadMessageBegin();
        if (msg.Type == TMessageType.Exception) {
          TApplicationException x = TApplicationException.Read(iprot_);
          iprot_.ReadMessageEnd();
          throw x;
        }
        HelloBoolean_result result = new HelloBoolean_result();
        result.Read(iprot_);
        iprot_.ReadMessageEnd();
        if (result.__isset.success) {
          return result.Success;
        }
        throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, "HelloBoolean failed: unknown result");
      }

      
      #if SILVERLIGHT
      public IAsyncResult Begin_HelloVoid(AsyncCallback callback, object state)
      {
        return send_HelloVoid(callback, state);
      }

      public void End_HelloVoid(IAsyncResult asyncResult)
      {
        oprot_.Transport.EndFlush(asyncResult);
        recv_HelloVoid();
      }

      #endif

      public void HelloVoid()
      {
        #if !SILVERLIGHT
        send_HelloVoid();
        recv_HelloVoid();

        #else
        var asyncResult = Begin_HelloVoid(null, null);
        End_HelloVoid(asyncResult);

        #endif
      }
      #if SILVERLIGHT
      public IAsyncResult send_HelloVoid(AsyncCallback callback, object state)
      #else
      public void send_HelloVoid()
      #endif
      {
        oprot_.WriteMessageBegin(new TMessage("HelloVoid", TMessageType.Call, seqid_));
        HelloVoid_args args = new HelloVoid_args();
        args.Write(oprot_);
        oprot_.WriteMessageEnd();
        #if SILVERLIGHT
        return oprot_.Transport.BeginFlush(callback, state);
        #else
        oprot_.Transport.Flush();
        #endif
      }

      public void recv_HelloVoid()
      {
        TMessage msg = iprot_.ReadMessageBegin();
        if (msg.Type == TMessageType.Exception) {
          TApplicationException x = TApplicationException.Read(iprot_);
          iprot_.ReadMessageEnd();
          throw x;
        }
        HelloVoid_result result = new HelloVoid_result();
        result.Read(iprot_);
        iprot_.ReadMessageEnd();
        return;
      }

      
      #if SILVERLIGHT
      public IAsyncResult Begin_HelloNull(AsyncCallback callback, object state)
      {
        return send_HelloNull(callback, state);
      }

      public string End_HelloNull(IAsyncResult asyncResult)
      {
        oprot_.Transport.EndFlush(asyncResult);
        return recv_HelloNull();
      }

      #endif

      public string HelloNull()
      {
        #if !SILVERLIGHT
        send_HelloNull();
        return recv_HelloNull();

        #else
        var asyncResult = Begin_HelloNull(null, null);
        return End_HelloNull(asyncResult);

        #endif
      }
      #if SILVERLIGHT
      public IAsyncResult send_HelloNull(AsyncCallback callback, object state)
      #else
      public void send_HelloNull()
      #endif
      {
        oprot_.WriteMessageBegin(new TMessage("HelloNull", TMessageType.Call, seqid_));
        HelloNull_args args = new HelloNull_args();
        args.Write(oprot_);
        oprot_.WriteMessageEnd();
        #if SILVERLIGHT
        return oprot_.Transport.BeginFlush(callback, state);
        #else
        oprot_.Transport.Flush();
        #endif
      }

      public string recv_HelloNull()
      {
        TMessage msg = iprot_.ReadMessageBegin();
        if (msg.Type == TMessageType.Exception) {
          TApplicationException x = TApplicationException.Read(iprot_);
          iprot_.ReadMessageEnd();
          throw x;
        }
        HelloNull_result result = new HelloNull_result();
        result.Read(iprot_);
        iprot_.ReadMessageEnd();
        if (result.__isset.success) {
          return result.Success;
        }
        throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, "HelloNull failed: unknown result");
      }

    }
    public class Processor : TProcessor {
      public Processor(Iface iface)
      {
        iface_ = iface;
        processMap_["HelloString"] = HelloString_Process;
        processMap_["HelloInt"] = HelloInt_Process;
        processMap_["HelloBoolean"] = HelloBoolean_Process;
        processMap_["HelloVoid"] = HelloVoid_Process;
        processMap_["HelloNull"] = HelloNull_Process;
      }

      protected delegate void ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);
      private Iface iface_;
      protected Dictionary<string, ProcessFunction> processMap_ = new Dictionary<string, ProcessFunction>();

      public bool Process(TProtocol iprot, TProtocol oprot)
      {
        try
        {
          TMessage msg = iprot.ReadMessageBegin();
          ProcessFunction fn;
          processMap_.TryGetValue(msg.Name, out fn);
          if (fn == null) {
            TProtocolUtil.Skip(iprot, TType.Struct);
            iprot.ReadMessageEnd();
            TApplicationException x = new TApplicationException (TApplicationException.ExceptionType.UnknownMethod, "Invalid method name: '" + msg.Name + "'");
            oprot.WriteMessageBegin(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID));
            x.Write(oprot);
            oprot.WriteMessageEnd();
            oprot.Transport.Flush();
            return true;
          }
          fn(msg.SeqID, iprot, oprot);
        }
        catch (IOException)
        {
          return false;
        }
        return true;
      }

      public void HelloString_Process(int seqid, TProtocol iprot, TProtocol oprot)
      {
        HelloString_args args = new HelloString_args();
        args.Read(iprot);
        iprot.ReadMessageEnd();
        HelloString_result result = new HelloString_result();
        result.Success = iface_.HelloString(args.Para);
        oprot.WriteMessageBegin(new TMessage("HelloString", TMessageType.Reply, seqid)); 
        result.Write(oprot);
        oprot.WriteMessageEnd();
        oprot.Transport.Flush();
      }

      public void HelloInt_Process(int seqid, TProtocol iprot, TProtocol oprot)
      {
        HelloInt_args args = new HelloInt_args();
        args.Read(iprot);
        iprot.ReadMessageEnd();
        HelloInt_result result = new HelloInt_result();
        result.Success = iface_.HelloInt(args.Para);
        oprot.WriteMessageBegin(new TMessage("HelloInt", TMessageType.Reply, seqid)); 
        result.Write(oprot);
        oprot.WriteMessageEnd();
        oprot.Transport.Flush();
      }

      public void HelloBoolean_Process(int seqid, TProtocol iprot, TProtocol oprot)
      {
        HelloBoolean_args args = new HelloBoolean_args();
        args.Read(iprot);
        iprot.ReadMessageEnd();
        HelloBoolean_result result = new HelloBoolean_result();
        result.Success = iface_.HelloBoolean(args.Para);
        oprot.WriteMessageBegin(new TMessage("HelloBoolean", TMessageType.Reply, seqid)); 
        result.Write(oprot);
        oprot.WriteMessageEnd();
        oprot.Transport.Flush();
      }

      public void HelloVoid_Process(int seqid, TProtocol iprot, TProtocol oprot)
      {
        HelloVoid_args args = new HelloVoid_args();
        args.Read(iprot);
        iprot.ReadMessageEnd();
        HelloVoid_result result = new HelloVoid_result();
        iface_.HelloVoid();
        oprot.WriteMessageBegin(new TMessage("HelloVoid", TMessageType.Reply, seqid)); 
        result.Write(oprot);
        oprot.WriteMessageEnd();
        oprot.Transport.Flush();
      }

      public void HelloNull_Process(int seqid, TProtocol iprot, TProtocol oprot)
      {
        HelloNull_args args = new HelloNull_args();
        args.Read(iprot);
        iprot.ReadMessageEnd();
        HelloNull_result result = new HelloNull_result();
        result.Success = iface_.HelloNull();
        oprot.WriteMessageBegin(new TMessage("HelloNull", TMessageType.Reply, seqid)); 
        result.Write(oprot);
        oprot.WriteMessageEnd();
        oprot.Transport.Flush();
      }

    }


    #if !SILVERLIGHT
    [Serializable]
    #endif
    public partial class HelloString_args : TBase
    {
      private string _para;

      public string Para
      {
        get
        {
          return _para;
        }
        set
        {
          __isset.para = true;
          this._para = value;
        }
      }


      public Isset __isset;
      #if !SILVERLIGHT
      [Serializable]
      #endif
      public struct Isset {
        public bool para;
      }

      public HelloString_args() {
      }

      public void Read (TProtocol iprot)
      {
        iprot.IncrementRecursionDepth();
        try
        {
          TField field;
          iprot.ReadStructBegin();
          while (true)
          {
            field = iprot.ReadFieldBegin();
            if (field.Type == TType.Stop) { 
              break;
            }
            switch (field.ID)
            {
              case 1:
                if (field.Type == TType.String) {
                  Para = iprot.ReadString();
                } else { 
                  TProtocolUtil.Skip(iprot, field.Type);
                }
                break;
              default: 
                TProtocolUtil.Skip(iprot, field.Type);
                break;
            }
            iprot.ReadFieldEnd();
          }
          iprot.ReadStructEnd();
        }
        finally
        {
          iprot.DecrementRecursionDepth();
        }
      }

      public void Write(TProtocol oprot) {
        oprot.IncrementRecursionDepth();
        try
        {
          TStruct struc = new TStruct("HelloString_args");
          oprot.WriteStructBegin(struc);
          TField field = new TField();
          if (Para != null && __isset.para) {
            field.Name = "para";
            field.Type = TType.String;
            field.ID = 1;
            oprot.WriteFieldBegin(field);
            oprot.WriteString(Para);
            oprot.WriteFieldEnd();
          }
          oprot.WriteFieldStop();
          oprot.WriteStructEnd();
        }
        finally
        {
          oprot.DecrementRecursionDepth();
        }
      }

      public override string ToString() {
        StringBuilder __sb = new StringBuilder("HelloString_args(");
        bool __first = true;
        if (Para != null && __isset.para) {
          if(!__first) { __sb.Append(", "); }
          __first = false;
          __sb.Append("Para: ");
          __sb.Append(Para);
        }
        __sb.Append(")");
        return __sb.ToString();
      }

    }


    #if !SILVERLIGHT
    [Serializable]
    #endif
    public partial class HelloString_result : TBase
    {
      private string _success;

      public string Success
      {
        get
        {
          return _success;
        }
        set
        {
          __isset.success = true;
          this._success = value;
        }
      }


      public Isset __isset;
      #if !SILVERLIGHT
      [Serializable]
      #endif
      public struct Isset {
        public bool success;
      }

      public HelloString_result() {
      }

      public void Read (TProtocol iprot)
      {
        iprot.IncrementRecursionDepth();
        try
        {
          TField field;
          iprot.ReadStructBegin();
          while (true)
          {
            field = iprot.ReadFieldBegin();
            if (field.Type == TType.Stop) { 
              break;
            }
            switch (field.ID)
            {
              case 0:
                if (field.Type == TType.String) {
                  Success = iprot.ReadString();
                } else { 
                  TProtocolUtil.Skip(iprot, field.Type);
                }
                break;
              default: 
                TProtocolUtil.Skip(iprot, field.Type);
                break;
            }
            iprot.ReadFieldEnd();
          }
          iprot.ReadStructEnd();
        }
        finally
        {
          iprot.DecrementRecursionDepth();
        }
      }

      public void Write(TProtocol oprot) {
        oprot.IncrementRecursionDepth();
        try
        {
          TStruct struc = new TStruct("HelloString_result");
          oprot.WriteStructBegin(struc);
          TField field = new TField();

          if (this.__isset.success) {
            if (Success != null) {
              field.Name = "Success";
              field.Type = TType.String;
              field.ID = 0;
              oprot.WriteFieldBegin(field);
              oprot.WriteString(Success);
              oprot.WriteFieldEnd();
            }
          }
          oprot.WriteFieldStop();
          oprot.WriteStructEnd();
        }
        finally
        {
          oprot.DecrementRecursionDepth();
        }
      }

      public override string ToString() {
        StringBuilder __sb = new StringBuilder("HelloString_result(");
        bool __first = true;
        if (Success != null && __isset.success) {
          if(!__first) { __sb.Append(", "); }
          __first = false;
          __sb.Append("Success: ");
          __sb.Append(Success);
        }
        __sb.Append(")");
        return __sb.ToString();
      }

    }


    #if !SILVERLIGHT
    [Serializable]
    #endif
    public partial class HelloInt_args : TBase
    {
      private int _para;

      public int Para
      {
        get
        {
          return _para;
        }
        set
        {
          __isset.para = true;
          this._para = value;
        }
      }


      public Isset __isset;
      #if !SILVERLIGHT
      [Serializable]
      #endif
      public struct Isset {
        public bool para;
      }

      public HelloInt_args() {
      }

      public void Read (TProtocol iprot)
      {
        iprot.IncrementRecursionDepth();
        try
        {
          TField field;
          iprot.ReadStructBegin();
          while (true)
          {
            field = iprot.ReadFieldBegin();
            if (field.Type == TType.Stop) { 
              break;
            }
            switch (field.ID)
            {
              case 1:
                if (field.Type == TType.I32) {
                  Para = iprot.ReadI32();
                } else { 
                  TProtocolUtil.Skip(iprot, field.Type);
                }
                break;
              default: 
                TProtocolUtil.Skip(iprot, field.Type);
                break;
            }
            iprot.ReadFieldEnd();
          }
          iprot.ReadStructEnd();
        }
        finally
        {
          iprot.DecrementRecursionDepth();
        }
      }

      public void Write(TProtocol oprot) {
        oprot.IncrementRecursionDepth();
        try
        {
          TStruct struc = new TStruct("HelloInt_args");
          oprot.WriteStructBegin(struc);
          TField field = new TField();
          if (__isset.para) {
            field.Name = "para";
            field.Type = TType.I32;
            field.ID = 1;
            oprot.WriteFieldBegin(field);
            oprot.WriteI32(Para);
            oprot.WriteFieldEnd();
          }
          oprot.WriteFieldStop();
          oprot.WriteStructEnd();
        }
        finally
        {
          oprot.DecrementRecursionDepth();
        }
      }

      public override string ToString() {
        StringBuilder __sb = new StringBuilder("HelloInt_args(");
        bool __first = true;
        if (__isset.para) {
          if(!__first) { __sb.Append(", "); }
          __first = false;
          __sb.Append("Para: ");
          __sb.Append(Para);
        }
        __sb.Append(")");
        return __sb.ToString();
      }

    }


    #if !SILVERLIGHT
    [Serializable]
    #endif
    public partial class HelloInt_result : TBase
    {
      private int _success;

      public int Success
      {
        get
        {
          return _success;
        }
        set
        {
          __isset.success = true;
          this._success = value;
        }
      }


      public Isset __isset;
      #if !SILVERLIGHT
      [Serializable]
      #endif
      public struct Isset {
        public bool success;
      }

      public HelloInt_result() {
      }

      public void Read (TProtocol iprot)
      {
        iprot.IncrementRecursionDepth();
        try
        {
          TField field;
          iprot.ReadStructBegin();
          while (true)
          {
            field = iprot.ReadFieldBegin();
            if (field.Type == TType.Stop) { 
              break;
            }
            switch (field.ID)
            {
              case 0:
                if (field.Type == TType.I32) {
                  Success = iprot.ReadI32();
                } else { 
                  TProtocolUtil.Skip(iprot, field.Type);
                }
                break;
              default: 
                TProtocolUtil.Skip(iprot, field.Type);
                break;
            }
            iprot.ReadFieldEnd();
          }
          iprot.ReadStructEnd();
        }
        finally
        {
          iprot.DecrementRecursionDepth();
        }
      }

      public void Write(TProtocol oprot) {
        oprot.IncrementRecursionDepth();
        try
        {
          TStruct struc = new TStruct("HelloInt_result");
          oprot.WriteStructBegin(struc);
          TField field = new TField();

          if (this.__isset.success) {
            field.Name = "Success";
            field.Type = TType.I32;
            field.ID = 0;
            oprot.WriteFieldBegin(field);
            oprot.WriteI32(Success);
            oprot.WriteFieldEnd();
          }
          oprot.WriteFieldStop();
          oprot.WriteStructEnd();
        }
        finally
        {
          oprot.DecrementRecursionDepth();
        }
      }

      public override string ToString() {
        StringBuilder __sb = new StringBuilder("HelloInt_result(");
        bool __first = true;
        if (__isset.success) {
          if(!__first) { __sb.Append(", "); }
          __first = false;
          __sb.Append("Success: ");
          __sb.Append(Success);
        }
        __sb.Append(")");
        return __sb.ToString();
      }

    }


    #if !SILVERLIGHT
    [Serializable]
    #endif
    public partial class HelloBoolean_args : TBase
    {
      private bool _para;

      public bool Para
      {
        get
        {
          return _para;
        }
        set
        {
          __isset.para = true;
          this._para = value;
        }
      }


      public Isset __isset;
      #if !SILVERLIGHT
      [Serializable]
      #endif
      public struct Isset {
        public bool para;
      }

      public HelloBoolean_args() {
      }

      public void Read (TProtocol iprot)
      {
        iprot.IncrementRecursionDepth();
        try
        {
          TField field;
          iprot.ReadStructBegin();
          while (true)
          {
            field = iprot.ReadFieldBegin();
            if (field.Type == TType.Stop) { 
              break;
            }
            switch (field.ID)
            {
              case 1:
                if (field.Type == TType.Bool) {
                  Para = iprot.ReadBool();
                } else { 
                  TProtocolUtil.Skip(iprot, field.Type);
                }
                break;
              default: 
                TProtocolUtil.Skip(iprot, field.Type);
                break;
            }
            iprot.ReadFieldEnd();
          }
          iprot.ReadStructEnd();
        }
        finally
        {
          iprot.DecrementRecursionDepth();
        }
      }

      public void Write(TProtocol oprot) {
        oprot.IncrementRecursionDepth();
        try
        {
          TStruct struc = new TStruct("HelloBoolean_args");
          oprot.WriteStructBegin(struc);
          TField field = new TField();
          if (__isset.para) {
            field.Name = "para";
            field.Type = TType.Bool;
            field.ID = 1;
            oprot.WriteFieldBegin(field);
            oprot.WriteBool(Para);
            oprot.WriteFieldEnd();
          }
          oprot.WriteFieldStop();
          oprot.WriteStructEnd();
        }
        finally
        {
          oprot.DecrementRecursionDepth();
        }
      }

      public override string ToString() {
        StringBuilder __sb = new StringBuilder("HelloBoolean_args(");
        bool __first = true;
        if (__isset.para) {
          if(!__first) { __sb.Append(", "); }
          __first = false;
          __sb.Append("Para: ");
          __sb.Append(Para);
        }
        __sb.Append(")");
        return __sb.ToString();
      }

    }


    #if !SILVERLIGHT
    [Serializable]
    #endif
    public partial class HelloBoolean_result : TBase
    {
      private bool _success;

      public bool Success
      {
        get
        {
          return _success;
        }
        set
        {
          __isset.success = true;
          this._success = value;
        }
      }


      public Isset __isset;
      #if !SILVERLIGHT
      [Serializable]
      #endif
      public struct Isset {
        public bool success;
      }

      public HelloBoolean_result() {
      }

      public void Read (TProtocol iprot)
      {
        iprot.IncrementRecursionDepth();
        try
        {
          TField field;
          iprot.ReadStructBegin();
          while (true)
          {
            field = iprot.ReadFieldBegin();
            if (field.Type == TType.Stop) { 
              break;
            }
            switch (field.ID)
            {
              case 0:
                if (field.Type == TType.Bool) {
                  Success = iprot.ReadBool();
                } else { 
                  TProtocolUtil.Skip(iprot, field.Type);
                }
                break;
              default: 
                TProtocolUtil.Skip(iprot, field.Type);
                break;
            }
            iprot.ReadFieldEnd();
          }
          iprot.ReadStructEnd();
        }
        finally
        {
          iprot.DecrementRecursionDepth();
        }
      }

      public void Write(TProtocol oprot) {
        oprot.IncrementRecursionDepth();
        try
        {
          TStruct struc = new TStruct("HelloBoolean_result");
          oprot.WriteStructBegin(struc);
          TField field = new TField();

          if (this.__isset.success) {
            field.Name = "Success";
            field.Type = TType.Bool;
            field.ID = 0;
            oprot.WriteFieldBegin(field);
            oprot.WriteBool(Success);
            oprot.WriteFieldEnd();
          }
          oprot.WriteFieldStop();
          oprot.WriteStructEnd();
        }
        finally
        {
          oprot.DecrementRecursionDepth();
        }
      }

      public override string ToString() {
        StringBuilder __sb = new StringBuilder("HelloBoolean_result(");
        bool __first = true;
        if (__isset.success) {
          if(!__first) { __sb.Append(", "); }
          __first = false;
          __sb.Append("Success: ");
          __sb.Append(Success);
        }
        __sb.Append(")");
        return __sb.ToString();
      }

    }


    #if !SILVERLIGHT
    [Serializable]
    #endif
    public partial class HelloVoid_args : TBase
    {

      public HelloVoid_args() {
      }

      public void Read (TProtocol iprot)
      {
        iprot.IncrementRecursionDepth();
        try
        {
          TField field;
          iprot.ReadStructBegin();
          while (true)
          {
            field = iprot.ReadFieldBegin();
            if (field.Type == TType.Stop) { 
              break;
            }
            switch (field.ID)
            {
              default: 
                TProtocolUtil.Skip(iprot, field.Type);
                break;
            }
            iprot.ReadFieldEnd();
          }
          iprot.ReadStructEnd();
        }
        finally
        {
          iprot.DecrementRecursionDepth();
        }
      }

      public void Write(TProtocol oprot) {
        oprot.IncrementRecursionDepth();
        try
        {
          TStruct struc = new TStruct("HelloVoid_args");
          oprot.WriteStructBegin(struc);
          oprot.WriteFieldStop();
          oprot.WriteStructEnd();
        }
        finally
        {
          oprot.DecrementRecursionDepth();
        }
      }

      public override string ToString() {
        StringBuilder __sb = new StringBuilder("HelloVoid_args(");
        __sb.Append(")");
        return __sb.ToString();
      }

    }


    #if !SILVERLIGHT
    [Serializable]
    #endif
    public partial class HelloVoid_result : TBase
    {

      public HelloVoid_result() {
      }

      public void Read (TProtocol iprot)
      {
        iprot.IncrementRecursionDepth();
        try
        {
          TField field;
          iprot.ReadStructBegin();
          while (true)
          {
            field = iprot.ReadFieldBegin();
            if (field.Type == TType.Stop) { 
              break;
            }
            switch (field.ID)
            {
              default: 
                TProtocolUtil.Skip(iprot, field.Type);
                break;
            }
            iprot.ReadFieldEnd();
          }
          iprot.ReadStructEnd();
        }
        finally
        {
          iprot.DecrementRecursionDepth();
        }
      }

      public void Write(TProtocol oprot) {
        oprot.IncrementRecursionDepth();
        try
        {
          TStruct struc = new TStruct("HelloVoid_result");
          oprot.WriteStructBegin(struc);

          oprot.WriteFieldStop();
          oprot.WriteStructEnd();
        }
        finally
        {
          oprot.DecrementRecursionDepth();
        }
      }

      public override string ToString() {
        StringBuilder __sb = new StringBuilder("HelloVoid_result(");
        __sb.Append(")");
        return __sb.ToString();
      }

    }


    #if !SILVERLIGHT
    [Serializable]
    #endif
    public partial class HelloNull_args : TBase
    {

      public HelloNull_args() {
      }

      public void Read (TProtocol iprot)
      {
        iprot.IncrementRecursionDepth();
        try
        {
          TField field;
          iprot.ReadStructBegin();
          while (true)
          {
            field = iprot.ReadFieldBegin();
            if (field.Type == TType.Stop) { 
              break;
            }
            switch (field.ID)
            {
              default: 
                TProtocolUtil.Skip(iprot, field.Type);
                break;
            }
            iprot.ReadFieldEnd();
          }
          iprot.ReadStructEnd();
        }
        finally
        {
          iprot.DecrementRecursionDepth();
        }
      }

      public void Write(TProtocol oprot) {
        oprot.IncrementRecursionDepth();
        try
        {
          TStruct struc = new TStruct("HelloNull_args");
          oprot.WriteStructBegin(struc);
          oprot.WriteFieldStop();
          oprot.WriteStructEnd();
        }
        finally
        {
          oprot.DecrementRecursionDepth();
        }
      }

      public override string ToString() {
        StringBuilder __sb = new StringBuilder("HelloNull_args(");
        __sb.Append(")");
        return __sb.ToString();
      }

    }


    #if !SILVERLIGHT
    [Serializable]
    #endif
    public partial class HelloNull_result : TBase
    {
      private string _success;

      public string Success
      {
        get
        {
          return _success;
        }
        set
        {
          __isset.success = true;
          this._success = value;
        }
      }


      public Isset __isset;
      #if !SILVERLIGHT
      [Serializable]
      #endif
      public struct Isset {
        public bool success;
      }

      public HelloNull_result() {
      }

      public void Read (TProtocol iprot)
      {
        iprot.IncrementRecursionDepth();
        try
        {
          TField field;
          iprot.ReadStructBegin();
          while (true)
          {
            field = iprot.ReadFieldBegin();
            if (field.Type == TType.Stop) { 
              break;
            }
            switch (field.ID)
            {
              case 0:
                if (field.Type == TType.String) {
                  Success = iprot.ReadString();
                } else { 
                  TProtocolUtil.Skip(iprot, field.Type);
                }
                break;
              default: 
                TProtocolUtil.Skip(iprot, field.Type);
                break;
            }
            iprot.ReadFieldEnd();
          }
          iprot.ReadStructEnd();
        }
        finally
        {
          iprot.DecrementRecursionDepth();
        }
      }

      public void Write(TProtocol oprot) {
        oprot.IncrementRecursionDepth();
        try
        {
          TStruct struc = new TStruct("HelloNull_result");
          oprot.WriteStructBegin(struc);
          TField field = new TField();

          if (this.__isset.success) {
            if (Success != null) {
              field.Name = "Success";
              field.Type = TType.String;
              field.ID = 0;
              oprot.WriteFieldBegin(field);
              oprot.WriteString(Success);
              oprot.WriteFieldEnd();
            }
          }
          oprot.WriteFieldStop();
          oprot.WriteStructEnd();
        }
        finally
        {
          oprot.DecrementRecursionDepth();
        }
      }

      public override string ToString() {
        StringBuilder __sb = new StringBuilder("HelloNull_result(");
        bool __first = true;
        if (Success != null && __isset.success) {
          if(!__first) { __sb.Append(", "); }
          __first = false;
          __sb.Append("Success: ");
          __sb.Append(Success);
        }
        __sb.Append(")");
        return __sb.ToString();
      }

    }

  }
}

HelloService定义了服务HelloService的五个方法,每个方法包含一个方法名,参数列表和返回类型。每个参数包括参数序号,参数类型以及参数名。包含了在 Hello.thrift 文件中描述的服务 HelloService 的接口定义,即HelloService.Iface 接口,以及服务调用的底层通信细节,包括客户端的调用逻辑HelloService.Client 以及服务器端的处理逻辑HelloService.Processor,用于构建客户端和服务器端的功能。

这样,C#版的接口代码就生成成功了。非常简单,下一篇,会具体的介绍服务器端如何实现调用这些接口和客户端如何调用这些接口。

 

C#总结(三)DataGridView增加全选列

最近的一个winform的项目中,碰到datagridview控件的第一列添加全选的功能,通常这个功能,有两种实现方式:1. 为控件添加DataGridViewCheckBoxColumn来实现,但是需要提供全选反选功能,2. 再加一个checkbox控件跟datagridview组合来实现全选反选功能。但是,感觉这两种实现效果都不是很好。网上查资料,发现一个老外的实现方法,比较简单通用。demo 代码最下面的连接给出。

他的实现方式就是:DataGridViewCheckBoxColumn的父类DataGridViewColumnHeaderCell 里面有个HeaderCell的属性,看下DataGridViewColumnHeaderCell 的继承关系,就可以知道它继承自DataGridViewCell类, 所以只需要重写DataGridViewColumnHeaderCell类的paint方法,用CheckBoxRenderer画一个Checkbox到单元格上。即可实现在datagridview的列头增加一个全选的checkbox 。以下是实现代码:

实现代码

public delegate void CheckBoxClickedHandler(bool state);
    public class DataGridViewCheckBoxHeaderCellEventArgs : EventArgs
    {
        bool _bChecked;
        public DataGridViewCheckBoxHeaderCellEventArgs(bool bChecked)
        {
            _bChecked = bChecked;
        }
        public bool Checked
        {
            get { return _bChecked; }
        }
    }
    class DatagridViewCheckBoxHeaderCell : DataGridViewColumnHeaderCell
    {
        Point checkBoxLocation;
        Size checkBoxSize;
        bool _checked = false;
        Point _cellLocation = new Point();
        System.Windows.Forms.VisualStyles.CheckBoxState _cbState = 
            System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
        public event CheckBoxClickedHandler OnCheckBoxClicked;
 
        public DatagridViewCheckBoxHeaderCell()
        {           
        }

        protected override void Paint(System.Drawing.Graphics graphics, 
            System.Drawing.Rectangle clipBounds, 
            System.Drawing.Rectangle cellBounds, 
            int rowIndex, 
            DataGridViewElementStates dataGridViewElementState, 
            object value, 
            object formattedValue, 
            string errorText, 
            DataGridViewCellStyle cellStyle, 
            DataGridViewAdvancedBorderStyle advancedBorderStyle, 
            DataGridViewPaintParts paintParts)
        {
            base.Paint(graphics, clipBounds, cellBounds, rowIndex, 
                dataGridViewElementState, value, 
                formattedValue, errorText, cellStyle, 
                advancedBorderStyle, paintParts);
            Point p = new Point();
            Size s = CheckBoxRenderer.GetGlyphSize(graphics, 
            System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);
            p.X = cellBounds.Location.X + 
                (cellBounds.Width / 2) - (s.Width / 2) ;
            p.Y = cellBounds.Location.Y + 
                (cellBounds.Height / 2) - (s.Height / 2);
            _cellLocation = cellBounds.Location;
            checkBoxLocation = p;
            checkBoxSize = s;
            if (_checked)
                _cbState = System.Windows.Forms.VisualStyles.
                    CheckBoxState.CheckedNormal;
            else
                _cbState = System.Windows.Forms.VisualStyles.
                    CheckBoxState.UncheckedNormal;
            CheckBoxRenderer.DrawCheckBox
            (graphics, checkBoxLocation, _cbState);
        }

        protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
        {
            Point p = new Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y);
            if (p.X >= checkBoxLocation.X && p.X <= 
                checkBoxLocation.X + checkBoxSize.Width 
            && p.Y >= checkBoxLocation.Y && p.Y <= 
                checkBoxLocation.Y + checkBoxSize.Height)
            {
                _checked = !_checked;
                if (OnCheckBoxClicked != null)
                {
                    OnCheckBoxClicked(_checked);
                    this.DataGridView.InvalidateCell(this);
                }
                
            } 
            base.OnMouseClick(e);
        }     
    }

调用方式

DataGridViewCheckBoxColumn colCB = new DataGridViewCheckBoxColumn();
DatagridViewCheckBoxHeaderCell cbHeader = new DatagridViewCheckBoxHeaderCell();
colCB.HeaderCell = cbHeader;
datagridview1.Columns.Add(colCB);
cbHeader.OnCheckBoxClicked += 
    new CheckBoxClickedHandler(cbHeader_OnCheckBoxClicked);

  1. 我们只需要定义一个DataGridViewCheckBoxColumn。

2. 然后为每一行的checkbox 定义一个CheckboxClicked 事件。

 

测试程序

创建一个Winform 项目,加个datagridview控件,初始化几行默认数据。注意:datagirdview有编辑状态,如果有一行数据在编辑状态,那这一行被编辑。

解决办法就是在事件的绑定方法里面增加EndEdit()调用。

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            InitDtSource();
        }

        private void cbHeader_OnCheckBoxClicked(bool state)
        {
            //这一句很重要结束编辑状态
            dgInfo.EndEdit();
            dgInfo.Rows.OfType().ToList().ForEach(t => t.Cells[0].Value = state);
        }

        private void InitDtSource()
        {
            try
            {
                var _dtSource = new DataTable();
                //1、添加列
                _dtSource.Columns.Add("姓名", typeof(string)); //数据类型为 文本
                _dtSource.Columns.Add("身份证号", typeof(string)); //数据类型为 文本
                _dtSource.Columns.Add("时间", typeof(string)); //数据类型为 文本
                _dtSource.Columns.Add("地点", typeof(string)); //数据类型为 文本

                for (int i = 0; i < 10; i++)
                {
                    DataRow drData = _dtSource.NewRow();
                    drData[0] = "test" + i;
                    drData[1] = "35412549554521263" + i;
                    drData[2] = "2017-05-21 10:55:21";
                    drData[3] = "北京市";
                    _dtSource.Rows.Add(drData);
                }

                dgInfo.DataSource = _dtSource;

                InitColumnInfo();
            }
            catch (Exception ex)
            {

            }
        }

        private void InitColumnInfo()
        {
            int index = 0;

            DataGridViewCheckBoxColumn colCB = new DataGridViewCheckBoxColumn();
            DatagridViewCheckBoxHeaderCell cbHeader = new DatagridViewCheckBoxHeaderCell();
            colCB.HeaderCell = cbHeader;
            colCB.HeaderText = "全选";
            cbHeader.OnCheckBoxClicked += new CheckBoxClickedHandler(cbHeader_OnCheckBoxClicked);
            dgInfo.Columns.Insert(index, colCB);


            index++;
            dgInfo.Columns[index].HeaderText = "姓名";
            dgInfo.Columns[index].Width = 90;

            index++;
            dgInfo.Columns[index].HeaderText = "身份证号";
            dgInfo.Columns[index].Width = 120;

            index++;
            dgInfo.Columns[index].HeaderText = "时间";
            dgInfo.Columns[index].Width = 150;

            index++;
            dgInfo.Columns[index].HeaderText = "地点";
            dgInfo.Columns[index].Width = 100;

            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
            dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;//211, 223, 240
            dataGridViewCellStyle2.ForeColor = System.Drawing.Color.Blue;
            dataGridViewCellStyle2.SelectionForeColor = System.Drawing.Color.Blue;
            dgInfo.Columns[index].DefaultCellStyle = dataGridViewCellStyle2;
        }
    }

 

其他

1. 参考地址:https://www.codeproject.com/Articles/20165/CheckBox-Header-Column-For-DataGridView

2. Demo下载

 

C#总结(二)事件Event 介绍总结

  最近在总结一些基础的东西,主要是学起来很难懂,但是在日常又有可能会经常用到的东西。前面介绍了 C# 的 AutoResetEvent的使用介绍, 这次介绍事件(event)。

  事件(event),对于初学者来说,确实比较神秘,难懂。但是在日常编程过程中却经常遇到。事件使用得当,会让你的代码更加整洁,也能少些很多代码。

 

  一、Event事件,是一种封装过的委托。

  它拥有以下三要素:

    1. 事件发行者:达到某些条件时激发事件的对象

    2. 事件订阅者:订阅事件并对事件发生时进行处理的对象

    3. 定义发行者和订阅者关系,一个发行者可能会有多个订阅者。

  事件发行者和事件订阅者通过委托(delegate) 来实现发送方和接收方的事件触发。

  它拥有哪些好处:

    在以往我们编写订阅这类程序中,往往采用等待机制,为了等待某件事情的发生,需要不断地检测事情什么时候发生,而通过事件(event),可以大大简化了这种过程:

    1. 使用事件,可以很方便地确定程序执行顺序。

    2. 当事件驱动程序等待事件时,它不占用很多资源。事件驱动程序与过程式程序最大的不同就在于,程序不再不停地检查输入设备,而是呆着不动,等待消息的到来,每个输入的消息会被排进队列,等待程序处理它。如果没有消息在等待,则程序会把控制交回给操作系统,以运行其他程序。

    3. 事件简化了编程。事件订阅者只是简单地将消息传送给事件发行者,由发行者的事件驱动程序确定事件的处理方法。不必知道程序的内部订阅触发机制,只是需要知道如何传递消息即可。

  二、事件和委托的区别

    1.委托允许直接通过委托去访问相应的处理函数,而事件只能通过公布的回调函数去调用,

    2.事件只能通过“+=”,“-=”方式注册和取消订户处理函数,而委托除此之外还可以使用“=”直接赋值处理函数。

 

  三、事件的声明(Event)

    在类的内部声明事件,首先必须声明该事件的委托类型。例如:

    public delegate void NumManipulationHandler(NumEventArgs e);

    然后,声明事件本身,使用 event 关键字:

    // 基于上面的委托定义事件
    public event NumManipulationHandler ChangeNum;

    上面的代码定义了一个名为 NumManipulationHandler 的委托和一个名为 ChangeNum 的事件,该事件是在某个值生成的时候会调用委托事件。

 

  四、实例

    public class Program
    {
        public static void Main()
        {

            NumEvent even = new NumEvent(0);
            even.ChangeNum += EventAction.Action;

            even.SetValue(7);
            even.SetValue(11);

            System.Console.ReadKey();
        }
    }

    public class NumEvent
    {
        private int value;

        public delegate void NumManipulationHandler(NumEventArgs e);

        public event NumManipulationHandler ChangeNum;

        public virtual void OnChangeNum(NumEventArgs e)
        {
            ChangeNum?.Invoke(e);
        }

        public NumEvent(int n)
        {
            SetValue(n);
        }

        public void SetValue(int n)
        {
            if (value != n)
            {
                NumEventArgs e = new NumEventArgs(n);
                value = n;
                OnChangeNum(e);
            }
        }
    }

    public class EventAction
    {
        public static void Action(NumEventArgs e)
        {
            System.Console.WriteLine("value : " + e.value);
        }
    }

    public class NumEventArgs : EventArgs
    {
        public int value;
        public NumEventArgs(int _value)
        {
            this.value = _value;
        }
    }

 

Quartz.NET总结(六)了解Jobs 和 Triggers

前面讲了一些Quartz.net 的一些东西, http://www.cnblogs.com/zhangweizhong/category/771057.html

但是发现有一些基础的东西,没有说到。Quartz.net 里面一些个基本的元素,比如Jobs,Triggers等。

Quartz API 的接口和类主要有:

  1. IScheduler – scheduler调度的主要接口api
  2. IJob – scheduler调度执行job需要实现的接口api
  3. IJobDetail – 用来定义jobs的实例
  4. ITrigger – 定义需要执行的job的调度构成
  5. JobBuilder – 用于定义/创建job的明细情况,定义job的实例
  6. TriggerBuilder – 用于定义/创建Trigger的实例

一个Scheduler调度的生命周期从开始创建,到结束关闭,都是通过SchedulerFactory 实现的。当我们创建了IScheduler接口后,我们就可以用来add、remove、或者list jobs和triggers,或者是其他与调度相关的工作。

Scheduler 调度的方法有:

  1. WithCalendarIntervalSchedule
  2. WithCronSchedule
  3. WithDailyTimeIntervalSchedule
  4. WithSimpleSchedule

使用DateBuilder可以轻松的构建特定的执行时间点等,如下个小时、十点钟、或者是其他的具体时间方式。

Jobs and Triggers

定义一个job必须实现了IJob接口,如下为IJob接口的方法:

namespace Quartz
{
    public interface IJob
    {
        void Execute(JobExecutionContext context);
    }
}

当 Job的trigger被触发后,scheduler调度的线程 就会执行 Execute 方法,同时 JobExecutionContext 包含运行环境参数等信息也会传递给execute方法。

比如说:job可以创建并存储在独立的scheduler调度中并由Trigger触发,不同的Trigger触发器可以触发相同的job任务。这种松耦合能够保证如果Job的触发规则变了,不用去该job,而只需修改某个Trigger的触发规则。同时,配置在scheduler调度中的job任务任然是可以被其他触发器使用,而不需要我们重新定义新的job任务。

接下来需要我们了解知道的job深入内容主要有IJob接口的Execute()方法和JobDetails。

对于一个job任务来说,我们知道如何制定特定type类型和如何编写实现一个job的代码,对于使用Quartz.NET来说我们需要了解它各种各样的属性参数,可以通过job的JobDetail明细类来进行分析。

JobDetail明细类的实例是通过JobBuilder创建的,JobBuilder允许我们使用连续的接口方式进行实现和编码。

        // define the job and tie it to our HelloJob class
        IJobDetail job = JobBuilder.Create()
          .WithIdentity("myJob", "group1") // name "myJob", group "group1"
          .Build();

        // Trigger the job to run now, and then every 40 seconds
        ITrigger trigger = TriggerBuilder.Create()
          .WithIdentity("myTrigger", "group1")
          .StartNow()
          .WithSimpleSchedule(x => x
            .WithIntervalInSeconds(40)
            .RepeatForever())
          .Build();

        // Tell quartz to schedule the job using our trigger
        sched.scheduleJob(job, trigger);

接下来定义一个job实现类HelloJob:

    public class HelloJob : IJob
    {
        public void Execute(IJobExecutionContext context)
        {
            Console.WriteLine("HelloJob is executing.");
        }
    }

说到这里,有些 job任务是需要设置一些属性或者参数的 ,怎么给运行中的Job赋值呢? JobDataMap 提供了这方面的实现,也就是在 JobDetail对象中,设置job需要的值或参数。

JobDataMap

JobDataMap可以存储容纳任意数量的可序列化对象,以便在job实例化执行的时候使用。JobDataMap是IDictionary接口的实现,并添加了一些方便存储和检索原始类型数据的方法。

下面演示一小段代码来说明将job任务添加到scheduler调度前先将数据添加到JobDataMap:

Setting Values in a JobDataMap

  // define the job and tie it to our DumbJob class
            IJobDetail job = JobBuilder.Create()
               .WithIdentity("myJob", "group1") // name "myJob", group "group1"
                  .UsingJobData("jobSays", "Hello World!")
               .UsingJobData("myFloatValue", 3.141f)
               .Build();

下面演示下job任务执行时候从JobDataMap中取出数据的代码片段:

Getting Values from a JobDataMap

  public class DumbJob : IJob
    {
        public void Execute(JobExecutionContext context)
        {
            JobKey key = context.JobDetail.Key;

            JobDataMap dataMap = context.JobDetail.JobDataMap;

            string jobSays = dataMap.GetString("jobSays");
            float myFloatValue = dataMap.GetFloat("myFloatValue");

            Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
        }
    }

有了JobDataMaps Job在执行的过程中,就可以取出需要的相关参数。