月度归档:2015年04月

分布式文件系统FastDFS设计原理

FastDFS是一个开源的轻量级分布式文件系统,由跟踪服务器(tracker server)、存储服务器(storage server)和客户端(client)三个部分组成,主要解决了海量数据存储问题,特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务。

点击查看原图

Storage server

Storage server(后简称storage)以组(卷,group或volume)为单位组织,一个group内包含多台storage机器,数据互为备份,存储空间以group内容量最小的storage为准,所以建议group内的多个storage尽量配置相同,以免造成存储空间的浪费。

以group为单位组织存储能方便的进行应用隔离、负载均衡、副本数定制(group内storage server数量即为该group的副本数),比如将不同应用数据存到不同的group就能隔离应用数据,同时还可根据应用的访问特性来将应用分配到不同的group来做负载均衡;缺点是group的容量受单机存储容量的限制,同时当group内有机器坏掉时,数据恢复只能依赖group内地其他机器,使得恢复时间会很长。

group内每个storage的存储依赖于本地文件系统,storage可配置多个数据存储目录,比如有10块磁盘,分别挂载在/data/disk1-/data/disk10,则可将这10个目录都配置为storage的数据存储目录。

storage接受到写文件请求时,会根据配置好的规则(后面会介绍),选择其中一个存储目录来存储文件。为了避免单个目录下的文件数太多,在storage第一次启动时,会在每个数据存储目录里创建2级子目录,每级256个,总共65536个文件,新写的文件会以hash的方式被路由到其中某个子目录下,然后将文件数据直接作为一个本地文件存储到该目录中。

Tracker server

Tracker是FastDFS的协调者,负责管理所有的storage server和group,每个storage在启动后会连接Tracker,告知自己所属的group等信息,并保持周期性的心跳,tracker根据storage的心跳信息,建立group==>[storage server list]的映射表。

Tracker需要管理的元信息很少,会全部存储在内存中;另外tracker上的元信息都是由storage汇报的信息生成的,本身不需要持久化任何数据,这样使得tracker非常容易扩展,直接增加tracker机器即可扩展为tracker cluster来服务,cluster里每个tracker之间是完全对等的,所有的tracker都接受stroage的心跳信息,生成元数据信息来提供读写服务。

Upload file

FastDFS向使用者提供基本文件访问接口,比如upload、download、append、delete等,以客户端库的方式提供给用户使用。

点击查看原图

选择tracker server

当集群中不止一个tracker server时,由于tracker之间是完全对等的关系,客户端在upload文件时可以任意选择一个trakcer

选择存储的group

tracker接收到upload file的请求时,会为该文件分配一个可以存储该文件的group,支持如下选择group的规则: 1. Round robin,所有的group间轮询 2. Specified group,指定某一个确定的group 3. Load balance,剩余存储空间多多group优先

选择storage server

当选定group后,tracker会在group内选择一个storage server给客户端,支持如下选择storage的规则: 1. Round robin,在group内的所有storage间轮询 2. First server ordered by ip,按ip排序 3. First server ordered by priority,按优先级排序(优先级在storage上配置)

选择storage path

当分配好storage server后,客户端将向storage发送写文件请求,storage将会为文件分配一个数据存储目录,支持如下规则: 1. Round robin,多个存储目录间轮询 2. 剩余存储空间最多的优先

生成Fileid

选定存储目录之后,storage会为文件生一个Fileid,由storage server ip、文件创建时间、文件大小、文件crc32和一个随机数拼接而成,然后将这个二进制串进行base64编码,转换为可打印的字符串。

选择两级目录

当选定存储目录之后,storage会为文件分配一个fileid,每个存储目录下有两级256*256的子目录,storage会按文件fileid进行两次hash(猜测),路由到其中一个子目录,然后将文件以fileid为文件名存储到该子目录下。

生成文件名

当文件存储到某个子目录后,即认为该文件存储成功,接下来会为该文件生成一个文件名,文件名由group、存储目录、两级子目录、fileid、文件后缀名(由客户端指定,主要用于区分文件类型)拼接而成。

点击查看原图

文件同步

写文件时,客户端将文件写至group内一个storage server即认为写文件成功,storage server写完文件后,会由后台线程将文件同步至同group内其他的storage server。

每个storage写文件后,同时会写一份binlog,binlog里不包含文件数据,只包含文件名等元信息,这份binlog用于后台同步,storage会记录向group内其他storage同步的进度,以便重启后能接上次的进度继续同步;进度以时间戳的方式进行记录,所以最好能保证集群内所有server的时钟保持同步。

storage的同步进度会作为元数据的一部分汇报到tracker上,tracke在选择读storage的时候会以同步进度作为参考。

比如一个group内有A、B、C三个storage server,A向C同步到进度为T1 (T1以前写的文件都已经同步到B上了),B向C同步到时间戳为T2(T2 > T1),tracker接收到这些同步进度信息时,就会进行整理,将最小的那个做为C的同步时间戳,本例中T1即为C的同步时间戳为T1(即所有T1以前写的数据都已经同步到C上了);同理,根据上述规则,tracker会为A、B生成一个同步时间戳。

Download file

客户端upload file成功后,会拿到一个storage生成的文件名,接下来客户端根据这个文件名即可访问到该文件。

点击查看原图

跟upload file一样,在download file时客户端可以选择任意tracker server。

tracker发送download请求给某个tracker,必须带上文件名信息,tracke从文件名中解析出文件的group、大小、创建时间等信息,然后为该请求选择一个storage用来服务读请求。由于group内的文件同步时在后台异步进行的,所以有可能出现在读到时候,文件还没有同步到某些storage server上,为了尽量避免访问到这样的storage,tracker按照如下规则选择group内可读的storage。

1. 该文件上传到的源头storage - 源头storage只要存活着,肯定包含这个文件,源头的地址被编码在文件名中。 2. 文件创建时间戳==storage被同步到的时间戳 且(当前时间-文件创建时间戳) > 文件同步最大时间(如5分钟) - 文件创建后,认为经过最大同步时间后,肯定已经同步到其他storage了。 3. 文件创建时间戳 < storage被同步到的时间戳。 - 同步时间戳之前的文件确定已经同步了 4. (当前时间-文件创建时间戳) > 同步延迟阀值(如一天)。 - 经过同步延迟阈值时间,认为文件肯定已经同步了。

