图片统一处理
在集群环境下面,图片要使用分布式文件系统统一管理。
方案选择&流程分析
选择合适分布式文件系统.
分布式文件系统:多个文件系统通过管理软件进行管理,得到分布式文件系统.
好处:
-
单点故障
-
海量存储
-
高可用
方案1:租用别人已经搭建好了的.
阿里云对象存储(收费),七牛云(10G内免费)好处:方便,小量数据可以
坏处:大量数据时,要花很多钱.
方案2:自己搭建-采纳
hdfs(hadoop),FastDfs(国产,小文件)…
采纳自己搭建分布式文件系统-fastdfs,这个搭建的过程和我们程序员没有关系,需要运维人员(在我们的项目组中专门有一个负责搭建)搭建。
具体实现步骤;
- 搭建fastDfs文件系统
- 上传图片
- 回显图片
Fastdfs
FastDFS 是用 c 语言编写的一款开源的分布式文件系统。FastDFS 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。
Tracker server 作用是负载均衡和调度,通过 Tracker server 在文件上传时可以根据一些策略找到 Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。
Storage server 作用是文件存储,客户端上传的文件最终存储在 Storage 服务器上,Storageserver 没有实现自己的文件系统而是利用操作系统 的文件系统来管理文件。可以将storage称为存储服务器。
服务端两个角色:
Tracker:管理集群,tracker 也可以实现集群。每个 tracker 节点地位平等。收集 Storage 集群的状态。
Storage:实际保存文件 Storage 分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。
参考:
官方网站:https://github.com/happyfish100/
配置文档:https://github.com/happyfish100/fastdfs/wiki/
参考资料:https://www.oschina.net/question/tag/fastdfs
Java客户端:https://github.com/happyfish100/fastdfs-client-java
上传和下载流程
上传
客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。
组名:文件上传后所在的 storage 组名称,在文件上传成功后有 storage 服务器返回,需要客户端自行保存。
虚拟磁盘路径:storage 配置的虚拟路径,与磁盘选项 store_path*对应。如果配置了
store_path0 则是 M00,如果配置了 store_path1 则是 M01,以此类推。
数据两级目录:storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据
文件。
文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储
服务器 IP 地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。
下载
最简单的FastDFS架构
(1)创建Maven工程fastDFSdemo
由于FastDFS客户端jar包并没有在中央仓库中,所以需要使用下列命令手动安装jar包到Maven本地仓库(将jar包放到d盘setup目录)课程配套的本地仓库已经有此jar包,此步可省略。
mvn install:install-file -DgroupId=org.csource.fastdfs -DartifactId=fastdfs -Dversion=1.2 -Dpackaging=jar -Dfile=d:\setup\fastdfs_client_v1.20.jar
pom.xml中引入
<!-- https://mvnrepository.com/artifact/cn.bestwu/fastdfs-client-java -->
<dependency>
<groupId>cn.bestwu</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27</version>
</dependency>
(2)添加配置文件fdfs_client.conf ,将其中的服务器地址设置为192.168.25.133
//…
tracker_server=122.51.119.246:22122
//…
(3)创建java类,main方法代码如下:
// 1、加载配置文件,配置文件中的内容就是 tracker 服务的地址。
ClientGlobal.init("D:/maven_work/fastDFS-demo/src/fdfs_client.conf");
// 2、创建一个 TrackerClient 对象。直接 new 一个。
TrackerClient trackerClient = new TrackerClient();
// 3、使用 TrackerClient 对象创建连接,获得一个 TrackerServer 对象。
TrackerServer trackerServer = trackerClient.getConnection();
// 4、创建一个 StorageServer 的引用,值为 null
StorageServer storageServer = null;
// 5、创建一个 StorageClient 对象,需要两个参数 TrackerServer 对象、StorageServer 的引用
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
// 6、使用 StorageClient 对象上传图片。
//扩展名不带“.”
String[] strings = storageClient.upload_file("D:/pic/benchi.jpg", "jpg",
null);
// 7、返回数组。包含组名和图片的路径。
for (String string : strings) {
System.out.println(string);
}
控制台输出如下结果:
group1
M00/00/00/wKgZhVkMP4KAZEy-AAA-tCf93Fo973.jpg
在浏览器输入:
http://122.51.119.246/group1/M00/00/00/wKgZhVkMP4KAZEy-AAA-tCf93Fo973.jpg
后台上传服务
导入jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Eureka 客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--配置中心支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
配置
Bootstrap.yml
spring:
profiles:
active: dev
cloud:
config:
name: application-common #github上面名称
profile: ${spring.profiles.active} #环境 java -jar -D xxx jar
label: master #分支
discovery:
enabled: true #从eureka上面找配置服务
service-id: hrm-config-server #指定服务名
#uri: http://127.0.0.1:1299 #配置服务器 单机配置
eureka: #eureka不能放到远程配置中
client:
service-url:
defaultZone: http://localhost:1010/eureka #告诉服务提供者要把服务注册到哪儿 #单机环境
instance:
prefer-ip-address: true #显示客户端真实ip
配置库 application-common-dev.yml
server:
port: 2090
spring:
application:
name: hrm-common
实现fastdfs服务
<dependency>
<groupId>org.hhw.aigou</groupId>
<artifactId>basic-util</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cn.bestwu/fastdfs-client-java -->
<dependency>
<groupId>cn.bestwu</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27</version>
</dependency>
fastdfscontroller
@RestController
public class FastDfsController {
@RequestMapping(value = "/upload",method = RequestMethod.POST)
public AjaxResult upload(@RequestParam(value = "file",required = true)MultipartFile file){
try {
System.out.println(file.getOriginalFilename() + ":" + file.getSize());
String originalFilename = file.getOriginalFilename();
// xxx.jpg
String extName = originalFilename.substring(originalFilename.lastIndexOf(".")+1);
System.out.println(extName);
String filePath = FastDfsApiOpr.upload(file.getBytes(), extName);
return AjaxResult.me().setResultObj(filePath);
} catch (IOException e) {
e.printStackTrace();
return AjaxResult.me().setSuccess(false).setMessage("上传失败!"+e.getMessage());
}
}
// /goup1/xxxxx/yyyy
@RequestMapping(value = "/del",method = RequestMethod.DELETE)
public AjaxResult upload(@RequestParam(value = "filePath",required = true)String filePath){
String pathTmp = filePath.substring(1); // goup1/xxxxx/yyyy
String groupName = pathTmp.substring(0, pathTmp.indexOf("/")); //goup1
String remotePath = pathTmp.substring(pathTmp.indexOf("/"));// /xxxxx/yyyy
FastDfsApiOpr.delete(groupName, remotePath);
return AjaxResult.me();
}
}
前端
<el-upload
class="upload-demo"
action="http://127.0.0.1:9527/services/common/upload"
:on-preview="handlePreview"
:on-remove="handleRemove"
:file-list="fileList2"
:on-success="handleSuccess"
list-type="picture"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
缩略图数据:
修改是做回显:
handleSuccess(response, file, fileList){
//上传成功回调
console.log(response, file, fileList)
console.log(this.form.logo)
this.form.logo = file.response.resultObj;
console.log(this.form.logo)
},
handleRemove(file, fileList) {
var filePath =file.response.resultObj;
this.$http.delete("/common/del?filePath="+filePath)
.then(res=>{
if(res.data.success){
this.$message({
message: '删除成功!',
type: 'success'
});
}else{
this.$message({
message: '删除失败!',
type: 'error'
});
}
})
},
handlePreview(file) {
console.log(file);
},
课程类型优化方案
为什么要优化
每次都要从数据库查询一次.
使用的地方&问题:
-
后台管理
课程类型树,在后面添加课程时会反复使用。
就算每个人使用时只查询一次,如果人比较多.也要对数据库进行频繁操作. -
课程主页
缓存还不够优化,如果一亿并发,就会访问redis一亿次.对缓存服务器也是一种压力.
优化方案
1)后台管理
缓存:用内存查询替换数据库磁盘查询.
经常查询,很少修改
2)课程主页
不能用缓存,还是会高并发访问缓存中数据.
页面静态化:以不发请求静态页面代替要发请求静态页面或者动态页面.没有对后台数据获取.
- 每次访问都是html静态页面,不能访问数据
- 只要数据发生改变,就要重新生成新的静态页面替换原来的静态页面.
页面基本不会变,除了数据
课程类型后台缓存优化
fastJson入门
//对象转换为json字符串,json字符串转换java对象
//JSONObject/JSONArray.toJSONString JSONObject.parseObject JSONArray.parseArray
@Test
public void test() throws Exception{
Person zs = new Person(1L, "zs");
//单个对象--json对象
String jsonObj = JSONObject.toJSONString(zs);
System.out.println(jsonObj);
//集合---json数组
String jsonArray = JSONArray.
toJSONString(Arrays.asList(zs, zs,
new Person(2L, "ls")));
System.out.println(jsonArray);
//json对象--单个对象
Person person = JSONObject.parseObject(jsonObj, Person.class);
System.out.println(person);
//json数组--集合
List<Person> persons = JSONArray.parseArray(jsonArray, Person.class);
System.out.println(persons);
}