首页 > 编程 > Java > 正文

SpringBoot集成FastDFS+Nginx整合基于Token的防盗链的方法

2019-11-26 09:03:25
字体:
来源:转载
供稿:网友

为什么要用SpringBoot?

SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

  • 创建独立的Spring应用程序
  • 嵌入的Tomcat,无需部署WAR文件
  • 简化Maven配置
  • 自动配置Spring
  • 提供生产就绪型功能,如指标,健康检查和外部配置
  • 绝对没有代码生成并且对XML也没有配置要求

为什么要用Nginx?

概述

Nginx(engine x)是一个开源的,支持高并发的www服务和代理服务软件。Nginx是俄罗斯人Igor Sysoev开发的,最初被应用到俄罗斯的大型网站(www.rambler.ru)上。后来作者将源代码以类BSD许可证的形式开源出来供全球使用。在功能应用方面,Nginx不仅是一个优秀的Web服务软件,还具有反向代理负载均衡和缓存的功能。在反向代理负载均衡方面类似于LVS负载均衡及HAProxy等你专业代理软件。Nginx部署起来更加方便简单,在缓存服务功能方面,有类似于Squid等专业的缓存服务软件。Nginx可以运行在UNIX、Linux、MS Windows Server、Mac OS X Server、Solaris等操作系统中。

Nginx的重要特性

  • 可以针对静态资源高速节点并发访问及缓存。
  • 可以使用反向代理加速,并且可以进行数据缓存。
  • 具有简单负载均衡,节点健康检查和容错功能。
  • 支持远程Fast CGI服务的缓存加速。
  • 支持Fast CGI、Uwsgi、SCGI、Memcached Server的加速和缓存。
  • 支持SSL、TLS、SNI。
  • 具有模块化的架构。
  • 过滤器包括gzip压缩、ranges支持、chunked响应、XSLT、SSL和图像缩放等功能。
  • 在SSL过滤器中,包含多个SSL页面,如果经由Fast CGI或反向代理处理,可以并行处理。

Nginx所具备的WWW服务特性

  • 支持基于域名、端口和IP的虚拟主机配置。
  • 支持KeepAlived和piplined连接。
  • 可进行简单、方便、灵活的配置和管理。
  • 支持修改Nginx配置,并且在代码上线时,可平滑重启,不中断业务访问。
  • 可自定义访问日志格式,临时缓冲写日志操作,快速日志轮询及通过rsyslog处理日志。
  • 可利用信号控制Nginx进程。
  • 支持3xx-5xxHTTP状态码重定向。
  • 支持rewrite模块,支持URI重写及正则表达式匹配。
  • 支持基于客户端IP地址和HTTP基本认证的访问控制。
  • 支持PUT、DELETE、MKCOL、COPY、MOVE等特殊的HTTP请求方法。
  • 支持FLV流和MP4流技术产品应用。
  • 支持HTTP响应速率限制。
  • 支持同一IP地址的并发连接或请求限制。
  • 支持邮件服务代理。
  • 支持高并发,可以支持几百万并发连接。
  • 资源消耗少,在3万并发连接下,可以开启10个nginx的线程消耗的内存不到200MB。
  • 可以做HTTP反向代理及加速缓存,及负载均衡功能,内置对RS节点服务器健康检查功能,折现但能够与专业的HAProxy或LVS的功能。
  • 具备Squid等专业缓存软件等的缓存功能。
  • 支持异步网络I/O事件模型epoll(Linux2.6+)。

Nginx软件主要企业应用

  • 作为Web服务软件。
  • 使用Nginx运行HTML、JS、CSS、小图片等静态数据(类似于Lighttpd)。
  • 结合Fast CGI运行PHP等动态程序(例如使用fastcgi_pass方式)。
  • Nginx结合Tomcat/Resin等支持Java动态程序(常用proxy_pass)。
  • 反向代理或负载均衡服务(Nginx从1.9.0开始就开始支持TCP的代理了)。
  • 前端业务数据缓存服务。

