分布式与集群
分布式
百度百科给出的定义是----->>>
简单来收就是现在的企业将不同的业务分不到不同的地方,以减少单一数据中心的压力;
在实际操作中分布式与集群常常会结合使用;
比如下面这个分布式图解----->>
集群
百度百科给的定义----->
简单来说就是由原来单一业务服务器发展为多台相同的业务服务器,他们为企业提供了一组业务服务器,每一台服务器都是一个节点,当主节点没有发生故障时可以帮助主节点分散一些负载,当主节点发生故障时另一个节点自动接管服务, 提高了系统的可靠性;
考虑到在分布式架构上,万一哪一天某一个服务器宕机了,这个业务就不可访问了,为了避免这种情况,所以就要有替补的服务器跟上,在原来分布式架构的基础上有了下面的演变 分布式加集群
这种分布式加集群的好处就是:
1,减少每一个服务器的负载(最大负载量比单纯的分布式要大);
2,提高了系统的容错性与可靠性;
3,提高了并发吞吐量;
分布式项目的开发
这里只是做一个比较简单的分布式项目开发,
当然这是在之前写的代码的基础上去完成分布式的实现------>>>
[解密贴]那些年我们上传的文件都去了哪里?
分布式的实现
首先需要有一个框架
多个服务器,这里选用多个tomcat做为服务器
整个框架是这样的---->
灰常简单,是不是!!
实践出真知----->
环境搭建
准备服务器
第一步,复制本地的tomcat,到本地磁盘的其他目录(当然如果有虚拟机的可以直接在虚拟机上开启),mysql服务器
第二步----->>>配置服务器
主要是解决一下两个tomcat启动的端口号冲突问题
哪些端口号会冲突了呢?
3306这是mysql的默认端口,暂时没有冲突
第一个肯定是8080 端口,还有别的吗?
暂且不知道,那就启动两个tomcat看看会报什么异常吧;
在idea中启动项目–tomcat
接着启动另一个tomcat,启动之前先把log里的日志文件清掉,以便于更好的查看端口冲突;
8080端口冲突
8005端口冲突
找到冲突的端口号之后,修改有冲突的端口号---->
注意,这里没有涉及到8443冲突,如果改了,那么tomcat虽然可以开启,但是关闭的时候会关不上;
还有一个很重要的配置文件---->>
修改web.xml中的 名为default的servlet 初始化参数 readonly为 false
刚开始配置的时候会没有这个属性,需要手动添加(因为有许多默认的配置信息并没有体现出来----只读默认为true,即可以读取但不允许写入,要想传输数据,那么要改为false)
这时候启动一下看看能否正常启动
正常关闭;
但是我发现了一个问题,即使复制了一份tomcat,并且设置了监听端口为8090,但是也会默认去找8080端口
想了想原因,原来是设置了默认的系统环境变量 CATALINA_HOME 以及path,删掉这两个环境变量就好了;
开始上传文件了;
首先准备一个小案例----->>
前端---->>
<%--
Created by IntelliJ IDEA.
User: Gavin
Date: 2021/12/20
Time: 19:46
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript"
src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.min.js">
</script>
<!-- 给进度条君添加一些样式-->
<style>
.progress {
width: 400px;
height: 10px;
border-radius: 10px;
margin: 10px 0px;
overflow: hidden;
}
.progress > div {
width: 0px;
height: 100px;
background-color: yellowgreen;
transition: all .3s ease;
}
</style>
</head>
<body>
<form method="post" action="addPlayer.do">
账号:<input type="text" name="name" id="name" placeholder="请输入账号"/>
<dev>${msg}</dev><!--检查信息-->
<br>
密码:<input type="password" name="pwd" id="pwd" placeholder="请输入密码"/>
<dev>${Info}</dev>
<br>
昵称:<input type="text" name="nickname" placeholder="请输入昵称"><br>
头像:<input type="file" id="upFile" value="选择文件"/>
<a href="javascript:void(0)" id="uploadFile" onclick="loadPic()">立即上传</a>
<br>
<div class="progress">
<div>
</div>
</div>
<div id="divNum"></div>
<div id="divimg" style="width: 150px;height: 200px">
<img id="img" style="width: 140px;height: 180px;" alt="您还未上传图片"/>
</div>
<br>
<input type="submit" value="注册"/>
</form>
<script type="text/javascript">
function loadPic() {
var photoFile = $("#upFile")[0].files[0];
// 将文件传到这个对象中
var formData = new FormData();
formData.append("headerPicture", photoFile);
$.ajax({
url: "${pageContext.request.contextPath}/fileUpload2.do",
data: formData,//传送的数据
type: "post",
processData: false,//告诉浏览器发送的是一个对象 请求数据
contentType: false,//告诉浏览器 请求数据的类型 二进制类型
// dataType: "json",
success: function (data) {//接收返回来的数据并修改img标签里的内容
console.log(data);
alert(data.msg);
$("#img").attr("src", data.newFileName);
},
xhr: function () {
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function (e) {
console.log(e);
var progressRate = (e.loaded / e.total) * 100 + '%';
if (photoFile != null) {
$("#divNum").text(progressRate);
}
if (photoFile != null) {
$('.progress > div').css('width', progressRate);
}
})
return xhr;
}
}
);
}
</script>
</body>
</html>
后端---->>
package com.gavin.controller;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Controller
public class FileController2 {
//上传到的文件位置---非本地
private final static String FILEDIR="http://127.0.0.1:8090/loadPic/";
@RequestMapping("/fileUpload2.do")
@ResponseBody
public Map<String, String> upDataPicture(MultipartFile headerPicture, HttpServletRequest request) throws IOException {//用MultipartFile接收数据
Map<String, String> map = new HashMap<>();
// 如果文件不存在
if (headerPicture==null){
map.put("msg","请上传图片!");
return map;
}
//有文件,则获取文件名
String originalFilename = headerPicture.getOriginalFilename();
// 控制文件的大小
if (headerPicture.getSize()>1024*1024*3){
map.put("msg","文件大小不超过3M");
return map;
}
// 避免名字冲突,用UUID替换文件名,但是扩展名不变
String uuid = UUID.randomUUID().toString();
// 获取拓展名
String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));
if (!(".jpg".equals(extendsName) || ".png".equals(extendsName))) {
map.put("msg", "文件类型不符合要求");
return map;
}
//拼接成新名字
String newFileName = uuid.concat(extendsName);
System.out.println(newFileName);
//创建 jersey 包中的client对象,用于跨服务器请求
Client client= Client.create();
System.out.println(client);
WebResource resource = client.resource(FILEDIR + newFileName);
String result= resource.put(String.class, headerPicture.getBytes());
System.out.println(result);
map.put("msg","文件上传成功");
// 返回新生成的文件名
map.put("newFileName",FILEDIR+newFileName);
map.put("fileType",headerPicture.getContentType());
return map;
}
}
我们也可以开一个虚拟机,在虚拟机开启一个图片服务器来接收;
为了便于服务器的迁移,我们上传的文件其实没必要写死地址的,
这个可以搞一个配置文件
通过配置文件来读取web地址;
在文件上传中的遇到的几个问题
第一个---->>中文文件名编码问题:
通过过滤器来解决,如果要记录上传时间还需要通过注解或者配置转换器来解决;
第二个---->>文件存储的位置问题
1,放在当前项目下,作为静态资源,
2,放在别处即单独开一个服务器专门用于处理相应的业务;
第三个----->>分服务器上传---->>
分服务器,实际企业中会用到
1,数据库服务器:运行我们的数据库
2,缓存和消息服务器:负责处理大并发访问的缓存和消息
3,文件服务器:负责存储用户上传文件的服务器。
4,应用服务器:负责部署我们的应用
总结:分服务器处理的目的是让服务器各司其职,
从而提高我们项目的运行效率核容错率