搜索词>>FastDFS Java客户端 耗时0.0050
  • spring boot FastDFS Java client使用-Java编程

    Java编程之spring boot FastDFS Java client使用,Java编程,FastDFS Java客户端<h2>一、获取FastDFS Java的客户端链接工具</h2> 由于maven库中并没有编译好的jar工具包,目前只能通过GitHub下载源码自己打包成jar<br /> 下载地址:<a href="https://github.com/happyfish100/fastdfs-client-java" rel="external nofollow" target="_blank">点击去下去</a><br /> <br /> 如果通过浏览器直接下载,则下载后是一个zip压缩包。<br /> 1.解压zip包<br /> 2.eclipse通过已存在的maven项目方式导入<br /> 3.执行maven install命令打包<br /> <br /> 打包完成后再target目录下有打包好的jar文件:<br /> <strong>fastdfs-client-java-1.27-SNAPSHOT.jar</strong><br />   <h2>二、导入spring boot项目</h2> 创建一个spring boot项目,在创建好的项目中创建一个lib的文件夹,将上面打包的jar文件复制进去。然后打开spring boot项目的pom.xml文件,添加本地依赖如下: <pre> <code class="language-xml"> <!-- fastfds 客户端 |https://github.com/happyfish100/fastdfs-client-java --> <dependency> <groupId>org.csource</groupId> <artifactId>fastdfs-client-java</artifactId> <version>1.27-SNAPSHOT</version> <scope>system</scope> <systemPath>${project.basedir}/lib/fastdfs-client-java-1.27-SNAPSHOT.jar</systemPath> </dependency></code></pre> <h2>三、配置文件</h2> 在spring boot项目的resource目录下创建一个fdfs_client.conf文件,内容如下: <pre> <code>#注1:tracker_server指向您自己IP地址和端口,1-n个 #注2:除了tracker_server,其它配置项都是可选的 #注3:.conf 配置文件文件所在位置可以是项目classpath(或OS文件系统目录比如/opt/): #注4:.conf 配置文件优先按OS文件系统路径读取,没有找到才查找项目classpath,尤其针对linux环境下的相对路径 #注5:其他相关参考:https://github.com/happyfish100/fastdfs-client-java connect_timeout = 120 network_timeout = 130 charset = UTF-8 http.tracker_http_port = 80 http.anti_steal_token = no http.secret_key = FastDFS1234567890 tracker_server = 192.168.8.202:22122</code></pre> 其中里面的tracker_server 以及端口均需要根据自身使用的实际请来修改 <h2><br /> 四、编写一个公用的Java 的fastdfs客户端</h2> 首先需要创建一个FastDFS的文件辅助类:<br /> <strong>FastDSFile.java</strong> <pre> <code class="language-java">public class FastDSFile { private String name; private byte[] content; private String ext; private String md5; public String getName() { return name; } public void setName(String name) { this.name = name; } public byte[] getContent() { return content; } public void setContent(byte[] content) { this.content = content; } public String getExt() { return ext; } public void setExt(String ext) { this.ext = ext; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } } </code></pre> <br /> <strong>FastDFSClient.java</strong> <pre> <code class="language-java">import java.io.File; import java.io.IOException; import org.csource.common.MyException; import org.csource.fastdfs.ClientGlobal; import org.csource.fastdfs.StorageClient1; import org.csource.fastdfs.StorageServer; import org.csource.fastdfs.TrackerClient; import org.csource.fastdfs.TrackerServer; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import com.alibaba.fastjson.JSONArray; /** * fastDFS文件服务Java客户端实现,所有执行方法均为静态方法。 * * @author xq * */ public class FastDFSClient { /** * 客户端 */ private static StorageClient1 storageClient1 = null; // 初始化客户端,加载类时候执行片段 static { try { Resource resource = new ClassPathResource("fdfs_client.conf"); File file = resource.getFile(); String configFile = file.getAbsolutePath(); ClientGlobal.init(configFile); // TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group); // TrackerServer trackerServer = trackerClient.getConnection(); // StorageServer storageServer = trackerClient.getStoreStorage(trackerServer); // storageClient1 = new StorageClient1(trackerServer, storageServer); System.out.println("FastDFS Client Init Success!"); } catch (Exception e) { e.printStackTrace(); System.out.println("FastDFS Client Init Fail!"); } } /*** * 文件上传 * * @param fastDSFile * @return * @throws IOException * @throws MyException */ public static JSONArray upload(FastDSFile fastDSFile) throws IOException, MyException { String[] uploadResult = storageClient1.upload_file(fastDSFile.getContent(), fastDSFile.getExt(), null); // String arr = JSONArray.toJSONString(uploadResult); JSONArray arr = (JSONArray) JSONArray.toJSON(uploadResult); return arr; } /** * 文件下载 * * @param groupName * @param remoteFileName * @return * @throws IOException * @throws MyException */ public static byte[] download(String groupName, String remoteFileName) throws IOException, MyException { return storageClient1.download_file(groupName, remoteFileName); } /** * 文件删除 * * @param groupName * @param remoteFileName * @throws Exception * @return 返回0成功;非0失败. */ public static int delete(String groupName, String remoteFileName) throws Exception { return storageClient1.delete_file(groupName, remoteFileName); } } </code></pre> <h2>五、编写测试</h2> <strong>DemoSpringbootFastdfsApplicationTests.java</strong> <pre> <code class="language-java">import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.alibaba.fastjson.JSONArray; import net.xqlee.project.demo.fastdfs.clients.FastDFSClient; import net.xqlee.project.demo.fastdfs.clients.FastDSFile; @RunWith(SpringRunner.class) @SpringBootTest public class DemoSpringbootFastdfsApplicationTests { @Test public void contextLoads() { try { FileInputStream fis = new FileInputStream(new File("C:/Users/xq/Pictures/tx.jpg")); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] cache = new byte[4096]; while (fis.read(cache) != -1) { bos.write(cache); } fis.close(); FastDSFile fastDSFile = new FastDSFile(); fastDSFile.setContent(bos.toByteArray()); fastDSFile.setExt("jpg"); // -------上传---- JSONArray rs = FastDFSClient.upload(fastDSFile); System.out.println("上传结束:" + rs); // -------下载---- byte[] dfile = FastDFSClient.download(rs.getString(0), rs.getString(1)); FileOutputStream fos = new FileOutputStream(new File("C:/Users/xq/Pictures/tx-fdfs.jpg")); fos.write(dfile); fos.flush(); fos.close(); // -------删除----- int ds=FastDFSClient.delete(rs.getString(0), rs.getString(1)); // System.out.println("Delete:"+ds); System.out.println("---End----"); } catch (Exception e) { e.printStackTrace(); } } } </code></pre> <br /> 运行测试,可以看到在上传图片的目录下多了一个名为tx-fdfs.jpg的文件
  • 分布式文件系统FastDFS详解

    分布式文件系统FastDFS详解<h2>为什么要使用分布式文件系统呢?</h2> <p>嗯,这个问题问的好,使用了它对我们有哪些好处?带着这个问题我们来往下看:</p> <h3>单机时代</h3> <p>初创时期由于时间紧迫,在各种资源有限的情况下,通常就直接在项目目录下建立静态文件夹,用于用户存放项目中的文件资源。如果按不同类型再细分,可以在项目目录下再建立不同的子目录来区分。例如: <code>resources\static\file</code>、 <code>resources\static\img</code>等。</p> <p><strong>优点</strong>:这样做比较便利,项目直接引用就行,实现起来也简单,无需任何复杂技术,保存数据库记录和访问起来也很方便。</p> <p><strong>缺点</strong>:如果只是后台系统的使用一般也不会有什么问题,但是作为一个前端网站使用的话就会存在弊端。一方面,文件和代码耦合在一起,文件越多存放越混乱;另一方面,如果流量比较大,静态文件访问会占据一定的资源,影响正常业务进行,不利于网站快速发展。</p> <h3>独立文件服务器</h3> <p>随着公司业务不断发展,将代码和文件放在同一服务器的弊端就会越来越明显。为了解决上面的问题引入独立图片服务器,工作流程如下:项目上传文件时,首先通过ftp或者ssh将文件上传到图片服务器的某个目录下,再通过ngnix或者apache来访问此目录下的文件,返回一个独立域名的图片URL地址,前端使用文件时就通过这个URL地址读取。</p> <p><strong>优点</strong>:图片访问是很消耗服务器资源的(因为会涉及到操作系统的上下文切换和磁盘I/O操作),分离出来后,Web/App服务器可以更专注发挥动态处理的能力;独立存储,更方便做扩容、容灾和数据迁移;方便做图片访问请求的负载均衡,方便应用各种缓存策略(HTTP Header、Proxy Cache等),也更加方便迁移到CDN。</p> <p><strong>缺点</strong>:单机存在性能瓶颈,容灾、垂直扩展性稍差</p> <h3>分布式文件系统</h3> <p>通过独立文件服务器可以解决一些问题,如果某天存储文件的那台服务突然down了怎么办?可能你会说,定时将文件系统备份,这台down机的时候,迅速切换到另一台就OK了,但是这样处理需要人工来干预。另外,当存储的文件超过100T的时候怎么办?单台服务器的性能问题?这个时候我们就应该考虑分布式文件系统了。</p> <p>业务继续发展,单台服务器存储和响应也很快到达了瓶颈,新的业务需要文件访问具有高响应性、高可用性来支持系统。分布式文件系统,一般分为三块内容来配合,服务的存储、访问的仲裁系统,文件存储系统,文件的容灾系统来构成,仲裁系统相当于文件服务器的大脑,根据一定的算法来决定文件存储的位置,文件存储系统负责保存文件,容灾系统负责文件系统和自己的相互备份。</p> <p><strong>优点</strong>:扩展能力: 毫无疑问,扩展能力是一个分布式文件系统最重要的特点;高可用性: 在分布式文件系统中,高可用性包含两层,一是整个文件系统的可用性,二是数据的完整和一致性;弹性存储: 可以根据业务需要灵活地增加或缩减数据存储以及增删存储池中的资源,而不需要中断系统运行</p> <p><strong>缺点</strong>:系统复杂度稍高,需要更多服务器</p> <h2>FastDFS</h2> <p>毫无疑问FastDFS就属于我们上面介绍的分布式文件系统,下面我们来详细了解一下:</p> <h3>什么是FastDFS</h3> <p>FastDFS是一个开源的轻量级分布式文件系统。它解决了大数据量存储和负载均衡等问题。特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务,如相册网站、视频网站等等。在UC基于FastDFS开发向用户提供了:网盘,社区,广告和应用下载等业务的存储服务。</p> <p>FastDFS是一款开源的轻量级分布式文件系统纯C实现,支持Linux、FreeBSD等UNIX系统类google FS,不是通用的文件系统,只能通过专有API访问,目前提供了C、Java和PHP API为互联网应用量身定做,解决大容量文件存储问题,追求高性能和高扩展性FastDFS可以看做是基于文件的key value pair存储系统,称作分布式文件存储服务更为合适。</p> <h3>FastDFS相关概念</h3> <p>FastDFS服务端有三个角色:跟踪服务器(tracker server)、存储服务器(storage server)和客户端(client)。</p> <p><strong>tracker server</strong>:跟踪服务器,主要做调度工作,起负载均衡的作用。在内存中记录集群中所有存储组和存储服务器的状态信息,是客户端和数据服务器交互的枢纽。相比GFS中的master更为精简,不记录文件索引信息,占用的内存量很少。</p> <p>Tracker是FastDFS的协调者,负责管理所有的storage server和group,每个storage在启动后会连接Tracker,告知自己所属的group等信息,并保持周期性的心跳,tracker根据storage的心跳信息,建立group==>[storage server list]的映射表。</p> <p>Tracker需要管理的元信息很少,会全部存储在内存中;另外tracker上的元信息都是由storage汇报的信息生成的,本身不需要持久化任何数据,这样使得tracker非常容易扩展,直接增加tracker机器即可扩展为tracker cluster来服务,cluster里每个tracker之间是完全对等的,所有的tracker都接受stroage的心跳信息,生成元数据信息来提供读写服务。</p> <p><strong>storage server</strong>:存储服务器(又称:存储节点或数据服务器),文件和文件属性(meta data)都保存到存储服务器上。Storage server直接利用OS的文件系统调用管理文件。</p> <p>Storage server(后简称storage)以组(卷,group或volume)为单位组织,一个group内包含多台storage机器,数据互为备份,存储空间以group内容量最小的storage为准,所以建议group内的多个storage尽量配置相同,以免造成存储空间的浪费。</p> <p>以group为单位组织存储能方便的进行应用隔离、负载均衡、副本数定制(group内storage server数量即为该group的副本数),比如将不同应用数据存到不同的group就能隔离应用数据,同时还可根据应用的访问特性来将应用分配到不同的group来做负载均衡;缺点是group的容量受单机存储容量的限制,同时当group内有机器坏掉时,数据恢复只能依赖group内地其他机器,使得恢复时间会很长。</p> <p>group内每个storage的存储依赖于本地文件系统,storage可配置多个数据存储目录,比如有10块磁盘,分别挂载在 <code>/data/disk1-/data/disk10</code>,则可将这10个目录都配置为storage的数据存储目录。</p> <p>storage接受到写文件请求时,会根据配置好的规则(后面会介绍),选择其中一个存储目录来存储文件。为了避免单个目录下的文件数太多,在storage第一次启动时,会在每个数据存储目录里创建2级子目录,每级256个,总共65536个文件,新写的文件会以hash的方式被路由到其中某个子目录下,然后将文件数据直接作为一个本地文件存储到该目录中。</p> <p><strong>client</strong>:客户端,作为业务请求的发起方,通过专有接口,使用TCP/IP协议与跟踪器服务器或存储节点进行数据交互。FastDFS向使用者提供基本文件访问接口,比如upload、download、append、delete等,以客户端库的方式提供给用户使用。</p> <p>另外两个概念:</p> <p><strong>group</strong> :组, 也可称为卷。 同组内服务器上的文件是完全相同的 ,同一组内的storage server之间是对等的, 文件上传、 删除等操作可以在任意一台storage server上进行 。</p> <p><strong>meta data</strong> :文件相关属性,键值对( Key Value Pair) 方式,如:width=1024,heigth=768 。</p> <p><img alt="1" class="img-thumbnail" src="/resources/assist/images/blog/8bcde557864841f88b181d6ec4a4ebd4.png" /></p> <p>Tracker相当于FastDFS的大脑,不论是上传还是下载都是通过tracker来分配资源;客户端一般可以使用ngnix等静态服务器来调用或者做一部分的缓存;存储服务器内部分为卷(或者叫做组),卷于卷之间是平行的关系,可以根据资源的使用情况随时增加,卷内服务器文件相互同步备份,以达到容灾的目的。</p> <h3>上传机制</h3> <p>首先客户端请求Tracker服务获取到存储服务器的ip地址和端口,然后客户端根据返回的IP地址和端口号请求上传文件,存储服务器接收到请求后生产文件,并且将文件内容写入磁盘并返回给客户端file_id、路径信息、文件名等信息,客户端保存相关信息上传完毕。</p> <p><img alt="2" class="img-thumbnail" src="/resources/assist/images/blog/622336b2401b41308372ec762d517c71.png" /></p> <p>内部机制如下:</p> <p><strong>1、选择tracker server</strong></p> <p>当集群中不止一个tracker server时,由于tracker之间是完全对等的关系,客户端在upload文件时可以任意选择一个trakcer。 选择存储的group 当tracker接收到upload file的请求时,会为该文件分配一个可以存储该文件的group,支持如下选择group的规则:</p> <ul> <li> <p>1、Round robin,所有的group间轮询</p> </li> <li> <p>2、Specified group,指定某一个确定的group</p> </li> <li> <p>3、Load balance,剩余存储空间多多group优先</p> </li> </ul> <p><strong>2、选择storage server</strong></p> <p>当选定group后,tracker会在group内选择一个storage server给客户端,支持如下选择storage的规则:</p> <ul> <li> <p>1、Round robin,在group内的所有storage间轮询</p> </li> <li> <p>2、First server ordered by ip,按ip排序</p> </li> <li> <p>3、First server ordered by priority,按优先级排序(优先级在storage上配置)</p> </li> </ul> <p><strong>3、选择storage path</strong></p> <p>当分配好storage server后,客户端将向storage发送写文件请求,storage将会为文件分配一个数据存储目录,支持如下规则:</p> <ul> <li> <p>1、Round robin,多个存储目录间轮询</p> </li> <li> <p>2、剩余存储空间最多的优先</p> </li> </ul> <p><strong>4、生成Fileid</strong></p> <p>选定存储目录之后,storage会为文件生一个Fileid,由storage server ip、文件创建时间、文件大小、文件crc32和一个随机数拼接而成,然后将这个二进制串进行base64编码,转换为可打印的字符串。 选择两级目录 当选定存储目录之后,storage会为文件分配一个fileid,每个存储目录下有两级256*256的子目录,storage会按文件fileid进行两次hash(猜测),路由到其中一个子目录,然后将文件以fileid为文件名存储到该子目录下。</p> <p><strong>5、生成文件名</strong></p> <p>当文件存储到某个子目录后,即认为该文件存储成功,接下来会为该文件生成一个文件名,文件名由group、存储目录、两级子目录、fileid、文件后缀名(由客户端指定,主要用于区分文件类型)拼接而成。</p> <h3>下载机制</h3> <p>客户端带上文件名信息请求Tracker服务获取到存储服务器的ip地址和端口,然后客户端根据返回的IP地址和端口号请求下载文件,存储服务器接收到请求后返回文件给客户端。</p> <p><img alt="3" class="img-thumbnail" src="/resources/assist/images/blog/d7ee87a725024e76b6080bc76b4d5031.png" /></p> <p>跟upload file一样,在download file时客户端可以选择任意tracker server。tracker发送download请求给某个tracker,必须带上文件名信息,tracke从文件名中解析出文件的group、大小、创建时间等信息,然后为该请求选择一个storage用来服务读请求。由于group内的文件同步时在后台异步进行的,所以有可能出现在读到时候,文件还没有同步到某些storage server上,为了尽量避免访问到这样的storage,tracker按照如下规则选择group内可读的storage。</p> <ul> <li> <p>1、该文件上传到的源头storage - 源头storage只要存活着,肯定包含这个文件,源头的地址被编码在文件名中。</p> </li> <li> <p>2、文件创建时间戳==storage被同步到的时间戳 且(当前时间-文件创建时间戳) > 文件同步最大时间(如5分钟) - 文件创建后,认为经过最大同步时间后,肯定已经同步到其他storage了。</p> </li> <li> <p>3、文件创建时间戳 < storage被同步到的时间戳。 - 同步时间戳之前的文件确定已经同步了</p> </li> <li> <p>4、(当前时间-文件创建时间戳) > 同步延迟阀值(如一天)。 - 经过同步延迟阈值时间,认为文件肯定已经同步了。</p> </li> </ul> <h3>同步时间管理</h3> <p>当一个文件上传成功后,客户端马上发起对该文件下载请求(或删除请求)时,tracker是如何选定一个适用的存储服务器呢? 其实每个存储服务器都需要定时将自身的信息上报给tracker,这些信息就包括了本地同步时间(即,同步到的最新文件的时间戳)。而tracker根据各个存储服务器的上报情况,就能够知道刚刚上传的文件,在该存储组中是否已完成了同步。同步信息上报如下图:</p> <p><img alt="4" class="img-thumbnail" src="/resources/assist/images/blog/a12c788adede4a28b8e567c88dc7fbc5.png" /></p> <p>写文件时,客户端将文件写至group内一个storage server即认为写文件成功,storage server写完文件后,会由后台线程将文件同步至同group内其他的storage server。</p> <p>每个storage写文件后,同时会写一份binlog,binlog里不包含文件数据,只包含文件名等元信息,这份binlog用于后台同步,storage会记录向group内其他storage同步的进度,以便重启后能接上次的进度继续同步;进度以时间戳的方式进行记录,所以最好能保证集群内所有server的时钟保持同步。</p> <p>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生成一个同步时间戳。</p> <h3>精巧的文件ID-FID</h3> <p>说到下载就不得不提文件索引(又称:FID)的精巧设计了。文件索引结构如下图,是客户端上传文件后存储服务器返回给客户端,用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。</p> <p><img alt="5" class="img-thumbnail" src="/resources/assist/images/blog/4e66f104b1e1430292b2db37a071d885.jpg" /></p> <ul> <li> <p>组名:文件上传后所在的存储组名称,在文件上传成功后有存储服务器返回,需要客户端自行保存。</p> </li> <li> <p>虚拟磁盘路径:存储服务器配置的虚拟路径,与磁盘选项store_path*对应。</p> </li> <li> <p>数据两级目录:存储服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。</p> </li> <li> <p>文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器IP地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。</p> </li> </ul> <p><strong>快速定位文件</strong></p> <p>知道FastDFS FID的组成后,我们来看看FastDFS是如何通过这个精巧的FID定位到需要访问的文件。</p> <ul> <li> <p>1、通过组名tracker能够很快的定位到客户端需要访问的存储服务器组,并将选择合适的存储服务器提供客户端访问;</p> </li> <li> <p>2、存储服务器根据“文件存储虚拟磁盘路径”和“数据文件两级目录”可以很快定位到文件所在目录,并根据文件名找到客户端需要访问的文件。</p> </li> </ul> <p><img alt="6" class="img-thumbnail" src="/resources/assist/images/blog/f1febebc58614ab4b71e2ce0dc2e5091.png" /></p> <p>如何搭建FastDFS?参考我博客的这篇文章 <strong>(FastDFS 集群 安装 配置:http://www.ityouknow.com/fastdfs/2017/10/10/cluster-building-fastdfs.html)</strong>,下图为某用户搭建的架构示意图</p> <p><img alt="7" class="img-thumbnail" src="/resources/assist/images/blog/ef960a1f4c574e6f9790895b3974c933.png" /></p> <blockquote> <p>文中图片均来源于网络</p> </blockquote> <br />  
  • FastDFS安装配置,fastDFS分布式文件系统(一)

    FastDFS分布式文件系统的下载和安装配置,FastDFS,FastDFS安装配置,分布式文件系统<h2><span style="font-family:宋体">什么是FastDFS?</span></h2>   FastDFS的开发者是这样介绍的:<br />   FastDFS is an open source high performance distributed file system (DFS). It's major functions include: file storing, file syncing and file accessing, and design for high capacity and load balance.<br />   FastDFS是一个开源高性能分布式文件系统(DFS)。它的主要功能包括:文件存储、文件同步和文件访问,以及高容量和负载平衡的设计。 <h2><br /> <span style="font-family:宋体">一、获取需要的安装文件</span></h2> <h3>1.1<span style="font-family:宋体">获取安装</span>FastDFS<span style="font-family:宋体">依赖的公共函数库</span></h3> <p><span style="font-family:宋体">下载地址</span>: <a href="https://github.com/happyfish100/libfastcommon/archive/V1.0.35.tar.gz" rel="external nofollow" target="_blank">https://github.com/happyfish100/libfastcommon/archive/V1.0.35.tar.gz</a></p> <p><span style="font-family:宋体">系统中执行命令</span>:</p> <p><strong>[root@localhost fastdfs-libs]# wget https://github.com/happyfish100/libfastcommon/archive/V1.0.35.tar.gz</strong></p> <p>--2017-07-20 09:03:34--  https://github.com/happyfish100/libfastcommon/archive/V1.0.35.tar.gz</p> <p>Resolving github.com... 192.30.255.113, 192.30.255.112</p> <p>Connecting to github.com|192.30.255.113|:443... connected.</p> <p>HTTP request sent, awaiting response... 302 Found</p> <p>Location: https://codeload.github.com/happyfish100/libfastcommon/tar.gz/V1.0.35 [following]</p> <p>--2017-07-20 09:03:36--  https://codeload.github.com/happyfish100/libfastcommon/tar.gz/V1.0.35</p> <p>Resolving codeload.github.com... 192.30.255.121, 192.30.255.120</p> <p>Connecting to codeload.github.com|192.30.255.121|:443... connected.</p> <p>HTTP request sent, awaiting response... 200 OK</p> <p>Length: 434734 (425K) [application/x-gzip]</p> <p>Saving to: “V1.0.35”</p> <p> </p> <p>100%[====================================================================================================================================================================================================================================>] 434,734     47.0K/s   in 9.7s   </p> <p>2017-07-20 09:03:47 (44.0 KB/s) - “V1.0.35” saved [434734/434734]</p> <p>[root@localhost fastdfs-libs]#</p> <h3>1.2<span style="font-family:宋体">获取</span>FastDFS<span style="font-family:宋体">安装文件</span></h3> <p><span style="font-family:宋体">下载地址</span>: https://github.com/happyfish100/fastdfs/archive/V5.10.tar.gz</p> <p><span style="font-family:宋体">下载命令</span>:</p> <p><strong>[root@localhost fastdfs-libs]# wget https://github.com/happyfish100/fastdfs/archive/V5.10.tar.gz</strong></p> <p>--2017-07-20 09:06:41--  https://github.com/happyfish100/fastdfs/archive/V5.10.tar.gz</p> <p>Resolving github.com... 192.30.255.112, 192.30.255.113</p> <p>Connecting to github.com|192.30.255.112|:443... connected.</p> <p>HTTP request sent, awaiting response... 302 Found</p> <p>Location: https://codeload.github.com/happyfish100/fastdfs/tar.gz/V5.10 [following]</p> <p>--2017-07-20 09:06:43--  https://codeload.github.com/happyfish100/fastdfs/tar.gz/V5.10</p> <p>Resolving codeload.github.com... 192.30.255.120, 192.30.255.121</p> <p>Connecting to codeload.github.com|192.30.255.120|:443... connected.</p> <p>HTTP request sent, awaiting response... 200 OK</p> <p>Length: unspecified [application/x-gzip]</p> <p>Saving to: “V5.10”</p> <p> </p> <p>    [                                        <=>                                                                                                                                                                                          ] 336,589     15.5K/s   in 15s    </p> <p> </p> <p>2017-07-20 09:06:59 (22.5 KB/s) - “V5.10” saved [336589]</p> <p>[root@localhost fastdfs-libs]#</p> <h3>1.3<span style="font-family:宋体">解压安装包</span></h3> <p>1.3.1<span style="font-family:宋体">查看刚才下载的两个文件</span></p> <p>[root@localhost fastdfs-libs]# ll</p> <p>total 760</p> <p>-rw-r--r--. 1 root root 434734 Jul 20 09:03 V1.0.35</p> <p>-rw-r--r--. 1 root root 336589 Jul 20 09:06 V5.10</p> <p>[root@localhost fastdfs-libs]#</p> <p><span style="font-family:宋体">说明</span>:<span style="font-family:宋体">由于</span>wget<span style="font-family:宋体">工具去掉了文件格式</span>,<span style="font-family:宋体">所以文件名就成上面这个样子了。其中</span></p> <p>V1.0.35<span style="font-family:宋体">是</span>libfastcommon<span style="font-family:宋体">公共函数库安装包</span></p> <p>V5.10<span style="font-family:宋体">是</span>FastDFS<span style="font-family:宋体">安装包</span></p> <p><span style="font-family:宋体">解压命令</span>:</p> <p><strong>[root@localhost fastdfs-libs]# tar -xzvf V5.10</strong></p> <p><strong>[root@localhost fastdfs-libs]# tar -xzvf V1.0.35</strong></p> <p><span style="font-family:宋体">解压后目录中的文件</span>:</p> <p>[root@localhost fastdfs-libs]# ll</p> <p>total 768</p> <p><strong>drwxrwxr-x. 10 root root   4096 Apr  5 18:38 fastdfs-5.10</strong></p> <p><strong>drwxrwxr-x.  5 root root   4096 Jul 20 09:23 libfastcommon-1.0.35</strong></p> <p>-rw-r--r--.  1 root root 434734 Jul 20 09:03 V1.0.35</p> <p>-rw-r--r--.  1 root root 336589 Jul 20 09:06 V5.10</p> <p>[root@localhost fastdfs-libs]#</p> <h2><span style="font-family:宋体">二、安装</span>libfastcommon<span style="font-family:宋体">函数库</span></h2> <p><em><strong><span style="font-family:宋体">注意</span>:FastDFS<span style="font-family:宋体">版本</span>5.05<span style="font-family:宋体">以前不需要安装</span>,<span style="font-family:宋体">如果版本低于</span>5.05<span style="font-family:宋体">则忽略该步骤</span></strong></em></p> <p><span style="font-family:宋体">切换至</span>libfastcommon<span style="font-family:宋体">目录内</span></p> <p><span style="font-family:宋体">命令</span>:</p> <p>[root@localhost fastdfs-libs]# ll</p> <p>total 764</p> <p>drwxrwxr-x. 5 root root   4096 Mar  2 18:47 libfastcommon-1.0.35</p> <p>-rw-r--r--. 1 root root 434734 Jul 20 09:03 V1.0.35</p> <p>-rw-r--r--. 1 root root 336589 Jul 20 09:06 V5.10</p> <p><strong>[root@localhost fastdfs-libs]# cd libfastcommon-1.0.35/</strong></p> <p>[root@localhost libfastcommon-1.0.35]#</p> <p><span style="font-family:宋体">编译文件</span></p> <p><span style="font-family:宋体">命令</span></p> <p><strong>[root@localhost libfastcommon-1.0.35]# ./make.sh</strong></p> <p>o fast_task_queue.lo fast_timer.lo process_ctrl.lo fast_mblock.lo connection_pool.lo fast_mpool.lo fast_allocator.lo fast_buffer.lo multi_skiplist.lo flat_skiplist.lo system_info.lo fast_blocked_queue.lo id_generator.lo char_converter.lo char_convert_loader.lo -lm -lpthread</p> <p>ar rcs libfastcommon.a hash.o chain.o shared_func.o ini_file_reader.o logger.o sockopt.o base64.o sched_thread.o http_func.o md5.o pthread_func.o local_ip_func.o avl_tree.o ioevent.o ioevent_loop.o fast_task_queue.o fast_timer.o process_ctrl.o fast_mblock.o connection_pool.o fast_mpool.o fast_allocator.o fast_buffer.o multi_skiplist.o flat_skiplist.o system_info.o fast_blocked_queue.o id_generator.o char_converter.o char_convert_loader.o</p> <p>[root@localhost libfastcommon-1.0.35]#</p> <p><span style="font-family:宋体">注意</span>,<span style="font-family:宋体">这里的并不是通用的系统</span>make<span style="font-family:宋体">命令</span>,<span style="font-family:宋体">而是执行目录里面的</span>make.sh<span style="font-family:宋体">脚本</span></p> <p><span style="font-family:宋体">安装文件</span></p> <p><span style="font-family:宋体">命令</span>:</p> <p><strong>[root@localhost libfastcommon-1.0.35]# ./make.sh install</strong></p> <p>mkdir -p /usr/lib64</p> <p>mkdir -p /usr/lib</p> <p>install -m 755 libfastcommon.so /usr/lib64</p> <p>install -m 755 libfastcommon.so /usr/lib</p> <p>mkdir -p /usr/include/fastcommon</p> <p>install -m 644 common_define.h hash.h chain.h logger.h base64.h shared_func.h pthread_func.h ini_file_reader.h _os_define.h sockopt.h sched_thread.h http_func.h md5.h local_ip_func.h avl_tree.h ioevent.h ioevent_loop.h fast_task_queue.h fast_timer.h process_ctrl.h fast_mblock.h connection_pool.h fast_mpool.h fast_allocator.h fast_buffer.h skiplist.h multi_skiplist.h flat_skiplist.h skiplist_common.h system_info.h fast_blocked_queue.h php7_ext_wrapper.h id_generator.h char_converter.h char_convert_loader.h /usr/include/fastcommon</p> <p>[root@localhost libfastcommon-1.0.35]#</p> <p><span style="font-family:宋体">如果安装提示缺少</span>gcc<span style="font-family:宋体">编译器,使用</span>yum<span style="font-family:宋体">安装</span>,</p> <p><span style="font-family:宋体">命令</span>:</p> <p><strong><span style="font-family:"Courier New""><span style="color:#333333">yum -y install gcc-c</span></span></strong><strong><span style="font-family:"Courier New""><span style="color:black">++</span></span></strong></p> <h2><span style="font-family:宋体">三、安装</span>FastDFS</h2> <p><span style="font-family:宋体">切换至</span>FastDFS<span style="font-family:宋体">安装文件目录</span></p> <p><span style="font-family:宋体">命令</span>:</p> <p><strong>[root@localhost fastdfs-libs]# cd fastdfs-5.10/</strong></p> <p><strong>[root@localhost fastdfs-5.10]# </strong></p> <h3>3.1<span style="font-family:宋体">编译</span>FastDFS</h3> <p><span style="font-family:宋体">命令</span>:</p> <p><strong>[root@localhost fastdfs-5.10]# ./make.sh</strong></p> <h3>3.2<span style="font-family:宋体">安装</span>FastDFS</h3> <p><span style="font-family:宋体">命令</span>:</p> <p><strong>[root@localhost fastdfs-5.10]# ./make.sh install</strong></p> <p>mkdir -p /usr/bin</p> <p>mkdir -p /etc/fdfs</p> <p>cp -f fdfs_trackerd /usr/bin</p> <p>if [ ! -f /etc/fdfs/tracker.conf.sample ]; then cp -f ../conf/tracker.conf /etc/fdfs/tracker.conf.sample; fi</p> <p>if [ ! -f /etc/fdfs/storage_ids.conf.sample ]; then cp -f ../conf/storage_ids.conf /etc/fdfs/storage_ids.conf.sample; fi</p> <p>mkdir -p /usr/bin</p> <p>mkdir -p /etc/fdfs</p> <p>cp -f fdfs_storaged  /usr/bin</p> <p>if [ ! -f /etc/fdfs/storage.conf.sample ]; then cp -f ../conf/storage.conf /etc/fdfs/storage.conf.sample; fi</p> <p>mkdir -p /usr/bin</p> <p>mkdir -p /etc/fdfs</p> <p>mkdir -p /usr/lib64</p> <p>mkdir -p /usr/lib</p> <p>cp -f fdfs_monitor fdfs_test fdfs_test1 fdfs_crc32 fdfs_upload_file fdfs_download_file fdfs_delete_file fdfs_file_info fdfs_appender_test fdfs_appender_test1 fdfs_append_file fdfs_upload_appender /usr/bin</p> <p>if [ 0 -eq 1 ]; then cp -f libfdfsclient.a /usr/lib64; cp -f libfdfsclient.a /usr/lib/;fi</p> <p>if [ 1 -eq 1 ]; then cp -f libfdfsclient.so /usr/lib64; cp -f libfdfsclient.so /usr/lib/;fi</p> <p>mkdir -p /usr/include/fastdfs</p> <p>cp -f ../common/fdfs_define.h ../common/fdfs_global.h ../common/mime_file_parser.h ../common/fdfs_http_shared.h ../tracker/tracker_types.h ../tracker/tracker_proto.h ../tracker/fdfs_shared_func.h ../storage/trunk_mgr/trunk_shared.h tracker_client.h storage_client.h storage_client1.h client_func.h client_global.h fdfs_client.h /usr/include/fastdfs</p> <p>if [ ! -f /etc/fdfs/client.conf.sample ]; then cp -f ../conf/client.conf /etc/fdfs/client.conf.sample; fi</p> <p><img alt="1" class="img-thumbnail" src="/resources/assist/images/blog/073c74afb6a846de851922001fd0ce8f.png" /></p> <p><span style="font-family:宋体">上方没有报错则安装成功。</span></p> <p> </p> <h3>3.3sample</h3> <p><span style="font-family:宋体">切换至目录</span>/etc/fast/<span style="font-family:宋体">查看</span></p> <p><span style="font-family:宋体">命令</span>:</p> <p>[root@localhost fastdfs-5.10]# cd /etc/fdfs</p> <p>[root@localhost fdfs]# ll</p> <p>total 24</p> <p>-rw-r--r--. 1 root root 1461 Jul 20 09:29 client.conf.sample</p> <p>-rw-r--r--. 1 root root 7927 Jul 20 09:29 storage.conf.sample</p> <p>-rw-r--r--. 1 root root  105 Jul 20 09:29 storage_ids.conf.sample</p> <p>-rw-r--r--. 1 root root 7389 Jul 20 09:29 tracker.conf.sample</p> <p>[root@localhost fdfs]#</p> <p> </p> <p><span style="background-color:white"><span style="font-family:宋体"><span style="color:#555555">如上,安装成功后就会生成如上的</span></span></span><span style="background-color:white"><span style="font-family:"微软雅黑",sans-serif"><span style="color:#555555">4</span></span></span><span style="background-color:white"><span style="font-family:宋体"><span style="color:#555555">个</span></span></span><span style="background-color:white"><span style="font-family:"微软雅黑",sans-serif"><span style="color:#555555">.sample</span></span></span><span style="background-color:white"><span style="font-family:宋体"><span style="color:#555555">文件(示例配置文件),我们再分别拷贝出</span></span></span><span style="background-color:white"><span style="font-family:"微软雅黑",sans-serif"><span style="color:#555555">4</span></span></span><span style="background-color:white"><span style="font-family:宋体"><span style="color:#555555">个后面用的正式的配置文件:</span></span></span></p> <p>[root@localhost fdfs]# cp client.conf.sample client.conf</p> <p>[root@localhost fdfs]# cp storage.conf.sample storage.conf</p> <p>[root@localhost fdfs]# cp tracker.conf.sample tracker.conf</p> <p>[root@localhost fdfs]# ll</p> <p>total 44</p> <p>-rw-r--r--. 1 root root 1461 Jul 20 09:37 client.conf</p> <p>-rw-r--r--. 1 root root 1461 Jul 20 09:29 client.conf.sample</p> <p>-rw-r--r--. 1 root root 7927 Jul 20 09:37 storage.conf</p> <p>-rw-r--r--. 1 root root 7927 Jul 20 09:29 storage.conf.sample</p> <p>-rw-r--r--. 1 root root  105 Jul 20 09:29 storage_ids.conf.sample</p> <p>-rw-r--r--. 1 root root 7389 Jul 20 09:37 tracker.conf</p> <p>-rw-r--r--. 1 root root 7389 Jul 20 09:29 tracker.conf.sample</p> <p>[root@localhost fdfs]#</p> <p> </p> <p><span style="font-family:宋体">至此</span>FastDFS<span style="font-family:宋体">已经安装完毕</span>,<span style="font-family:宋体">接下来就是相关配置</span></p> <p> </p> <h2><span style="font-family:宋体">四、</span>FastDFS<span style="font-family:宋体">配置</span></h2> <h3>4.1<span style="font-family:宋体">配置</span>Tracker</h3> <p><span style="font-family:宋体">在配置</span>Tracker<span style="font-family:宋体">之前,首先需要创建</span>Tracker<span style="font-family:宋体">服务器的文件路径,即用于存储</span>Tracker<span style="font-family:宋体">的数据文件和日志文件等,我这里选择在</span>/opt<span style="font-family:宋体">目录下创建一个</span>fastdfs_tracker<span style="font-family:宋体">目录用于存放</span>Tracker<span style="font-family:宋体">服务器的相关文件:</span></p> <p><span style="font-family:宋体">命令</span>:</p> <p><strong>[root@localhost opt]# mkdir -p  /opt/fastdfs_tracker</strong></p> <p><span style="font-family:宋体">接下来就要重新编辑上一步准备好的</span>/etc/fdfs<span style="font-family:宋体">目录下的</span>tracker.conf<span style="font-family:宋体">配置文件,打开文件后依次做以下修改:</span></p> <ol> <li style="text-align:left"><span style="background-color:white"><span style="color:#555555"><span style="font-family:"微软雅黑",sans-serif">disabled=false #</span><span style="font-family:宋体">启用配置文件(默认启用)</span></span></span></li> <li style="text-align:left"><span style="background-color:white"><span style="color:#555555"><span style="font-family:"微软雅黑",sans-serif">port=22122 #</span><span style="font-family:宋体">设置</span><span style="font-family:"微软雅黑",sans-serif">tracker</span><span style="font-family:宋体">的端口号,通常采用</span><span style="font-family:"微软雅黑",sans-serif">22122</span><span style="font-family:宋体">这个默认端口</span></span></span></li> <li style="text-align:left"><span style="background-color:white"><span style="color:red"><span style="font-family:"微软雅黑",sans-serif">base_path=/opt/fastdfs_tracker #</span><span style="font-family:宋体">设置</span><span style="font-family:"微软雅黑",sans-serif">tracker</span><span style="font-family:宋体">的数据文件和日志目录</span></span></span></li> <li style="text-align:left"><span style="background-color:white"><span style="color:red"><span style="font-family:"微软雅黑",sans-serif">http.server_port=8800 #</span><span style="font-family:宋体">设置</span><span style="font-family:"微软雅黑",sans-serif">http</span><span style="font-family:宋体">端口号,默认为</span><span style="font-family:"微软雅黑",sans-serif">8080</span></span></span></li> </ol> <p><img alt="2" class="img-thumbnail" src="/resources/assist/images/blog/63d30e78854041649c34056f79717844.png" /></p> <p><span style="font-family:宋体">配置完成后就可以启动</span>Tracker<span style="font-family:宋体">服务器了,但首先依然要为启动脚本创建软引用,因为</span>fdfs_trackerd<span style="font-family:宋体">等命令在</span>/usr/local/bin<span style="font-family:宋体">中并没有,而是在</span>/usr/bin<span style="font-family:宋体">路径下:</span></p> <p>[root@localhost fdfs]# ln -s /usr/bin/fdfs_trackerd /usr/local/bin</p> <p>[root@localhost fdfs]# ln -s /usr/bin/stop.sh /usr/local/bin</p> <p>[root@localhost fdfs]# ln -s /usr/bin/restart.sh /usr/local/bin</p> <p>[root@localhost fdfs]#</p> <p><img alt="3" class="img-thumbnail" src="/resources/assist/images/blog/eb79dbf97c5f4f20b976391e6d56277e.png" /></p> <p><span style="font-family:宋体">最后通过命令启动</span>Tracker<span style="font-family:宋体">服务器:</span></p> <p><strong>[root@localhost fdfs]# service fdfs_trackerd start</strong></p> <p>Starting FastDFS tracker server:</p> <p>[root@localhost fdfs]# ps -ef|grep tracker</p> <p>root       8649      1  0 09:44 ?        00:00:00 /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf</p> <p>root       8657   2652  0 09:44 pts/0    00:00:00 grep tracker</p> <p>[root@localhost fdfs]#</p> <p><span style="font-family:宋体">如果启动命令执行成功,那么同时在刚才创建的</span>tracker<span style="font-family:宋体">文件目录</span>/opt/fastdfs_tracker<span style="font-family:宋体">中就可以看到启动后新生成的</span>data<span style="font-family:宋体">和</span>logs<span style="font-family:宋体">目录,</span>tracker<span style="font-family:宋体">服务的端口也应当被正常监听,最后再通过</span>netstat<span style="font-family:宋体">命令查看一下端口监听情况:</span></p> <p><img alt="4" class="img-thumbnail" src="/resources/assist/images/blog/938bbff4b35649f582476f2705a86c1a.png" /></p> <p>[root@localhost fdfs]# netstat -unltp|grep fdfs</p> <p>tcp        0      0 0.0.0.0:22122               0.0.0.0:*                   LISTEN      8649/fdfs_trackerd </p> <p>[root@localhost fdfs]#</p> <p><span style="font-family:宋体">可以看到</span>tracker<span style="font-family:宋体">服务运行的</span>22122<span style="font-family:宋体">端口正常被监听</span></p> <p><span style="font-family:宋体">确认</span>tracker<span style="font-family:宋体">正常启动后可以将</span>tracker<span style="font-family:宋体">设置为开机启动,打开</span>/etc/rc.d/rc.local<span style="font-family:宋体">并在其中加入以下配置</span></p> <p>service fdfs_trackerd start</p> <p><span style="font-family:宋体">如果重启后发现未能自动启动则通过命令</span>ll /etc/rc.d/rc.local<span style="font-family:宋体">检查一下</span>rc.local<span style="font-family:宋体">是否具备可执行权限,若是无可执行权限则通过</span>chmod +x /etc/rc.d/rc.local<span style="font-family:宋体">进行授权</span></p> <p><span style="background-color:white"><span style="font-family:"微软雅黑",sans-serif"><span style="color:#555555">Tracker</span></span></span><span style="background-color:white"><span style="font-family:宋体"><span style="color:#555555">至此就配置好了</span></span></span></p> <h3>4.2<span style="font-family:宋体">配置</span>Storage</h3> <p><span style="font-family:宋体">同理,步骤基本与配置</span>Tracker<span style="font-family:宋体">一致,首先是创建</span>Storage<span style="font-family:宋体">服务器的文件目录,需要注意的是同</span>Tracker<span style="font-family:宋体">相比我多建了一个目录,因为</span>Storage<span style="font-family:宋体">还需要一个文件存储路径,用于存放接收的文件:</span></p> <p><span style="font-family:宋体">创建目录命令</span>:</p> <p>[root@localhost fdfs]# mkdir /opt/fastdfs_storage</p> <p>[root@localhost fdfs]# mkdir /opt/fastdfs_storage_data</p> <p><span style="font-family:宋体">接下来修改</span>/etc/fdfs<span style="font-family:宋体">目录下的</span>storage.conf<span style="font-family:宋体">配置文件,打开文件后依次做以下修改:</span></p> <ol> <li style="text-align:left"><span style="background-color:white"><span style="color:#555555"><span style="font-family:"微软雅黑",sans-serif">disabled=false #</span><span style="font-family:宋体">启用配置文件(默认启用)</span></span></span></li> <li style="text-align:left"><span style="background-color:white"><span style="color:#555555"><span style="font-family:"微软雅黑",sans-serif">group_name=group1 #</span><span style="font-family:宋体">组名,根据实际情况修改</span></span></span></li> <li style="text-align:left"><span style="background-color:white"><span style="color:#555555"><span style="font-family:"微软雅黑",sans-serif">port=23000 #</span><span style="font-family:宋体">设置</span><span style="font-family:"微软雅黑",sans-serif">storage</span><span style="font-family:宋体">的端口号,默认是</span><span style="font-family:"微软雅黑",sans-serif">23000</span><span style="font-family:宋体">,同一个组的</span><span style="font-family:"微软雅黑",sans-serif">storage</span><span style="font-family:宋体">端口号必须一致</span></span></span></li> <li style="text-align:left"><span style="background-color:white"><span style="color:#555555"><span style="font-family:"微软雅黑",sans-serif"><span style="color:red">base_path=/opt/fastdfs_storage #</span></span><span style="font-family:宋体"><span style="color:red">设置</span></span><span style="font-family:"微软雅黑",sans-serif"><span style="color:red">storage</span></span><span style="font-family:宋体"><span style="color:red">数据文件和日志目录</span></span></span></span></li> <li style="text-align:left"><span style="background-color:white"><span style="color:#555555"><span style="font-family:"微软雅黑",sans-serif">store_path_count=1 #</span><span style="font-family:宋体">存储路径个数,需要和</span><span style="font-family:"微软雅黑",sans-serif">store_path</span><span style="font-family:宋体">个数匹配</span></span></span></li> <li style="text-align:left"><span style="background-color:white"><span style="color:#555555"><span style="font-family:"微软雅黑",sans-serif"><span style="color:red">store_path0=/opt/fastdfs_storage_data #</span></span><span style="font-family:宋体"><span style="color:red">实际文件存储路径</span></span></span></span></li> <li style="text-align:left"><span style="background-color:white"><span style="color:red"><span style="font-family:"微软雅黑",sans-serif">tracker_server=</span> <span style="font-family:"微软雅黑",sans-serif">192.168.8.202:22122 #tracker </span><span style="font-family:宋体">服务器的</span><span style="font-family:"微软雅黑",sans-serif"> IP</span><span style="font-family:宋体">地址和端口号,如果是单机搭建,</span><span style="font-family:"微软雅黑",sans-serif">IP</span><span style="font-family:宋体">不要写</span><span style="font-family:"微软雅黑",sans-serif">127.0.0.1</span><span style="font-family:宋体">,否则启动不成功(此处的</span><span style="font-family:"微软雅黑",sans-serif">ip</span><span style="font-family:宋体">是我的</span><span style="font-family:"微软雅黑",sans-serif">CentOS</span><span style="font-family:宋体">虚拟机</span><span style="font-family:"微软雅黑",sans-serif">ip</span><span style="font-family:宋体">)</span></span></span></li> <li style="text-align:left"><span style="background-color:white"><span style="color:#555555"><span style="font-family:"微软雅黑",sans-serif">http.server_port=8888 #</span><span style="font-family:宋体">设置</span><span style="font-family:"微软雅黑",sans-serif"> http </span><span style="font-family:宋体">端口号</span></span></span></li> </ol> <p><span style="font-family:宋体">上述红色代表修改部分</span></p> <p><span style="font-family:宋体">配置完成后同样要为</span>Storage<span style="font-family:宋体">服务器的启动脚本设置软引用:</span></p> <p><strong>[root@localhost fdfs]# ln -s /usr/bin/fdfs_storaged /usr/local/bin</strong></p> <p>[root@localhost fdfs]#</p> <p><span style="font-family:宋体">接下来就可以启动</span>Storage<span style="font-family:宋体">服务了:</span></p> <p><strong>[root@localhost fdfs]# service fdfs_storaged start</strong></p> <p>Starting FastDFS storage server:</p> <p>[root@localhost fdfs]# ps -ef |grep storage</p> <p>root       8677      1 26 09:58 ?        00:00:03 /usr/bin/fdfs_storaged /etc/fdfs/storage.conf</p> <p>root       8687   2652  0 09:58 pts/0    00:00:00 grep storage</p> <p>[root@localhost fdfs]#</p> <p><span style="font-family:宋体">同理,如果启动成功,</span>/opt/fastdfs_storage<span style="font-family:宋体">中就可以看到启动后新生成的</span>data<span style="font-family:宋体">和</span>logs<span style="font-family:宋体">目录,端口</span>23000<span style="font-family:宋体">也应被正常监听,还有一点就是文件存储路径下会生成多级存储目录,那么接下来看看是否启动成功了:</span></p> <p><img alt="5" class="img-thumbnail" src="/resources/assist/images/blog/3e9f629acff74f97967e2936ca4758f4.png" /></p> <p><strong><span style="font-family:宋体">端口监听情况</span>:</strong></p> <p><strong>[root@localhost data]# netstat -unltp|grep fdfs</strong></p> <p>tcp        0      0 0.0.0.0:23000               0.0.0.0:*                   LISTEN      8677/fdfs_storaged </p> <p>tcp        0      0 0.0.0.0:22122               0.0.0.0:*                   LISTEN      8649/fdfs_trackerd </p> <p>[root@localhost data]#</p> <p><img alt="6" class="img-thumbnail" src="/resources/assist/images/blog/7ad6a2679bbc4accacf7f3706abc96cc.png" /></p> <p><span style="font-family:宋体">如上图,可以看到此时已经正常监听</span>tracker<span style="font-family:宋体">的</span>22122<span style="font-family:宋体">端口和</span>storage<span style="font-family:宋体">的</span>23000<span style="font-family:宋体">端口,至此</span>storage<span style="font-family:宋体">服务器就已经配置完成,确定了</span>storage<span style="font-family:宋体">服务器启动成功后,还有一项工作就是看看</span>storage<span style="font-family:宋体">服务器是否已经登记到</span> tracker<span style="font-family:宋体">服务器(也可以理解为</span>tracker<span style="font-family:宋体">与</span>storage<span style="font-family:宋体">是否整合成功),运行以下命令:</span></p> <p>[root@localhost data]# /usr/bin/fdfs_monitor /etc/fdfs/storage.conf</p> <p>[2017-07-20 10:03:17] 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: 0</p> <p> </p> <p>server_count=1, server_index=0</p> <p> </p> <p>tracker server is 192.168.8.202:22122</p> <p> </p> <p>group count: 1</p> <p> </p> <p>Group 1:</p> <p>group name = group1</p> <p>disk total space = 26917 MB</p> <p>disk free space = 21646 MB</p> <p>trunk free space = 0 MB</p> <p>storage server count = 1</p> <p>active server count = 1</p> <p>storage server port = 23000</p> <p>storage HTTP port = 8888</p> <p>store path count = 1</p> <p>subdir count per path = 256</p> <p>current write server index = 0</p> <p>current trunk file id = 0</p> <p> </p> <p>         Storage 1:</p> <p>                   id = 192.168.8.202</p> <p>                   ip_addr = 192.168.8.202  ACTIVE</p> <p>                   http domain =</p> <p>                   version = 5.10</p> <p>                   join time = 2017-07-20 09:58:37</p> <p>                   up time = 2017-07-20 09:58:37</p> <p>                   total storage = 26917 MB</p> <p>                   free storage = 21646 MB</p> <p>                   upload priority = 10</p> <p>                   store_path_count = 1</p> <p>                   subdir_count_per_path = 256</p> <p>                   storage_port = 23000</p> <p>                   storage_http_port = 8888</p> <p>                   current_write_path = 0</p> <p>                   source storage id =</p> <p>                   if_trunk_server = 0</p> <p>                   connection.alloc_count = 256</p> <p>                   connection.current_count = 0</p> <p>                   connection.max_count = 0</p> <p>                   total_upload_count = 0</p> <p>                   success_upload_count = 0</p> <p>                   total_append_count = 0</p> <p>                   success_append_count = 0</p> <p>                   total_modify_count = 0</p> <p>                   success_modify_count = 0</p> <p>                   total_truncate_count = 0</p> <p>                   success_truncate_count = 0</p> <p>                   total_set_meta_count = 0</p> <p>                   success_set_meta_count = 0</p> <p>                   total_delete_count = 0</p> <p>                   success_delete_count = 0</p> <p>                   total_download_count = 0</p> <p>                   success_download_count = 0</p> <p>                   total_get_meta_count = 0</p> <p>                   success_get_meta_count = 0</p> <p>                   total_create_link_count = 0</p> <p>                   success_create_link_count = 0</p> <p>                   total_delete_link_count = 0</p> <p>                   success_delete_link_count = 0</p> <p>                   total_upload_bytes = 0</p> <p>                   success_upload_bytes = 0</p> <p>                   total_append_bytes = 0</p> <p>                  success_append_bytes = 0</p> <p>                   total_modify_bytes = 0</p> <p>                   success_modify_bytes = 0</p> <p>                   stotal_download_bytes = 0</p> <p>                   success_download_bytes = 0</p> <p>                   total_sync_in_bytes = 0</p> <p>                   success_sync_in_bytes = 0</p> <p>                   total_sync_out_bytes = 0</p> <p>                   success_sync_out_bytes = 0</p> <p>                   total_file_open_count = 0</p> <p>                   success_file_open_count = 0</p> <p>                   total_file_read_count = 0</p> <p>                   success_file_read_count = 0</p> <p>                   total_file_write_count = 0</p> <p>                   success_file_write_count = 0</p> <p>                   last_heart_beat_time = 2017-07-20 10:03:15</p> <p>                   last_source_update = 1969-12-31 16:00:00</p> <p>                   last_sync_update = 1969-12-31 16:00:00</p> <p>                   last_synced_timestamp = 1969-12-31 16:00:00</p> <p>[root@localhost data]#</p> <p><img alt="7" class="img-thumbnail" src="/resources/assist/images/blog/4281900037f449cdb9470fa1b401c62c.png" /></p> <p> </p> <p><span style="font-family:宋体">如上所示,看到</span>192.168.8.202 ACTIVE <span style="font-family:宋体">字样即可说明</span>storage<span style="font-family:宋体">服务器已经成功登记到了</span>tracker<span style="font-family:宋体">服务器,同理别忘了添加开机启动,打开</span>/etc/rc.d/rc.local<span style="font-family:宋体">并将如下配置追加到文件中:</span></p> <p>service fdfs_storaged start</p> <p><span style="font-family:宋体">至此我们就已经完成了</span>fastdfs<span style="font-family:宋体">的全部配置,此时也就可以用客户端工具进行文件上传下载的测试了。</span></p> <h2><span style="font-family:宋体">五、测试</span>FastDFS</h2> <h3>5.1<span style="font-family:宋体">配置</span> FastDFSLinux<span style="font-family:宋体">上的客户端</span></h3> <p><span style="font-family:宋体">编辑配置文件</span>/etc/fdfs/client.conf</p> <p><span style="font-family:宋体">命令</span>:</p> <p><strong>[root@localhost fdfs]# vi /etc/fdfs/client.conf</strong></p> <p><span style="font-family:宋体">修改内容</span>:</p> <ol> <li><span style="color:red">base_path=/opt/fastdfs_tracker #tracker</span><span style="font-family:宋体"><span style="color:red">服务器文件路径</span></span></li> <li><span style="color:red">tracker_server=192.168.8.202:22122 #tracker</span><span style="font-family:宋体"><span style="color:red">服务器</span></span><span style="color:red">IP</span><span style="font-family:宋体"><span style="color:red">地址和端口号</span></span></li> <li><span style="color:red">http.tracker_server_port=8800 # tracker </span><span style="font-family:宋体"><span style="color:red">服务器的</span></span><span style="color:red"> http </span><span style="font-family:宋体"><span style="color:red">端口号,必须和</span></span><span style="color:red">tracker</span><span style="font-family:宋体"><span style="color:red">的设置对应起来</span></span></li> </ol> <p><img alt="8" class="img-thumbnail" src="/resources/assist/images/blog/48d6df422eb1411986ae7a21d46b9564.png" /></p> <p><img alt="9" class="img-thumbnail" src="/resources/assist/images/blog/9384fd77734a49af8067b868cdee6f95.png" /></p> <p><img alt="10" class="img-thumbnail" src="/resources/assist/images/blog/cf3b4f6c3ebe46968724be99a03df04a.png" /></p> <p> </p> <h3>5.2<span style="font-family:宋体">测试</span></h3> <p><span style="font-family:宋体">配置完成后就可以模拟文件上传了,先给</span>/opt<span style="font-family:宋体">目录下放一张图片(使用</span>ftp<span style="font-family:宋体">类似工具上传到服务器)</span></p> <p><img alt="11" class="img-thumbnail" src="/resources/assist/images/blog/6f6aa6411d5c4a4fa9ea3c8974be950b.png" /></p> <p>[root@localhost fdfs]# ll /opt/</p> <p>total 24</p> <p>drwxr-xr-x. 4 root root 4096 Jul 20 09:58 fastdfs_storage</p> <p>drwxr-xr-x. 3 root root 4096 Jul 20 09:58 fastdfs_storage_data</p> <p>drwxr-xr-x. 4 root root 4096 Jul 20 09:44 fastdfs_tracker</p> <p>drwxr-xr-x. 2 root root 4096 Nov 22  2013 rh</p> <p>-rw-r--r--. 1 root root 7014 Jul 21 02:20 tx.jpg</p> <p>[root@localhost fdfs]#</p> <p> </p> <p><span style="font-family:宋体">通过执行客户端上传命令尝试上传:</span></p> <p><strong>[root@localhost fdfs]# /usr/bin/fdfs_upload_file  /etc/fdfs/client.conf /opt/tx.jpg </strong></p> <p>group1/M00/00/00/wKgIyllxx82AeS-3AAAbZoAXENE710.jpg</p> <p>[root@localhost fdfs]#</p> <p><img alt="12" class="img-thumbnail" src="/resources/assist/images/blog/184ba258f47648b8831a28f6146f3bdd.png" /></p> <p><span style="font-family:宋体">返回信息解释说明:</span></p> <p><strong>group1                                                                    : </strong><strong><span style="font-family:宋体">组名</span></strong></p> <p><strong>MOO                                                                         : </strong><strong><span style="font-family:宋体">磁盘</span></strong></p> <p><strong>00/00                                                                        : </strong><strong><span style="font-family:宋体">目录(相对于</span>storage</strong><strong><span style="font-family:宋体">的</span>fastdfs_storage_data</strong><strong><span style="font-family:宋体">目录下)</span></strong></p> <p><strong>wKgIyllxx82AeS-3AAAbZoAXENE710.jpg     :</strong><strong><span style="font-family:宋体">文件名</span></strong></p> <p> </p> <p><span style="font-family:宋体">可以切换到</span>storeage<span style="font-family:宋体">的</span>data<span style="font-family:宋体">目录下查看</span></p> <p><span style="font-family:宋体">命令:</span></p> <p><strong>[root@localhost data]# cd /opt/fastdfs_storage_data/data/</strong></p> <p>[root@localhost data]# ls</p> <p>00  04  08  0C  10  14  18  1C  20  24  28  2C  30  34  38  3C  40  44  48  4C  50  54  58  5C  60  64  68  6C  70  74  78  7C  80  84  88  8C  90  94  98  9C  A0  A4  A8  AC  B0  B4  B8  BC  C0  C4  C8  CC  D0  D4  D8  DC  E0  E4  E8  EC  F0  F4  F8  FC</p> <p>01  05  09  0D  11  15  19  1D  21  25  29  2D  31  35  39  3D  41  45  49  4D  51  55  59  5D  61  65  69  6D  71  75  79  7D  81  85  89  8D  91  95  99  9D  A1  A5  A9  AD  B1  B5  B9  BD  C1  C5  C9  CD  D1  D5  D9  DD  E1  E5  E9  ED  F1  F5  F9  FD</p> <p>02  06  0A  0E  12  16  1A  1E  22  26  2A  2E  32  36  3A  3E  42  46  4A  4E  52  56  5A  5E  62  66  6A  6E  72  76  7A  7E  82  86  8A  8E  92  96  9A  9E  A2  A6  AA  AE  B2  B6  BA  BE  C2  C6  CA  CE  D2  D6  DA  DE  E2  E6  EA  EE  F2  F6  FA  FE</p> <p>03  07  0B  0F  13  17  1B  1F  23  27  2B  2F  33  37  3B  3F  43  47  4B  4F  53  57  5B  5F  63  67  6B  6F  73  77  7B  7F  83  87  8B  8F  93  97  9B  9F  A3  A7  AB  AF  B3  B7  BB  BF  C3  C7  CB  CF  D3  D7  DB  DF  E3  E7  EB  EF  F3  F7  FB  FF</p> <p><strong>[root@localhost data]# cd 00/00/</strong></p> <p>[root@localhost 00]# ls</p> <p><strong>wKgIyllxx82AeS-3AAAbZoAXENE710.jpg</strong></p> <p>[root@localhost 00]#</p> <p><img alt="13" class="img-thumbnail" src="/resources/assist/images/blog/f5ef521b94ed46ae926170c8d4b8d118.png" /></p> <p><span style="font-family:宋体">测试完成。</span></p> <p>相关推荐:<br /> <a rel="external nofollow" href="http://www.leftso.com/blog/244.html" target="_blank">FastDFS卸载(二)</a><br /> <a rel="external nofollow" href="http://www.leftso.com/blog/245.html" target="_blank">FastDFS配置集群(三)</a></p>
  • FastDFS5.10卸载,centos卸载FastDFS(二)

    FastDFS5.10卸载,在centos6系统中卸载FastDFS(二),FastDFS卸载,FastDFS<p><span style="font-family:宋体">由于</span>FastDFS<span style="font-family:宋体">软件并未提供直接的一键卸载,所以卸载有点麻烦</span>,FastDFS<span style="font-family:宋体">卸载步骤如下</span>:</p> <p><strong><em><span style="font-family:宋体"><span style="color:red">注意</span></span><span style="color:red">:</span></em></strong><strong><em><span style="font-family:宋体"><span style="color:red">所有删除操作请务必先查看是否存在文件文件是否为</span></span><span style="color:red">FastDFS</span></em></strong><strong><em><span style="font-family:宋体"><span style="color:red">相关</span></span></em></strong></p> <h3 style="margin-left:18.0pt">1) <span style="font-family:宋体">停止服务</span></h3> <p><strong>[root@bogon fdfs]#service fdfs_trackerd stop</strong></p> <p><strong>[root@bogon fdfs]#service fdfs_storaged stop</strong></p> <p style="margin-left:18.0pt"> </p> <h3 style="margin-left:18.0pt">2) <span style="font-family:宋体">通过</span>storage.conf<span style="font-family:宋体">找到</span>base_path<span style="font-family:宋体">和</span>store_path<span style="font-family:宋体">然后删除</span></h3> <p><strong>[root@bogon fdfs]# cat /etc/fdfs/storage.conf |grep base_path</strong></p> <p>base_path=<strong><span style="color:red">/opt/fastdfs_storage</span></strong></p> <p># store_path#, based 0, if store_path0 not exists, it's value is base_path</p> <p>[root@bogon fdfs]#</p> <p>[root@bogon fdfs]# cat /etc/fdfs/storage.conf |grep store_path</p> <p>store_path_count=1</p> <p># store_path#, based 0, if store_path0 not exists, it's value is base_path</p> <p>store_path0=<strong><span style="color:red">/opt/fastdfs_storage_data</span></strong></p> <p>#store_path1=/home/yuqing/fastdfs2</p> <p># store_path (disk), value can be 1 to 256, default value is 256</p> <p>[root@bogon fdfs]#</p> <p> </p> <p><span style="font-family:宋体">删除上面标红的路径</span>,</p> <p><strong><span style="font-family:宋体"><span style="color:red">注意</span></span><span style="color:red">,</span></strong><strong><span style="font-family:宋体"><span style="color:red">如果有未备份的文件,请先备份再删除</span></span></strong></p> <p> </p> <p><strong>[root@bogon fdfs]# rm -rf /opt/fastdfs_storage</strong></p> <p><strong>[root@bogon fdfs]# rm -rf /opt/fastdfs_storage_data</strong></p> <p> </p> <h3 style="margin-left:18.0pt">3) <span style="font-family:宋体">通过</span>tracker.conf<span style="font-family:宋体">找到</span>base_path<span style="font-family:宋体">然后删除</span></h3> <p><strong>[root@bogon fdfs]# cat /etc/fdfs/tracker.conf |grep base_path</strong></p> <p>base_path=/opt/fastdfs_tracker</p> <p><strong>[root@bogon fdfs]#rm –rf /opt/fastdfs_tracker</strong></p> <h3 style="margin-left:18.0pt">4) <span style="font-family:宋体">删除配置文件目录</span></h3> <p><strong>[root@bogon ~]# pwd</strong></p> <p>/root</p> <p><strong>[root@bogon ~]# rm -rf /etc/fdfs/</strong></p> <p>[root@bogon ~]#</p> <p> </p> <h3 style="margin-left:18.0pt">5) <span style="font-family:宋体">删除链接文件</span></h3> <p><span style="font-family:宋体">删除</span>tracker<span style="font-family:宋体">的链接文件</span></p> <p><strong>#rm –rf /usr/local/bin/fdfs_trackerd</strong></p> <p><strong>#rm –rf /usr/local/bin/stop.sh</strong></p> <p><strong>#rm –rf /usr/local/bin/restart.sh</strong></p> <p><span style="font-family:宋体">删除</span>storage<span style="font-family:宋体">的链接文件</span></p> <p><strong>#rm –rf /usr/local/bin/fdfs_storaged</strong></p> <h3 style="margin-left:18.0pt">6) <span style="font-family:宋体">删除</span>/usr/bin<span style="font-family:宋体">目录下</span>FastDFS<span style="font-family:宋体">的可执行文件</span></h3> <p><span style="font-family:宋体">首先通过</span>ls<span style="font-family:宋体">命令查看文件</span>,<span style="font-family:宋体">然后删除</span></p> <p>[root@bogon ~]# ll /usr/bin/fdfs_*</p> <p>-rwxr-xr-x. 1 root root 262099 Jul 27 03:01 /usr/bin/fdfs_appender_test</p> <p>-rwxr-xr-x. 1 root root 261796 Jul 27 03:01 /usr/bin/fdfs_appender_test1</p> <p>-rwxr-xr-x. 1 root root 252140 Jul 27 03:01 /usr/bin/fdfs_append_file</p> <p>-rwxr-xr-x. 1 root root 251274 Jul 27 03:01 /usr/bin/fdfs_crc32</p> <p>-rwxr-xr-x. 1 root root 252223 Jul 27 03:01 /usr/bin/fdfs_delete_file</p> <p>-rwxr-xr-x. 1 root root 253062 Jul 27 03:01 /usr/bin/fdfs_download_file</p> <p>-rwxr-xr-x. 1 root root 252756 Jul 27 03:01 /usr/bin/fdfs_file_info</p> <p>-rwxr-xr-x. 1 root root 265444 Jul 27 03:01 /usr/bin/fdfs_monitor</p> <p>-rwxr-xr-x. 1 root root 878573 Jul 27 03:01 /usr/bin/fdfs_storaged</p> <p>-rwxr-xr-x. 1 root root 268499 Jul 27 03:01 /usr/bin/fdfs_test</p> <p>-rwxr-xr-x. 1 root root 267636 Jul 27 03:01 /usr/bin/fdfs_test1</p> <p>-rwxr-xr-x. 1 root root 374059 Jul 27 03:01 /usr/bin/fdfs_trackerd</p> <p>-rwxr-xr-x. 1 root root 253166 Jul 27 03:01 /usr/bin/fdfs_upload_appender</p> <p>-rwxr-xr-x. 1 root root 254296 Jul 27 03:01 /usr/bin/fdfs_upload_file</p> <p>[root@bogon ~]#</p> <p><img alt="1" class="img-thumbnail" src="/resources/assist/images/blog/38e2e3de419d45fa9a13c58310f08be4.png" /></p> <p><span style="font-family:宋体">看到全是</span>FastDFS<span style="font-family:宋体">的文件,删除</span>:</p> <p><strong>[root@bogon ~]# rm -rf  /usr/bin/fdfs_*</strong></p> <p>[root@bogon ~]#</p> <h3 style="margin-left:18.0pt">7) <span style="font-family:宋体">删除</span>/usr/include/<span style="font-family:宋体">目录下</span>FastDFS<span style="font-family:宋体">相关的</span>shell<span style="font-family:宋体">脚本</span></h3> <p><span style="font-family:宋体">首先查看文件</span>:</p> <p>[root@bogon ~]# ll /usr/include/fastdfs/*</p> <p>-rw-r--r--. 1 root root  3752 Jul 27 03:01 /usr/include/fastdfs/client_func.h</p> <p>-rw-r--r--. 1 root root   794 Jul 27 03:01 /usr/include/fastdfs/client_global.h</p> <p>-rw-r--r--. 1 root root   531 Jul 27 03:01 /usr/include/fastdfs/fdfs_client.h</p> <p>-rw-r--r--. 1 root root   946 Jul 27 03:01 /usr/include/fastdfs/fdfs_define.h</p> <p>-rw-r--r--. 1 root root  1005 Jul 27 03:01 /usr/include/fastdfs/fdfs_global.h</p> <p>-rw-r--r--. 1 root root  3117 Jul 27 03:01 /usr/include/fastdfs/fdfs_http_shared.h</p> <p>-rw-r--r--. 1 root root  2699 Jul 27 03:01 /usr/include/fastdfs/fdfs_shared_func.h</p> <p>-rw-r--r--. 1 root root   990 Jul 27 03:01 /usr/include/fastdfs/mime_file_parser.h</p> <p>-rw-r--r--. 1 root root 20104 Jul 27 03:01 /usr/include/fastdfs/storage_client1.h</p> <p>-rw-r--r--. 1 root root 21755 Jul 27 03:01 /usr/include/fastdfs/storage_client.h</p> <p>-rw-r--r--. 1 root root 11555 Jul 27 03:01 /usr/include/fastdfs/tracker_client.h</p> <p>-rw-r--r--. 1 root root 11983 Jul 27 03:01 /usr/include/fastdfs/tracker_proto.h</p> <p>-rw-r--r--. 1 root root 14349 Jul 27 03:01 /usr/include/fastdfs/tracker_types.h</p> <p>-rw-r--r--. 1 root root  6945 Jul 27 03:01 /usr/include/fastdfs/trunk_shared.h</p> <p>[root@bogon ~]#</p> <p><img alt="2" class="img-thumbnail" src="/resources/assist/images/blog/325929d5f44c4e49bc2b4d35495e05cd.png" /></p> <p><span style="font-family:宋体">删除</span>:</p> <p><strong>[root@bogon ~]# rm -rf /usr/include/fastdfs/</strong></p> <p>[root@bogon ~]#</p> <p> </p> <h3 style="margin-left:18.0pt">8) <span style="font-family:宋体">删除</span>/usr/lib64<span style="font-family:宋体">目录下的库文件</span></h3> <p><span style="font-family:宋体">查看</span>:</p> <p>[root@bogon lib64]# ll libfdfsclient*</p> <p>-rwxr-xr-x. 1 root root 255538 Jul 27 03:01 libfdfsclient.so</p> <p><span style="font-family:宋体">删除</span>:</p> <p><strong>[root@bogon lib64]# rm -rf libfdfsclient*</strong></p> <p> </p> <h3 style="margin-left:18.0pt">9) <span style="font-family:宋体">删除</span>/usr/lib/<span style="font-family:宋体">目录下的库</span></h3> <p><span style="font-family:宋体">查看</span>:</p> <p>[root@bogon lib64]# ll libfdfsclient*</p> <p>-rwxr-xr-x. 1 root root 255538 Jul 27 03:01 libfdfsclient.so</p> <p><span style="font-family:宋体">删除</span>:</p> <p><strong>[root@bogon lib64]# rm -rf libfdfsclient*</strong></p> <p> </p> <p> </p> <h3><span style="font-family:宋体">经过以上步骤</span>,FastDFS<span style="font-family:宋体">已经卸载。</span></h3>
  • FastDFS配置文件详细说明

    FastDFS配置文件详细说明<h2>一、tracker.conf</h2> <pre> <code># 这个配置文件是否无效,false表示有效 # is this config file disabled # false for enabled # true for disabled disabled=false # 是否绑定IP # bind_addr= 后面为绑定的IP地址 (常用于服务器有多个IP但只希望一个IP提供服务)。如果不填则表示所有的(一般不填就OK) # bind an address of this host # empty for bind all addresses of this host bind_addr= # 提供服务的端口 # the tracker server port port=22122 # 连接超时时间,针对socket套接字函数connect # connect timeout in seconds # default value is 30s connect_timeout=30 # tracker server的网络超时,单位为秒。发送或接收数据时,如果在超时时间后还不能发送或接收数据,则本次网络通信失败 # network timeout in seconds # default value is 30s network_timeout=60 # the base path to store data and log files base_path=/home/yuqing/fastdfs # 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的同步) # 系统提供服务时的最大连接数。对于V1.x,因一个连接由一个线程服务,也就是工作线程数。 # 对于V2.x,最大连接数和工作线程数没有任何关系 # max concurrent connections this server supported max_connections=256 # work thread count, should <= max_connections # default value is 4 # since V2.00 # V2.0引入的这个参数,工作线程数,通常设置为CPU数 work_threads=4 # 上传组(卷) 的方式 0:轮询方式 1: 指定组 2: 平衡负载(选择最大剩余空间的组(卷)上传) # 这里如果在应用层指定了上传到一个固定组,那么这个参数被绕过 # the method of selecting group to upload files # 0: round robin # 1: specify group # 2: load balance, select the max free space group to upload file store_lookup=2 # 当上一个参数设定为1 时 (store_lookup=1,即指定组名时),必须设置本参数为系统中存在的一个组名。如果选择其他的上传方式,这个参数就没有效了 # which group to upload file # when store_lookup set to 1, must set store_group to the group name store_group=group2 # 选择哪个storage server 进行上传操作(一个文件被上传后,这个storage server就相当于这个文件的storage server源,会对同组的storage server推送这个文件达到同步效果) # 0: 轮询方式 # 1: 根据ip 地址进行排序选择第一个服务器(IP地址最小者) # 2: 根据优先级进行排序(上传优先级由storage server来设置,参数名为upload_priority) # which storage server to upload file # 0: round robin (default) # 1: the first server order by ip address # 2: the first server order by priority (the minimal) store_server=0 # 选择storage server 中的哪个目录进行上传。storage server可以有多个存放文件的base path(可以理解为多个磁盘)。 # 0: 轮流方式,多个目录依次存放文件 # 2: 选择剩余空间最大的目录存放文件(注意:剩余磁盘空间是动态的,因此存储到的目录或磁盘可能也是变化的) # which path(means disk or mount point) of the storage server to upload file # 0: round robin # 2: load balance, select the max free space path to upload file store_path=0 # 选择哪个 storage server 作为下载服务器 # 0: 轮询方式,可以下载当前文件的任一storage server # 1: 哪个为源storage server 就用哪一个 (前面说过了这个storage server源 是怎样产生的) 就是之前上传到哪个storage server服务器就是哪个了 # which storage server to download file # 0: round robin (default) # 1: the source storage server which the current file uploaded to download_server=0 # storage server 上保留的空间,保证系统或其他应用需求空间。可以用绝对值或者百分比(V4开始支持百分比方式)。 #(指出 如果同组的服务器的硬盘大小一样,以最小的为准,也就是只要同组中有一台服务器达到这个标准了,这个标准就生效,原因就是因为他们进行备份) # reserved storage space for system or other applications. # if the free(available) space of any stoarge server in # a group <= reserved_storage_space, # no file can be uploaded to this group. # bytes unit can be one of follows: ### G or g for gigabyte(GB) ### M or m for megabyte(MB) ### K or k for kilobyte(KB) ### no unit for byte(B) ### XX.XX% as ratio such as reserved_storage_space = 10% reserved_storage_space = 10% # 选择日志级别 #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level=info # 操作系统运行FastDFS的用户组 (不填 就是当前用户组,哪个启动进程就是哪个) #unix group name to run this program, #not set (empty) means run by the group of current user run_by_group= # 操作系统运行FastDFS的用户 (不填 就是当前用户,哪个启动进程就是哪个) #unix username to run this program, #not set (empty) means run by current user run_by_user= # 可以连接到此 tracker server 的ip范围(对所有类型的连接都有影响,包括客户端,storage server) # allow_hosts can ocur more than once, host can be hostname or ip address, # "*" means match all ip addresses, can use range like this: 10.0.1.[1-15,20] or # host[01-08,20-25].domain.com, for example: # allow_hosts=10.0.1.[1-15,20] # allow_hosts=host[01-08,20-25].domain.com allow_hosts=* # 同步或刷新日志信息到硬盘的时间间隔,单位为秒 # 注意:tracker server 的日志不是时时写硬盘的,而是先写内存 # sync log buff to disk every interval seconds # default value is 10 seconds sync_log_buff_interval = 10 # 检测 storage server 存活的时间隔,单位为秒。 # storage server定期向tracker server 发心跳,如果tracker server在一个check_active_interval内还没有收到storage server的一次心跳,那边将认为该storage server已经下线。所以本参数值必须大于storage server配置的心跳时间间隔。通常配置为storage server心跳时间间隔的2倍或3倍 # check storage server alive interval seconds check_active_interval = 120 # 线程栈的大小。FastDFS server端采用了线程方式。tracker server线程栈不应小于64KB # 线程栈越大,一个线程占用的系统资源就越多。如果要启动更多的线程(V1.x对应的参数为max_connections, V2.0为work_threads),可以适当降低本参数值 # thread stack size, should >= 64KB # default value is 64KB thread_stack_size = 64KB # 这个参数控制当storage server IP地址改变时,集群是否自动调整。注:只有在storage server进程重启时才完成自动调整 # auto adjust when the ip address of the storage server changed # default value is true storage_ip_changed_auto_adjust = true # V2.0引入的参数。存储服务器之间同步文件的最大延迟时间,缺省为1天。根据实际情况进行调整 # 注:本参数并不影响文件同步过程。本参数仅在下载文件时,判断文件是否已经被同步完成的一个阀值(经验值) # storage sync file max delay seconds # default value is 86400 seconds (one day) # since V2.00 storage_sync_file_max_delay = 86400 # V2.0引入的参数。存储服务器同步一个文件需要消耗的最大时间,缺省为300s,即5分钟。 # 注:本参数并不影响文件同步过程。本参数仅在下载文件时,作为判断当前文件是否被同步完成的一个阀值(经验值) # the max time of storage sync a file # default value is 300 seconds # since V2.00 storage_sync_file_max_time = 300 # V3.0引入的参数。是否使用小文件合并存储特性,缺省是关闭的 # if use a trunk file to store several small files # default value is false # since V3.00 use_trunk_file = false # V3.0引入的参数。 # trunk file分配的最小字节数。比如文件只有16个字节,系统也会分配slot_min_size个字节 # the min slot size, should <= 4KB # default value is 256 bytes # since V3.00 slot_min_size = 256 # V3.0引入的参数。 # 只有文件大小<=这个参数值的文件,才会合并存储。如果一个文件的大小大于这个参数值,将直接保存到一个文件中(即不采用合并存储方式)。 # the max slot size, should > slot_min_size # store the upload file to trunk file when it's size <= this value # default value is 16MB # since V3.00 slot_max_size = 16MB # V3.0引入的参数。 # 合并存储的trunk file大小,至少4MB,缺省值是64MB。不建议设置得过大 # the trunk file size, should >= 4MB # default value is 64MB # since V3.00 trunk_file_size = 64MB # 是否提前创建trunk file。只有当这个参数为true,下面3个以trunk_create_file_打头的参数才有效 # if create trunk file advancely # default value is false # since V3.06 trunk_create_file_advance = false # 提前创建trunk file的起始时间点(基准时间),02:00表示第一次创建的时间点是凌晨2点 # the time base to create trunk file # the time format: HH:MM # default value is 02:00 # since V3.06 trunk_create_file_time_base = 02:00 # 创建trunk file的时间间隔,单位为秒。如果每天只提前创建一次,则设置为86400 # the interval of create trunk file, unit: second # default value is 38400 (one day) # since V3.06 trunk_create_file_interval = 86400 # 提前创建trunk file时,需要达到的空闲trunk大小 # 比如本参数为20G,而当前空闲trunk为4GB,那么只需要创建16GB的trunk file即可 # the threshold to create trunk file # when the free trunk file size less than the threshold, will create # the trunk files # default value is 0 # since V3.06 trunk_create_file_space_threshold = 20G # trunk初始化时,是否检查可用空间是否被占用 # if check trunk space occupying when loading trunk free spaces # the occupied spaces will be ignored # default value is false # since V3.09 # NOTICE: set this parameter to true will slow the loading of trunk spaces # when startup. you should set this parameter to true when neccessary. trunk_init_check_occupying = false # 是否无条件从trunk binlog中加载trunk可用空间信息 # FastDFS缺省是从快照文件storage_trunk.dat中加载trunk可用空间, # 该文件的第一行记录的是trunk binlog的offset,然后从binlog的offset开始加载 # if ignore storage_trunk.dat, reload from trunk binlog # default value is false # since V3.10 # set to true once for version upgrade when your version less than V3.10 trunk_init_reload_from_binlog = false # 是否使用server ID作为storage server标识 # if use storage ID instead of IP address # default value is false # since V4.00 use_storage_id = false # use_storage_id 设置为true,才需要设置本参数 # 在文件中设置组名、server ID和对应的IP地址,参见源码目录下的配置示例:conf/storage_ids.conf # specify storage ids filename, can use relative or absolute path # since V4.00 storage_ids_filename = storage_ids.conf #文件名中的id类型,有ip和id两种,只有当use_storage_id设置为true时该参数才有效 # id type of the storage server in the filename, values are: ## ip: the ip address of the storage server ## id: the server id of the storage server # this paramter is valid only when use_storage_id set to true # default value is ip # since V4.03 id_type_in_filename = ip # 存储从文件是否采用symbol link(符号链接)方式 # 如果设置为true,一个从文件将占用两个文件:原始文件及指向它的符号链接 # if store slave file use symbol link # default value is false # since V4.01 store_slave_file_use_link = false # 是否定期轮转error log,目前仅支持一天轮转一次 # if rotate the error log every day # default value is false # since V4.02 rotate_error_log = false # error log定期轮转的时间点,只有当rotate_error_log设置为true时有效 # rotate error log time base, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # default value is 00:00 # since V4.02 error_log_rotate_time=00:00 # error log按大小轮转 # 设置为0表示不按文件大小轮转,否则当error log达到该大小,就会轮转到新文件中 # rotate error log when the log file exceeds this size # 0 means never rotates log file by log file size # default value is 0 # since V4.02 rotate_error_log_size = 0 # 是否使用连接池 # if use connection pool # default value is false # since V4.05 use_connection_pool = false # 如果一个连接的空闲时间超过这个值将会被自动关闭 # connections whose the idle time exceeds this time will be closed # unit: second # default value is 3600 # since V4.05 connection_pool_max_idle_time = 3600 # 用于提供http服务的端口 # HTTP port on this tracker server http.server_port=8080 # 检查http server是否还在工作的时间间隔,如果该值小于0则永远不检查 # check storage HTTP server alive interval seconds # <= 0 for never check # default value is 30 http.check_alive_interval=30 # 检查http server是否存活的类型,有tcp和http两种 # tcp方式只有http端口被连接 # http方式检查必须返回状态值200 # check storage HTTP server alive type, values are: # tcp : connect to the storge server with HTTP port only, # do not request and get response # http: storage check alive url must return http status 200 # default value is tcp http.check_alive_type=tcp # check storage HTTP server alive uri/url # NOTE: storage embed HTTP server support uri: /status.html http.check_alive_uri=/status.html</code></pre> <h2>二、storage.conf</h2> <pre> <code># 这个配置文件是否无效,false表示有效 # is this config file disabled # false for enabled # true for disabled disabled=false # 指定 此 storage server 所在组(卷) # the name of the group this storage server belongs to group_name=group1 # 是否绑定IP # bind_addr= 后面为绑定的IP地址 (常用于服务器有多个IP但只希望一个IP提供服务)。如果不填则表示所有的(一般不填就OK) # bind an address of this host # empty for bind all addresses of this host bind_addr= # bind_addr通常是针对server的。当指定bind_addr时,本参数才有效。 # 本storage server作为client连接其他服务器(如tracker server、其他storage server),是否绑定bind_addr。 # if bind an address of this host when connect to other servers # (this storage server as a client) # true for binding the address configed by above parameter: "bind_addr" # false for binding any address of this host client_bind=true # storage server服务端口 # the storage server port port=23000 # 连接超时时间,针对socket套接字函数connect # connect timeout in seconds # default value is 30s connect_timeout=30 # storage server 网络超时时间,单位为秒。发送或接收数据时,如果在超时时间后还不能发送或接收数据,则本次网络通信失败。 # network timeout in seconds # default value is 30s network_timeout=60 # 心跳间隔时间,单位为秒 (这里是指主动向tracker server 发送心跳) # heart beat interval in seconds heart_beat_interval=30 # storage server向tracker server报告磁盘剩余空间的时间间隔,单位为秒 # disk usage report interval in seconds stat_report_interval=60 # base_path 目录地址,根目录必须存在 子目录会自动生成 (注 :这里不是上传的文件存放的地址,之前是的,在某个版本后更改了) # the base path to store data and log files base_path=/home/yuqing/fastdfs # 系统提供服务时的最大连接数 # max concurrent connections the server supported # default value is 256 # more max_connections means more memory will be used max_connections=256 # V2.0引入本参数。设置队列结点的buffer大小。工作队列消耗的内存大小 = buff_size * max_connections # 设置得大一些,系统整体性能会有所提升。 # 消耗的内存请不要超过系统物理内存大小。另外,对于32位系统,请注意使用到的内存不要超过3GB # the buff size to recv / send data # this parameter must more than 8KB # default value is 64KB # since V2.00 buff_size = 256KB # 工作线程的数量,工作线程用于处理网络IO,应当小于max_connections的值 # work thread count, should <= max_connections # work thread deal network io # default value is 4 # since V2.00 work_threads=4 # V2.0引入本参数。磁盘IO读写是否分离,缺省是分离的 # if disk read / write separated ## false for mixed read and write ## true for separated read and write # default value is true # since V2.00 disk_rw_separated = true # V2.0引入本参数。针对单个存储路径的读线程数,缺省值为1。 # 读写分离时,系统中的读线程数 = disk_reader_threads * store_path_count # 读写混合时,系统中的读写线程数 = (disk_reader_threads + disk_writer_threads) * store_path_count # disk reader thread count per store base path # for mixed read / write, this parameter can be 0 # default value is 1 # since V2.00 disk_reader_threads = 1 # V2.0引入本参数。针对单个存储路径的写线程数,缺省值为1。 # 读写分离时,系统中的写线程数 = disk_writer_threads * store_path_count # 读写混合时,系统中的读写线程数 = (disk_reader_threads + disk_writer_threads) * store_path_count # disk writer thread count per store base path # for mixed read / write, this parameter can be 0 # default value is 1 # since V2.00 disk_writer_threads = 1 # 同步文件时,如果从binlog中没有读到要同步的文件,休眠N毫秒后重新读取。0表示不休眠,立即再次尝试读取。 # 出于CPU消耗考虑,不建议设置为0。如何希望同步尽可能快一些,可以将本参数设置得小一些,比如设置为10ms # when no entry to sync, try read binlog again after X milliseconds # must > 0, default value is 200ms sync_wait_msec=50 # 同步上一个文件后,再同步下一个文件的时间间隔,单位为毫秒,0表示不休眠,直接同步下一个文件。 # after sync a file, usleep milliseconds # 0 for sync successively (never call usleep) sync_interval=0 # 下面二个一起解释。允许系统同步的时间段 (默认是全天) 。一般用于避免高峰同步产生一些问题而设定,相信sa都会明白 # storage sync start time of a day, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 sync_start_time=00:00 # storage sync end time of a day, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 sync_end_time=23:59 # 同步完N个文件后,把storage的mark文件同步到磁盘 # 注:如果mark文件内容没有变化,则不会同步 # write to the mark file after sync N files # default value is 500 write_mark_file_freq=500 # 存放文件时storage server支持多个路径(例如磁盘)。这里配置存放文件的基路径数目,通常只配一个目录。 # path(disk or mount point) count, default value is 1 store_path_count=1 # 逐一配置store_path个路径,索引号基于0。注意配置方法后面有0,1,2 ......,需要配置0到store_path - 1。 # 如果不配置base_path0,那边它就和base_path对应的路径一样。 # store_path#, based 0, if store_path0 not exists, it's value is base_path # the paths must be exist store_path0=/home/yuqing/fastdfs #store_path1=/home/yuqing/fastdfs2 # FastDFS存储文件时,采用了两级目录。这里配置存放文件的目录个数 (系统的存储机制,大家看看文件存储的目录就知道了) # 如果本参数只为N(如:256),那么storage server在初次运行时,会自动创建 N * N 个存放文件的子目录。 # subdir_count * subdir_count directories will be auto created under each # store_path (disk), value can be 1 to 256, default value is 256 subdir_count_per_path=256 # tracker_server 的列表 要写端口的哦 (再次提醒是主动连接tracker_server ) # 有多个tracker server时,每个tracker server写一行 # tracker_server can ocur more than once, and tracker_server format is # "host:port", host can be hostname or ip address tracker_server=192.168.209.121:22122 # 日志级别 #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level=info # 操作系统运行FastDFS的用户组 (不填 就是当前用户组,哪个启动进程就是哪个) #unix group name to run this program, #not set (empty) means run by the group of current user run_by_group= # 操作系统运行FastDFS的用户 (不填 就是当前用户,哪个启动进程就是哪个) #unix username to run this program, #not set (empty) means run by current user run_by_user= # 允许连接本storage server的IP地址列表 (不包括自带HTTP服务的所有连接) # 可以配置多行,每行都会起作用 # allow_hosts can ocur more than once, host can be hostname or ip address, # "*" means match all ip addresses, can use range like this: 10.0.1.[1-15,20] or # host[01-08,20-25].domain.com, for example: # allow_hosts=10.0.1.[1-15,20] # allow_hosts=host[01-08,20-25].domain.com allow_hosts=* # 文件在data目录下分散存储策略。 # 0: 轮流存放,在一个目录下存储设置的文件数后(参数file_distribute_rotate_count中设置文件数),使用下一个目录进行存储。 # 1: 随机存储,根据文件名对应的hash code来分散存储。 # the mode of the files distributed to the data path # 0: round robin(default) # 1: random, distributted by hash code file_distribute_path_mode=0 # 当上面的参数file_distribute_path_mode配置为0(轮流存放方式)时,本参数有效。 # 当一个目录下的文件存放的文件数达到本参数值时,后续上传的文件存储到下一个目录中。 # valid when file_distribute_to_path is set to 0 (round robin), # when the written file count reaches this number, then rotate to next path # default value is 100 file_distribute_rotate_count=100 # 当写入大文件时,每写入N个字节,调用一次系统函数fsync将内容强行同步到硬盘。0表示从不调用fsync # call fsync to disk when write big file # 0: never call fsync # other: call fsync when written bytes >= this bytes # default value is 0 (never call fsync) fsync_after_written_bytes=0 # 同步或刷新日志信息到硬盘的时间间隔,单位为秒 # 注意:storage server 的日志信息不是时时写硬盘的,而是先写内存。 # sync log buff to disk every interval seconds # must > 0, default value is 10 seconds sync_log_buff_interval=10 # 同步binglog(更新操作日志)到硬盘的时间间隔,单位为秒 # 本参数会影响新上传文件同步延迟时间 # sync binlog buff / cache to disk every interval seconds # default value is 60 seconds sync_binlog_buff_interval=10 # 把storage的stat文件同步到磁盘的时间间隔,单位为秒。 # 注:如果stat文件内容没有变化,不会进行同步 # sync storage stat info to disk every interval seconds # default value is 300 seconds sync_stat_file_interval=300 # 线程栈的大小。FastDFS server端采用了线程方式。 # 对于V1.x,storage server线程栈不应小于512KB;对于V2.0,线程栈大于等于128KB即可。 # 线程栈越大,一个线程占用的系统资源就越多。 # 对于V1.x,如果要启动更多的线程(max_connections),可以适当降低本参数值。 # thread stack size, should >= 512KB # default value is 512KB thread_stack_size=512KB # 本storage server作为源服务器,上传文件的优先级,可以为负数。值越小,优先级越高。这里就和 tracker.conf 中store_server= 2时的配置相对应了 # the priority as a source server for uploading file. # the lower this value, the higher its uploading priority. # default value is 10 upload_priority=10 # 网卡别名前缀,就像Linux中的eth,可以使用ifconfig -a命令来查看 # 多个别名之间使用逗号分隔,如果不设置这个值表示自动的被系统类型设置 # the NIC alias prefix, such as eth in Linux, you can see it by ifconfig -a # multi aliases split by comma. empty value means auto set by OS type # default values is empty if_alias_prefix= # 是否检测上传文件已经存在。如果已经存在,则不存在文件内容,建立一个符号链接以节省磁盘空间。 # 这个应用要配合FastDHT 使用,所以打开前要先安装FastDHT # 1或yes 是检测,0或no 是不检测 # if check file duplicate, when set to true, use FastDHT to store file indexes # 1 or yes: need check # 0 or no: do not check # default value is 0 check_file_duplicate=0 # 文件去重时,文件内容的签名方式: # hash: 4个hash code # md5:MD5 # file signature method for check file duplicate ## hash: four 32 bits hash code ## md5: MD5 signature # default value is hash # since V4.01 file_signature_method=hash # 当上个参数设定为1 或 yes时 (true/on也是可以的) , 在FastDHT中的命名空间 # namespace for storing file indexes (key-value pairs) # this item must be set when check_file_duplicate is true / on key_namespace=FastDFS # 与FastDHT servers 的连接方式 (是否为持久连接) ,默认是0(短连接方式)。可以考虑使用长连接,这要看FastDHT server的连接数是否够用。 # set keep_alive to 1 to enable persistent connection with FastDHT servers # default value is 0 (short connection) keep_alive=0 # 下面是关于FastDHT servers 的设定 需要对FastDHT servers 有所了解,这里只说字面意思了 # 可以通过 #include filename 方式来加载 FastDHT servers 的配置,装上FastDHT就知道该如何配置啦。 # 同样要求 check_file_duplicate=1 时才有用,不然系统会忽略 # fdht_servers.conf 记载的是 FastDHT servers 列表 # you can use "#include filename" (not include double quotes) directive to # load FastDHT server list, when the filename is a relative path such as # pure filename, the base path is the base path of current/this config file. # must set FastDHT server list when check_file_duplicate is true / on # please see INSTALL of FastDHT for detail ##include /home/yuqing/fastdht/conf/fdht_servers.conf # 是否将文件操作记录到access log # if log to access log # default value is false # since V4.00 use_access_log = false # 是否定期轮转access log,目前仅支持一天轮转一次 # if rotate the access log every day # default value is false # since V4.00 rotate_access_log = false # access log定期轮转的时间点,只有当rotate_access_log设置为true时有效 # rotate access log time base, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # default value is 00:00 # since V4.00 access_log_rotate_time=00:00 # 是否定期轮转error log,目前仅支持一天轮转一次 # if rotate the error log every day # default value is false # since V4.02 rotate_error_log = false # error log定期轮转的时间点,只有当rotate_error_log设置为true时有效 # rotate error log time base, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # default value is 00:00 # since V4.02 error_log_rotate_time=00:00 # access log按文件大小轮转 # 设置为0表示不按文件大小轮转,否则当access log达到该大小,就会轮转到新文件中 # rotate access log when the log file exceeds this size # 0 means never rotates log file by log file size # default value is 0 # since V4.02 rotate_access_log_size = 0 # error log按文件大小轮转 # 设置为0表示不按文件大小轮转,否则当error log达到该大小,就会轮转到新文件中 # rotate error log when the log file exceeds this size # 0 means never rotates log file by log file size # default value is 0 # since V4.02 rotate_error_log_size = 0 # 文件同步的时候,是否忽略无效的binlog记录 # if skip the invalid record when sync file # default value is false # since V4.02 file_sync_skip_invalid_record=false # 是否使用连接池 # if use connection pool # default value is false # since V4.05 use_connection_pool = false # 如果一个连接的空闲时间超过这个值将会被自动关闭 # connections whose the idle time exceeds this time will be closed # unit: second # default value is 3600 # since V4.05 connection_pool_max_idle_time = 3600 # storage server上web server域名,通常仅针对单独部署的web server。这样URL中就可以通过域名方式来访问storage server上的文件了, # 这个参数为空就是IP地址的方式。 # use the ip address of this storage server if domain_name is empty, # else this domain name will ocur in the url redirected by the tracker server http.domain_name= # web server的端口 # the port of the web server on this storage server http.server_port=8888</code></pre> <h2>三、client.conf</h2> <pre> <code># 连接超时时间,针对socket套接字函数connect # connect timeout in seconds # default value is 30s connect_timeout=30 # client的网络超时,单位为秒。发送或接收数据时,如果在超时时间后还不能发送或接收数据,则本次网络通信失败 # network timeout in seconds # default value is 30s network_timeout=60 # 存储日志的根目录 # the base path to store log files base_path=/home/yuqing/fastdfs # tracker_server 的列表 要写端口 # tracker_server can ocur more than once, and tracker_server format is # "host:port", host can be hostname or ip address tracker_server=192.168.0.197:22122 # 日志的级别 #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level=info # 是否使用连接池 # if use connection pool # default value is false # since V4.05 use_connection_pool = false # 如果一个连接的空闲时间超过这个值将会被自动关闭 # connections whose the idle time exceeds this time will be closed # unit: second # default value is 3600 # since V4.05 connection_pool_max_idle_time = 3600 # 是否从FastDFS的tracker server加载参数 # if load FastDFS parameters from tracker server # since V4.05 # default value is false load_fdfs_parameters_from_tracker=false 是否使用storage ID 代替IP,只有当load_fdfs_parameters_from_tracker为false时才有效 # if use storage ID instead of IP address # same as tracker.conf # valid only when load_fdfs_parameters_from_tracker is false # default value is false # since V4.05 use_storage_id = false # 指定storage_ids的路径,可以使用绝对路径和相对路径,只有当load_fdfs_parameters_from_tracker为false时才有效 # specify storage ids filename, can use relative or absolute path # same as tracker.conf # valid only when load_fdfs_parameters_from_tracker is false # since V4.05 storage_ids_filename = storage_ids.conf #tracker server的http端口 #HTTP settings http.tracker_server_port=8080 #use "#include" directive to include HTTP other settiongs ##include http.conf</code></pre>
  • FastDFS集群配置(三)

    FastDFS集群配置(三)<h2 style="margin-left:21pt">集群总揽</h2> <h3 style="margin-left:21.0pt"><img alt="FastDFS集群总览" class="img-thumbnail" src="/resources/assist/images/blog/d8454cdb2657476ab547acafc8a78da7.png" /><br /> <br /> 1) <span style="font-family:宋体">双机集群图说明</span></h3> <p><img alt="集群图" class="img-thumbnail" src="/resources/assist/images/blog/d4e064476e814ee4ad21073beb33fd17.png" /></p> <h3 style="margin-left:21.0pt">2) <span style="font-family:宋体">配置说明</span></h3> <ol> <li><span style="font-family:宋体">修改</span>storage A<span style="font-family:宋体">的配置文件</span>,<span style="font-family:宋体">添加两个</span>tracker<span style="font-family:宋体">的指向和组(</span>group<span style="font-family:宋体">)信息</span></li> </ol> <p><img alt="1" class="img-thumbnail" src="/resources/assist/images/blog/f54075a109a04b85ac2573e4991b7336.png" /></p> <p><strong><em><span style="font-family:宋体">说明</span>:tracker</em></strong><strong><em><span style="font-family:宋体">的</span>ip</em></strong><strong><em><span style="font-family:宋体">和端口根据自身安装情况配置修改</span></em></strong></p> <p><span style="font-family:宋体">组名配置</span>:</p> <p><strong><span style="font-family:宋体">这里默认采用</span>group1,</strong><strong><span style="font-family:宋体">注意同一个组的</span>storage</strong><strong><span style="font-family:宋体">的组名必须保持一样</span></strong></p> <p><img alt="2" class="img-thumbnail" src="/resources/assist/images/blog/28617e614c1a41a08324ab93cdfeec36.png" /></p> <ol start="2"> <li><span style="font-family:宋体">重启</span>storage A<span style="font-family:宋体">服务</span></li> </ol> <p><span style="font-family:宋体">停止</span>:</p> <p><strong>#service fdfs_storaged stop</strong></p> <p><span style="font-family:宋体">启动</span>:</p> <p><strong>#service fdfs_storaged start</strong></p> <p> </p> <ol start="3"> <li><span style="font-family:宋体">修改</span>storage B<span style="font-family:宋体">配置</span>,<span style="font-family:宋体">同样添加两个</span>tarcker<span style="font-family:宋体">的配置</span></li> </ol> <p><img alt="3" class="img-thumbnail" src="/resources/assist/images/blog/9566ec49e26d4f4caa908564a94733a1.png" /></p> <p><span style="font-family:宋体">配置组名</span>:</p> <p><strong><span style="font-family:宋体">这里配置为</span>group1</strong><strong><span style="font-family:宋体">,与</span>storage A</strong><strong><span style="font-family:宋体">的组名一直</span>,</strong><strong><span style="font-family:宋体">注意同一个组的</span>storage</strong><strong><span style="font-family:宋体">组名必须一致</span></strong></p> <p><img alt="2" class="img-thumbnail" src="/resources/assist/images/blog/28617e614c1a41a08324ab93cdfeec36.png" /></p> <ol start="4"> <li><span style="font-family:宋体">重启</span>storage B<span style="font-family:宋体">服务</span></li> </ol> <p><span style="font-family:宋体">停止</span>:</p> <p><strong>#service fdfs_storaged stop</strong></p> <p><span style="font-family:宋体">启动</span>:</p> <p><strong>#service fdfs_storaged start</strong></p> <p> </p> <h3 style="margin-left:21.0pt">3) <span style="font-family:宋体">配置注意事项</span></h3> <ol> <li><strong><span style="font-family:宋体"><span style="color:red">每个</span></span><span style="color:red">storage</span></strong><strong><span style="font-family:宋体"><span style="color:red">的</span></span><span style="color:red">store_path</span></strong><strong><span style="font-family:宋体"><span style="color:red">和</span></span><span style="color:red">store_path_count</span></strong><strong><span style="font-family:宋体"><span style="color:red">数量必须一致,目录名称可以不一样;否则将会注册到</span></span><span style="color:red">tracker</span></strong><strong><span style="font-family:宋体"><span style="color:red">失败</span></span></strong></li> <li><strong><span style="font-family:宋体"><span style="color:red">同一个组的</span></span><span style="color:red">storage</span></strong><strong><span style="font-family:宋体"><span style="color:red">的组名必须配置一致;同一个组的</span></span><span style="color:red">storage</span></strong><strong><span style="font-family:宋体"><span style="color:red">是进行镜像冗余的,并非分布式存储;</span></span></strong></li> </ol> <p> </p> <h3 style="margin-left:21.0pt">4) <span style="font-family:宋体">集群测试</span></h3> <p><span style="font-family:宋体">测试方式</span>:</p> <ol> <li><span style="font-family:宋体">使用</span>FastDFS<span style="font-family:宋体">自带的</span>client<span style="font-family:宋体">进行测试</span>,client<span style="font-family:宋体">配置</span>A<span style="font-family:宋体">的</span>tracker,<span style="font-family:宋体">上传一个文件</span>,<span style="font-family:宋体">然后观察两边的</span>storage<span style="font-family:宋体">对应的目录下是否同步存在文件;</span></li> <li><span style="font-family:宋体">使用</span>FastDFS<span style="font-family:宋体">自带的</span>client<span style="font-family:宋体">进行测试</span>,client<span style="font-family:宋体">配置</span>B<span style="font-family:宋体">的</span>tracker,<span style="font-family:宋体">上传一个文件</span>,<span style="font-family:宋体">然后观察两边的</span>storage<span style="font-family:宋体">对应的目录下是否同步存在文件;</span></li> </ol>
  • AES 使用JavaScript加密然后用Java解密

    AES 使用JavaScript加密然后用Java解密,AES代表高级加密系统,它是一种对称加密算法。很多时候我们需要在客户端加密一些明文,例如密码,并将其发送到服务器,然后服务器将其解密以进一步处理.AES加密和解密更容易在Android客户端和Java服务器等相同的平台上实现,但有时在跨平台环境(如Javascript客户端和Java Server,如Spring mvc框架)中解密AES加密密码变得非常具有挑战性,因为如果任何系统默认值不匹配解密将失败。<h2>引言</h2> <blockquote> <p style="text-align:start"><span style="font-family:Tahoma,Arial,Verdana,sans-serif">AES代表高级加密系统,它是一种对称加密算法。很多时候我们需要在客户端加密一些明文,例如密码,并将其发送到服务器,然后服务器将其解密以进一步处理.AES加密和解密更容易在Android客户端和Java服务器等相同的平台上实现,但有时在跨平台环境(如Javascript客户端和Java Server,如Spring mvc框架)中解密AES加密密码变得非常具有挑战性,因为如果任何系统默认值不匹配解密将失败。</span></p> </blockquote> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">    在本文中,我们将使用spring mvc和angular js客户端创建一个应用程序。我们将有一个带有用户名和密码的表单输入的登录页面。在将密码发送到服务器之前,密码将使用CryptoJS在JavaScript中加密,并且相同的加密密码将在java中解密,并且会进行比较以匹配密码。我们将在javascript中生成salt和IV,然后生成从密码,salt和密钥大小中使用PBKDF2函数的密钥。之后,我们将使用密钥和IV对明文进行加密,并且这些密钥将在Java中进行解密。因此,基本上我们将开发一种可与Java进行可互操作的AES加密的机制,的JavaScript。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">    在继续进行之前,让我们明确一点,该机制仅在数据的有线传输期间(最有可能)增加了一种额外的安全性,但并未提供充分的证明安全性。如果您不使用SSL,则攻击者可以执行中间人攻击并通过为用户提供不同的密钥来窃取数据。</span></span></span></p> <h2 style="text-align:start">项目结构</h2> 我们这里有个spring  boot和angular js webapp项目。项目结构如下:<img srcset="" width="" size="" class="img-thumbnail" alt="项目结构图" src="/resources/assist/images/blog/e0c03ca22f804d51b2ab0dbc4e93828d.png" /> <h2 style="margin-left:0px; margin-right:0px; text-align:start">JavaScript中的Aes加密</h2> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">    对于JavaScript中的AES加密,我们已经导入了两个js文件 - <code>crypto.js</code>并且<code>pbkdf2.js</code>我们有<code>AesUtil.js</code>用于执行加密和解密的通用代码。这里this.keySize是4字节块的密钥大小。因此,要使用128位密钥,我们将位数除以32得到用于CryptoJS的密钥大小。</span></span></span></p> <strong>AesUtil.js:</strong> <pre> <code class="language-javascript">var AesUtil = function(keySize, iterationCount) { this.keySize = keySize / 32; this.iterationCount = iterationCount; }; AesUtil.prototype.generateKey = function(salt, passPhrase) { var key = CryptoJS.PBKDF2( passPhrase, CryptoJS.enc.Hex.parse(salt), { keySize: this.keySize, iterations: this.iterationCount }); return key; } AesUtil.prototype.encrypt = function(salt, iv, passPhrase, plainText) { var key = this.generateKey(salt, passPhrase); var encrypted = CryptoJS.AES.encrypt( plainText, key, { iv: CryptoJS.enc.Hex.parse(iv) }); return encrypted.ciphertext.toString(CryptoJS.enc.Base64); } AesUtil.prototype.decrypt = function(salt, iv, passPhrase, cipherText) { var key = this.generateKey(salt, passPhrase); var cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Base64.parse(cipherText) }); var decrypted = CryptoJS.AES.decrypt( cipherParams, key, { iv: CryptoJS.enc.Hex.parse(iv) }); return decrypted.toString(CryptoJS.enc.Utf8); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">密码加密在JavaScript中</h2> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">    该方法<code>logMeIn()</code>将在点击提交按钮后被调用。此方法将使用定义的通用代码<code>AesUtil.js</code>对密码进行加密并使POST请求验证密码。发送的密码将采用以下格式:<code>iv::salt::ciphertext</code>在服务器端,java将解密密码并在响应中发送解密密码显示在警告框中。</span></span></span><br />  </p> <pre> <code class="language-javascript">var app = angular.module('demoApp', []); app.controller('loginController', ['$scope', '$rootScope', '$http', function ($scope, $rootScope, $http) { $scope.logMeIn = function(){ if(!$scope.userName || !$scope.password){ $scope.showMessage("Missing required fields.", false); return; } var iv = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex); var salt = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex); var aesUtil = new AesUtil(128, 1000); var ciphertext = aesUtil.encrypt(salt, iv, $('#key').text(), $scope.password); var aesPassword = (iv + "::" + salt + "::" + ciphertext); var password = btoa(aesPassword); var data = { userName: $scope.userName, password: password } $http.post('/login',data).then(function (response){ if(response.status === 200){ alert("Password is " + response.data.password); }else { alert("Error occurred"); } }) }; }]);</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">Java中的AES解密</h2> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">首先让我们实现将拦截登录请求的控制器类。在这里,我们对密钥进行了硬编码。此密钥将由服务器唯一地生成并发送给客户端以用于每个登录请求。客户端将使用相同的密钥,而加密和服务器将使用相同的密钥进行解密。确保密钥长度为16,因为我们使用的是128位加密。请记住我们从客户端发送的加密文本的格式<code>iv::salt::ciphertext</code>。文本以相同的格式解密。我们已经有IV,盐和密文。</span></span></span><br />  </p> <pre> <code class="language-java">import com.example.demo.model.Credentials; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.UUID; @Controller public class WelcomeController { private static final Logger LOGGER = LoggerFactory.getLogger(WelcomeController.class); @RequestMapping(value={"/login"},method = RequestMethod.GET) public String loginPage(HttpServletRequest request){ LOGGER.info("Received request for login page with id - " + request.getSession().getId()); String randomKey = UUID.randomUUID().toString(); //String uniqueKey = randomKey.substring(randomKey.length()-17, randomKey.length() -1); String uniqueKey = "1234567891234567"; request.getSession().setAttribute("key", uniqueKey); return "index"; } @RequestMapping(value={"/login"},method = RequestMethod.POST) public @ResponseBody ResponseEntity login(@RequestBody Credentials credentials, HttpServletRequest request) { String decryptedPassword = new String(java.util.Base64.getDecoder().decode(credentials.getPassword())); AesUtil aesUtil = new AesUtil(128, 1000); Map map = new HashMap<>(); if (decryptedPassword != null && decryptedPassword.split("::").length == 3) { LOGGER.info("Password decrypted successfully for username - " + credentials.getUserName()); String password = aesUtil.decrypt(decryptedPassword.split("::")[1], decryptedPassword.split("::")[0], "1234567891234567", decryptedPassword.split("::")[2]); map.put("password", password); } return new ResponseEntity<>(map, HttpStatus.OK); } }</code></pre> 以下是用于AES加密和解密的java util类。您可以按照<a href="https://www.javacodegeeks.com/2018/03/aes-encryption-and-decryption-in-javacbc-mode.html" rel="external nofollow" style="padding:0px; margin:0px; outline:none; list-style:none; border:0px none; color:#326693; text-decoration:none; transition:all 0.2s ease-in-out; font-family:Tahoma, Arial, Verdana, sans-serif; font-size:15px; font-style:normal; font-variant-ligatures:normal; font-variant-caps:normal; font-weight:normal; letter-spacing:normal; orphans:2; text-align:start; text-transform:none; white-space:normal; widows:2; word-spacing:0px; -webkit-text-stroke-width:0px; background-color:#ffffff" target="_blank" >java中的AES加密和解密</a>获得关于以下实现的更多详细说明。 <pre> <code class="language-java">import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; public class AesUtil { private final int keySize; private final int iterationCount; private final Cipher cipher; public AesUtil(int keySize, int iterationCount) { this.keySize = keySize; this.iterationCount = iterationCount; try { cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { throw fail(e); } } public String decrypt(String salt, String iv, String passphrase, String ciphertext) { try { SecretKey key = generateKey(salt, passphrase); byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext)); return new String(decrypted, "UTF-8"); } catch (UnsupportedEncodingException e) { return null; }catch (Exception e){ return null; } } private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) { try { cipher.init(encryptMode, key, new IvParameterSpec(hex(iv))); return cipher.doFinal(bytes); } catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { return null; } } private SecretKey generateKey(String salt, String passphrase) { try { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), iterationCount, keySize); SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES"); return key; } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { return null; } } public static byte[] base64(String str) { return Base64.decodeBase64(str); } public static byte[] hex(String str) { try { return Hex.decodeHex(str.toCharArray()); } catch (DecoderException e) { throw new IllegalStateException(e); } } private IllegalStateException fail(Exception e) { return null; } }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">测试AES加密和解密</h2> <code>    DemoApplication.java</code>作为Java应用程序运行,然后打到http:// localhost:8080。一旦出现登录页面,您可以输入用户名和密码,然后单击提交按钮,您可以在警报中看到解密的密码。<br /> <br /> <br /> <strong><img srcset="" width="" size="" class="img-thumbnail" alt="测试结果" src="/resources/assist/images/blog/ac54eebc032e4d06929bb72b807b1381.png" /></strong><br />   <h2 style="margin-left:0px; margin-right:0px; text-align:start">总结</h2> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">在这篇文章中,我们讨论了使用Java和Javascript进行可互操作的AES加密。我们使用Crypto.js库在javascript中执行此加密。</span></span></span></p> <br /> <a href="http://www.leftso.com/resource/1005.html" target="_blank" ><strong>项目源码下载</strong></a><br /> <br />  
  • Java编程纯jdk java编写webservice服务和客服端

    Java编程中纯jdk java方式编写webservice服务(server)和客服端(client)Java编程中纯jdk java方式编写webservice服务(server)和客服端(client)<br /> <br /> 1.server<br /> <br /> 编写一个简单的server接口 <pre> <code class="language-java">package com.leftso.ws; import javax.jws.WebMethod; import javax.jws.WebService; @WebService public interface CommService { // 使用@WebMethod注解标注WebServiceI接口中的方法 @WebMethod String sayHello(String name); } </code></pre> <br /> 编写接口实现: <pre> <code class="language-java">package com.leftso.ws; import javax.jws.WebService; /** * 使用@WebService注解标注WebServiceI接口的实现类WebServiceImpl * * @author leftso * */ @WebService public class CommServiceImp implements CommService { @Override public String sayHello(String name) { return "Hello ," + name; } } </code></pre> <br /> 将该接口以webservice方式发布出去: <pre> <code class="language-java">package com.leftso.ws; import javax.xml.ws.Endpoint; public class WebServicePublish { public static void main(String[] args) { // 定义WebService的发布地址,这个地址就是提供给外界访问Webervice的URL地址,URL地址格式为:http://ip:端口号/xxxx // String address = "http://192.168.1.100:8989/";这个WebService发布地址的写法是合法的 // String address = // "http://192.168.1.100:8989/Webservice";这个WebService发布地址的是合法的 String address = "http://127.0.0.1:9000/WS_Server/Webservice"; // 使用Endpoint类提供的publish方法发布WebService,发布时要保证使用的端口号没有被其他应用程序占用 Endpoint.publish(address, new CommServiceImp()); System.out.println("发布webservice成功!"); System.out.println("请使用:"+address+"?wsdl 访问"); } } </code></pre> <br /> 运行main方法,访问地址http://127.0.0.1:9000/WS_Server/Webservice?wsdl<br /> 可以看见wsdl文档如下<br /> <img alt="wsdl" class="img-thumbnail" src="/resources/assist/images/blog/f26343d873c54ffd93e74e4b14952f0f.png" /><br /> 2.client<br /> 调用,可以用最简单的jdk自带的工具wsimport生成本地Java代码,以类的方式直接调用方法就行 <pre> <code>wsimport -keep url_wsdl(自己的wsdl地址)</code></pre> <br />  
  • spring boot整合cxf发布webservice服务和spring boot整合cxf客户端调用webservice服务

    spring boot整合cxf发布webservice服务和cxf客户端调用,说起web service最近几年restful大行其道,大有取代传统soap web service的趋势,但是一些特有或相对老旧的系统依然使用了传统的soap web service,例如银行、航空公司的机票查询接口等。<h2>一.前言</h2>     说起web service最近几年restful大行其道,大有取代传统soap web service的趋势,但是一些特有或相对老旧的系统依然使用了传统的soap web service,例如银行、航空公司的机票查询接口等。本博客主要讲解得是spring boot整合cxf发布webservice服务和spring boot整合cxf客户端调用webservice服务<br /> 本案例使用maven方式 <h2>二.编码</h2> 核心文件清单<br /> <strong>1.pom.xml</strong> <pre> <code class="language-xml"><?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>com.leftso</groupId> <artifactId>demo-webservice-cxf</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo-webservice-cxf</name> <description>Demo project for Spring Boot security</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.5.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- CXF webservice --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-spring-boot-starter-jaxws</artifactId> <version>3.1.7</version> </dependency> <!-- CXF webservice --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project></code></pre> <br /> <strong>2.CommonService.java 服务接口</strong> <pre> <code class="language-java">package com.leftso.webservice; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; /** * 接口 * * @author leftso * */ @WebService(name = "CommonService", // 暴露服务名称 targetNamespace = "http://webservice.leftso.com/"// 命名空间,一般是接口的包名倒序 ) public interface CommonService { @WebMethod @WebResult(name = "String", targetNamespace = "") public String sayHello(@WebParam(name = "userName") String name); }</code></pre> <br /> <strong>3.接口实现</strong> <pre> <code class="language-java">package com.leftso.webservice; import javax.jws.WebService; import org.springframework.stereotype.Component; /** * 接口实现 * * @author leftso * */ @WebService(serviceName = "CommonService", // 与接口中指定的name一致 targetNamespace = "http://webservice.leftso.com/", // 与接口中的命名空间一致,一般是接口的包名倒 endpointInterface = "com.leftso.webservice.CommonService"// 接口地址 ) @Component public class CommonServiceImp implements CommonService { @Override public String sayHello(String name) { return "Hello ," + name; } }</code></pre> <br /> <strong>4.配置cxf服务发布,默认服务在Host:port/services/***路径下</strong> <pre> <code class="language-java">package com.leftso.config; import javax.xml.ws.Endpoint; import org.apache.cxf.Bus; import org.apache.cxf.jaxws.EndpointImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.leftso.webservice.CommonService; @Configuration public class CxfConfig { @Autowired private Bus bus; @Autowired CommonService commonService; /** JAX-WS **/ @Bean public Endpoint endpoint() { EndpointImpl endpoint = new EndpointImpl(bus, commonService); endpoint.publish("/CommonService"); return endpoint; } }</code></pre> 这里相当于把Commonservice接口发布在了路径<span style="color:#ff0000">/services/CommonService</span>下,wsdl文档路径为<span style="color:#ff0000">http://localhost:8080/services/CommonService?wsdl</span><br /> <br /> <strong>创建基于cxf的客服端调用webservice接口(非使用wsdl文档生成java类)</strong> <pre> <code class="language-java">package com.leftso.client; import org.apache.cxf.endpoint.Client; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory; import com.leftso.webservice.CommonService; public class CxfClient { public static void main(String[] args) { cl1(); } /** * 方式1.代理类工厂的方式,需要拿到对方的接口 */ public static void cl1() { try { // 接口地址 String address = "http://localhost:8080/services/CommonService?wsdl"; // 代理工厂 JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean(); // 设置代理地址 jaxWsProxyFactoryBean.setAddress(address); // 设置接口类型 jaxWsProxyFactoryBean.setServiceClass(CommonService.class); // 创建一个代理接口实现 CommonService cs = (CommonService) jaxWsProxyFactoryBean.create(); // 数据准备 String userName = "Leftso"; // 调用代理接口的方法调用并返回结果 String result = cs.sayHello(userName); System.out.println("返回结果:" + result); } catch (Exception e) { e.printStackTrace(); } } /** * 动态调用方式 */ public static void cl2() { // 创建动态客户端 JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance(); Client client = dcf.createClient("http://localhost:8080/services/CommonService?wsdl"); // 需要密码的情况需要加上用户名和密码 // client.getOutInterceptors().add(new ClientLoginInterceptor(USER_NAME, // PASS_WORD)); Object[] objects = new Object[0]; try { // invoke("方法名",参数1,参数2,参数3....); objects = client.invoke("sayHello", "Leftso"); System.out.println("返回数据:" + objects[0]); } catch (java.lang.Exception e) { e.printStackTrace(); } } }</code></pre> <br /> <br /> 调用后返回结果输出为 <pre> <code class="language-html">Hello,Leftso</code></pre> <br /> <br /> 项目源码GITHUB下载:<a href="https://github.com/leftso/demo-spring-boot-cxf" rel="external nofollow" target="_blank" >点击下载</a> <h2>三.问题Q&A</h2> <h3>3.1已知问题1</h3> 将上面例子的Spring Boot版本升级至1.5.x。启动项目报错: <pre> <code class="language-html">java.lang.NoClassDefFoundError: org/springframework/boot/context/embedded/ServletRegistrationBean at java.lang.Class.getDeclaredMethods0(Native Method) ~[na:1.8.0_40] at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) ~[na:1.8.0_40] at java.lang.Class.getDeclaredMethods(Class.java:1975) ~[na:1.8.0_40] at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:613) ~[spring-core-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:524) ~[spring-core-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:510) ~[spring-core-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.util.ReflectionUtils.getUniqueDeclaredMethods(ReflectionUtils.java:570) ~[spring-core-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:697) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:640) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:609) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1484) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:425) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:395) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96) ~[spring-context-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687) ~[spring-context-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525) ~[spring-context-4.3.12.RELEASE.jar:4.3.12.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.8.RELEASE.jar:1.5.8.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.8.RELEASE.jar:1.5.8.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.8.RELEASE.jar:1.5.8.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.8.RELEASE.jar:1.5.8.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.8.RELEASE.jar:1.5.8.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.8.RELEASE.jar:1.5.8.RELEASE] at com.leftso.Application.main(Application.java:10) [classes/:na] Caused by: java.lang.ClassNotFoundException: org.springframework.boot.context.embedded.ServletRegistrationBean at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_40] at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_40] at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[na:1.8.0_40] at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_40] ... 23 common frames omitted</code></pre> <strong>解决办法:</strong><br />     出现这个问题的第一时间,我以为是常规的maven管理jar包依赖问题。后面群里有小伙伴多次尝试证明了不是maven的依赖包问题。而是spring boot的版本和cxf的版本不兼容问题。<br /> 已知兼容版本: <ul> <li><span style="color:#ff0000"><strong>Spring boot 1.4.x ------>cxf-spring-boot-starter-jaxws 3.1.x</strong></span></li> <li><span style="color:#ff0000"><strong>Spring boot 1.5.x------->cxf-spring-boot-starter-jaxws 3.2.x</strong></span></li> </ul> 所以出现上面问题,且用了spring boot 1.5.x的小伙伴请将cxf-spring-boot-starter-jaxws 版本升级到3.2.1即可解决。<br /> <br /> <strong>3.2修改spring boot cxf整合后默认的/services路径</strong><br /> 在spring boto的application配置文件中配置<br /> cxf.path=/yourpath
  • Spring 5 WebClient和WebTestClient使用教程

    Spring 5 WebClient和WebTestClient使用教程,Spring开发人员,您是否曾经觉得需要一个易于使用且高效的流畅功能样式 API 的异步/非阻塞 HTTP客户端? 如果是,那么我欢迎您阅读关于WebClient的文章,WebClient是Spring 5中引入的新的被动HTTP客户端。<h2 style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">1.引言</span></span></span></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">Spring开发人员,您是否曾经觉得需要一个易于使用且高效的<strong>流畅功能样式</strong> API 的<strong>异步/非阻塞</strong> HTTP客户端?</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">如果是,那么我欢迎您阅读关于WebClient的文章,WebClient是Spring 5中引入的新的被动HTTP客户端。</span></span></span><br /> <br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">2.如何使用WebClient</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">WebClient是Spring 5的反应性Web框架Spring WebFlux的一部分。要使用WebClient,您需要将<code>spring-webflux</code>模块包含在您的项目中。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><strong>在现有的Spring Boot项目中添加依赖项</strong></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">如果您有一个现有的Spring Boot项目,则可以<code>spring-webflux</code>通过在该<code>pom.xml</code>文件中添加以下依赖项来添加该模块-</span></span></span></p> <pre> <code class="language-xml"><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency></code></pre> <blockquote> <p style="margin-left:0px; margin-right:0px; text-align:start">请注意,您需要Spring Boot 2.xx版本才能使用Spring WebFlux模块。</p> </blockquote> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><strong>从Scratch创建一个新项目</strong></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">如果您从头开始创建项目,那么您可以使用<a href="http://start.spring.io/" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >Spring Initializr</a>网站的<code>spring-webflux</code>模块生成初始项目-</span></span></span></p> <ol> <li>转到<a href="http://start.spring.io/" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >http://start.spring.io</a>。</li> <li>选择弹簧引导版本<strong>2.xx的</strong>。</li> <li>在依赖项部分添加<strong>反应性Web</strong>依赖项。</li> <li>如果需要,请更改<em>组</em>和<em>工件的</em>详细信息,然后单击生成工程下载项目。</li> </ol> <h2 style="margin-left:0px; margin-right:0px; text-align:start">3.使用WebClient消费远程API</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><em>让我们做一些有趣的事情,并使用WebClient来使用Real World API。</em></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在本文中,我们将使用WebClient来使用<a href="https://developer.github.com/v3/" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >Github的API</a>。我们将使用WebClient在用户的Github存储库上执行CRUD操作。</span></span></span></p> <h3 style="margin-left:0px; margin-right:0px; text-align:start">创建WebClient的一个实例</h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><strong>1.使用该<code>create()</code>方法创建WebClient</strong></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您可以使用<code>create()</code>工厂方法创建WebClient的实例-</span></span></span></p> <pre> <code class="language-java">WebClient webClient = WebClient.create();</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">如果您只使用特定服务的API,那么您可以使用该服务的baseUrl来初始化WebClient</p> <pre> <code class="language-java">WebClient webClient = WebClient.create("https://api.github.com");</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><strong>2.使用WebClient构建器创建WebClient</strong></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">WebClient还附带了一个构建器,它为您提供了一些自定义选项,包括过滤器,默认标题,cookie,客户端连接器等 -</span></span></span></p> <pre> <code class="language-java">WebClient webClient = WebClient.builder() .baseUrl("https://api.github.com") .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.github.v3+json") .defaultHeader(HttpHeaders.USER_AGENT, "Spring 5 WebClient") .build();</code></pre> <h3 style="margin-left:0px; margin-right:0px; text-align:start">使用WebClient发出请求并检索响应</h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">以下是如何使用WebClient <code>GET</code>向<a href="https://developer.github.com/v3/repos/#list-your-repositories" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >Github的List Repositories API</a>发出请求-</span></span></span></p> <pre> <code class="language-java">public Flux<GithubRepo> listGithubRepositories(String username, String token) { return webClient.get() .uri("/user/repos") .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToFlux(GithubRepo.class); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">了解API调用的简单性和简洁性!</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">假设我们有一个名为类<code>GithubRepo</code>,确认到GitHub的API响应,上面的函数会返回一个<code>Flux</code>的<code>GithubRepo</code>对象。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">请注意,我使用<a href="https://developer.github.com/v3/auth/#via-oauth-tokens" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >Github的基本认证</a>机制来调用API。它需要您的github用户名和个人访问令牌,您可以从<a href="https://github.com/settings/tokens" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >https://github.com/settings/tokens中</a>生成该令牌。</span></span></span></p> <h4 style="margin-left:0px; margin-right:0px; text-align:start">使用exchange()方法来检索响应</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">该<code>retrieve()</code>方法是获取响应主体的最简单方法。但是,如果您希望对响应拥有更多的控制权,那么您可以使用可<code>exchange()</code>访问整个<code>ClientResponse</code>标题和正文的方法 -</span></span></span></p> <pre> <code class="language-java">public Flux<GithubRepo> listGithubRepositories(String username, String token) { return webClient.get() .uri("/user/repos") .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .exchange() .flatMapMany(clientResponse -> clientResponse.bodyToFlux(GithubRepo.class)); }</code></pre> <h4 style="margin-left:0px; margin-right:0px; text-align:start">在请求URI中使用参数</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您可以在请求URI中使用参数,并在<code>uri()</code>函数中分别传递它们的值。所有参数都被花括号包围。在提出请求之前,这些参数将被WebClient自动替换 -</span></span></span></p> <pre> <code class="language-java">public Flux<GithubRepo> listGithubRepositories(String username, String token) { return webClient.get() .uri("/user/repos?sort={sortField}&direction={sortDirection}", "updated", "desc") .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToFlux(GithubRepo.class); }</code></pre> <h4 style="margin-left:0px; margin-right:0px; text-align:start">使用URIBuilder构造请求URI</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您也可以使用<code>UriBuilder</code>类似的方法获取对请求URI的完全程序控制,</span></span></span></p> <pre> <code class="language-java">public Flux<GithubRepo> listGithubRepositories(String username, String token) { return webClient.get() .uri(uriBuilder -> uriBuilder.path("/user/repos") .queryParam("sort", "updated") .queryParam("direction", "desc") .build()) .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToFlux(GithubRepo.class); }</code></pre> <h3 style="margin-left:0px; margin-right:0px; text-align:start">在WebClient请求中传递Request Body</h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">如果你有一个<code>Mono</code>或一个形式的请求体<code>Flux</code>,那么你可以直接将它传递给<code>body()</code>WebClient中的方法,否则你可以从一个对象中创建一个单声道/通量并像这样传递 -</span></span></span></p> <pre> <code class="language-java">public Mono<GithubRepo> createGithubRepository(String username, String token, RepoRequest createRepoRequest) { return webClient.post() .uri("/user/repos") .body(Mono.just(createRepoRequest), RepoRequest.class) .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToMono(GithubRepo.class); }</code></pre> 如果您具有实际值而不是<code>Publisher</code>(<code>Flux</code>/ <code>Mono</code>),则可以使用<code>syncBody()</code>快捷方式传递请求正文 - <pre> <code class="language-java">public Mono<GithubRepo> createGithubRepository(String username, String token, RepoRequest createRepoRequest) { return webClient.post() .uri("/user/repos") .syncBody(createRepoRequest) .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToMono(GithubRepo.class); }</code></pre> 最后,你可以使用<code>BodyInserters</code>类提供的各种工厂方法来构造一个<code>BodyInserter</code>对象并将其传递给该<code>body()</code>方法。本<code>BodyInserters</code>类包含的方法来创建一个<code>BodyInserter</code>从<code>Object</code>,<code>Publisher</code>,<code>Resource</code>,<code>FormData</code>,<code>MultipartData</code>等- <pre> <code class="language-java">public Mono<GithubRepo> createGithubRepository(String username, String token, RepoRequest createRepoRequest) { return webClient.post() .uri("/user/repos") .body(BodyInserters.fromObject(createRepoRequest)) .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToMono(GithubRepo.class); }</code></pre> <h3 style="margin-left:0px; margin-right:0px; text-align:start">添加过滤器功能</h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">WebClient支持使用<code>ExchangeFilterFunction</code>。您可以使用过滤器函数以任何方式拦截和修改请求。例如,您可以使用过滤器函数为<code>Authorization</code>每个请求添加一个标头,或记录每个请求的详细信息。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">这<code>ExchangeFilterFunction</code>需要两个参数 -</span></span></span></p> <ol style="margin-left:30px; margin-right:0px"> <li>在<code>ClientRequest</code>与</li> <li><code>ExchangeFilterFunction</code>过滤器链中的下一个。</li> </ol> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">它可以修改<code>ClientRequest</code>并调用<code>ExchangeFilterFucntion</code>过滤器链中的下一个来继续下一个过滤器或<code>ClientRequest</code>直接返回修改以阻止过滤器链。</span></span></span></p> <h4 style="margin-left:0px; margin-right:0px; text-align:start">1.使用过滤器功能添加基本认证</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在上面的所有示例中,我们都包含一个<code>Authorization</code>用于使用Github API进行基本身份验证的标头。由于这是所有请求共有的内容,因此您可以在创建过滤器函数时将此逻辑添加到过滤器函数中<code>WebClient</code>。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">该<code>ExchaneFilterFunctions</code>API已经为基本认证提供了一个过滤器。你可以像这样使用它 -</span></span></span></p> <pre> <code class="language-java">WebClient webClient = WebClient.builder() .baseUrl(GITHUB_API_BASE_URL) .defaultHeader(HttpHeaders.CONTENT_TYPE, GITHUB_V3_MIME_TYPE) .filter(ExchangeFilterFunctions .basicAuthentication(username, token)) .build();</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">现在,您不需要<code>Authorization</code>在每个请求中添加标题。过滤器函数将拦截每个WebClient请求添加此标头。</span></span></span></p> <h4 style="margin-left:0px; margin-right:0px; text-align:start">2.使用过滤器功能记录所有请求</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">我们来看一个习惯的例子<code>ExchangeFilterFunction</code>。我们将编写一个过滤器函数来拦截并记录每个请求 -</span></span></span></p> <pre> <code class="language-java">WebClient webClient = WebClient.builder() .baseUrl(GITHUB_API_BASE_URL) .defaultHeader(HttpHeaders.CONTENT_TYPE, GITHUB_V3_MIME_TYPE) .filter(ExchangeFilterFunctions .basicAuthentication(username, token)) .filter(logRequest()) .build();</code></pre> 这里是<code>logRequest()</code>过滤器功能的实现- <pre> <code class="language-java">private ExchangeFilterFunction logRequest() { return (clientRequest, next) -> { logger.info("Request: {} {}", clientRequest.method(), clientRequest.url()); clientRequest.headers() .forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value))); return next.exchange(clientRequest); }; }</code></pre> <h4 style="margin-left:0px; margin-right:0px; text-align:start">3.使用ofRequestProcessor()和ofResponseProcessor()工厂方法来创建过滤器</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">ExchangeFilterFunction API提供两个名为工厂方法<code>ofRequestProcessor()</code>和<code>ofResponseProcessor()</code>用于创建分别截获该请求和响应滤波器的功能。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><code>logRequest()</code>我们在前一节中创建的过滤器函数可以使用<code>ofRequestProcessor()</code>这种工厂方法创建-</span></span></span></p> <pre> <code class="language-java">private ExchangeFilterFunction logRequest() { ExchangeFilterFunction.ofRequestProcessor(clientRequest -> { logger.info("Request: {} {}", clientRequest.method(), clientRequest.url()); clientRequest.headers() .forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value))); return Mono.just(clientRequest); }); } </code></pre> 如果您想拦截WebClient响应,则可以使用该<code>ofResponseProcessor()</code>方法创建像这样的过滤器功能 - <pre> <code class="language-java">private ExchangeFilterFunction logResposneStatus() { return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { logger.info("Response Status {}", clientResponse.statusCode()); return Mono.just(clientResponse); }); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">处理WebClient错误</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">只要接收到状态码为4xx或5xx的响应<code>retrieve()</code>,WebClient中的方法<code>WebClientResponseException</code>就会抛出一个。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您可以使用<code>onStatus()</code>像这样的方法来自定义,</span></span></span><br />  </p> <pre> <code class="language-java">public Flux<GithubRepo> listGithubRepositories() { return webClient.get() .uri("/user/repos?sort={sortField}&direction={sortDirection}", "updated", "desc") .retrieve() .onStatus(HttpStatus::is4xxClientError, clientResponse -> Mono.error(new MyCustomClientException()) ) .onStatus(HttpStatus::is5xxServerError, clientResponse -> Mono.error(new MyCustomServerException()) ) .bodyToFlux(GithubRepo.class); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">请注意,与<code>retrieve()</code>方法不同,该<code>exchange()</code>方法在4xx或5xx响应的情况下不会引发异常。您需要自己检查状态代码,并以您想要的方式处理它们。</span></span></span></p> <h4 style="margin-left:0px; margin-right:0px; text-align:start">使用<code>@ExceptionHandler</code>控制器内部的WebClientResponseExceptions处理</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您可以<code>@ExceptionHandler</code>在控制器内部使用这种方式来处理<code>WebClientResponseException</code>并返回适当的响应给客户端 -</span></span></span></p> <pre> <code class="language-java">@ExceptionHandler(WebClientResponseException.class) public ResponseEntity<String> handleWebClientResponseException(WebClientResponseException ex) { logger.error("Error from WebClient - Status {}, Body {}", ex.getRawStatusCode(), ex.getResponseBodyAsString(), ex); return ResponseEntity.status(ex.getRawStatusCode()).body(ex.getResponseBodyAsString()); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">使用Spring 5 WebTestClient测试Rest API</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">WebTestClient包含类似于WebClient的请求方法。另外,它还包含检查响应状态,标题和正文的方法。您也可以像<code>AssertJ</code>使用WebTestClient 一样使用断言库。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">查看以下示例以了解如何使用WebTestClient执行其他API测试 -</span></span></span></p> <blockquote> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif">提示:<a href="http://www.leftso.com/resource/1011.html" target="_blank" >项目源码下载</a></span></p> </blockquote>