小文件合并存储

小文件合并存储主要解决如下几个问题:

1. 本地文件系统inode数量有限,从而存储的小文件数量也就受到限制。 2. 多级目录+目录里很多文件,导致访问文件的开销很大(可能导致很多次IO 3. 按小文件存储,备份与恢复的效率低

FastDFS在V3.0版本里引入小文件合并存储的机制,可将多个小文件存储到一个大的文件(trunk file),为了支持这个机制,FastDFS生成的文件fileid需要额外增加16个字节

1. trunk file id 2. 文件在trunk file内部的offset 3. 文件占用的存储空间大小 (字节对齐及删除空间复用,文件占用存储空间>=文件大小)

每个trunk file由一个id唯一标识,trunk file由group内的trunk server负责创建(trunk server是tracker选出来的),并同步到group内其他的storage,文件存储合并存储到trunk file后,根据其offset就能从trunk file读取到文件。

文件在trunk file内的offset编码到文件名,决定了其在trunk file内的位置是不能更改的,也就不能通过compact的方式回收trunk file内删除文件的空间。但当trunk file内有文件删除时,其删除的空间是可以被复用的,比如一个100KB的文件被删除,接下来存储一个99KB的文件就可以直接复用这片删除的存储空间。

HTTP访问支持

FastDFS的tracker和storage都内置了http协议的支持,客户端可以通过http协议来下载文件,tracker在接收到请求时,通过http的redirect机制将请求重定向至文件所在的storage上;除了内置的http协议外,FastDFS还提供了通过apache或nginx扩展模块下载文件的支持。

点击查看原图

其他特性

FastDFS提供了设置/获取文件扩展属性的接口(setmeta/getmeta),扩展属性以key-value对的方式存储在storage上的同名文件(拥有特殊的前缀或后缀),比如/group/M00/00/01/some_file为原始文件,则该文件的扩展属性存储在/group/M00/00/01/.some_file.meta文件(真实情况不一定是这样,但机制类似),这样根据文件名就能定位到存储扩展属性的文件。

以上两个接口作者不建议使用,额外的meta文件会进一步“放大”海量小文件存储问题,同时由于meta非常小,其存储空间利用率也不高,比如100bytes的meta文件也需要占用4K(block_size)的存储空间。

FastDFS还提供appender file的支持,通过upload_appender_file接口存储,appender file允许在创建后,对该文件进行append操作。实际上,appender file与普通文件的存储方式是相同的,不同的是,appender file不能被合并存储到trunk file。

问题讨论

从FastDFS的整个设计看,基本上都已简单为原则。比如以机器为单位备份数据,简化了tracker的管理工作;storage直接借助本地文件系统原样存储文件,简化了storage的管理工作;文件写单份到storage即为成功、然后后台同步,简化了写文件流程。但简单的方案能解决的问题通常也有限,FastDFS目前尚存在如下问题(欢迎探讨)。

数据安全性

  • 写一份即成功:从源storage写完文件至同步到组内其他storage的时间窗口内,一旦源storage出现故障,就可能导致用户数据丢失,而数据的丢失对存储系统来说通常是不可接受的。
  • 缺乏自动化恢复机制:当storage的某块磁盘故障时,只能换存磁盘,然后手动恢复数据;由于按机器备份,似乎也不可能有自动化恢复机制,除非有预先准备好的热备磁盘,缺乏自动化恢复机制会增加系统运维工作。
  • 数据恢复效率低:恢复数据时,只能从group内其他的storage读取,同时由于小文件的访问效率本身较低,按文件恢复的效率也会很低,低的恢复效率也就意味着数据处于不安全状态的时间更长。
  • 缺乏多机房容灾支持:目前要做多机房容灾,只能额外做工具来将数据同步到备份的集群,无自动化机制。

存储空间利用率

  • 单机存储的文件数受限于inode数量
  • 每个文件对应一个storage本地文件系统的文件,平均每个文件会存在block_size/2的存储空间浪费。
  • 文件合并存储能有效解决上述两个问题,但由于合并存储没有空间回收机制,删除文件的空间不保证一定能复用,也存在空间浪费的问题

负载均衡

  • group机制本身可用来做负载均衡,但这只是一种静态的负载均衡机制,需要预先知道应用的访问特性;同时group机制也导致不可能在group之间迁移数据来做动态负载均衡。

备注

  • 本文的配图均来自互联网,如涉及版权问题,请联系我删除。

CentOS 6.3 KVM

参考文章:

RedHat/CentOS6.2 x86系统KVM虚拟机网络配置 http://www.linuxidc.com/Linux/2012-05/61445.htm 

作业环境

服务器端

操作系统:CentOS 6.3 final x86_64

IP: 133.133.10.50

Hostname:myKVM

KVM:qemu-kvm-0.12.1.2-2.295.el6_3.2.x86_64

客户端:Ubuntu和Win7,先在服务器端装好VNC,通过VNC连接服务器CentOS

一、安装KVM及相关软件

1、KVM 需要有 CPU 的支持(Intel vmx 或 AMD svm),在安装 KVM 之前检查一下 CPU 是否提供了虚拟技术的支持:

[root@myKVM ~]# egrep '^flags.*(vmx|svm)' /proc/cpuinfo

有显示, 有显示则说明处理器具有VT功能

2、在主板BIOS中开启CPU的Virtual Technolege(VT,虚化技术)

3、安装kvm及其需要的软件包

[root@myKVM ~]# yum install kvm kmod-kvm qemu kvm-qemu-img virt-viewer virt-manager libvirt libvirt-python python-virtinst

[root@myKVM ~]# yum groupinstall KVM

4、检查kvm模块是否安装,使用以下命令显示两个模块则表示安装完成

[root@myKVM ~]# lsmod | grep kvm

kvm_intel              52570  0 

kvm                   314739  1 kvm_intel

二、启动virt-manager管理界面

1、客户端

VNC到服务器端,因为需要用服务器的图形界面

2、服务器端



启动libvirtd服务,并保证下次自动启动

[root@myKVM ~]# service libvirtd start

Starting libvirtd daemon:                                  [ 确定 ]

[root@myKVM ~]# chkconfig libvirtd on

三、远程创建和管理KVM虚拟机

1. 打开Application=> System Tools => Virtual Machine Manager就可以装虚拟机了,功能跟VMware类似


点击查看原图

2. “Details”中可查看或修改Guest OS的配置信息

点击查看原图

点击查看原图

3、安装完成后需要将默认的IDE硬盘和网卡换成virtio,并打上驱动程序,这样性能就可以得到提升。(默认好像就是这样的)

点击查看原图

 

点击查看原图

4、在远程管理KVM虚拟化物理主机的过程中,可以动态调节VM的内存,实现clone,开机,关机,暂停主机等操作。

5、服务器端使用virsh list命令可查看主机虚拟化情况

[root@myKVM ~]# virsh list

 Id    Name                           State

----------------------------------------------------

 1     CentOSKVM                      running

我在myKVM上由装了centOS的虚拟机,命名为centOSKVM

 

8、KVM虚拟机的信息保存在 /var/lib/libvirt目录下

点击查看原图

三、KVM默认网络配置

1、kvm上网有两种配置,一种是default,它支持主机与虚拟机的互访,同时也支持虚拟机访问互联网,但不支持外界访问虚拟机。另外一种方式是bridge方式,可以使用虚拟机成为网络中具有独立IP的主机。

四、配置bridge上网方式

1、创建桥接器

在/etc/sysconfig/network-scripts目录下,创建一个ifcfg-br0 文件,其类型设为Bridge:

[root@myKVM libvirt]# cat /etc/sysconfig/network-scripts/ifcfg-br0

DEVICE="br0"

TYPE="Bridge"

BOOTPROTO="static"

#HWADDR="00:23:AE:72:50:90"

NM_CONTROLLED="yes"

ONBOOT="yes"

#TYPE="Ethernet"

IPADDR=133.133.10.50

NETMASK=255.255.0.0

2、将物理接口桥接到桥接器

修改eth0的内容(本服务器是用eth0上网的),去掉其IP相关信息,加上“BRIDGE=br0”,将其桥接到br0上;如果是双网卡或是多网卡,照此过程修改:

[root@myKVM libvirt]# cat /etc/sysconfig/network-scripts/ifcfg-eth0

DEVICE="eth0"

BOOTPROTO="static"

HWADDR="00:23:AE:72:50:90"

NM_CONTROLLED="yes"

ONBOOT="yes"

TYPE="Ethernet"

#UUID="18ffa985-2081-4be5-951b-fb0d136741bf"

#IPADDR=133.133.10.50

#NETMASK=255.255.0.0

BRIDGE=br0

3、重启物理机网络服务

[root@etone ~]# service network restart

4、查看当前桥接情况(有一个虚拟机正在运行)

[root@myKVM libvirt]# brctl show

bridge name     bridge id               STP enabled     interfaces

br0             8000.0023ae725090       no              eth0

virbr0          8000.525400efd043       yes             virbr0-nic

                                                        vnet0

5、将虚拟机接口桥接到桥接器

[root@myKVM ~]# brctl delif virbr0 vnet0

[root@myKVM ~]# brctl addif br0 vnet0

[root@myKVM ~]# brctl show

[root@myKVM libvirt]# brctl show

bridge name     bridge id               STP enabled     interfaces

br0             8000.0023ae725090       no              eth0

                                                   vnet0

virbr0          8000.525400efd043       yes             virbr0-nic

这一步也可以在Virtual Machine Manager中进行。

点击查看原图

6、将虚拟机centOSKVM IP设置为同一网段地址:133.133.10.51

此时,该虚机可以访问外部网络,并可被外部网络访问。

服务器可以连虚拟机

[root@myKVM libvirt]# ping 133.133.10.51

PING 133.133.10.51 (133.133.10.51) 56(84) bytes of data.

64 bytes from 133.133.10.51: icmp_seq=1 ttl=64 time=0.700 ms

64 bytes from 133.133.10.51: icmp_seq=2 ttl=64 time=0.093 ms

64 bytes from 133.133.10.51: icmp_seq=3 ttl=64 time=0.072 ms

^C

--- 133.133.10.51 ping statistics ---

3 packets transmitted, 3 received, 0% packet loss, time 2137ms

rtt min/avg/max/mdev = 0.072/0.288/0.700/0.291 ms

虚拟机可以连外网
[root@centOSKVM ~]# ping 133.133.10.50
PING 133.133.10.50 (133.133.10.50) 56(84) bytes of data.
64 bytes from 133.133.10.50: icmp_seq=1 ttl=64 time=0.090 ms
64 bytes from 133.133.10.50: icmp_seq=2 ttl=64 time=0.075 ms
^C
--- 133.133.10.50 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1492ms
rtt min/avg/max/mdev = 0.075/0.082/0.090/0.011 ms
[root@centOSKVM ~]# ping 133.133.133.53
PING 133.133.133.53 (133.133.133.53) 56(84) bytes of data.
64 bytes from 133.133.133.53: icmp_seq=1 ttl=64 time=2.14 ms
64 bytes from 133.133.133.53: icmp_seq=2 ttl=64 time=0.323 ms
^C
--- 133.133.133.53 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1836ms
rtt min/avg/max/mdev = 0.323/1.235/2.147/0.912 ms

ASA5505 failover

热备必需为SEC版本

ASA 5505是 security plus 的licence,支持Stateless A/S的failover,不支持状态化的failover,

当一台设备挂了,另一台会开始工作,但是所有session会断掉,也就是用户会明显感觉断网过网,如果做stateful的AA就不会出现这种问题

5505是没有context的,也就是虚拟防火墙,所以不支持AA failover

AA的failover 需要 5510以上的ASA,5510需security plus的,若为5520以上的就不用购买security plus 的,因为5510普通版是不支持AA的

另外,ASA5505 failover和VPN 不能同时做,当ASA作为Ezvpn 的client端以后,就不能做failover,当做了failover就不能支持EZVPN

LogParser

Logparser是一款非常强大的日志分析软件,可以帮助你详细的分析网站日志。是所有数据分析和网站优化人员都应该会的一个软件。Logparser是微软的一款软件完全免费的,大家可以在微软的官网上去下载,下载地址:

http://www.microsoft.com/en-us/download/details.aspx?id=24659

   下载后安装也非常简单,只要按照步骤去安装就可以了,虽然软件语言是英语但是都是比较容易的。

使用方法:

打开logparser

点击查看原图

    出现的是这个类似命令行的窗口,我们就是在这个窗口输入各种命令对日志进行分析。那我们怎么去运行呢?

最基本的格式:LogParser –i:输入文件的格式 –o:输出格式 “SQL语句”

    我们输入一个最简单的例子,把一个IIS日志转化成一个csv格式的表格,那么我们应该怎么做呢?

C:\Program Files\Log Parser 2.2> logparser -i:iisw3c -o:csv “SELECT *FROM C:rizhi.log” >D:rizhi.scv

   这就是完成了最简单的转换,把C盘中rizhi.log这个日志转换成csv格式并保存到D盘。我们要注意的是-i:iisw3c,-i代表的是输入,iisw3c代表的是日志格式,例子中分析的是iis日志,因此标准格式的iisw3c。-o:scv,-o 代表的是输出,csv是输出文件的格式。”SELECT*FROM”这个是分析日志的SQL命令语句,我们可以用不同的SQL语句来分析日志。

注意:logparser是区分大小写的,因此SQL语句一定要用大写,不然会出错的。

明白了logparser怎么去用,那么接下来给大家介绍一些我们经常用到的操作命令。

--对日志中的url进行归并统计 

LogParser -o:csv "SELECT cs-uri-stem, COUNT(*) into a.csv FROM iis.log GROUP BY cs-uri-stem" 

--取出所有的asp页面
SELECT COUNT(*) FROM ex040528.log WHERE EXTRACT_EXTENSION(cs-uri-stem) LIKE 'asp'

--求出各个路径的访问次数
SELECT cs-uri-stem, COUNT(*) FROM ex040528.log GROUP BY cs-uri-stem

--求出各个资源类型的访问次数
SELECT EXTRACT_EXTENSION(cs-uri-stem) AS PageType, COUNT(*)
FROM ex040528.log
GROUP BY PageType

--group by两个东东
SELECT EXTRACT_EXTENSION(cs-uri-stem) AS PageType, sc-status, COUNT(*)
FROM ex040528.log
GROUP BY PageType, sc-status

--求出各个资源类型的访问次数,并按访问次数降序排列
SELECT EXTRACT_EXTENSION(cs-uri-stem) AS PageType, COUNT(*) AS PageTypeHits
FROM ex040528.log
GROUP BY PageType
ORDER BY PageTypeHits DESC

--求出各个页面类型,各种返回结果占总点击数的比重
SELECT EXTRACT_EXTENSION(cs-uri-stem) AS PageType, sc-status, MUL(PROPCOUNT(*), 100.0) AS Hits
FROM ex040528.log
GROUP BY PageType, sc-status
ORDER BY PageType, sc-status

--求出各个页面类型、各种返回结果的点击数占各种页面类型点击数的百分比
SELECT EXTRACT_EXTENSION(cs-uri-stem) AS PageType, sc-status, MUL(PROPCOUNT(*) ON (PageType), 100.0) AS Hits
FROM ex040528.log
GROUP BY PageType, sc-status
ORDER BY PageType, sc-status
--求解各种类型的客户端环境占总环境数的百分比
SELECT DISTINCT cs(User-Agent) as IEType,count(*) as total,mul(propcount(*),100) as [percent(%)]
FROM F:/挑战赛资料/ex081213.log
group by IEType

转义字符:
/
注意:
order by后只能有一个desc或者asc
order by的项必须要在select子句中
having子句必须紧跟group子句

LogParser Developer

微软有个工具Log Parser可以帮助你分析日志。它功能强大,使用简单,可以分析基于文本的日志文件、XML 文件、CSV(逗号分隔符)文件,以及操作系统的事件日志、注册表、文件系统、Active Directory。它可以像使用 SQL 语句一样查询分析这些数据,甚至可以把分析结果以各种图表的形式展现出来。

Log Parser可以到微软的网站下载,安装完后,就会有命令行的执行程序LogParser.exe,供API使用的LogParser.dll及说明文件LogParser.chm,里面还会有一些Sample Code可以供参考.

Log Parser支持的格式很多,输入格式如下:

点击查看原图

输出格式如下:

点击查看原图

通过 .NET Framework 的 COM interop (COM 交互操作)特性,可以很方便地在 .NET 应用程序中使用 Log Parser,.NET Framework 的 COM interop 是通过 Runtime Callable Wrappers (RCW) 来实现对 COM 的操作的,RCW 是 .NET 中的一个类。

现在要玩的是,怎么用LogParser.dll来开发更适合的API,其实命令行的做法就可以满足大部份的需求,但有时有时特殊的判断,在命令行模式下就有难度了,比如说,我们需要用程序自动去处理大批量的日志文件分析等,所以这时用API就方便很多.

下面我们用.NET封装下LogParser的Com接口,从LogParser的操作流程来看,无非就是不同格式文件的日志文件的输入,通过类SQL的分析输出我们需要的结果,核心算法就是类似于

          // 初始化LogQuery 对象 
            var logQuery = new LogQueryClass();

            // 缓存输入上下文 
            if (myInputContext == null) 
                myInputContext = GetInputContext(); 
            // 执行查询   
            var oRecordSet = logQuery.Execute(query, myInputContext); 
           
             // 浏览记录 
            for (; !oRecordSet.atEnd(); oRecordSet.moveNext()) 
            { 
                // 获取当前的记录 
                ILogRecord logRecord = oRecordSet.getRecord();              
            } 
            oRecordSet.close();

使用OOP方式封装接口,大家很容易的就会得出类似下面的设计,类图如下:

点击查看原图

每一种类型的日志的分析主要是格式的不同,通过一个配置类去记录每种类型的不同配置,根据配置去生成相应的输入、输出格式类。下面我们就来做个简单的Demo来演示下IIS日志分析。

      private void QryData()
      {

          //构造查询语句           StringBuilder query = new StringBuilder("SELECT ");
          if (txtRecord.Text.Trim().Length > 0)
          {
              int rec = 0;
              if (int.TryParse(txtRecord.Text.Trim(), out rec))
              {
                  if (rec > 0)
                  {//如果有值,而且数字大于0,则取数字取得Top n                       query.Append(" TOP ");
                      query.Append(rec);
                  }
              }
          }
          if (btnLocalTime.Checked)
          {
              query.Append("UserIP,Username,TO_LOCALTIME(TO_TIMESTAMP(date,time)) as LocalTime");
          }
          else           {
              query.Append("UserIP,Username,TO_TIMESTAMP(date,time) as LocalTime");
          }

          query.Append(",ServiceInstance,HostName,ServerIP,TimeTaken,BytesSent,bytesReceived,StatusCode" +
                       ",Win32StatusCode,RequestType,Target,Parameters " +
                       "FROM ");
          query.Append(openFileDialog1.FileName);
          if (txtWhere.Text.Trim().Length > 0)
          {
              query.Append(" Where ");
              query.Append(txtWhere.Text.Trim());
          }

          IISLogParserConfig config = new IISLogParserConfig()
          {
               Codepage =936,
              
          };
          IISLogParser logParser = new IISLogParser(config);
          DataTable dt = logParser.RunQuery(query.ToString());
          dataGridView1.DataSource = dt;
      }

这段Code就是把Log读进来,并用DataGrid去显示,如果IIS Log里的时间不是当地时间,还可用TO_LOCALTIME做时间转换,看这Script,还可以用 Select Top n from 文件路径,这里有一点要注意,路径不能有中文名称,Query有那些可以用,可以参考说明文件的Query Syntax章节.

点击查看原图

注意:LogParser.dll是需要注册的,如果没有注册,是会抛出错误信息,注册的方式很简单,也就是注册Com组件,在命令行模式下 : C:\LogParser>regsvr32 LogParser.dll

ibdata1 & mysql-bin

ibdata1和mysql-bin
问题:磁盘空间报警,经查发现ibdata1和mysql-bin日志占用空间太多(其中ibdata1超过120G,mysql-bin超过80G)
原因:ibdata1是存储格式,在INNODB类型数据状态下,ibdata1用来存储文件的数据和索引,而库名的文件夹里的那些表文件只是结构而已。
innodb存储引擎有两种表空间的管理方式,分别是:
1)共享表空间(可拆分为多个小的表空间文件),这个是我们目前多数数据库使用的方法;
2)独立表空间,每一个表有一个独立的表空间(磁盘文件)
对于两种管理方式,各有优劣,具体如下:
①共享表空间:
优点:可以将表空间分成多个文件存放到不同的磁盘上(表空间文件大小不受表大小的限制,一个表可以分布在不同步的文件上)。
缺点:所有数据和索引存放在一个文件中,则随着数据的增加,将会有一个很大的文件,虽然可以把一个大文件分成多个小文件,但是多个表及索引在表空间中混合存储,这样如果对于一个表做了大量删除操作后表空间中将有大量空隙。对于共享表空间管理的方式下,一旦表空间被分配,就不能再回缩了。当出现临时建索引或是创建一个临时表的操作表空间扩大后,就是删除相关的表也没办法回缩那部分空间了。
②独立表空间:在配置文件(my.cnf)中设置: innodb_file_per_table
特点:每个表都有自已独立的表空间;每个表的数据和索引都会存在自已的表空间中。
优点:表空间对应的磁盘空间可以被收回(Drop table操作自动回收表空间,如果对于删除大量数据后的表可以通过:alter table tbl_name engine=innodb;回缩不用的空间。
缺点:如果单表增加过大,如超过100G,性能也会受到影响。在这种情况下,如果使用共享表空间可以把文件分开,但有同样有一个问题,如果访问的范围过大同样会访问多个文件,一样会比较慢。如果使用独立表空间,可以考虑使用分区表的方法,在一定程度上缓解问题。此外,当启用独立表空间模式时,需要合理调整innodb_open_files参数的设置。
解决:
1)ibdata1数据太大:只能通过dump,导出建库的sql语句,再重建的方法。
2)mysql-bin Log太大:
①手动删除:
删除某个日志:mysql>PURGE MASTER LOGS TO ‘mysql-bin.010′;
删除某天前的日志:mysql>PURGE MASTER LOGS BEFORE ’2010-12-22 13:00:00′;
②在/etc/my.cnf里设置只保存N天的bin-log日志
expire_logs_days = 30 //Binary Log自动删除的天数

