FastDFS一步步搭建存储服务器
一:FastDFS简介
1:FastDFS简介
FastDFS是一个开源的轻量级分布式文件系统,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合中小文件(建议范围:4KB < file_size <500MB),对以文件为载体的在线服务,如相册网站、视频网站等。
(1):单机时代
初创时期由于时间紧迫,在各种资源有限的情况下,通常就直接在项目目录下建立静态文件夹,用于用户存放项目中的文件资源。
如果按不同类型再细分,可以在项目目录下再建立不同的子目录来区分。如resources\static\file、resources\static\img等
# 优点:这样做比较便利,项目直接引用就行,实现起来也简单,无需任何复杂技术,保存数据库记录和访问起来也很方便。
# 缺点:如果只是后台系统的使用一般也不会有什么问题,但是作为一个前端网站使用的话就会存在弊端。一方面,文件和代码耦合
在一起,文件越多存放越混乱;另一方面,如果流量比较大,静态文件访问会占据一定的资源,影响正常业务进行,不利于网站
快速发展。 (2):独立文件服务器
随着公司业务不断发展,将代码和文件放在同一服务器的弊端就会越来越明显。为了解决上面的问题引入独立图片服务器,工作
流程如下:项目上传文件时,首先通过ftp或者ssh将文件上传到图片服务器的某个目录下,再通过ngnix或者apache来访问此
目录下的文件,返回一个独立域名的图片URL地址,前端使用文件时就通过这个URL地址读取。
# 优点:图片访问是很消耗服务器资源的(因为会涉及到操作系统的上下文切换和磁盘I/O操作),分离出来后,Web/App服务器
可以更专注发挥动态处理的能力;独立存储,更方便做扩容、容灾和数据迁移;方便做图片访问请求的负载均衡,方便应用各
种缓存策略(HTTP Header、Proxy Cache等),也更加方便迁移到CDN。
# 缺点:单机存在性能瓶颈,容灾、垂直扩展性稍差 (3):分布式文件系统
通过独立文件服务器可以解决一些问题,如果某天存储文件的那台服务突然down了怎么办?可能你会说,定时将文件系统备份,
这台down机的时候,迅速切换到另一台就OK了,但是这样处理需要人工来干预。另外,当存储的文件超过100T的时候怎么办?
单台服务器的性能问题?这个时候我们就应该考虑分布式文件系统了。业务继续发展,单台服务器存储和响应也很快到达了瓶颈,
新的业务需要文件访问具有高响应性、高可用性来支持系统。分布式文件系统,一般分为三块内容来配合,服务的存储、访问的
仲裁系统,文件存储系统,文件的容灾系统来构成,仲裁系统相当于文件服务器的大脑,根据一定的算法来决定文件存储的位置,
文件存储系统负责保存文件,容灾系统负责文件系统和自己的相互备份。
# 优点:扩展能力: 毫无疑问,扩展能力是一个分布式文件系统最重要的特点;高可用性: 在分布式文件系统中,高可用性包含两
层,一是整个文件系统的可用性,二是数据的完整和一致性;弹性存储: 可以根据业务需要灵活地增加或缩减数据存储以及增删
存储池中的资源,而不需要中断系统运行
# 缺点:系统复杂度稍高,需要更多服务器
为什么使用分布式文件系统?
2:FastDFS系统架构
FastDFS由跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client)构成。
(1)跟踪服务器Tracker Server
主要做调度工作,起到均衡的作用;负责管理所有的 storage server和 group,每个 storage 在启动后会连接 Tracker,告知自己所属 group 等信息,并保持周期性心跳。tracker根据storage的心跳信息,建立group==>[storage serverlist]的映射表。
Tracker需要管理的元信息很少,会全部存储在内存中;另外tracker上的元信息都是由storage汇报的信息生成的,本身不需要持久化任何数据,这样使得tracker非常容易扩展,直接增加tracker机器即可扩展为tracker cluster来服务,cluster里每个tracker之间是完全对等的,所有的tracker都接受stroage的心跳信息,生成元数据信息来提供读写服务。
(2)存储服务器Storage Server
主要提供容量和备份服务;以 group 为单位,每个 group 内可以有多台 storage server,数据互为备份。以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的方式被路由到其中某个子目录下,然后将文件数据作为本地文件存储到该目录中。
(3)客户端Client
主要是上传下载数据的服务器,也就是我们自己的项目所部署在的服务器。每个客户端服务器都需要安装Nginx
Tracker相当于FastDFS的大脑,不论是上传还是下载都是通过tracker来分配资源;客户端一般可以使用ngnix等静态服务器来调用或者做一部分的缓存;存储服务器内部分为卷(或者叫做组),卷于卷之间是平行的关系,可以根据资源的使用情况随时增加,卷内服务器文件相互同步备份,以达到容灾的目的。
(4)FastDFS的存储策略
为了支持大容量,存储节点(服务器)采用了分卷(或分组)的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷的文件容量累加就是整个存储系统中的文件容量。一个卷可以由一台或多台存储服务器组成,一个卷下的存储服务器中的文件都是相同的,卷中的多台存储服务器起到了冗余备份和负载均衡的作用。
在卷中增加服务器时,同步已有的文件由系统自动完成,同步完成后,系统自动将新增服务器切换到线上提供服务。当存储空间不足或即将耗尽时,可以动态添加卷。只需要增加一台或多台服务器,并将它们配置为一个新的卷,这样就扩大了存储系统的容量。
(5)FastDFS的上传过程
FastDFS向使用者提供基本文件访问接口,比如upload、download、append、delete等,以客户端库的方式提供给用户使用。
我们知道Storage Server会定期的向Tracker Server发送自己的存储信息。当Tracker Server Cluster中的Tracker Server不止一个时,各个Tracker之间的关系是对等的,所以客户端上传时可以选择任意一个Tracker。
当Tracker收到客户端上传文件的请求时,会为该文件分配一个可以存储文件的group,当选定了group后就要决定给客户端分配group中的哪一个storage server。当分配好storage server后,客户端向storage发送写文件请求,storage将会为文件分配一个数据存储目录。然后为文件分配一个fileid,最后根据以上的信息生成文件名存储文件。文件名的格式如下:
(6)FastDFS的文件同步
写文件时,客户端将文件写至group内一个storage server即认为写文件成功,storage server写完文件后,会由后台线程将文件同步至同group内其他的storage server。
每个storage写文件后,同时会写一份binlog,binlog里不包含文件数据,只包含文件名等元信息,这份binlog用于后台同步,storage会记录向group内其他storage同步的进度,以便重启后能接上次的进度继续同步;进度以时间戳的方式进行记录,所以最好能保证集群内所有server的时钟保持同步。
storage的同步进度会作为元数据的一部分汇报到tracker上,tracke在选择读storage的时候会以同步进度作为参考。
(7)FastDFS的文件下载
客户端uploadfile成功后,会拿到一个storage生成的文件名,接下来客户端根据这个文件名即可访问到该文件。
跟upload file一样,在downloadfile时客户端可以选择任意tracker server。tracker发送download请求给某个tracker,必须带上文件名信息,tracke从文件名中解析出文件的group、大小、创建时间等信息,然后为该请求选择一个storage用来服务读请求。
二:搭建FastDFS环境(单机版)
1:基本准备
(1):系统准备
①:刚重置的腾讯云 CentOS 7.6 系统
(2):文件准备
①:libfastcommon:是FastDFS文件系统运行需要的公共 C 语言函数库
②:FastDFS:主体文件需要安装部署在Linux环境下
③:fastdfs-client-java : java客户端基本代码(需maven手动编译)
④:fastdfs-nginx-module :扩展模块,集成Nginx里实现负载均衡等功能
⑤:Nginx :实现负载均衡及文件的访问是用到
(3):上传准备的文件,出fastdfs-client-java 其它全部上传
切换到根目录下上传 个人喜好 cd /root
执行上传命令:rz -y
(1):环境检查是否有 gcc 、libevent 、libevent-devel
检查命令:
yum list installed | grep gcc
yum list installed | grep libevent
yum list installed | grep libevent-devel
安装命令,如果有新版则更新
yum install gcc libevent libevent-devel -y
2:安装 libfastcommon 库
libfastcommon 库是 FastDFS 文件系统运行需要的公共 C 语言函数库;注意:v1.0.39和FastDFS5.11不兼容
>>解压
tar -zxvf libfastcommon-1.0.48.tar.gz
>>进入解压后的目录
cd libfastcommon-1.0.48/
>>编译解压的文件 这里需要gcc环境
./make.sh
>>安装编译后的文件
./make.sh install
>>完成
3:安装 FastDFS 主体文件
FastDFS没有Windows版本,不能在Windows下使用。FastDFS需要安装部署在Linux环境下
>>解压
tar -zxvf fastdfs-6.07.tar.gz
>>进入解压后的目录
cd fastdfs-6.07/
>>编译解压的文件 和 安装编译的文件
./make.sh && ./make.sh install
>>检查是否安装了 (安装后命令会存在/usr/bin)
ls /usr/bin | grep fdfs
(1):查看 FastDFS 配置文件
安装FastDFS后配置文件存放在 /etc/fdfs 下;注:另外注意需要把之前解压的fastdfs-6.0.7/conf目录下的两个文件拷贝到安装后产生的/etc/fdfs/目录下 ,否则后续会有很多奇怪问题不好解决 http.conf 和 mime.types
cp /root/fastdfs-6.07/conf/http.conf /etc/fdfs
cp /root/fastdfs-6.07/conf/mime.types /etc/fdfs
因为这些文件后缀都是 .sample 示例的,所有我们把 .sample后缀都去除
mv /etc/fdfs/client.conf.sample client.conf
mv /etc/fdfs/tracker.conf.sample tracker.conf
mv /etc/fdfs/storage.conf.sample storage.conf
mv /etc/fdfs/storage_ids.conf.sample storage_ids.conf
4:FastDFS的配置 /etc/fdfs/xx.conf
要想运行我们的FastDFS服务,我们得配置我们必要的信息,启动tracker服务则需要配置tracker.conf,启动storage服务也需要配置对应文件
(1):负载均衡 tracker.conf 配置文件的配置修改
如果不方便使用vim更改的话,可以使用 rz -y 上传 和 sz file名称 下载 到本地编辑
配置 tracker 存储数据的目录(自定义配置后期tracker产生的数据和日志保存的地方)
配置:base_path = /opt/fastdfs/tracker 注:设置的路径我们必须要手动创建
# 配置tracker.conf 文件是否生效 false生效 true屏蔽
disabled = false # 程序的监听地址,如果不设定则监听所有地址(0.0.0.0)
bind_addr = # tracker监听的端口
port = 22122 # 连接超时时间(秒)。
# 默认值为30。
# 注意:在内网(LAN)中,2秒就足够了。
connect_timeout = 5 # 发送和接收的网络超时时间(秒)。
# 默认值为30
# tracker server的网络超时,单位为秒。发送或接收数据时,如果在超时时间后还不能发送
# 或接收数据,则本次网络通信失败。
network_timeout = 60 # ------------------------------------------------------
# base_path 目录地址(根目录必须存在,子目录会自动创建)
# 附目录说明:
# tracker server目录及文件结构:
# ${base_path}
# |__data
# | |__storage_groups.dat:存储分组信息
# | |__storage_servers.dat:存储服务器列表
# |__logs
# |__trackerd.log:tracker server日志文件
# 数据文件storage_groups.dat和storage_servers.dat中的记录之间以换行符(\n)分隔,字段之间以西文逗号(,)分隔。
# storage_groups.dat中的字段依次为:
# 1. group_name:组名
# 2. storage_port:storage server端口号
#
# storage_servers.dat中记录storage server相关信息,字段依次为:
# 1. group_name:所属组名
# 2. ip_addr:ip地址
# 3. status:状态
# 4. sync_src_ip_addr:向该storage server同步已有数据文件的源服务器
# 5. sync_until_timestamp:同步已有数据文件的截至时间(UNIX时间戳)
# 6. stat.total_upload_count:上传文件次数
# 7. stat.success_upload_count:成功上传文件次数
# 8. stat.total_set_meta_count:更改meta data次数
# 9. stat.success_set_meta_count:成功更改meta data次数
# 10. stat.total_delete_count:删除文件次数
# 11. stat.success_delete_count:成功删除文件次数
# 12. stat.total_download_count:下载文件次数
# 13. stat.success_download_count:成功下载文件次数
# 14. stat.total_get_meta_count:获取meta data次数
# 15. stat.success_get_meta_count:成功获取meta data次数
# 16. stat.last_source_update:最近一次源头更新时间(更新操作来自客户端)
# 17. stat.last_sync_update:最近一次同步更新时间(更新操作来自其他storage server的同步)
base_path = /home/yuqing/fastdfs # 此服务器支持的最大并发连接数。
# 默认值为256
max_connections = 1024 # 接受线程数。
# 默认值为1,推荐使用。
# 从V4.07开始
# 该参数决定接收客户端连接的线程数,默认值为1,适当放大该参数可改善Storage处理连接的能力,改
# 成2[线上环境cpu为32核心可支持足够多的线程数]。
accept_threads = 1 # 工作线程数。
# 处理网络IO的工作线程数。
# 默认值为4。
# 从V2.00开始
# 工作线程用来处理网络IO,默认值为4,该参数影响Stroage可以同时处理的连接数
work_threads = 4 # 最小网络缓冲区大小。
# 默认值8KB
# 接收/发送数据的buff大小,必须大于8KB
min_buff_size = 8KB # 最大网络缓冲区大小。
# 默认值128KB
# 接收/发送数据的buff大小必须小于128KB
max_buff_size = 128KB # 在存储文件时选择group的策略。
# 0:轮询。
# 1:指定组。
# 2:负载均衡,选择上传文件的最大可用空间组
store_lookup = 2 # 上传文件到哪组。
# 当 store_lookup设置为1时,必须将 store_group设置为指定上传的组名
store_group = group2 # 上传文件到哪台存储服务器。
# 0:循环(默认)。
# 1:第一台按IP地址排序的服务器。
# 2:根据优先级排序,第一个(最小)。
# 注意:如果 use_trunk_file设置为 true,则必须将 set store_server设置为1或2
store_server = 0 # 选择文件上传到storage中的哪个(目录/挂载点),storage可以有多个存放文件的base
# path 0:轮训策略 2:负载均衡,选择空闲空间最大的
store_path = 0 # 选择那个storage作为主下载服务器,
# 0:轮训策略 1:主上传storage作为主下载服务器
download_server = 0 # 系统预留空间,当一个group中的任何storage的剩余空间小于定义的值,整个group就不能上传文件了
reserved_storage_space = 20% # 设置日志级别
### emerg for emergency
### alert
### crit for critical
### error
### warn for warning
### notice
### info
### debug
log_level = info # 进程以那个用户/用户组运行,不指定默认是当前用户
run_by_group=
run_by_user = # 允许那些机器连接tracker默认是所有机器
allow_hosts = * # 定时将日志缓冲区同步到磁盘。
# 默认值为10秒
sync_log_buff_interval = 1 # 检测 storage server 存活的时间隔,单位为秒。
# storage server定期向 tracker server 发心跳,如果tracker server在一个check_active_interval
# 内还没有收到storage server的一次心跳,那就认为该storage server已经下线。所以本参数值必须大于
# storage server配置的心跳时间间隔。通常配置为storage server心跳时间间隔的2倍或3倍。
check_active_interval = 120 # 线程栈的大小。FastDFS server端采用了线程方式。更正一下,
# tracker server线程栈不应小于64KB,不是512KB。
thread_stack_size = 256KB # 存储服务器IP地址更改时自动调整。
# 默认值为true
# storage的ip改变后服务端是否自动调整,storage进程重启时才自动调整
storage_ip_changed_auto_adjust = true # 存储同步文件最大延迟秒数。
# 默认值为86400秒(一天)。
# 从V2.00开始
storage_sync_file_max_delay = 86400 # 文件同步最大存储时间。
# 默认值为300秒。
# 从V2.00开始
# 就是存储服务器同步一个文件需要消耗的最大时间,单位为300s,即5分钟。
storage_sync_file_max_time = 300 # 是否使用小文件合并存储特性 [false代表关闭]
use_trunk_file = false # 最小插槽大小,应<=4KB。
# 默认值为256字节。
# 从V3.00开始
# trunk file分配的最小字节数。比如文件只有16个字节,系统也会分配 slot_min_size个字节。
slot_min_size = 256 # 最大插槽大小,应>插槽最小大小。
# 当上传文件大小<=此值时,将上传文件存储到主干文件。
# 默认值为16MB。
# 从V3.00开始
# 只有文件大小<=这个参数值的文件,才会合并存储。
# 如果一个文件的大小大于这个参数值,将直接保存到一个文件中(即不采用合并存储方式)
slot_max_size = 1MB # 对齐大小越大,磁盘碎片的可能性越小,但浪费的空间也更多。
trunk_alloc_alignment_size = 256 # 是否合并中继文件的连续可用空间
# since V6.05
trunk_free_space_merge = true # files 删除/回收未使用的中继文件
# since V6.05
delete_unused_trunk_files = false # trunk file 大小
# since V3.00
trunk_file_size = 64MB # 是否提前创建 trunk file
# since V3.06
trunk_create_file_advance = false # 提前创建trunk file的起始时间点(基准时间),02:00表示第一次创建的时间点是凌晨2点
trunk_create_file_time_base = 02:00 # 创建 trunk file 时间间隔, 86400 即隔一天
trunk_create_file_interval = 86400 # 提前创建trunk file时,需要达到的空闲trunk大小
# 比如本参数为20G,而当前空闲trunk为4GB,那么只需要创建16GB的trunk file即可。
trunk_create_file_space_threshold = 20G # trunk初始化时,是否检查可用空间是否被占用
trunk_init_check_occupying = false # 是否无条件从trunk binlog中加载trunk可用空间信息
# FastDFS缺省是从快照文件storage_trunk.dat中加载trunk可用空间,
# 该文件的第一行记录的是trunk binlog的offset,然后从binlog的offset开始加载
trunk_init_reload_from_binlog = false # 压缩 trunk binlog 文件的最小间隔
trunk_compress_binlog_min_interval = 86400 # 压缩 trunk binlog 文件的间隔时间 (默认1天)
trunk_compress_binlog_interval = 86400 # 压缩 trunk binlog 文件的时间点
trunk_compress_binlog_time_base = 03:00 # trunk binlog 文件最大备份数
trunk_binlog_max_backups = 7 # 是否使用server ID作为storage server标识
use_storage_id = false # 指定存储ID文件名,可以使用相对路径或绝对路径。
# 只有当use_storage_id设置为true时,该参数才有效。
storage_ids_filename = storage_ids.conf # 文件名中存储服务器的#id类型,值为:
## IP:存储服务器的IP地址。
## id:存储服务器的服务器id。
# 仅当use_storage_id设置为true时,此参数才有效。
# 默认值为ip。
# since V4.03
id_type_in_filename = id # 存储从文件是否使用符号链接
store_slave_file_use_link = false # 是否定期轮转error log,目前仅支持一天轮转一次
rotate_error_log = false # error log定期轮转的时间点,只有当rotate_error_log设置为true时有效
error_log_rotate_time = 00:00 # 是否压缩旧的错误日志
compress_old_error_log = false # 压缩几天前的错误日志 (compress_old_error_log = false本参数不生效)
compress_error_log_days_before = 7 # 当日志文件超过此大小时轮换错误日志。
# 设置为0表示不按文件大小轮转,否则当error log达到该大小,就会轮转到新文件中
rotate_error_log_size = 0 # 日志文件保留几天(0代表永久保留)
log_file_keep_days = 0 # 是否使用连接池
use_connection_pool = true # 连接空闲时长,超过则连接被关闭 (单位秒)
connection_pool_max_idle_time = 3600 # 此跟踪器服务器上的HTTP端口
http.server_port = 8080 # 检查存储HTTP服务器活动间隔秒数。
# <=0表示从不检查。
# 默认值为30
http.check_alive_interval = 30 # 检查存储HTTP服务器活动类型,值为:
# TCP:仅使用HTTP端口连接到存储服务器。
# 不请求,不获取响应。
# http:存储检查活动url必须返回http状态200。
# 默认值为tcp
http.check_alive_type = tcp # 检查存储HTTP服务器活动uri/url。
# 注意:Storage Embedded HTTP服务器支持uri:/status.html
http.check_alive_uri = /status.html
tracker.conf 配置详细介绍
(2):文件存储服务器 storage.conf 配置文件的配置修改
配置 storage.conf 是指定后期运行产生的数据和真实的文件存储位置,注设置的路径我们必须手动创建
配置storage存储数据的目录:base_path = /opt/fastdfs/storage
配置真正存放文件的目录: store_path0=/opt/fastdfs/storage/files
配置当前存储节点的跟踪器地址(就是tracker):tracker_server=119.29.68.209:22122
# 配置storage.conf 文件是否生效 false生效 true屏蔽
disabled = false # 该存储服务器所属的组名。
# 注释或删除此项目以从跟踪器服务器获取,
# 在这种情况下,tracker.conf中的use_storage_id必须设置为true。
# 和storage_ids.conf必须正确配置。
group_name = group1 # 程序的监听地址,如果不设定则监听所有地址(0.0.0.0)
bind_addr = # bind_addr通常是针对server的。当指定bind_addr时,本参数才有效。
# 本storage server作为client连接其他服务器(如tracker server、
# 其他storage server),是否绑定bind_addr。
client_bind = true # storage 默认端口
port = 23000 # 连接超时时间(秒)。
# 默认值为30。
# 注意:在内网(LAN)中,2秒就足够了。
connect_timeout = 5 # 发送和接收的网络超时时间(秒)。
# 默认值为30
network_timeout = 60 # 心跳间隔(秒)。
# 存储服务器定期向Tracker服务器发送心跳。
# 默认值为30
heart_beat_interval = 30 # 磁盘使用情况报告间隔(秒)。
# 存储服务器定期向Tracker服务器发送磁盘使用情况报告。
# 默认值为300
stat_report_interval = 60 # 数据存储目录地址
base_path = /home/yuqing/fastdfs # 最大连接数
max_connections = 1024 # 设置队列结点的buffer大小。工作队列消耗的内存大小 = buff_size * max_connections
# 设置得大一些,系统整体性能会有所提升。
buff_size = 256KB # 接收数据的线程数
accept_threads = 1 # 工作线程数,一般为cpu个数,当然CPU核数太多的话可以稍小一点。如我们是12CPU,这里设置为8
work_threads = 4 # 磁盘IO读写是否分离。磁盘读/写分离为false则为混合读写,
# 如果为true则为分离读写的。默认值为V2.00以后为true。
disk_rw_separated = true # 针对单个存储路径的读线程数,默认值为1。
# 读写分离时:
# 系统中的读线程数 = disk_reader_threads * store_path_count
# 读写混合时:
# 系统中的读写线程数 = (disk_reader_threads + disk_writer_threads) * store_path_count
disk_reader_threads = 1 # 针对单个存储路径的写线程数,默认值为1。
# 读写分离时:
# 系统中的写线程数 = disk_writer_threads * store_path_count
# 读写混合时:
# 系统中的读写线程数 = (disk_reader_threads + disk_writer_threads) * store_path_count
disk_writer_threads = 1 # 同步文件时,如果从binlog中没有读到要同步的文件,休眠N毫秒后重新读取。0表示不休眠,立即再次尝试读取。
# 出于CPU消耗考虑,不建议设置为0。如何希望同步尽可能快一些,可以将本参数设置得小一些,比如设置为10ms
sync_wait_msec = 50 # 同步上一个文件后,再同步下一个文件的时间间隔,单位为毫秒,0表示不休眠,直接同步下一个文件。
sync_interval = 0 # 下面二个一起解释。允许系统同步的时间段 (默认是全天) 。一般用于避免高峰同步产生一些问题而设定
sync_start_time = 00:00
sync_end_time = 23:59 # 同步 N 个文件后就写入标记文件
write_mark_file_freq = 500 # 硬盘恢复线程数
disk_recovery_threads = 3 # storage在存储文件时支持多路径,默认只设置一个
store_path_count = 1 # store_path(0~⚮),根据0配置存储文件的存储路径。
# 如果store_path0不存在,则取值为base_path(不推荐)。
# 路径必须存在
store_path0 = /home/yuqing/fastdfs
#store_path1 = /home/yuqing/fastdfs2 # subdir_count * subdir_count个目录会在store_path下创建,采用两级存储
subdir_count_per_path = 256 # 设置tracker_server 服务器地址 多个代表多个Tracker集群
tracker_server = 192.168.209.121:22122
tracker_server = 192.168.209.122:22122 # 日志级别
### emerg for emergency
### alert
### crit for critical
### error
### warn for warning
### notice
### info
### debug
log_level = info # 进程以那个用户/用户组运行,不指定默认是当前用户
run_by_group =
run_by_user = # 允许那些机器连接tracker默认是所有机器
allow_hosts = * # 文件分发到数据路径的方式。
# 0: 轮流存放,在一个目录下存储设置的文件数后
#(参数file_distribute_rotate_count中设置文件数),使用下一个目录进行存储。
# 1: 随机存储,根据文件名对应的hash code来分散存储。
file_distribute_path_mode = 0 # 当上面的参数file_distribute_path_mode配置为0(轮流存放方式)时,本参数有效。
# 当一个目录下的文件存放的文件数达到本参数值时,后续上传的文件存储到下一个目录中。
file_distribute_rotate_count = 100 # 当写入大文件时,每写入N个字节,调用一次系统函数fsync将内容强行同步到硬盘。0表示从不调用fsync
fsync_after_written_bytes = 0 # 同步或刷新日志信息到硬盘的时间间隔,单位为秒
# 注意:storage server 的日志信息不是时时写硬盘的,而是先写内存。
sync_log_buff_interval = 1 # 同步binglog(更新操作日志)到硬盘的时间间隔,单位为秒
# 本参数会影响新上传文件同步延迟时间
sync_binlog_buff_interval = 1 # 把storage的stat文件同步到磁盘的时间间隔,单位为秒。
# 注:如果stat文件内容没有变化,不会进行同步
sync_stat_file_interval = 300 # 线程栈大小,线程栈越大,一个线程占用的系统资源就越多。
thread_stack_size = 512KB # 本storage server作为源服务器,上传文件的优先级,可以为负数。值越小,
# 优先级越高。这里就和 tracker.conf 中store_server= 2时的配置相对应了
upload_priority = 10 # 网卡别名,用ifconfig -a可以看到很多本机的网卡别名,类似eth0,eth0:0等等。
# 多个网卡别名使用逗号分割,默认为空,让系统自动选择。
if_alias_prefix = # 是否检测上传文件已经存在。如果已经存在,则不存在文件内容,建立一个符号链接以节省磁盘空间。
# 这个应用要配合FastDHT 使用,所以打开前要先安装FastDHT
# 1或yes 是检测,0或no 是不检测
check_file_duplicate = 0 # 文件去重时,文件内容的签名方式:
## hash:4个hash code
## md5:MD5
file_signature_method = hash #当check_file_duplicate设置为1时,此值必须设置
key_namespace = FastDFS # FastDHT建立连接的方式 0:短连接 1:长连接
keep_alive = 0 # 是否将文件操作记录到access log
use_access_log = false # 是否定期轮转access log,目前仅支持一天轮转一次
rotate_access_log = false # access log定期轮转的时间点,只有当rotate_access_log设置为true时有效
access_log_rotate_time = 00:00 # 是否压缩旧的访问日志
compress_old_access_log = false # 压缩几天前的访问日期
compress_access_log_days_before = 7 # 是否每天轮转错误日志
rotate_error_log = false # 错误日志轮转时间
error_log_rotate_time = 00:00 # 压缩旧的错误日志
compress_old_error_log = false # 压给它几天前的错误日志
compress_error_log_days_before = 7 # access log按文件大小轮转
# 设置为0表示不按文件大小轮转,否则当access log达到该大小,就会轮转到新文件中
rotate_access_log_size = 0 # error log按文件大小轮转
# 设置为0表示不按文件大小轮转,否则当error log达到该大小,就会轮转到新文件中
rotate_error_log_size = 0 # 保留日志文件的日期0表示不删除旧的日志文件
log_file_keep_days = 0 # 文件同步的时候,是否忽略无效的binlog记录
file_sync_skip_invalid_record = false # 是否使用连接池
use_connection_pool = true # 连接的空闲时间超过这个时间将被关闭,单位:秒
connection_pool_max_idle_time = 3600 # 是否使用gzip压缩二进制日志文件
compress_binlog = true # 压给它二进制日志时间点
compress_binlog_time = 01:30 # 是否检查存储路径的标记以防止混淆,建议开启,如果两个服务使用
# 一个相同的存储路径,此参数要设置为 false
check_store_path_mark = true # 服务域名, 如果为空则表示使用 IP 地址
http.domain_name = # http 端口
http.server_port = 8888
storage.conf 配置详细介绍
(3):创建我们之前在配置里指定的一些路径
必须创建,否则无法启动
mkdir -p /opt/fastdfs/tracker
mkdir -p /opt/fastdfs/storage
mkdir -p /opt/fastdfs/storage/files
5:启动FastDFS服务
我们修改好配置后,并且在配置文件配置的文件路径也被我们手动创建后就可以来启动服务了
# 启动 Tracker 追踪器服务
fdfs_trackerd /etc/fdfs/tracker.conf
# 启动 Storage 存储服务器服务
fdfs_storaged /etc/fdfs/storage.conf
# 重启
fdfs_trackerd /etc/fdfs/tracker.conf restart
fdfs_storaged /etc/fdfs/storage.conf restart
# 关闭
fdfs_trackerd /etc/fdfs/tracker.conf stop
fdfs_storaged /etc/fdfs/storage.conf stop
(1):查询tracker文件下是否被自动创建了data 和 log 文件
命令:ls /opt/fastdfs/tracker/
(2):查询storage文件下是否自动创建了data 和 log 和files具体文件路径
6:FastDFS测试上传、删除
我们完成了上面的配置后并且也可以运行服务后,我们就可以来测试我们的FastDFS是否存在问题了,在测试前我们需要配置一个 /etc/fdfs/client.conf 的文件
配置client存储数据的目录:base_path = /opt/fastdfs/client 注:别忘了把文件夹创建出来
配置当前存储节点的跟踪器地址(就是tracker):tracker_server=119.29.68.209:22122
# 连接的超时时间
connect_timeout = 5 # 网络超时(秒),默认值 60s
network_timeout = 60 # 存储日志文件的基本路径
base_path = /home/yuqing/fastdfs # tracker server的列表 多个tracker就设置多个
tracker_server = 192.168.0.196:22122
tracker_server = 192.168.0.197:22122 # 日志级别
### emerg for emergency
### alert
### crit for critical
### error
### warn for warning
### notice
### info
### debug
log_level = info # 是否使用连接池
use_connection_pool = false # 连接的空闲时间超过这个时间将被关闭,单位:秒
connection_pool_max_idle_time = 3600 # 是否加载来自跟踪服务器的FastDFS参数,默认值为false。这里可以设置为true。
load_fdfs_parameters_from_tracker = false # 是否使用storage id替换ip作为storage server标识,默认为false
use_storage_id = false # 在文件中设置组名、server ID和对应的IP地址
storage_ids_filename = storage_ids.conf # 端口
http.tracker_server_port = 80
client.conf 配置具体说明
我们要使用测试则需要使用到 fdfs_test 命令来完成测试
命令:fdfs_test /etc/fdfs/client.conf upload 上传的文件名称
查询文件 这里存储到../00/00里
测试删除
fdfs_delete_file /etc/fdfs/client.conf group1/M00/00/00/dx1E0WArYHCALYE2AAAADEQKaqU633_big.txt
三:分布式文件系统之FastDFS的HTTP访问
在文件上传的时候,上传成功的信息中有提示我们可以通过某个路径去访问上传的文件,但是我们直接访问这个路径,却不可以,那么已经上传到FastDFS文件系统中的文件,我们如何在浏览器中访问呢?FastDFS提供了一个Nginx扩展模块,利用该模块,我们可以通过Nginx访问已经上传到FastDFS上的文件
1:安装Nginx并添加fastDFS模块
安装Nginx环境检查:
gcc编译器是否安装
检查是否安装:yum list installed | grep gcc
执行安装:yum install gcc -y
openssl库是否安装
检查是否安装:yum list installed | grep openssl
执行安装:yum install openssl openssl-devel -y
pcre库是否安装
检查是否安装:yum list installed | grep pcre
执行安装:yum install pcre pcre-devel -y
zlib库是否安装
检查是否安装:yum list installed | grep zlib
执行安装:yum install zlib zlib-devel -y
全安装:
yum install gcc openssl openssl-devel pcre pcre-devel zlib zlib-devel –y >>解压:fastdfs-nginx-module-1.22.tar.gz
tar -zxvf fastdfs-nginx-module-1.22.tar.gz
>>解压:nginx-1.19.6.tar.gz
tar -zxvf nginx-1.19.6.tar.gz
>>进入:nginx-1.19.6
cd nginx-1.19.6/
>>安装前配置
./configure --prefix=/usr/local/nginx_fdfs --add-module=/root/fastdfs-nginx-module-1.22/src
--prefix:指定准备安装到哪 --add-module:指定集成的fdfs模块
>>编译 && 安装
make && make install
2:配置Nginx访问设置
(1):拷贝模块文件到 /etc/fdfs
我们需要先把扩展模块 fastdfs-nginx-module-master/src 里的 mod_fastdfs.conf 文件拷贝到 /etc/fdfs/ 目录下,这样才可以正常启动Nginx
命令:cp /root/fastdfs-nginx-module-1.22/src/mod_fastdfs.conf /etc/fdfs
(2):修改拷贝过来的 /etc/fdfs/mod_fastdfs.conf 文件
# 配置存储运行产生的日志数据等等 文件路径必须存在
base_path=/opt/fastdfs/nginx_mod
# 设置tracker服务地址
tracker_server=119.29.68.209:22122
# 设置文件url中是否有group名
url_have_group_name = true
# 文件数据存储路径
store_path0=/opt/fastdfs/storage/files
# 连接超时时间(秒)。
# 默认值为30s
connect_timeout=2 # 网络接收和发送超时(秒)。
# 默认值为30s
network_timeout=30 # 日志存储路径
base_path=/tmp # 从跟踪器服务器加载FastDFS参数。
# 从V1.12开始。
# 默认值为False
load_fdfs_parameters_from_tracker=true # 存储同步文件最大延迟秒数。
# 与tracker.conf相同。
# 仅当load_fdfs_Parameters_from_tracker为FALSE时有效。
# 从V1.12开始。
# 默认值为86400秒(一天)
storage_sync_file_max_delay = 86400 # 如果使用存储ID而不是IP地址。
# 与tracker.conf相同。
# 仅当load_fdfs_Parameters_from_tracker为FALSE时有效。
# 默认值为False。
# 从V1.13开始
use_storage_id = false # 指定存储ID文件名,可以使用相对路径或绝对路径。
# 与tracker.conf相同。
# 仅当load_fdfs_Parameters_from_tracker为FALSE时有效。
# 从V1.13开始
storage_ids_filename = storage_ids.conf # tracker 服务列表
tracker_server=tracker:22122 # storage server端口号
storage_server_port=23000 # 本地存储服务器的组名
group_name=group1 # 文件url中是否有group名
url_have_group_name = false # 存储路径个数,需要和store_path个数匹配
store_path_count=1 # store_path#,以0为基数,如果store_path0不存在,则其值为base_path。
# 路径必须存在。
# 必须与storage.conf相同
store_path0=/home/yuqing/fastdfs
#store_path1=/home/yuqing/fastdfs1 # 日志级别:
### emerg for emergency
### alert
### crit for critical
### error
### warn for warning
### notice
### info
### debug
log_level=info # 设置日志文件名,如/usr/local/apache2/logs/mod_fast dfs.log。
# Empty表示输出到stderr(apache和nginx error_log文件)
log_filename= # 本地文件系统中不存在文件时的响应方式。
## proxy:从其他存储服务器获取内容,然后发送给客户端。
## redirect:重定向到原存储服务器(HTTP Header为Location)
response_mode=proxy # 网卡别名前缀,如Linux中的eth,可以通过ifconfig-a查看。
# 多个别名以逗号分隔。空值表示按操作系统类型自动设置。
# 该参数用于获取本地主机的所有IP地址。
# 默认值为空
if_alias_prefix= # IF支持FLV。
# 默认值为False。
# 从v1.15开始
flv_support = true # FLV文件扩展名。
# 默认值为flv。
# 从v1.15开始
flv_extension = flv # 设置群数。
# 设置为无零以支持此存储服务器上的多组。
# 仅针对单个组设置为0。
# 将设置部分分组为[group1]、[group2]、...、[groupn]。
# 默认值为0。
# 从v1.14开始
group_count = 0 # group settings for group #1
# since v1.14
# when support multi-group on this storage server, uncomment following section
[group1]
# 组名称#
group_name=group1
# 组端口
storage_server_port=23000
# 存储路径个数,需要和store_path个数匹配
store_path_count=2
store_path0=/home/yuqing/fastdfs
store_path1=/home/yuqing/fastdfs1 # group settings for group #2
# since v1.14
# when support multi-group, uncomment following section as neccessary
[group2]
group_name=group2
storage_server_port=23000
store_path_count=1
store_path0=/home/yuqing/fastdfs
mod_fastdfs.conf 配置具体说明
(3):修改Nginx配置文件
完成上面,我们就得开始配置我们的Nginx配置了,这个配置按照之前的安装是在 /usr/local/nginx_fdfs/conf/nginx.conf
命令:vim /usr/local/nginx_fdfs/conf/nginx.conf
#拦截请求路径中包含 /group[1-9]/M[0-9][0-9] 的请求,用 fastdfs的Nginx 模块进行转发
location ~ /group[1-9]/M[0-9][0-9] {
ngx_fastdfs_module;
}
#注:ngx_fastdfs_module:这个指令不是Nginx本身提供的,是扩展模块提供的,根据这个指令找
# 到FastDFS提供的Nginx模块配置文件,然后找到Tracker,最终找到Stroager
(4):运行Nginx服务
切换到Nginx sbin目录下:cd /usr/local/nginx_fdfs/sbin/
执行nginx目录:./nginx
查询是否启动:ps -ef | grep nginx
(5):测试,上传一个文件并在本地浏览器访问
我们随便上传一个文件后,上传成功后会给我们返回一个带http的具体链接,我们直接在本地浏览器就可以访问(开启80端口)
# 上传文件测试
fdfs_test /etc/fdfs/client.conf upload abc.txt
# 返回获取的http地址准备本地访问
http://119.29.68.209/group1/M00/00/00/dx1E0WArksiARSeLAAAADEQKaqU125.txt
四:FastDFS在Java项目中开发
在真实开发中我们肯定不会使用如 fdfs_test 命令来上传文件,通常使用java代码来编写指定的上传代码,但是我们想使用得要一个jar包,就是我们最初下载的 fastdfs-client-java-1.28.tar.gz ,由于是源码,我们使用 maven 的 mvn 命令编译一下即可
1:编译源码来生成jar包
安装过后就会出现到你的maven仓库了..\org\csource\fastdfs-client-java
2:代码编写
我们以创建一个简单的maven项目为例
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>demo_001</artifactId>
<version>1.0-SNAPSHOT</version>
<!--maven编译版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<!--导入坐标-->
<dependencies>
<!--导入FastDFS客户端jar包-->
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.28-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
pom.xml坐标
完成了 pom.xml 文件的编写,我们就去resource下创建一个 fastdfs.conf 的配置文件指定 tracker地址
tracker_server = 119.29.68.209:22122
public class Test {
public static void main(String[] args) {
//上传
//upload();
//下载
//download();
//删除
delete();
}
//上传方法
//注意22122 23000端口的开放
public static void upload() {
try {
//加载配置文件
ClientGlobal.init("fastdfs.conf");
//创建 TrackerClient跟踪器客户端
TrackerClient trackerClient = new TrackerClient();
//通过 TrackerClient跟踪器客户端 获取 TrackerServer跟踪器服务端
TrackerServer trackerServer = trackerClient.getTrackerServer();
//通过 TrackerClient跟踪器客户端 获取 StorageServer存储服务器服务端
StorageServer storeStorage = trackerClient.getStoreStorage(trackerServer);
//创建存储服务器客户端
StorageClient storageClient = new StorageClient(trackerServer, storeStorage);
//开始上传
// 第一个参数文件路径,也可以是二进制数组
// 第二个参数是文件类型
String[] strings = storageClient.upload_file("h:\\1.jpg", "jpg", null);
for (String str : strings) {
System.out.println(str);
//打印:
// group1
// M00/00/00/dx1E0WArsfeAWVTvAAEoDtcZimw368.jpg
}
//关闭资源
storageClient.close();
} catch (IOException | MyException e) {
e.printStackTrace();
}
} //下载
public static void download() {
try {
//加载配置文件
ClientGlobal.init("fastdfs.conf");
//创建 TrackerClient跟踪器客户端
TrackerClient trackerClient = new TrackerClient();
//通过 TrackerClient跟踪器客户端 获取 TrackerServer跟踪器服务端
TrackerServer trackerServer = trackerClient.getTrackerServer();
//通过 TrackerClient跟踪器客户端 获取 StorageServer存储服务器服务端
StorageServer storeStorage = trackerClient.getStoreStorage(trackerServer);
//创建存储服务器客户端
StorageClient storageClient = new StorageClient(trackerServer, storeStorage);
//开始下载
byte[] bytes = storageClient.download_file("group1", "M00/00/00/dx1E0WArsfeAWVTvAAEoDtcZimw368.jpg");
//写出文件
new FileOutputStream("H:\\downloadfile.jpg").write(bytes);
//关闭资源
storageClient.close();
} catch (IOException | MyException e) {
e.printStackTrace();
}
} //删除
public static void delete() {
try {
//加载配置文件
ClientGlobal.init("fastdfs.conf");
//创建 TrackerClient跟踪器客户端
TrackerClient trackerClient = new TrackerClient();
//通过 TrackerClient跟踪器客户端 获取 TrackerServer跟踪器服务端
TrackerServer trackerServer = trackerClient.getTrackerServer();
//通过 TrackerClient跟踪器客户端 获取 StorageServer存储服务器服务端
StorageServer storeStorage = trackerClient.getStoreStorage(trackerServer);
//创建存储服务器客户端
StorageClient storageClient = new StorageClient(trackerServer, storeStorage);
//开始删除
int result = storageClient.delete_file("group1", "M00/00/00/dx1E0WArsfeAWVTvAAEoDtcZimw368.jpg");
System.out.println(result == 0 ? "删除成功" : "删除失败"); //0成功,返回其它数字都是失败
//关闭资源
storageClient.close();
} catch (IOException | MyException e) {
e.printStackTrace();
}
}
}
上传、下载、删除具体代码,没有封装
.
FastDFS一步步搭建存储服务器的更多相关文章
- 用FastDFS一步步搭建图片服务器(单机版)
一.FastDFS介绍 FastDFS开源地址:https://github.com/happyfish100 参考:分布式文件系统FastDFS设计原理 参考:FastDFS分布式文件系统 1.简介 ...
- 用FastDFS一步步搭建文件管理系统
一.FastDFS介绍 FastDFS开源地址:https://github.com/happyfish100 参考:分布式文件系统FastDFS设计原理 参考:FastDFS分布式文件系统 个人封装 ...
- 基于【 centos7】三 || 分布式文件系统FastDFS+Nginx环境搭建
1. FastDFS介绍 1.1 FastDFS定义 FastDFS是用c语言编写的一款开源的分布式文件系统.FastDFS为互联网量身定制,充分考虑了冗余备份.负载均衡.线性扩容等机制,并注重高可用 ...
- 一步步搭建自己的博客 .NET版(2、评论功能)
前言 这次开发的博客主要功能或特点: 第一:可以兼容各终端,特别是手机端. 第二:到时会用到大量html5,炫啊. 第三:导入博客园的精华文章,并做分类.(不要封我) 第四:做 ...
- 一步步搭建react-native环境(苹果OS X)
因重新升级了系统,一步步搭建react-native环境. 1.安装Homebrew 打开终端命令->ruby -e "$(curl -fsSL https://raw.githubu ...
- flow.ci + Github + Slack 一步步搭建 Python 自动化持续集成
理想的程序员必须懒惰,永远追随自动化法则.Automating shapes smarter future. 在一个 Python 项目的开发过程中可能会做的事情:编译.手动或自动化测试.部署环境配置 ...
- 一步步搭建docker私有仓库并从私有仓库中下载镜像
一步步搭建docker私有仓库 #下载镜像 docker pull registry#查看镜像 docker images #运行私有仓库,指定端口和数据卷 docker run -d -p : -v ...
- FastDFS图片服务器搭建
*FastDFS图片服务器搭建准备:1.需要libfastcommon安装包 选择最新稳定版(libfastcommon-1.0.36.tar.gz)2.需要FastDFS安装包 选择最新稳定版(fa ...
- 一步步搭建物联网系统——无处不在的CSS
无处不在的CSS 或许你觉得CSS一点儿也不重要,而事实上,如果说HTML是建筑的框架,CSS就是房子的装修.那么Javascript呢,我听到的最有趣的说法是小三--还是先让我们回到代码上来吧. C ...
随机推荐
- django学习-21.优化表数据的标题展示
目录结构 1.前言 2.表数据的标题默认展示的数据格式是[模型类名 object(主键名)]的相关信息 3.优化表数据的标题展示的数据格式是[改成我们想要展示的数据格式]的相关完整操作步骤 3.1.第 ...
- Python算法_爬楼梯(08)
假设你正在爬楼梯.需要 n 阶你才能到达楼顶. 每次你可以爬 1 或 2 个台阶.你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数. 示例 1: 输入: 2输出: 2解释: 有两种方 ...
- 进阶高阶IoT架构-教你如何简单实现一个消息队列
前言 消息队列是软件系统领域用来实现系统间通信最广泛的中间件.基于消息队列的方式是指由应用中的某个系统负责发送消息,由关心这条消息的相关系统负责接收消息,并在收到消息后进行各自系统内的业务处理.消息可 ...
- Dokcer中Mysql的数据导入导出
导出 1.首先进入容器,输入提取数据库文件命令 mysqldump -u root -p rw 数据库名> 输出.sql,提取到当前容器 2.退出容器,进入linux:输入拷贝命令 docker ...
- 谈一下HashMap的底层原理是什么?
底层原理:Map + 无序 + 键唯一 + 哈希表 (数组+Entry)+ 存取值 1.HashMap是Map接口的实现类.实现HashMap对数据的操作,允许有一个null键,多个null值. Co ...
- Redis缓存中的常见问题
缓存穿透:是指查询一个Redis和数据库中都不存在的数据. 问题:查询一个Redis和数据库中都不存在的数据,大量请求去访问数据库,导致数据库宕机. 解决办法: 1.根据id查询,如果id是自增的,将 ...
- Spring IoC总结
Spring 复习 1.Spring IoC 1.1 基本概念 1.1.1 DIP(Dependency Inversion Principle) 字面意思依赖反转原则,即调用某个类的构造器创建对象时 ...
- python爬虫登录保持及对http总结
[前言]这几天一直看python爬虫登录保持.实现接口太多,太乱,新手难免云山雾罩.各种get.post,深入理解一下,其实就是由于http的特性需要这些操作.http是一种无状态.不保存上次通信结果 ...
- Salesforce LWC学习(三十一) Quick Action适配
本篇参考:https://www.lightningdesignsystem.com/components/modals/ 随着salesforce lwc的优化,越来越多的项目从aura转到了lwc ...
- 谈一谈C#的事件
谈一谈C#的事件 C#中事件基于委托,要理解事件要先理解委托,如果觉得自己关于委托不是很了解可以看看我前面写委托的文章 事件基于委托,是一种功能受限的委托,为委托提供了一种发布/订阅机制 使用委托时, ...