搜索词>>cdn 耗时0.0050
  • CDN导致字体图标的字体出现跨域问题无法加载问题解决

    CDN导致字体图标的字体出现跨域问题无法加载问题解决办法CDN导致字体图标的字体出现跨域问题无法加载问题解决办法
  • apache 24 配置301跳转 解决cdn问题

    apache 24 配置301跳转 解决cdn问题<IfModule rewrite_module> Options +FollowSymLinks RewriteEngine on RewriteCond Host: ^localhost$ #RedirectMatch 301 /static/(.*) http\://cdn\.icdicn\.com/static/$1 RedirectMatch 301 /static/img/(.*) http\://cdn\.icdicn\.com/static/img/$1 RedirectMatch 301 /static/echarts\.min\.js http\://cdn\.icdicn\.com/static/echarts\.min\.js RedirectMatch 301 /static/public-framework\.js http\://cdn\.icdicn\.com/static/public-framework\.js </IfModule>​​​​​​​代码段 小部件
  • Vue 2.0项目配置CDN服务解决加载慢问题

    vue打包会把vue相关的组件打包到一个文件vendor.*.js(*是个随机数)步骤一 资源引入vue最外层index.html文件引入资源文<body>l;     <div id="app">l;</div>l;       <script srvue打包会把vue相关的组件打包到一个文件vendor.*.js(*是个随机数)步骤一 资源引入vue最外层index.html文件引入资源文<body>     <div id="app"></div>       <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>     <script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>     <script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script> </body>步骤二 添加配置在bulid/webpack.base.conf.js文件中,增加externals,将引用的外部模块导入,如下:module.exports = {   entry: {     app: './src/main.js'   },   externals:{     'vue': 'Vue',     'vue-router': 'VueRouter',     'vuex':'Vuex'   }  【注意】 externals的key:value不要随便修改,暂时理解为 key是npm安装使用的名称,value为组件对外提供的名称不可随意更改(打开组件源码可见)步骤三 去掉原有的引用1.main.js  文件中注释掉vue // import Vue from 'vue'2.router--> index.js  文件中注释掉 vue和 vue-router:// import Vue from 'vue' // import VueRouter from 'vue-router'3.修改名vue 使用路由配置为以下Vue.use(VueRouter)
  • Spring Boot 2 Shiro 去掉URL jsessionid

    一、项目环境Spring Boot 2.1.2.RELEASEshiro-spring 1.4二、去掉URL jsessionid在shiro配置中,配置关闭url中显示sessionId @Bean public Defau一、项目环境Spring Boot 2.1.2.RELEASEshiro-spring 1.4二、去掉URL jsessionid在shiro配置中,配置关闭url中显示sessionId @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); // 去掉shiro登录时url里的JSESSIONID sessionManager.setSessionIdUrlRewritingEnabled(false); if ("redis".equals(cacheType)){ sessionManager.setCacheManager(shiroRedisCacheManager()); Log.info("当前Shiro缓存框架为:redis",getClass()); }else if ("ehcache".equals(cacheType)){ sessionManager.setCacheManager(shiroEhCacheManager()); Log.info("当前Shiro缓存框架为:ehcache",getClass()); }else{ throw new RuntimeException("缓存类型值应该为:redis|ehcache;当前输入值:" + cacheType); } return sessionManager; }核心代码:sessionManager.setSessionIdUrlRewritingEnabled(false);以上片段代码关闭了URL中带SESSIONID三、其他注意事项通过上方得配置,常规得URL后面是不会跟sessionId了,但是thymeleaf模板中,如果有以下写<link rel="stylesheet" th:href="@{/assets/framework/bootstrap/dist/css/bootstrap.min.css}"/>在加载这个资源文件得时候同样会带出sessionid,可能会引起首次打开网页CDN加载失败,刷新一次恢复正常。简单得说就是在使用CDN得情况下,thymeleaf模板引擎标准引入资源文件会首次失败得情况(亲测)​目前我的解决办法是直接普通写法:<link rel="stylesheet" href="/assets/framework/bootstrap/dist/css/bootstrap.min.css"/>如果有更合理得解决办法请告诉我,谢谢。
  • ajax和axios、fetch 区别

    1.jQuery ajax$.ajax({ type: 'POST', url: url, data: data, dataType: dataType, success: function () {},1.jQuery ajax$.ajax({ type: 'POST', url: url, data: data, dataType: dataType, success: function () {}, error: function () {} });传统 Ajax 指的是 XMLHttpRequest(XHR), 最早出现的发送后端请求技术,隶属于原始js中,核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱。JQuery ajax 是对原生XHR的封装,除此以外还增添了对JSONP的支持。经过多年的更新维护,真的已经是非常的方便了,优点无需多言;如果是硬要举出几个缺点,那可能只有:1.本身是针对MVC的编程,不符合现在前端MVVM的浪潮2.基于原生的XHR开发,XHR本身的架构不清晰。3.JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)4.不符合关注分离(Separation of Concerns)的原则5.配置和调用方式非常混乱,而且基于事件的异步模型不友好。PS:MVVM(Model-View-ViewModel), 源自于经典的 Model–View–Controller(MVC)模式。MVVM 的出现促进了 GUI 前端开发与后端业务逻辑的分离,极大地提高了前端开发效率。MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的最重要一环。如下图所示:​2.axiosaxios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });Vue2.0之后,尤雨溪推荐大家用axios替换JQuery ajax,想必让axios进入了很多人的目光中。axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,它本身具有以下特征:1.从浏览器中创建 XMLHttpRequest2.支持 Promise API3.客户端支持防止CSRF4.提供了一些并发请求的接口(重要,方便了很多的操作)5.从 node.js 创建 http 请求6.拦截请求和响应7.转换请求和响应数据8.取消请求9.自动转换JSON数据PS:防止CSRF:就是让你的每个请求都带一个从cookie中拿到的key, 根据浏览器同源策略,假冒的网站是拿不到你cookie中得key的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。3.fetchtry { let response = await fetch(url); let data = response.json(); console.log(data); } catch(e) { console.log("Oops, error", e); }fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。但是,一定记住fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。fetch的优点:1.符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里2.更好更方便的写法坦白说,上面的理由对我来说完全没有什么说服力,因为不管是Jquery还是Axios都已经帮我们把xhr封装的足够好,使用起来也足够方便,为什么我们还要花费大力气去学习fetch?我认为fetch的优势主要优势就是:1.语法简洁,更加语义化 2.基于标准 Promise 实现,支持 async/await 3.同构方便,使用 [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) 4.更加底层,提供的API丰富(request, response) 5.脱离了XHR,是ES规范里新的实现方式最近在使用fetch的时候,也遇到了不少的问题:fetch是一个低层次的API,你可以把它考虑成原生的XHR,所以使用起来并不是那么舒服,需要进行封装。例如:1)fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。 2)fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: 'include'}) 3)fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费 4)fetch没有办法原生监测请求的进度,而XHR可以总结axios既提供了并发的封装,也没有fetch的各种问题,而且体积也较小,当之无愧现在最应该选用的请求的方式。
  • Spring boot 入门之CORS 跨域配置详解

    Spring boot 入门之CORS 跨域配置详解,spring 跨域配置详解。<h2>引言</h2>     在Java 编程中,web项目还是经常遇到一些跨域的使用。这里主要记录几种spring框架,spring boot中的一些常见的几种配置跨域的方法。<br /> <br />     CORS(<em>Cross-origin resource sharing-</em>跨源资源共享)允许网页从其他域向浏览器请求额外的资源,例如 字体,CSS或来自CDN的静态图像。 CORS有助于将来自多个域的网页内容提供给通常具有相同安全策略的浏览器。 在这个例子中,我们将学习如何在Spring MVC应用程序的方法和全局级别启用CORS支持。 <h2>一.spring框架中使用@CrossOrigin注解方法级CORS</h2>     Spring MVC提供了@CrossOrigin注解。 这个注释标注了注释的方法或类型,允许跨源请求。<br /> <br />     默认情况下,@CrossOrigin允许所有的来源,所有的头文件,@RequestMapping注解中指定的HTTP方法和30分钟的maxAge。 您可以覆盖这些赋予注释属性值的设置:<br />   <table class="table table-bordered table-hover"> <tbody> <tr> <th>属性</th> <th>说明</th> </tr> <tr> <td><code>origins</code></td> <td>允许的来源列表. 他的值放置在HTTP协议的响应header的<code>Access-Control-Allow-Origin</code> .<br /> – <code>* </code>– 意味着所有的源都是被允许的。<br /> – 如果未定义,则允许所有来源。</td> </tr> <tr> <td><code>allowedHeaders</code></td> <td>实际请求期间可以使用的请求标头列表. 值用于预检的响应header Access-Control-Allow-Headers。<br /> – <code>* </code>– 意味着允许客户端请求的所有头文件。<br /> – 如果未定义,则允许所有请求的headers。</td> </tr> <tr> <td><code>methods</code></td> <td>支持的HTTP请求方法列表。 如果未定义,则使用由RequestMapping注释定义的方法。</td> </tr> <tr> <td><code>exposedHeaders</code></td> <td>浏览器允许客户端访问的响应头列表。 在实际响应报头Access-Control-Expose-Headers中设置值。<br /> – 如果未定义,则使用空的暴露标题列表。</td> </tr> <tr> <td><code>allowCredentials</code></td> <td>它确定浏览器是否应该包含与请求相关的任何cookie。<br /> – <code>false</code> – cookies 不应该包括在内。<br /> – <code>""</code> (空字符串) – 意味着未定义.<br /> – <code>true</code> – 预响应将包括值设置为true的报头Access-Control-Allow-Credentials。<br /> – 如果未定义,则允许所有凭据。</td> </tr> <tr> <td><code>maxAge</code></td> <td>预响应的高速缓存持续时间的最大时间(以秒为单位)。 值在标题Access-Control-Max-Age中设置。<br /> – 如果未定义, 最大时间设置为1800秒(30分钟).</td> </tr> </tbody> </table> <h3>1.1@CrossOrigin Class/Controller Level</h3> <pre> <code class="language-java">@CrossOrigin(origins = "*", allowedHeaders = "*") @Controller public class HomeController { @GetMapping(path="/") public String homeInit(Model model) { return "home"; } }</code></pre> <h3>1.2@CrossOrigin at Method Level</h3> <pre> <code class="language-java">@Controller public class HomeController { @CrossOrigin(origins = "*", allowedHeaders = "*") @GetMapping(path="/") public String homeInit(Model model) { return "home"; } }</code></pre> <h3>1.3 @CrossOrigin Overridden at Method Level</h3> homeInit()方法只能从域http://example.com访问。 其他方法在HomeController中将可以从所有域访问。 <pre> <code class="language-java">@Controller @CrossOrigin(origins = "*", allowedHeaders = "*") public class HomeController { @CrossOrigin(origins = "http://example.com") @GetMapping(path="/") public String homeInit(Model model) { return "home"; } }</code></pre> <h2>二.Spring框架全局CORS 配置</h2> <h3>2.1Spring MVC CORS 使用WebMvcConfigurerAdapter配置</h3> 要为整个应用程序启用CORS,请使用<code>WebMvcConfigurerAdapter</code> 添加 <code>CorsRegistry</code>。 <pre> <code class="language-java">@Configuration @EnableWebMvc public class CorsConfiguration extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedMethods("GET", "POST"); } }</code></pre> <h3>2.2Spring Boot CORS 使用WebMvcConfigurer配置</h3> 在spring boot应用程序中,建议只声明一个<code>WebMvcConfigurer</code> bean。 <pre> <code class="language-java">@Configuration public class CorsConfiguration { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**"); } }; } }</code></pre> <h3>2.3CORS 使用Spring Security配置</h3> 要通过Spring安全性启用CORS支持,请配置<code>CorsConfigurationSource</code> bean并使用<code>HttpSecurity.cors()</code> 配置。 <pre> <code class="language-java">@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors().and() //other config } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("https://example.com")); configuration.setAllowedMethods(Arrays.asList("GET","POST")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }</code></pre>
  • 分布式文件系统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="/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="/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="/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="/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="/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="/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="/assist/images/blog/ef960a1f4c574e6f9790895b3974c933.png" /></p> <blockquote> <p>文中图片均来源于网络</p> </blockquote> <br />