有的页面访问人数很多,但是在一定时间段内不会改变(数据没变化).页面静态化.
好处:
①降低数据库或缓存压力
②提高响应速度,增强用户体验.
页面静态化这是一种方案,而模板技术是实现这种方案的技术。
静态页面=模板(结构)+数据(内容)
单体项目方案分析
集群项目方案分析
技术点:
velocity + rabbitMq + Nosql(redis) + Dfs(fastdfs)
课程主页静态化实现
(1)准备模板
(2)准备模块:hrm-course-service-2020
Pom
<!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
导入
测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CouseServiceApplication2020.class)
public class ICourseTypeServiceTest {
@Autowired
private ICourseTypeService courseTypeService;
@Test
public void testVelocityTmplate() {
//数据+模板=静态页面
Map<String,Object> model = new HashMap<>();
model.put("staticRoot","D:\\0924\\2020-02-24-hrm-课程中心-课程主站-页面静态化实现\\resources\\home");
model.put("courseTypes",courseTypeService.treeData(0L));
VelocityUtils.staticByTemplate(model,
"D:\\0924\\2020-02-24-hrm-课程中心-课程主站-页面静态化实现\\resources\\home\\home.vm",
"D:\\0924\\2020-02-24-hrm-课程中心-课程主站-页面静态化实现\\resources\\home\\home.vm"+".html");
}
(3)创建前台触发页面
(4)课程模块,初始化课程管理首页
测试方法
//初始化课程管理首页
@Test
public void testInitCourseSiteIndex()throws Exception{
courseTypeService.InitCourseSiteIndex();
}
@Autowired
private PageConfigClient pageConfigClient;
@Override
public void InitCourseSiteIndex() {
//1 准备模板,并且上传fastdfs
//2存放数据到redis
List<CourseType> courseTypes = queryTypeTree(0L);
String dataKey = "courseTypes";
redisClient.set(dataKey, JSONArray.toJSONString(courseTypes));
//3调用静态化接口产生静态页面,并且放入fastdfs
String pageName = "CourseIndex";
//本来应该通过PageName获取page后设置pageconfig传递,由于数据在查询端,还不如直接传入pageName到那边查询.
Map<String,String> map = new HashMap<>();
map.put("dataKey",dataKey);
map.put("pageName",pageName);
pageConfigClient.staticPage(map);
//4往消息队列放一个消息,让pageAgent来下载静态页面
}
@Override
public boolean insert(CourseType entity) {
courseTypeMapper.insert(entity);
List<CourseType> courseTypes = queryTypeTree(0L);
courseTypeCache.setCourseTypes(courseTypes);
//同步页面静态化
this.InitCourseSiteIndex();
return true;
}
@Override
public boolean deleteById(Serializable id) {
courseTypeMapper.deleteById(id);
List<CourseType> courseTypes = queryTypeTree(0L);
courseTypeCache.setCourseTypes(courseTypes);
//同步页面静态化
this.InitCourseSiteIndex();
return true;
}
@Override
public boolean updateById(CourseType entity) {
courseTypeMapper.updateById(entity);
List<CourseType> courseTypes = queryTypeTree(0L);
courseTypeCache.setCourseTypes(courseTypes);
//同步页面静态化
this.InitCourseSiteIndex();
return true;
}
(5)页面生成者
PageConfigController
//页面静态化接口
@PostMapping("/staticPage")
AjaxResult staticPage(@RequestBody Map<String, String> map){
String dataKey = map.get("dataKey");
String pageName = map.get("pageName");
try {
pageConfigService.staticPage(dataKey,pageName);
return AjaxResult.me();
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.me().setSuccess(false).setMessage("静态化失败!"+e.getMessage());
}
}
@Override
public void staticPage(String dataKey, String pageName) {
FileOutputStream os = null;
InputStream is = null;
try {
//一 页面静态化
Pager pager = pagerMapper
.selectList(new EntityWrapper<Pager>().eq("name", pageName)).get(0);
String templateUrl = pager.getTemplateUrl(); //fastdfs地址 zip包的
String templateName = pager.getTemplateName(); //要执行模板文件
//1.1 下载fastdfs上面压缩包
Response response =
fastDfsClient.download(templateUrl); //通过fastdfs下载压缩包
is = response.body().asInputStream();
//1.2 所有静态化中间数据都写入临时目录
//1)跨操作系统
//2 操作系统会自动维护,不用删除
String tmpdir=System.getProperty("java.io.tmpdir");
System.out.println(tmpdir+"jjjjj.........");
String zipPath = tmpdir+"/temp.zip"; //要下载zip包路径
String unZipPath = tmpdir + "/temp/"; //解压到路径
os = new FileOutputStream(zipPath);
IOUtils.copy(is , os); //保存到本地
ZipUtil.unzip(zipPath,unZipPath); // 解压缩
//1.3 获取到模板
String templatePath = unZipPath+"/"+templateName; //模板路径 temp/home.vm
System.out.println(templatePath+"zz.........");
//2 生成静态页面的路劲
String templatePagePath = templatePath+".html"; //本地静态页面地址
System.out.println(templatePagePath+"xxx.........");
//3 生成数据
String courseTypes =redisClient.get("courseTypes"); //redis数据
List<CourseTypeDto> courseTypeDtos = JSONArray.parseArray(courseTypes,CourseTypeDto.class);
Map<String, Object> modelMap = new HashMap<>(); //封装两个参数作为一个对象传入进去
modelMap.put("staticRoot", unZipPath);
modelMap.put("courseTypes", courseTypeDtos);
//4 做页面静态化
VelocityUtils.staticByTemplate(modelMap,templatePath,templatePagePath); //进行页面静态化
// 5 传递到fastdfs
String pageUrl = fastDfsClient.upload(
new CommonsMultipartFile(createFileItem(new File(templatePagePath),"file")));
//二 PageConfig 并且进行保存
PageConfig config = new PageConfig();
config.setTemplateUrl(templateUrl);
config.setTemplateName(templateName);
config.setDataKey(dataKey);
config.setPhysicalPath(pager.getPhysicalPath());
config.setDfsType(0L); //0表示fastDfs
config.setPageUrl(pageUrl);
config.setPageId(pager.getId());
pageConfigMapper.insert(config);
//三 往消息队列放入消息
String routingKey = siteMapper
.selectList(new EntityWrapper<Site>().eq("id", pager.getSiteId())).get(0).getSn();
System.out.println(routingKey+"ddh......");
JSONObject jsonObject = new JSONObject();
jsonObject.put("fileSysType",0);
jsonObject.put("staticPageUrl",pageUrl);
jsonObject.put("physicalPath",pager.getPhysicalPath());
System.out.println(jsonObject.toJSONString()+"dbl.....");
rabbitTemplate.convertAndSend(
RabbitmqConstants.EXCHANGE_DIRECT_INFORM,routingKey,jsonObject.toJSONString());
}catch (Exception e){
e.printStackTrace();
}finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
创建FileItem
*/
private FileItem createFileItem(File file,String filedName) {
FileItemFactory factory = new DiskFileItemFactory(16, null);
FileItem item = factory.createItem(filedName, "text/plain", true, file.getName());
int bytesRead = 0;
byte[] buffer = new byte[8192];
try {
FileInputStream fis = new FileInputStream(file);
OutputStream os = item.getOutputStream();
while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
return item;
}
(6)页面发布消费方
package cn.itsource.hrm.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 主站
* 课程管理
* 职位管理
*/
@Configuration
public class RabbitmqConfig {
@Value("${rabbitmq.queues.routingKey}")
public String routingKey;
public static final String EXCHANGE_DIRECT_INFORM = RabbitmqConstants.EXCHANGE_DIRECT_INFORM;
public static final String QUEUE_INFORM_PAGESTATIC = RabbitmqConstants.QUEUE_INFORM_PAGESTATIC;
/**
* 交换机配置
* ExchangeBuilder提供了fanout、direct、topic、header交换机类型的配置
*
* @return the exchange
*/
@Bean(EXCHANGE_DIRECT_INFORM)
public Exchange exchange_direct_inform() {
//durable(true)持久化,消息队列重启后交换机仍然存在
return ExchangeBuilder.directExchange(EXCHANGE_DIRECT_INFORM).durable(true).build();
}
//声明队列
@Bean(QUEUE_INFORM_PAGESTATIC) //交给spring管理的bean的名字可以随便的
public Queue pageStaticQueue() {
Queue queue = new Queue(QUEUE_INFORM_PAGESTATIC); //队列名
return queue;
}
// Qualifier//获取特定名称bean
@Bean
public Binding BINDING_QUEUE_INFORM_HRMJOBSITE(@Qualifier(QUEUE_INFORM_PAGESTATIC) Queue queue,
@Qualifier(EXCHANGE_DIRECT_INFORM) Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
}
public String getRoutingKey() {
return routingKey;
}
public void setRoutingKey(String routingKey) {
this.routingKey = routingKey;
}
}
@Component
public class StaticPageDownloadHandler {
@Autowired
private FastDfsClient fastDfsClient;
@RabbitListener(queues = RabbitmqConstants.QUEUE_INFORM_PAGESTATIC)
public void receiveHomeSite(String msg, Message message, Channel channel) {
//msg -fileSysType,staticPageUrl,physicalPath
JSONObject jsonObject = JSONObject.parseObject(msg);
Integer fileSysType = jsonObject.getInteger("fileSysType");
String staticPageUrl = jsonObject.getString("staticPageUrl");
String physicalPath = jsonObject.getString("physicalPath");
System.out.println(staticPageUrl);
System.out.println(physicalPath);
switch (fileSysType) {
case 0: //fastdfs
fastDfsDownloadAndCopy(staticPageUrl, physicalPath);
break;
case 1: //hdfs
hdfsDownloadAndCopy(staticPageUrl, physicalPath);
break;
default:
break;
}
}
/**
* 通过fastdfs下载文件,并且拷贝到特定的目录
* @param staticPageUrl
* @param physicalPath
*/
private void hdfsDownloadAndCopy(String staticPageUrl, String physicalPath) {
//@TODO
}
/**
* 通过fastdfs下载文件,并且拷贝到特定的目录
* @param staticPageUrl
* @param physicalPath
*/
private void fastDfsDownloadAndCopy(String staticPageUrl, String physicalPath) {
Response response = fastDfsClient.download(staticPageUrl);
InputStream is = null;
OutputStream os = null;
try {
is = response.body().asInputStream();
os = new FileOutputStream(physicalPath);
IOUtils.copy(is,os);
} catch (Exception e) {
e.printStackTrace();
}
finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}