MySql ibdata1文件

MySql innodb如果是共享表空间,ibdata1文件越来越大,达到了30多个G,对一些没用的表进行清空:

truncate table xxx;

然后optimize table xxx; 没有效果

因为对共享表空间不起作用。

mysql ibdata1存放数据,索引等,是MYSQL的最主要的数据。

如果不把数据分开存放的话,这个文件的大小很容易就上了G,甚至几十G。对于某些应用来说,并不是太合适。因此要把此文件缩小。

无法自动收缩,必须数据导出,删除ibdata1,然后数据导入,比较麻烦,因此需要改为每个表单独的文件。

解决方法:数据文件单独存放(共享表空间如何改为每个表独立的表空间文件)。

步骤如下:

 

1)备份数据库

备份全部数据库,执行命令

#mysqldump -q -uroot -ppassword --add-drop-table --all-databases >/home/backup/all.sql

做完此步后,停止数据库服务。

#service mysqld stop

 

2)找到my.ini或my.cnf文件

linux下执行 

# /usr/libexec/mysqld --verbose --help | grep -A 1 'Default options'

Default options are read from the following files in the given order:

/etc/mysql/my.cnf /etc/my.cnf ~/.my.cnf 

windows环境下可以:

mysqld --verbose --help > mysqlhelp.txt