Web服务应用产品性能对比

  • 静态数据的访问上:处理小文件(小于1MB)时,Nginx和Lighttpd比Apache更有优势,Nginx处理小文件的优势明显,Lighttpd综合最强。
  • 动态数据的访问上:三者差距不大,Apache更有优势,因为处理动态数据的能力在于PHP(Java)和后端数据库的服务能力,也就是说瓶颈不在Web服务器上。
  • 一般情况下普通PHP引擎支持的并发连接参考值3001000。Java引擎和数据库的并发连接参考值3001500。

为什么Nginx比Apache的性能高?

  • Nginx使用最新版的eepoll(Linux 2.6内核)和kqueue(FreeBSD)异步网络I/O模型,而Apache使用的是传统的select模型。
  • 目前Linux下能够承受高并发访问的Squid、Memcached软件采用都是epoll模型。
  • 处理大量的连接的读写时,Apache所采用的select网络I/O模型比较低。

如何正确采用Web服务器?

  • 静态业务:如果是高并发场景,尽量采用Nginx或Lighttpd,二者首选Nginx。
  • 动态业务:理论上采用Nginx和Apache均可,建议使用Nginx,为了避免相同业务服务的软件多样化,增加维护成本,动态业务可以使用Nginx兼做前端代理,再根据页面的元素或目录转发到其他的服务器进行处理。
  • 既有动态业务又有静态业务,就用Nginx。

关于部署,就不在重复了,如果需要请移步《Java高级架构之FastDFS分布式文件集群》:

使用IDEA场景启动器创建工程

创建Maven工程,修改POM.xml文件添加如下依赖:

<dependencies>  <!-- SpringBoot的自动配置相关依赖 -->  <dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-autoconfigure</artifactId>    <version>1.5.20.RELEASE</version>  </dependency>  <dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-configuration-processor</artifactId>    <version>1.5.20.RELEASE</version>  </dependency>  <!-- 日志相关的依赖 -->  <dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-logging</artifactId>    <version>1.5.20.RELEASE</version>  </dependency>  <!-- 对象池相关的依赖 -->  <dependency>    <groupId>org.apache.commons</groupId>    <artifactId>commons-pool2</artifactId>    <version>2.6.0</version>  </dependency></dependencies>

创建必要的包

  • annotation:存放相关的注解
  • autoconfiguation: 存储自动配置类
  • factory: 存放工厂类
  • properties: 存放配置参数类
  • service: 存放服务类

一般情况下,SpringBoot都会提供相应的@EnableXxx注解标注在应用的主启动类上开启某个功能:

// EnableFastdfsClient.java@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Import(FastdfsAutoConfiguration.class)@Documentedpublic @interface EnableFastdfsClient {}

下面是相关的自动配置类:

// FastdfsAutoConfiguration.java@Configuration@EnableConfigurationProperties(FastdfsProperties.class)public class FastdfsAutoConfiguration {  @Autowired  private FastdfsProperties fastdfsProperties;  @Bean  @ConditionalOnMissingBean(FastdfsClientService.class)  public FastdfsClientService fastdfsClientService() throws Exception {    return new FastdfsClientService(fastdfsProperties);  }}

创建相关的工厂类:

// StorageClientFactory.java// 用于创建连接对象的工厂类public class StorageClientFactory implements PooledObjectFactory<StorageClient> {  @Override  public PooledObject<StorageClient> makeObject() throws Exception {    TrackerClient client = new TrackerClient();    TrackerServer server = client.getConnection();    return new DefaultPooledObject<>(new StorageClient(server, null));  }  @Override  public void destroyObject(PooledObject<StorageClient> p) throws Exception {    p.getObject().getTrackerServer().close();  }  @Override  public boolean validateObject(PooledObject<StorageClient> p) {    return false;  }  @Override  public void activateObject(PooledObject<StorageClient> p) throws Exception {  }  @Override  public void passivateObject(PooledObject<StorageClient> p) throws Exception {  }}

