为什么要用 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 引擎支持的并发连接参考值 300~1000.Java 引擎和数据库的并发连接参考值 300~1500.
为什么 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 分布式文件集群》:https://blog.51cto.com/xvjunjie/2377669
使用 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.YAML 配置文件:
- // 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"
- }
- ]
- }
- ]
- }
将自定义 starter 添加到项目
创建 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.YAML
- 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");
- }
- }
项目主页:
国内项目主页:
来源: http://blog.51cto.com/xvjunjie/2381361