notepad mysqlhelp.txt

在里面查找Default options,可以看到查找my.ini的顺序,以找到真实目录

 

3)修改mysql配置文件

打开my.ini或my.cnf文件

[mysqld]下增加下面配置

innodb_file_per_table=1

验证配置是否生效,可以重启mysql后,执行

#service mysqld restart

#mysql -uroot -ppassword

mysql> show variables like '%per_table%';

+-----------------------+-------+

| Variable_name         | Value |

+-----------------------+-------+

| innodb_file_per_table | ON    |

+-----------------------+-------+

1 row in set (0.00 sec)





mysql> 



看看innodb_file_per_table变量是否为ON

 

4)删除原数据文件

删除原来的ibdata1文件及日志文件ib_logfile*,删除/var/lib/mysql目录下的应用数据库文件夹(mysql文件夹不要删)

 

5)还原数据库

启动数据库服务

从命令行进入MySQL Server

还原全部数据库,执行命令

#service mysqld start

#mysql -uroot -pocs < /home/backup/all.sql 





经过以上几步后,可以看到新的ibdata1文件就只有几十M了,数据及索引都变成了针对单个表的小ibd文件了,它们在相应数据库的文件夹下面。





# ll

total 295028

drwx------  2 mysql mysql     36864 Apr 22 14:16 glpi

