当前位置: 移动技术网 > IT编程>开发语言>Java > SpringBoot集成FastDFS+Nginx整合基于Token的防盗链的方法

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

2019年07月19日  | 移动技术网IT编程  | 我要评论

为什么要用springboot?

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

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

为什么要用nginx?

概述

nginx(engine x)是一个开源的,支持高并发的www服务和代理服务软件。nginx是俄罗斯人igor sysoev开发的,最初被应用到俄罗斯的大型网站()上。后来作者将源代码以类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)
@documented
public @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.java
public 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:8000
fastdfs.tracker-servers=192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122
fastdfs.http-secret-key=2scpwmpctxhblvoyb0jyuyqzytoofmfcbiye65n56ppyvwrntxzlidbpdvddljm8qhhkxsgwtcr+9vdg3yptkw
fastdfs.http-anti-steal-token=true
fastdfs.http-tracker-http-port=8080
fastdfs.network-timeout=30
fastdfs.connect-timeout=5
fastdfs.connection-pool-max-idle=18
fastdfs.connection-pool-min-idle=2
fastdfs.connection-pool-max-total=18
fastdfs.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");
  }
}

项目主页:

国内项目主页:

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网