Properties类用来映射application.properties或者application.yml配置文件:

// FastdfsProperties.java@ConfigurationProperties(prefix = "fastdfs")public class FastdfsProperties {  // 连接超时时间  // 网络超时时间  // 字符集编码  // 是否使用Token  // Token加密密钥  // 跟踪器IP地址,多个使用分号隔开  // 连接池的连接对象最大个数  // 连接池的最大空闲对象个数  // 连接池的最小空闲对象个数  // Nginx服务器IP,多个使用分号分割  // 获取连接对象时可忍受的等待时长(毫秒)  private String connectTimeout = "5";  private String networkTimeout = "30";  private String charset = "UTF-8";  private String httpAntiStealToken = "false";  private String httpSecretKey = "";  private String httpTrackerHttpPort = "";  private String trackerServers = "";  private String connectionPoolMaxTotal = "18";  private String connectionPoolMaxIdle = "18";  private String connectionPoolMinIdle = "2";  private String nginxServers = "";  // 需要创建相关的Setter和Getter方法}

在Service类中封装方法, 下面仅展示3个常用的方法:

// FastdfsClientSerivce.javapublic class FastdfsClientService {  // SpringBoot加载的配置文件  // 连接池配置项  // 转换后的配置条目  // 连接池  // Nginx服务器地址  private FastdfsProperties fdfsProp;  private GenericObjectPoolConfig config;  private Properties prop;  private GenericObjectPool<StorageClient> pool;  private String[] nginxServers;  private Logger logger;  public FastdfsClientService(FastdfsProperties fdfsProp) throws Exception {    this.fdfsProp = fdfsProp;    this.logger = LoggerFactory.getLogger(getClass());    init();    create();    info();  }  /**   * 初始化全局客户端   */  private void init() throws Exception {    this.prop = new Properties();    this.logger.info("FastDFS: reading config file...");    this.logger.info("FastDFS: fastdfs.connect_timeout_in_seconds=" + this.fdfsProp.getConnectTimeout());    this.logger.info("FastDFS: fastdfs.network_timeout_in_seconds=" + this.fdfsProp.getNetworkTimeout());    this.logger.info("FastDFS: fastdfs.charset=" + this.fdfsProp.getCharset());    this.logger.info("FastDFS: fastdfs.http_anti_steal_token=" + this.fdfsProp.getHttpAntiStealToken());    this.logger.info("FastDFS: fastdfs.http_secret_key=" + this.fdfsProp.getHttpSecretKey());    this.logger.info("FastDFS: fastdfs.http_tracker_http_port=" + this.fdfsProp.getHttpTrackerHttpPort());    this.logger.info("FastDFS: fastdfs.tracker_servers=" + this.fdfsProp.getTrackerServers());    this.logger.info("FastDFS: fastdfs.connection_pool_max_total=" + this.fdfsProp.getConnectionPoolMaxTotal());    this.logger.info("FastDFS: fastdfs.connection_pool_max_idle=" + this.fdfsProp.getConnectionPoolMaxIdle());    this.logger.info("FastDFS: fastdfs.connection_pool_min_idle=" + this.fdfsProp.getConnectionPoolMinIdle());    this.logger.info("FastDFS: fastdfs.nginx_servers=" + this.fdfsProp.getNginxServers());    this.prop.put("fastdfs.connect_timeout_in_seconds", this.fdfsProp.getConnectTimeout());    this.prop.put("fastdfs.network_timeout_in_seconds", this.fdfsProp.getNetworkTimeout());    this.prop.put("fastdfs.charset", this.fdfsProp.getCharset());    this.prop.put("fastdfs.http_anti_steal_token", this.fdfsProp.getHttpAntiStealToken());    this.prop.put("fastdfs.http_secret_key", this.fdfsProp.getHttpSecretKey());    this.prop.put("fastdfs.http_tracker_http_port", this.fdfsProp.getHttpTrackerHttpPort());    this.prop.put("fastdfs.tracker_servers", this.fdfsProp.getTrackerServers());    ClientGlobal.initByProperties(this.prop);  }  /**   * 显示初始化信息   */  private void info() {    this.logger.info("FastDFS parameter: ConnectionPoolMaxTotal ==> " + this.pool.getMaxTotal());    this.logger.info("FastDFS parameter: ConnectionPoolMaxIdle ==> " + this.pool.getMaxIdle());    this.logger.info("FastDFS parameter: ConnectionPoolMinIdle ==> " + this.pool.getMinIdle());    this.logger.info("FastDFS parameter: NginxServer ==> " + Arrays.toString(this.nginxServers));    this.logger.info(ClientGlobal.configInfo());  }  /**   * 创建连接池   */  private void create() {    this.config = new GenericObjectPoolConfig();    this.logger.info("FastDFS Client: Creating connection pool...");    this.config.setMaxTotal(Integer.parseInt(this.fdfsProp.getConnectionPoolMaxTotal()));    this.config.setMaxIdle(Integer.parseInt(this.fdfsProp.getConnectionPoolMaxIdle()));    this.config.setMinIdle(Integer.parseInt(this.fdfsProp.getConnectionPoolMinIdle()));    StorageClientFactory factory = new StorageClientFactory();    this.pool = new GenericObjectPool<StorageClient>(factory, this.config);    this.nginxServers = this.fdfsProp.getNginxServers().split(",");  }  /**   * Nginx服务器负载均衡算法   *   * @param servers 服务器地址   * @param address 客户端IP地址   * @return 可用的服务器地址   */  private String getNginxServer(String[] servers, String address) {    int size = servers.length;    int i = address.hashCode();    int index = abs(i % size);    return servers[index];  }  /**   * 带有防盗链的下载   *   * @param fileGroup    文件组名   * @param remoteFileName 远程文件名称   * @param clientIpAddress 客户端IP地址   * @return 完整的URL地址   */  public String autoDownloadWithToken(String fileGroup, String remoteFileName, String clientIpAddress) throws Exception {    int ts = (int) (System.currentTimeMillis() / 1000);    String token = ProtoCommon.getToken(remoteFileName, ts, ClientGlobal.getG_secret_key());    String nginx = this.getNginxServer(this.nginxServers, clientIpAddress);    return "http://" + nginx + "/" + fileGroup + "/" + remoteFileName + "?token=" + token + "&ts=" + ts;  }  /**   * 上传文件,适合上传图片   *   * @param buffer 字节数组   * @param ext  扩展名   * @return 文件组名和ID   */  public String[] autoUpload(byte[] buffer, String ext) throws Exception {    String[] upload = this.upload(buffer, ext, null);    return upload;  }  /**   * 不带防盗链的下载,如果开启防盗链会导致该方法抛出异常   *   * @param fileGroup    文件组名   * @param remoteFileName 远程文件ID   * @param clientIpAddress 客户端IP地址,根据客户端IP来分配Nginx服务器   * @return 完整的URL地址   */  public String autoDownloadWithoutToken(String fileGroup, String remoteFileName, String clientIpAddress) throws Exception {    if (ClientGlobal.getG_anti_steal_token()) {      this.logger.error("FastDFS Client: You've turned on Token authentication.");      throw new Exception("You've turned on Token authentication.");    }    String nginx = this.getNginxServer(this.nginxServers, clientIpAddress);    return "http://" + nginx + fileGroup + "/" + remoteFileName;  }  // 后面还有好多方法,就不一一展示了}