drwx------  2 mysql mysql     36864 Feb 15 13:45 glpi-1

-rw-rw----  1 mysql mysql  10485760 Apr 22 14:27 ibdata1

-rw-rw----. 1 mysql mysql 270532608 Apr 22 14:14 ibdata1-1

-rw-rw----  1 mysql mysql   5242880 Apr 22 14:27 ib_logfile0

-rw-rw----. 1 mysql mysql   5242880 Apr 22 14:14 ib_logfile0_bak

-rw-rw----  1 mysql mysql   5242880 Apr 22 14:28 ib_logfile1

-rw-rw----. 1 mysql mysql   5242880 Apr 21 22:50 ib_logfile1_bak

drwx------  2 mysql mysql      4096 Apr 22 14:16 mrbs

drwx------  2 mysql mysql      4096 Apr 14 12:05 mrbs-1

drwx------. 2 mysql mysql      4096 Apr 22 14:16 mysql

srwxrwxrwx  1 mysql mysql         0 Apr 22 14:16 mysql.sock

drwx------  2 mysql mysql     12288 Apr 22 14:16 ocsweb

drwx------  2 mysql mysql     12288 Nov 16  2011 ocsweb-1





# ll mrbs

total 808

-rw-rw---- 1 mysql mysql     61 Apr 22 14:16 db.opt

