fastdfs服务搭建先要安装gcc编译器:
yum -y install gcc-c++
安装lib依赖包tar -zxvf v1.0.43.tar.gzcd libfastcommon-1.0.43./make.sh./make.sh install
ln -s /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.soln -s /usr/lib64/libfastcommon.so /usr/lib/libfastcommon.soln -s /usr/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.soln -s /usr/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so
安装fastdfs服务tar -zxvf v6.06.tar.gzcd fastdfs-6.06./make.sh./make.sh install
配置tracker服务mkdir -p /data/fastdfs/trackercd /etc/fdfscp tracker.conf.sample tracker.confvim tracker.conf
要修改的内容为
#启用配置文件(默认启用)disabled=false #设置tracker的端口号,通常采用22122这个默认端口port=22122 #设置tracker的数据文件和日志目录base_path=/data/fastdfs/tracker#设置http端口号,默认为8080http.server_port=80
启动服务
#启动/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start
查看是否启动
#查看启动端口ss -ant | grep 22122
查看启动日志
#查看启动日志tail -f /data/fastdfs/tracker/logs/trackerd.log
加入开机启动
vim /etc/rc.d/rc.local
在加入启动命令
如果重启后发现未能自动启动则通过命令
ll /etc/rc.d/rc.local
检查一下rc.local是否具备可执行权限,若是无可执行权限则通过命令进行授权
chmod +x /etc/rc.d/rc.local
配置storage服务mkdir -p /data/fastdfs/storagemkdir -p /data/fastdfs/storage/filecd /etc/fdfscp storage.conf.sample storage.confvim storage.conf
要修改的内容为
#启用配置文件(默认启用)disabled=false#组名,根据实际情况修改 group_name=group1 #设置storage的端口号,默认是23000,同一个组的storage端口号必须一致port=23000 #设置storage数据文件和日志目录base_path=/data/fastdfs/storage #存储路径个数,需要和store_path个数匹配store_path_count=1 #实际文件存储路径store_path0=/data/fastdfs/storage/file#tracker 服务器的 ip地址和端口号,如果是单机搭建,ip不要写127.0.0.1,否则启动不成功(此处的ip是我的centos虚拟机ip)tracker_server=172.16.6.50:22122 #设置 http 端口号http.server_port=8888
启动服务
#启动/usr/bin/fdfs_storaged /etc/fdfs/storage.conf start
查看启动端口
#查看启动端口ss -ant | grep 23000
查看启动日志
#查看启动日志tail -f /data/fastdfs/storage/logs/storaged.log
通过monitor查看storage是否绑定成功
[root@localhost /]# /usr/bin/fdfs_monitor /etc/fdfs/storage.conf[2021-09-23 12:59:26] debug - base_path=/opt/fastdfs_storage, connect_timeout=30, network_timeout=60, tracker_server_count=1, anti_steal_token=0, anti_steal_secret_key length=0, use_connection_pool=0, g_connection_pool_max_idle_time=3600s, use_storage_id=0, storage server id count: 0server_count=1, server_index=0tracker server is 172.16.8.11:22122group count: 1group 1:group name = group1disk total space = 6818 mbdisk free space = 2169 mbtrunk free space = 0 mb……
加入开机启动
vim /etc/rc.d/rc.local
在该文件中,加入启动命令
/usr/bin/fdfs_storaged /etc/fdfs/storage.conf start
测试验证[root@localhost ~]# ps -ef|grep fdfsroot 10335 17685 0 23:50 pts/3 00:00:00 grep --color=auto fdfsroot 13335 1 0 13:17 ? 00:00:07 /usr/bin/fdfs_storaged /etc/fdfs/storage.conf startroot 15779 1 0 12:59 ? 00:00:04 /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf
用fdfs客户端进行测试
cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf
测试时需要设置客户端的配置文件
vim /etc/fdfs/client.conf
打开文件后依次做以下修改:
#tracker服务器文件路径base_path=/data/fastdfs/tracker#tracker服务器ip地址和端口号tracker_server=172.16.7.50:22122# tracker 服务器的 http 端口号,必须和tracker的设置对应起来http.tracker_server_port=80
配置完成后就可以模拟文件上传了,先给/data目录下放一文件test.txt,然后通过执行客户端上传命令尝试上传:
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf /data/test.txt
单机redis服务搭建1.获取redis资源,并解压tar xzvf redis-4.0.8.tar.gz
2.安装cd redis-4.0.8makecd srcmake install prefix=/usr/local/redis
3.移动配置文件到安装目录下cd ../mkdir /usr/local/redis/etcmv redis.conf /usr/local/redis/etc
4.配置redis为后台启动vim /usr/local/redis/etc/redis.conf
注意,将daemonize no 改成daemonize yes,及注释掉bind 127.0.0.1,可以远程访问
5.将redis加入到开机启动 vim /etc/rc.d/rc.local
在里面添加内容:
/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
意思就是开机调用这段开启redis的命令.
6.启动redis/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
7.将redis-cli,redis-server拷贝到bin下,让redis-cli指令可以在任意目录下直接使用cp /usr/local/redis/bin/redis-server /usr/local/bin/cp /usr/local/redis/bin/redis-cli /usr/local/bin/
8.设置redis密码a.运行命令:redis-cli
b.查看现有的redis密码(可选操作,可以没有)运行命令:
config get requirepass
如果没有设置过密码的话运行结果会如下图所示
c.设置redis密码运行命令:
config set requirepass *******
(******为你要设置的密码),设置成功的话会返回‘ok’字样
d.测试连接重启redis服务
//****为你设置的密码redis-cli -h 127.0.0.1 -p 6379 -a ****
也可以,输入 redis-cli 进入命令模式,使用 auth '*****' (****为你设置的密码)登陆
9.让外网能够访问redisa.配置防火墙:#开放6379端口 firewall-cmd --zone=public --add-port=6379/tcp --permanent#重启防火墙以使配置即时生效systemctl restart firewalld#查看系统所有开放的端口firewall-cmd --zone=public --list-ports
b.若是仍不可远程访问:此时 虽然防火墙开放了6379端口,但是外网还是无法访问的,因为redis监听的是127.0.0.1:6379,并不监听外网的请求。
把文件夹目录里的redis.conf配置文件里的bind 127.0.0.1前面加#注释掉
命令:redis-cli连接到redis后,通过 config get daemonize和config get protected-mode 是不是都为no,如果不是,就用config set 配置名 属性 改为no。
其他redis相关常用命令卸载redis:#停止redispkill redis #删除安装目录rm -rf /usr/local/redis #删除所有redis相关命令脚本rm -rf /usr/bin/redis-* #删除redis解压文件夹rm -rf /root/download/redis-4.0.4
报异常,尝试重启原因: redis已经启动
解决: 关掉redis,重启即可
redis-cli shutdown redis-server &
然后你就能看到redis愉快的运行了.
客户端检测连接是否正常使用redis-cli客户端检测连接是否正常
$redis-cli 127.0.0.1:6379> keys * (empty list or set) 127.0.0.1:6379> set key "hello world" ok 127.0.0.1:6379> get key "hello world"
redis客户端常见操作redis是key-value数据库,支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
当value是string类型,命令包括set get setnx incr del 等。> set server:name "fido" // 设置键值ok> get server:name // 获取键值"fido"> setnx connections 10 // set if not existsok> incr connections // 原子性增加values值(integer) 11> incr connections(integer) 12> del connections // 删除key(integer) 1> incr connections(integer) 1
当value是list类型,命令包括rpush lpush llen lrange lpop rpop del 等。> rpush friends "alice" // 在末尾追加(integer) 1> rpush friends "bob"(integer) 2> lpush friends "sam" // 插入到开头(integer) 3 > lrange friends 0 -1 // 返回列表的子集,类似切片操作1) "sam"2) "alice"3) "bob"> lrange friends 0 11) "sam"2) "alice"> lrange friends 1 21) "alice"2) "bob" > llen friends // 返回列表长度(integer) 3> lpop friends // 删除并返回列表第一个元素"sam"> rpop friends // 删除并返回列表最后一个元素"bob"> lrange friends 0 -11) "alice" > del friends // 删除key(integer) 1 // 1表示成功,0表示失败
当value是set类型,命令包括sadd srem sismember smembers sunion del等。> sadd superpowers "flight" // 添加元素(integer) 1> sadd superpowers "x-ray vision"(integer) 1> sadd superpowers "reflexes"(integer) 1> srem superpowers "reflexes" // 删除元素1 > sismember superpowers "flight" // 测试元素是否在集合中(integer) 1> sismember superpowers "reflexes"(integer) 0> smembers superpowers // 返回集合中所有元素1) "x-ray vision"2) "flight" > sadd birdpowers "pecking"(integer) 1> sadd birdpowers "flight"(integer) 1> sunion superpowers birdpowers // 合并多个set,返回合并后的元素列表1) "x-ray vision"2) "flight"3) "pecking" > del superpowers // 删除key(integer) 1
当value是zset类型,命令包括 zadd zrange del等,注意给value一个编号用于排序。> zadd hacker 1940 "alan kay" // 给value指定一个编号,比如以年份1940作为编号(integer) 1> zadd hacker 1906 "grace hopper"(integer) 1> zadd hacker 1953 "richard stallman"(integer) 1> zadd hacker 1965 "yukihiro matsumoto"(integer) 1> zadd hacker 1916 "claude shannon"(integer) 1> zadd hacker 1969 "linux torvalds"(integer) 1> zadd hacker 1957 "sophie wilson"(integer) 1> zadd hacker 1912 "alan turing"(integer) 1 > zrange hacker 2 4 // 切片返回有序集合中元素1) "claude shannon"2) "alan kay"3) "richard stallman" > del hacker // 删除key(integer) 1
当value是hash类型,hash类型可以理解为字典,需要给value指定一个field用于映射,命令包括hset hmset hget hgetall hdel hincrby del 等。> hset user:1000 name "john smith" // 给value指定一个field,比如name(integer) 1> hset user:1000 email "john.smith@example.com"(integer) 1> hset user:1000 password "s3cret"(integer) 1> hgetall user:1000 // 获得hash表中所有成员,包括field和value1) "name"2) "john smith"3) "email"4) "john.smith@example.com"5) "password"6) "s3cret" > hmset user:1001 name "mary jones" password "hidden" email"mjones@example.com" // 设置多个field和valueok> hget user:1001 name // 根据field获取value"mary jones" > hset user:1000 visits 10 // field可以映射到数字值(integer) 1> hincrby user:1000 visits 1 // 原子性增加value的值,增加1(integer) 11> hincrby user:1000 visits 10 // 增加10(integer) 21> hdel user:1000 visits // 删除field及其value(integer) 1> hincrby user:1000 visits 1(integer) 1 > del user:1000 // 删除key(integer) 1
设置和查看key的生命周期,key过期会被自动删除,命令包括expire ttl 等。> set resource:lock "redis demo"ok> expire resource:lock 120 // 设置生命周期为120s(integer) 1> ttl resource:lock // 查看当前生命周期还剩多少时间(integer) 109> ttl resource:lock // 120s后查看,返回-2表示已过期或不存在(integer) -2 > set resource:lock "redis demo 2"ok> ttl resource:lock // 返回-1表示永不过期(integer) -1
springboot实现h5与fastdfs之间的断点续传,大文件上传,秒传文件和批量上传 对比,只是单纯返回一个string类型?
改为upload_list8888,页面调用是失败的
改回upload_list
threadlocal与redis秒配,解决了redis线程池在被共享访问时带来的线程安全问题打个比方,现在公司所有人都要填写一个表格,但是只有一支笔,这个时候就只能上个人用完了之后,下个人才可以使用,为了保证"笔"这个资源的可用性,只需要保证在接下来每个人的获取顺序就可以了,这就是 lock 的作用,当这支笔被别人用的时候,我就加 lock ,你来了那就进入队列排队等待获取资源(非公平方式那就另外说了),这支笔用完之后就释放 lock ,然后按照顺序给下个人使用。
但是完全可以一个人一支笔对不对,这样的话,你填写你的表格,我填写我的表格,咱俩谁都不耽搁谁。这就是 threadlocal 在做的事情,因为每个 thread 都有一个副本,就不存在资源竞争,所以也就不需要加锁,这不就是拿空间去换了时间嘛!
了解决redis线程池对象(笔),不能被多线程(多个人)共享访问的问题,通过 threadlocal.set() 方法,将redis线程池对象实例保存在每个线程,自己所拥有的 threadlocalmap中(生成多个副本)。
这样的话,每个线程都使用自己的redis线程池对象实例,彼此不会影响,从而达到了隔离的作用,这样就解决了redis线程池对象在被共享访问时带来的线程安全问题。
import cn.hutool.core.util.strutil;import org.slf4j.logger;import org.slf4j.loggerfactory;import redis.clients.jedis.jedis;import redis.clients.jedis.jedispool;import redis.clients.jedis.jedispoolconfig;import java.util.list;public class redisutil { private redisutil() { } private static logger _logger = loggerfactory.getlogger(redisutil.class); ; protected static final threadlocal<jedis> threadlocaljedis = new threadlocal<jedis>(); //redis服务器ip private static string addr_array = readproper.getresourcevalue("spring.redis.host"); //redis的端口号 private static int port = integer.parseint(readproper.getresourcevalue("spring.redis.port")); //访问密码 private static string auth = readproper.getresourcevalue("spring.redis.password"); //可用连接实例的最大数目,默认值为8; //如果赋值为-1,则表示不限制;如果pool已经分配了maxactive个jedis实例,则此时pool的状态为exhausted(耗尽)。 private static int max_active = -1; //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。 private static int max_idle = 16; //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出jedisconnectionexception; private static int max_wait = 1000 * 5; //超时时间 private static int timeout = 1000 * 5; //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的; private static boolean test_on_borrow = true; private static jedispool jedispool ; //默认的数据库为0 /** * redis过期时间,以秒为单位 */ public final static int exrp_hour = 60 * 60; //一小时 public final static int exrp_day = 60 * 60 * 24; //一天 public final static int exrp_month = 60 * 60 * 24 * 30; //一个月 /** * 初始化redis连接池,注意一定要在使用前初始化一次,一般在项目启动时初始化就行了 */ public static jedispool initialpool() { jedispool jp=null; try { jedispoolconfig config = new jedispoolconfig(); config.setmaxtotal(max_active); config.setmaxidle(max_idle); config.setmaxwaitmillis(max_wait); config.settestonborrow(test_on_borrow); config.settestoncreate(true); config.settestwhileidle(true); config.settestonreturn(true); config.setnumtestsperevictionrun(-1); jp = new jedispool(config, addr_array, port, timeout, auth); jedispool=jp; threadlocaljedis.set(getjedis()); } catch (exception e) { e.printstacktrace(); _logger.error("redis服务器异常",e); } return jp; } public static void close(jedis jedis) { if (threadlocaljedis.get() == null && jedis != null){ jedis.close(); } }// /**// * 在多线程环境同步初始化// */// private static synchronized void poolinit() {// if (jedispool == null) {// initialpool();// }// } /** * 获取jedis实例,一定先初始化 * * @return jedis */ public static jedis getjedis() { boolean success = false; jedis jedis = null;// if (jedispool == null) {// poolinit();// } int i=0; while (!success) { i++; try { if (jedispool != null) { jedis=threadlocaljedis.get(); if (jedis==null){ jedis = jedispool.getresource(); }else { if(! jedis.isconnected()&&!jedis.getclient().isbroken()){ threadlocaljedis.set(null); jedis = jedispool.getresource(); } //system.out.println(thread.currentthread().getname()+":第"+i+"次获取成功#@利用了本地缓存redis"); return jedis; } }else { throw new runtimeexception("redis连接池初始化失败"); } } catch (exception e) { system.out.println(thread.currentthread().getname()+":第"+i+"次获取失败!!!"); success = false; e.printstacktrace(); _logger.error("redis服务器异常",e); } if (jedis!=null){ success=true; } if (i>=10&&i<20){ try { thread.sleep(1000); } catch (interruptedexception e) { e.printstacktrace(); } } if (i>=20&&i<30){ try { thread.sleep(2000); } catch (interruptedexception e) { e.printstacktrace(); } } if (i>=30&&i<40){ try { thread.sleep(3000); } catch (interruptedexception e) { e.printstacktrace(); } } if (i>=40){ system.out.println("redis彻底连不上了~~~~(>_<)~~~~"); return null; } } if (threadlocaljedis.get()==null){threadlocaljedis.set(jedis);} //system.out.println(thread.currentthread().getname()+":第"+i+"次获取成功@"); return jedis; } /** * 设置 string * * @param key * @param value */ public static void setstring(string key, string value) { jedis jo = null; try { value = strutil.isblank(value) ? "" : value; jo = getjedis(); jo.set(key, value); } catch (exception e) { threadlocaljedis.set(null); _logger.error("redis服务器异常",e); throw new runtimeexception("redis服务器异常"); } finally { if (jo != null) { close(jo); } } } /** * 设置 过期时间 * * @param key * @param seconds 以秒为单位 * @param value */ public static void setstring(string key, int seconds, string value) { jedis jo = null; try { value = strutil.isblank(value) ? "" : value; jo = getjedis(); jo.setex(key, seconds, value); } catch (exception e) { threadlocaljedis.set(null); e.printstacktrace(); _logger.error("redis服务器异常",e); throw new runtimeexception("redis服务器异常"); } finally { if (jo != null) { close(jo); } } } /** * 获取string值 * * @param key * @return value */ public static string getstring(string key) { jedis jo = null; try { jo = getjedis(); if (jo == null || !jo.exists(key)) { return null; } return jo.get(key); } catch (exception e) { threadlocaljedis.set(null); e.printstacktrace(); _logger.error("redis服务器异常",e); throw new runtimeexception("redis操作错误"); } finally { if (jo != null) { close(jo); } } } public static long incrby(string key, long integer) { jedis jo = null; try { jo = getjedis(); return jo.incrby(key, integer); } catch (exception e) { threadlocaljedis.set(null); e.printstacktrace(); _logger.error("redis服务器异常",e); throw new runtimeexception("redis操作错误"); } finally { if (jo != null) { close(jo); } } } public static long decrby(string key, long integer) { jedis jo = null; try { jo = getjedis(); return jo.decrby(key, integer); } catch (exception e) { threadlocaljedis.set(null); e.printstacktrace(); _logger.error("redis服务器异常",e); throw new runtimeexception("redis操作错误"); } finally { if (jo != null) { close(jo); } } } //删除多个key public static long delkeys(string [] keys){ jedis jo = null; try { jo = getjedis(); return jo.del(keys); } catch (exception e) { threadlocaljedis.set(null); e.printstacktrace(); _logger.error("redis服务器异常",e); throw new runtimeexception("redis操作错误"); } finally { if (jo != null) { close(jo); } } } //删除单个key public static long delkey(string key){ jedis jo = null; try { jo = getjedis(); return jo.del(key); } catch (exception e) { threadlocaljedis.set(null); e.printstacktrace(); _logger.error("redis服务器异常",e); throw new runtimeexception("redis操作错误"); } finally { if (jo != null) { close(jo); } } } //添加到队列尾 public static long rpush(string key,string node){ jedis jo = null; try { jo = getjedis(); return jo.rpush(key,node); } catch (exception e) { threadlocaljedis.set(null); e.printstacktrace(); _logger.error("redis服务器异常",e); throw new runtimeexception("redis操作错误"); } finally { if (jo != null) { close(jo); } } } //删除list元素 public static long dellistnode(string key,int count,string value){ jedis jo = null; try { jo = getjedis(); return jo.lrem(key,count,value); } catch (exception e) { threadlocaljedis.set(null); e.printstacktrace(); _logger.error("redis服务器异常",e); throw new runtimeexception("redis操作错误"); } finally { if (jo != null) { close(jo); } } } //获取所有list public static list getlistall(string key){ jedis jo = null; list list=null; try { jo = getjedis(); list= jo.lrange(key,0,-1); } catch (exception e) { threadlocaljedis.set(null); e.printstacktrace(); _logger.error("redis服务器异常",e); throw new runtimeexception("redis操作错误"); } finally { if (jo != null) { close(jo); } } return list; } //清理缓存redis public static void cleanloacl(jedis jo){ threadlocaljedis.set(null); close(jo); }static { initialpool();}}
使用webuploader组件实现大文件分片上传,断点续传webuploader:是一个以html5为主, flash为辅的文件上传组件,采用大文件分片/并发上传的方式,极大地提高了文件上传的效率,同时兼容多种浏览器版本;
前端
引入百度webuploader组件,需要注意标签的id/nama属性,这些将在后面的javascript中使用到进行文件切分、验证。
以上js组件,将完成文件上传、md5验证、删除、切片、上传进度条显示、暂停、继续上传及上传成功/失败时候的回调。
后端前端,给后端提供封装的chunk,及request
后端,主要是判断文件是否有分片,如果没有,则直接存放到目的目录;
如果存在分片,则创建临时目录,存放分片信息;
之后判断当前分片所属的文件的所有分片是否已经传输完毕,如果当前分片数==所属文件总分片数,则开始合并文件并转移完整文件到目的目录,并且删除临时目录
检测完文件,以后,开始上传操作
//上传操作path = appendfilestorageclient.uploadappenderfile(uploadconstant.default_group, file.getinputstream(),file.getsize(), fileutil.extname((string) parammap.get("name")));//更新操作appendfilestorageclient.modifyfile(uploadconstant.default_group, nogrouppath, file.getinputstream(),file.getsize(),historyupload);
hutool工具的巧妙运用可参考hutool资料http://www.mianshigee.com/tutorial/hutool/26e24c7a37d93249.md
<dependency> <groupid>cn.hutool</groupid> <artifactid>hutool-all</artifactid> <version>5.0.6</version></dependency>
cn.hutool.core.collection.collutil判断非空
cn.hutool.json.jsonobject与jsonutilif (collutil.isnotempty(filelist)){ for (string e:filelist){ jsonobject jsonobject= jsonutil.parseobj(e); jsonobjects.add(jsonobject); } }
cn.hutool.core.convert.convert类型转换
cn.hutool.core.util.randomutil生成随机字符串及strutil判断字符为空
解析文件的扩展名,来获该文件的类型import cn.hutool.core.io.fileutil;fileutil.extname((string) parammap.get("name")));
以上就是如何搭建fastdfs服务及单机redis服务的详细内容。