为了在IDEA中使用便捷的配置提示功能,我们需要创建元数据文件(resources/spring-configuration-metadata.json):

{ "groups": [  {   "name": "fastdfs",   "type": "com.bluemiaomiao.properties.FastdfsProperties",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties"  } ], "properties": [  {   "name": "connectTimeout",   "type": "java.lang.String",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties",   "defaultValue": "5"  },  {   "name": "networkTimeout",   "type": "java.lang.String",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties",   "defaultValue": "30"  },  {   "name": "charset",   "type": "java.lang.String",   "defaultValue": "UTF-8"  },  {   "name": "httpAntiStealToken",   "type": "java.lang.String",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties",   "defaultValue": "false"  },  {   "name": "httpSecretKey",   "type": "java.lang.String",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties"  },  {   "name": "httpTrackerHttpPort",   "type": "java.lang.Integer",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties"  },  {   "name": "trackerServers",   "type": "java.lang.String",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties"  },  {   "name": "connectionPoolMaxTotal",   "type": "java.lang.Integer",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties",   "defaultValue": "18"  },  {   "name": "connectionPoolMaxIdle",   "type": "java.lang.Integer",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties",   "defaultValue": "18"  },  {   "name": "connectionPoolMinIdle",   "type": "java.lang.Integer",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties",   "defaultValue": "2"  },  {   "name": "nginxServers",   "type": "java.lang.String",   "sourceType": "com.bluemiaomiao.properties.FastdfsProperties"  } ], "hints": [  {   "name": "http_anti_steal_token",   "values": [    {     "value": "false"    },    {     "value": "true"    }   ]  } ]}