-rw-rw---- 1 mysql mysql  10492 Apr 22 14:16 mrbs_area.frm

-rw-rw---- 1 mysql mysql  98304 Apr 22 14:16 mrbs_area.ibd

-rw-rw---- 1 mysql mysql   9264 Apr 22 14:16 mrbs_entry.frm

-rw-rw---- 1 mysql mysql 131072 Apr 22 14:16 mrbs_entry.ibd

-rw-rw---- 1 mysql mysql   9442 Apr 22 14:16 mrbs_repeat.frm

-rw-rw---- 1 mysql mysql  98304 Apr 22 14:16 mrbs_repeat.ibd

-rw-rw---- 1 mysql mysql   8888 Apr 22 14:16 mrbs_room.frm

-rw-rw---- 1 mysql mysql 114688 Apr 22 14:16 mrbs_room.ibd

-rw-rw---- 1 mysql mysql   8688 Apr 22 14:16 mrbs_users.frm

-rw-rw---- 1 mysql mysql  98304 Apr 22 14:16 mrbs_users.ibd

-rw-rw---- 1 mysql mysql   8658 Apr 22 14:16 mrbs_variables.frm

-rw-rw---- 1 mysql mysql  98304 Apr 22 14:16 mrbs_variables.ibd

-rw-rw---- 1 mysql mysql   8738 Apr 22 14:16 mrbs_zoneinfo.frm

-rw-rw---- 1 mysql mysql  98304 Apr 22 14:16 mrbs_zoneinfo.ibd





# ll mrbs-1

total 88

-rw-rw---- 1 mysql mysql    61 Apr 14 12:05 db.opt

-rw-rw---- 1 mysql mysql 10492 Apr 14 12:05 mrbs_area.frm

-rw-rw---- 1 mysql mysql  9264 Apr 14 12:05 mrbs_entry.frm

-rw-rw---- 1 mysql mysql  9442 Apr 14 12:05 mrbs_repeat.frm

-rw-rw---- 1 mysql mysql  8888 Apr 14 12:05 mrbs_room.frm

-rw-rw---- 1 mysql mysql  8688 Apr 14 12:05 mrbs_users.frm

-rw-rw---- 1 mysql mysql  8658 Apr 14 12:05 mrbs_variables.frm

-rw-rw---- 1 mysql mysql  8738 Apr 14 12:05 mrbs_zoneinfo.frm

网上找到的一个 dot net X509 证书生成类

网上找到的一个 dot net X509 证书生成类

 

using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using SecureString = System.Security.SecureString;
using RuntimeHelpers = System.Runtime.CompilerServices.RuntimeHelpers;

internal class Certificate
{
    public static byte[] CreateSelfSignCertificatePfx(
        string x500,
        DateTime startTime,
        DateTime endTime)
    {
        byte[] pfxData = CreateSelfSignCertificatePfx(
            x500,
            startTime,
            endTime,
            (SecureString)null);
        return pfxData;
    }

    public static byte[] CreateSelfSignCertificatePfx(
        string x500,
        DateTime startTime,
        DateTime endTime,
        string insecurePassword)
    {
        byte[] pfxData;
        SecureString password = null;

        try
        {
            if (!string.IsNullOrEmpty(insecurePassword))
            {
                password = new SecureString();
                foreach (char ch in insecurePassword)
                {
                    password.AppendChar(ch);
                }

                password.MakeReadOnly();
            }

            pfxData = CreateSelfSignCertificatePfx(
                x500,
                startTime,
                endTime,
                password);
        }
        finally
        {
            if (password != null)
            {
                password.Dispose();
            }
        }

        return pfxData;
    }

