SpringBoot集成FastDFS+Nginx整合基于Token的防盜鏈的方法
為什么要用SpringBoot?
SpringBoot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發(fā)過程。該框架使用了特定的方式來進行配置,從而使開發(fā)人員不再需要定義樣板化的配置。通過這種方式,Spring Boot致力于在蓬勃發(fā)展的快速應用開發(fā)領域(rapid application development)成為領導者。
- 創(chuàng)建獨立的Spring應用程序
- 嵌入的Tomcat,無需部署WAR文件
- 簡化Maven配置
- 自動配置Spring
- 提供生產(chǎn)就緒型功能,如指標,健康檢查和外部配置
- 絕對沒有代碼生成并且對XML也沒有配置要求
為什么要用Nginx?
概述
Nginx(engine x)是一個開源的,支持高并發(fā)的www服務和代理服務軟件。Nginx是俄羅斯人Igor Sysoev開發(fā)的,最初被應用到俄羅斯的大型網(wǎng)站(www.rambler.ru)上。后來作者將源代碼以類BSD許可證的形式開源出來供全球使用。在功能應用方面,Nginx不僅是一個優(yōu)秀的Web服務軟件,還具有反向代理負載均衡和緩存的功能。在反向代理負載均衡方面類似于LVS負載均衡及HAProxy等你專業(yè)代理軟件。Nginx部署起來更加方便簡單,在緩存服務功能方面,有類似于Squid等專業(yè)的緩存服務軟件。Nginx可以運行在UNIX、Linux、MS Windows Server、Mac OS X Server、Solaris等操作系統(tǒng)中。
Nginx的重要特性
- 可以針對靜態(tài)資源高速節(jié)點并發(fā)訪問及緩存。
- 可以使用反向代理加速,并且可以進行數(shù)據(jù)緩存。
- 具有簡單負載均衡,節(jié)點健康檢查和容錯功能。
- 支持遠程Fast CGI服務的緩存加速。
- 支持Fast CGI、Uwsgi、SCGI、Memcached Server的加速和緩存。
- 支持SSL、TLS、SNI。
- 具有模塊化的架構。
- 過濾器包括gzip壓縮、ranges支持、chunked響應、XSLT、SSL和圖像縮放等功能。
- 在SSL過濾器中,包含多個SSL頁面,如果經(jīng)由Fast CGI或反向代理處理,可以并行處理。
Nginx所具備的WWW服務特性
- 支持基于域名、端口和IP的虛擬主機配置。
- 支持KeepAlived和piplined連接。
- 可進行簡單、方便、靈活的配置和管理。
- 支持修改Nginx配置,并且在代碼上線時,可平滑重啟,不中斷業(yè)務訪問。
- 可自定義訪問日志格式,臨時緩沖寫日志操作,快速日志輪詢及通過rsyslog處理日志。
- 可利用信號控制Nginx進程。
- 支持3xx-5xxHTTP狀態(tài)碼重定向。
- 支持rewrite模塊,支持URI重寫及正則表達式匹配。
- 支持基于客戶端IP地址和HTTP基本認證的訪問控制。
- 支持PUT、DELETE、MKCOL、COPY、MOVE等特殊的HTTP請求方法。
- 支持FLV流和MP4流技術產(chǎn)品應用。
- 支持HTTP響應速率限制。
- 支持同一IP地址的并發(fā)連接或請求限制。
- 支持郵件服務代理。
- 支持高并發(fā),可以支持幾百萬并發(fā)連接。
- 資源消耗少,在3萬并發(fā)連接下,可以開啟10個nginx的線程消耗的內(nèi)存不到200MB。
- 可以做HTTP反向代理及加速緩存,及負載均衡功能,內(nèi)置對RS節(jié)點服務器健康檢查功能,折現(xiàn)但能夠與專業(yè)的HAProxy或LVS的功能。
- 具備Squid等專業(yè)緩存軟件等的緩存功能。
- 支持異步網(wǎng)絡I/O事件模型epoll(Linux2.6+)。
Nginx軟件主要企業(yè)應用
- 作為Web服務軟件。
- 使用Nginx運行HTML、JS、CSS、小圖片等靜態(tài)數(shù)據(jù)(類似于Lighttpd)。
- 結合Fast CGI運行PHP等動態(tài)程序(例如使用fastcgi_pass方式)。
- Nginx結合Tomcat/Resin等支持Java動態(tài)程序(常用proxy_pass)。
- 反向代理或負載均衡服務(Nginx從1.9.0開始就開始支持TCP的代理了)。
- 前端業(yè)務數(shù)據(jù)緩存服務。
Web服務應用產(chǎn)品性能對比
- 靜態(tài)數(shù)據(jù)的訪問上:處理小文件(小于1MB)時,Nginx和Lighttpd比Apache更有優(yōu)勢,Nginx處理小文件的優(yōu)勢明顯,Lighttpd綜合最強。
- 動態(tài)數(shù)據(jù)的訪問上:三者差距不大,Apache更有優(yōu)勢,因為處理動態(tài)數(shù)據(jù)的能力在于PHP(Java)和后端數(shù)據(jù)庫的服務能力,也就是說瓶頸不在Web服務器上。
- 一般情況下普通PHP引擎支持的并發(fā)連接參考值3001000。Java引擎和數(shù)據(jù)庫的并發(fā)連接參考值3001500。
為什么Nginx比Apache的性能高?
- Nginx使用最新版的eepoll(Linux 2.6內(nèi)核)和kqueue(FreeBSD)異步網(wǎng)絡I/O模型,而Apache使用的是傳統(tǒng)的select模型。
- 目前Linux下能夠承受高并發(fā)訪問的Squid、Memcached軟件采用都是epoll模型。
- 處理大量的連接的讀寫時,Apache所采用的select網(wǎng)絡I/O模型比較低。
如何正確采用Web服務器?
- 靜態(tài)業(yè)務:如果是高并發(fā)場景,盡量采用Nginx或Lighttpd,二者首選Nginx。
- 動態(tài)業(yè)務:理論上采用Nginx和Apache均可,建議使用Nginx,為了避免相同業(yè)務服務的軟件多樣化,增加維護成本,動態(tài)業(yè)務可以使用Nginx兼做前端代理,再根據(jù)頁面的元素或目錄轉發(fā)到其他的服務器進行處理。
- 既有動態(tài)業(yè)務又有靜態(tài)業(yè)務,就用Nginx。
關于部署,就不在重復了,如果需要請移步《Java高級架構之FastDFS分布式文件集群》:
使用IDEA場景啟動器創(chuàng)建工程
創(chuàng)建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>
創(chuàng)建必要的包
- annotation:存放相關的注解
- autoconfiguation: 存儲自動配置類
- factory: 存放工廠類
- properties: 存放配置參數(shù)類
- 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);
}
}
創(chuàng)建相關的工廠類:
// StorageClientFactory.java
// 用于創(chuàng)建連接對象的工廠類
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 {
// 連接超時時間
// 網(wǎng)絡超時時間
// 字符集編碼
// 是否使用Token
// Token加密密鑰
// 跟蹤器IP地址,多個使用分號隔開
// 連接池的連接對象最大個數(shù)
// 連接池的最大空閑對象個數(shù)
// 連接池的最小空閑對象個數(shù)
// 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 = "";
// 需要創(chuàng)建相關的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());
}
/**
* 創(chuàng)建連接池
*/
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 字節(jié)數(shù)組
* @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地址,根據(jù)客戶端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中使用便捷的配置提示功能,我們需要創(chuàng)建元數(shù)據(jù)文件(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"
}
]
}
]
}
應用到項目中
創(chuàng)建SpringBoot項目,勾選Web選項,版本選擇1.5.20
進入場景啟動器的項目目錄執(zhí)行mvn clean install 將其安裝到本地
在POM.xml文件中添加依賴:
<dependency> <groupId>com.bluemiaomiao</groupId> <artifactId>fastdfs-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
記得開啟IDEA的自動導入功能
創(chuàng)建配置文件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
創(chuàng)建控制器類測試方法
// controllers.DownloadController.java
@Controller
@RequestMapping(value = "/download")
public class DownloadController {
@Autowired
private FastdfsClientService service;
@ResponseBody
@RequestMapping(value = "/image")
public String image() throws Exception {
// 之前上傳過的數(shù)據(jù),實際應用場景應該使用SQL數(shù)據(jù)庫來存儲
return service.autoDownloadWithToken("group1", "M00/00/00/wKhQA1ysjSGAPjXbAAVFOL7FJU4.tar.gz", "192.168.80.1");
}
}
項目主頁:https://github.com/bluemiaomiao/fastdfs-spring-boot-starter
國內(nèi)項目主頁:https://gitee.com/bluemiaomiao/fastdfs-spring-boot-starter
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Spring?Boot實現(xiàn)web.xml功能示例詳解
這篇文章主要介紹了Spring?Boot實現(xiàn)web.xml功能,通過本文介紹我們了解到,在Spring Boot應用中,我們可以通過注解和編程兩種方式實現(xiàn)web.xml的功能,包括如何創(chuàng)建及注冊Servlet、Filter以及Listener等,需要的朋友可以參考下2023-09-09
Spring Security CsrfFilter過濾器用法實例
這篇文章主要介紹了Spring Security CsrfFilter過濾器用法實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-11-11
springboot創(chuàng)建線程池的兩種方式小結
這篇文章主要介紹了springboot創(chuàng)建線程池的兩種方式小結,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
Mybatis-Plus使用saveOrUpdate及問題解決方法
本文主要介紹了Mybatis-Plus使用saveOrUpdate及問題解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-01-01
mybatis-plus動態(tài)數(shù)據(jù)源讀寫分離方式
在分布式項目開發(fā)中,動態(tài)數(shù)據(jù)源的配置與使用至關重要,通過創(chuàng)建DynamicDatasourceService,實現(xiàn)數(shù)據(jù)源的動態(tài)添加與調(diào)用,有效管理主從庫操作,減輕數(shù)據(jù)庫壓力,此外,通過配置類與@DS注解,實現(xiàn)了靈活的分庫查詢功能,為高效處理數(shù)據(jù)提供了強有力的支持2024-10-10
Java中的ConcurrentLinkedQueue松散隊列解析
這篇文章主要介紹了Java中的ConcurrentLinkedQueue松散隊列解析,鏈表是松散的,鏈表節(jié)點并不都是有效的,允許存在無效節(jié)點val=null,但是只有最后一個節(jié)點才能next=null,需要的朋友可以參考下2023-12-12