应用到项目中

创建SpringBoot项目,勾选Web选项,版本选择1.5.20

进入场景启动器的项目目录执行mvn clean install 将其安装到本地

在POM.xml文件中添加依赖:

<dependency>  <groupId>com.bluemiaomiao</groupId>  <artifactId>fastdfs-spring-boot-starter</artifactId>  <version>1.0-SNAPSHOT</version></dependency>

记得开启IDEA的自动导入功能

创建配置文件application.properties

fastdfs.nginx-servers=192.168.80.2:8000,192.168.80.3:8000,192.168.80.4:8000fastdfs.tracker-servers=192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122fastdfs.http-secret-key=2scPwMPctXhbLVOYB0jyuyQzytOofmFCBIYe65n56PPYVWrntxzLIDbPdvDDLJM8QHhKxSGWTcr+9VdG3yptkwfastdfs.http-anti-steal-token=truefastdfs.http-tracker-http-port=8080fastdfs.network-timeout=30fastdfs.connect-timeout=5fastdfs.connection-pool-max-idle=18fastdfs.connection-pool-min-idle=2fastdfs.connection-pool-max-total=18fastdfs.charset=UTF-8

或者使用application.yml

fastdfs: charset: UTF-8 connect-timeout: 5 http-secret-key: 2scPwMPctXhbLVOYB0jyuyQzytOofmFCBIYe65n56PPYVWrntxzLIDbPdvDDLJM8QHhKxSGWTcr+9VdG3yptkw network-timeout: 30 http-anti-steal-token: true http-tracker-http-port: 8080 connection-pool-max-idle: 20 connection-pool-max-total: 20 connection-pool-min-idle: 2 nginx-servers: 192.168.80.2:8000,192.168.80.3:8000,192.168.80.4:8000 tracker-servers: 192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122

创建控制器类测试方法

// controllers.DownloadController.java@Controller@RequestMapping(value = "/download")public class DownloadController {  @Autowired  private FastdfsClientService service;  @ResponseBody  @RequestMapping(value = "/image")  public String image() throws Exception {    // 之前上传过的数据,实际应用场景应该使用SQL数据库来存储    return service.autoDownloadWithToken("group1", "M00/00/00/wKhQA1ysjSGAPjXbAAVFOL7FJU4.tar.gz", "192.168.80.1");  }}

项目主页:https://github.com/bluemiaomiao/fastdfs-spring-boot-starter

国内项目主页:https://gitee.com/bluemiaomiao/fastdfs-spring-boot-starter

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表