    public static byte[] CreateSelfSignCertificatePfx(
        string x500,
        DateTime startTime,
        DateTime endTime,
        SecureString password)
    {
        byte[] pfxData;

        if (x500 == null)
        {
            x500 = "";
        }

        SystemTime startSystemTime = ToSystemTime(startTime);
        SystemTime endSystemTime = ToSystemTime(endTime);
        string containerName = Guid.NewGuid().ToString();

        GCHandle dataHandle = new GCHandle();
        IntPtr providerContext = IntPtr.Zero;
        IntPtr cryptKey = IntPtr.Zero;
        IntPtr certContext = IntPtr.Zero;
        IntPtr certStore = IntPtr.Zero;
        IntPtr storeCertContext = IntPtr.Zero;
        IntPtr passwordPtr = IntPtr.Zero;
        RuntimeHelpers.PrepareConstrainedRegions();
        try
        {
            Check(NativeMethods.CryptAcquireContextW(
                out providerContext,
                containerName,
                null,
                1, // PROV_RSA_FULL
                8)); // CRYPT_NEWKEYSET

            Check(NativeMethods.CryptGenKey(
                providerContext,
                1, // AT_KEYEXCHANGE
                1, // CRYPT_EXPORTABLE
                out cryptKey));

            IntPtr errorStringPtr;
            int nameDataLength = 0;
            byte[] nameData;

            // errorStringPtr gets a pointer into the middle of the x500 string,
            // so x500 needs to be pinned until after we've copied the value
            // of errorStringPtr.
            dataHandle = GCHandle.Alloc(x500, GCHandleType.Pinned);

            if (!NativeMethods.CertStrToNameW(
                0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
                dataHandle.AddrOfPinnedObject(),
                3, // CERT_X500_NAME_STR = 3
                IntPtr.Zero,
                null,
                ref nameDataLength,
                out errorStringPtr))
            {
                string error = Marshal.PtrToStringUni(errorStringPtr);
                throw new ArgumentException(error);
            }

            nameData = new byte[nameDataLength];

            if (!NativeMethods.CertStrToNameW(
                0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
                dataHandle.AddrOfPinnedObject(),
                3, // CERT_X500_NAME_STR = 3
                IntPtr.Zero,
                nameData,
                ref nameDataLength,
                out errorStringPtr))
            {
                string error = Marshal.PtrToStringUni(errorStringPtr);
                throw new ArgumentException(error);
            }

            dataHandle.Free();
            
            dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
            CryptoApiBlob nameBlob = new CryptoApiBlob(
                nameData.Length,
                dataHandle.AddrOfPinnedObject());

            CryptKeyProviderInformation kpi = new CryptKeyProviderInformation();
            kpi.ContainerName = containerName;
            kpi.ProviderType = 1; // PROV_RSA_FULL
            kpi.KeySpec = 1; // AT_KEYEXCHANGE

            certContext = NativeMethods.CertCreateSelfSignCertificate(
                providerContext,
                ref nameBlob,
                0,
                ref kpi,
                IntPtr.Zero, // default = SHA1RSA
                ref startSystemTime,
                ref endSystemTime,
                IntPtr.Zero);
            Check(certContext != IntPtr.Zero);
            dataHandle.Free();

            certStore = NativeMethods.CertOpenStore(
                "Memory", // sz_CERT_STORE_PROV_MEMORY
                0,
                IntPtr.Zero,
                0x2000, // CERT_STORE_CREATE_NEW_FLAG
                IntPtr.Zero);
            Check(certStore != IntPtr.Zero);

            Check(NativeMethods.CertAddCertificateContextToStore(
                certStore,
                certContext,
                1, // CERT_STORE_ADD_NEW
                out storeCertContext));

            NativeMethods.CertSetCertificateContextProperty(
                storeCertContext,
                2, // CERT_KEY_PROV_INFO_PROP_ID
                0,
                ref kpi);

            if (password != null)
            {
                passwordPtr = Marshal.SecureStringToCoTaskMemUnicode(password);
            }

            CryptoApiBlob pfxBlob = new CryptoApiBlob();
            Check(NativeMethods.PFXExportCertStoreEx(
                certStore,
                ref pfxBlob,
                passwordPtr,
                IntPtr.Zero,
                7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY

            pfxData = new byte[pfxBlob.DataLength];
            dataHandle = GCHandle.Alloc(pfxData, GCHandleType.Pinned);
            pfxBlob.Data = dataHandle.AddrOfPinnedObject();
            Check(NativeMethods.PFXExportCertStoreEx(
                certStore,
                ref pfxBlob,
                passwordPtr,
                IntPtr.Zero,
                7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
            dataHandle.Free();
        }
        finally
        {
            if (passwordPtr != IntPtr.Zero)
            {
                Marshal.ZeroFreeCoTaskMemUnicode(passwordPtr);
            }

            if (dataHandle.IsAllocated)
            {
                dataHandle.Free();
            }

            if (certContext != IntPtr.Zero)
            {
                NativeMethods.CertFreeCertificateContext(certContext);
            }

            if (storeCertContext != IntPtr.Zero)
            {
                NativeMethods.CertFreeCertificateContext(storeCertContext);
            }

            if (certStore != IntPtr.Zero)
            {
                NativeMethods.CertCloseStore(certStore, 0);
            }

            if (cryptKey != IntPtr.Zero)
            {
                NativeMethods.CryptDestroyKey(cryptKey);
            }

            if (providerContext != IntPtr.Zero)
            {
                NativeMethods.CryptReleaseContext(providerContext, 0);
                NativeMethods.CryptAcquireContextW(
                    out providerContext,
                    containerName,
                    null,
                    1, // PROV_RSA_FULL
                    0x10); // CRYPT_DELETEKEYSET
            }
        }

        return pfxData;
    }

    private static SystemTime ToSystemTime(DateTime dateTime)
    {
        long fileTime = dateTime.ToFileTime();
        SystemTime systemTime;
        Check(NativeMethods.FileTimeToSystemTime(ref fileTime, out systemTime));
        return systemTime;
    }

    private static void Check(bool nativeCallSucceeded)
    {
        if (!nativeCallSucceeded)
        {
            int error = Marshal.GetHRForLastWin32Error();
            Marshal.ThrowExceptionForHR(error);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct SystemTime
    {
        public short Year;
        public short Month;
        public short DayOfWeek;
        public short Day;
        public short Hour;
        public short Minute;
        public short Second;
        public short Milliseconds;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct CryptoApiBlob
    {
        public int DataLength;
        public IntPtr Data;

        public CryptoApiBlob(int dataLength, IntPtr data)
        {
            this.DataLength = dataLength;
            this.Data = data;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct CryptKeyProviderInformation
    {
        [MarshalAs(UnmanagedType.LPWStr)] public string ContainerName;
        [MarshalAs(UnmanagedType.LPWStr)] public string ProviderName;
        public int ProviderType;
        public int Flags;
        public int ProviderParameterCount;
        public IntPtr ProviderParameters; // PCRYPT_KEY_PROV_PARAM
        public int KeySpec;
    }

    private static class NativeMethods
    {
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool FileTimeToSystemTime(
            [In] ref long fileTime,
            out SystemTime systemTime);

        [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CryptAcquireContextW(
            out IntPtr providerContext,
            [MarshalAs(UnmanagedType.LPWStr)] string container,
            [MarshalAs(UnmanagedType.LPWStr)] string provider,
            int providerType,
            int flags);

        [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CryptReleaseContext(
            IntPtr providerContext,
            int flags);

        [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CryptGenKey(
            IntPtr providerContext,
            int algorithmId,
            int flags,
            out IntPtr cryptKeyHandle);

        [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CryptDestroyKey(
            IntPtr cryptKeyHandle);

        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CertStrToNameW(
            int certificateEncodingType,
            IntPtr x500,
            int strType,
            IntPtr reserved,
            [MarshalAs(UnmanagedType.LPArray)] [Out] byte[] encoded,
            ref int encodedLength,
            out IntPtr errorString);

        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
        public static extern IntPtr CertCreateSelfSignCertificate(
            IntPtr providerHandle,
            [In] ref CryptoApiBlob subjectIssuerBlob,
            int flags,
            [In] ref CryptKeyProviderInformation keyProviderInformation,
            IntPtr signatureAlgorithm,
            [In] ref SystemTime startTime,
            [In] ref SystemTime endTime,
            IntPtr extensions);

        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CertFreeCertificateContext(
            IntPtr certificateContext);

        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
        public static extern IntPtr CertOpenStore(
            [MarshalAs(UnmanagedType.LPStr)] string storeProvider,
            int messageAndCertificateEncodingType,
            IntPtr cryptProvHandle,
            int flags,
            IntPtr parameters);

        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CertCloseStore(
            IntPtr certificateStoreHandle,
            int flags);

        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CertAddCertificateContextToStore(
            IntPtr certificateStoreHandle,
            IntPtr certificateContext,
            int addDisposition,
            out IntPtr storeContextPtr);

        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CertSetCertificateContextProperty(
            IntPtr certificateContext,
            int propertyId,
            int flags,
            [In] ref CryptKeyProviderInformation data);

        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool PFXExportCertStoreEx(
            IntPtr certificateStoreHandle,
            ref CryptoApiBlob pfxBlob,
            IntPtr password,
            IntPtr reserved,
            int flags);
    }
}

 

Adf 开发项目结构建议

共享程序集位置

所有开发人员必需具有,c:\develop\bin 目录,此目录用于存放共享程序集、第三方插件集等程序集,此目录由应该使用 https://svn.aooshi.org:8080/svn/adf/bin  导出/检出。若有共享库,第三方库均应签入此版本管理库中

若遇个别项目无法生成,缺少程序集,可SVN Update 此目录
 

WEB项目标准结构

所有项目标准结构如下(Example 为项目名举例)

建立 Web Application 项目, 项目名为  Example

Example

n  Logic                    逻辑层目录

n  PageBase.cs             通用网页基类

n  Helper.cs                    通用工具库

n  Entity                            实体目录

n  SqlServer                     SqlServer 数据库访问代码目录(项目SqlServer时存在)

n  Factory.cs                  数据库连接器

n  Mysql                           MySql 数据库访问代码目录(项目MySql 时存在)

n  Api                                          接口所在目录,统一使用 ashx 一般应用程序,ajax接口

n  Config                                    配置文件目录(非必需)

n  Static                                               项目用静态文件地址

n  styles                          样式、字体文件目录(注意:小写严格遵守)

n  images                        项目用图片目录(注意:小写严格遵守)

n  scripts                         脚本目录(注意:小写严格遵守)

n  Upfiles                                   文件上传目录(若需要)

n  Alipay                                     支付宝所有接口目录(仅一级)(项目需要时存在)

n  Users                                              项目用户目录(大量操作时使用)

n  Index.aspx                                     索引首页(所有项目均以Index.aspx为首页)

 

根据以上规则,所有对外接口项目均建立一个目录,除特殊性况外,均使用一级目录。

遵循以上目录结构,每一个大模块均建立一级目录用于存储。

常规项目所有网页文件均存放至根目录,不建立块项目录,当某个模块网页量大于100或特殊安全要求,商议建立目录

数据库目录下(如: SqlServer/MySql)基本保证每表一类,Logic目录为逻辑类,以模块划分,比如: 用户,日志,支付等

项目参考: https://svn.aooshi.org:8080/svn/adf/tags/examples/

 

数据访问与ADF框架

除数据库目录文件外(如:SqlServer目录),不允许在其它位置出现Sql语句,如: 网页中。

 

所有进行直接拼接的语句,只要参数为字符串必需进行 安全转换(db. InjectReplace

 

统一使用ADF 数据操作基类进行数据访问,代码地址: http://www.aooshi.org/adf

 

在线帮助: http://www.aooshi.org/adf/help

离线帮助: http://www.aooshi.org/adf/help/Adf.Documentation.chm

实体生成器: http://www.aooshi.org/adf/db/download/

 

使用方式参考 Example 项目

实体生成方式:

实体生成器下载后解压, 配置Adf.Db.Desktop.exe.config 文件

connectionStrings 节点添加一个数据库连接,名字为库名,此处以Example为例

DbMap 节点添加一项:

1、  名字为项目名字(Example)

2、  target为刚添加的数据库名

3、  namespace 为你的项目+Entity (如:Example.Entity)

4、  factory 为你的数据库操作类(如:Example.DbFactory

<add name="Example" target="Example"

namespace=" Example.Entity " factory=" Example.DbFactory "/>

 

注意:BaseClass 配置项,若需要进行实体类更新需则需要填写 DbEntity, 否则该项中不应出现此值

 

假定一个数据库连接:  192.168.1.200,  ID=sa,  pwd=sa 示例:

<connectionStrings>

<add name="Example"

connectionString="Data Source=192.168.1.200;User Id=sa;Password=sa;Initial Catalog=Example;"

providerName="SQLServer"/>

  </connectionStrings>

  <DbMap>

<add name="Example" target="Example" namespace="Example.Entity"

logicnamespace="Example.Logic" factory="Example.DbFactory"/>

  </DbMap>

 

配置后保存,双击 Adf.Db.Desktop.exe 运行

 

点击查看原图

点击查看原图

点击查看原图

复制代码至你的项目或  Save As 存储文件

 

基本表方法生成:

点击查看原图

点击查看原图

复制代码至你的项目相应DB文件或  Save As 存储文件