文章目录
1.当页面数据发生变化时触发页面静态化方法
页面数据发生改变时触发页面静态化方法
1.将改变的数据存放到redis中
2.将数据在redis中的key和需要静态化的页面名称通过feign发送给页面静态化服务
3.页面静态化服务通过页面名字从数据库中查找页面的模板,并下载解压到本地临时目录
4.通过key从redis中查找静态化需要的数据
5.将数据和模板整合生成html
6.将html上传到fastdfs
7.在静态化服务端集成rabbitmq发送端
8.在nginx端设置代理服务,集成rabbitmq接收端,从fastdfs下载html,并将html放到nginx目录中
private void doStaticForHome(){
//页面静态化的数据存储到redis
List<CourseType> courseTypesDataTree = selectList(null);
//将课程类型转成json
String data = JSON.toJSONString(courseTypesDataTree);
//将数据保存到redis
redisClient.set(RedisContants.KEY_DATATREE_COURSE_TYPE, data);
HashMap<String, String> map = new HashMap<>();
//页面静态化页面名称
map.put(PageStaticContants.STATIC_PAGE_NAME, PageStaticContants.STATIC_COURSETYPE);
//页面静态化存在redis中的key
map.put(PageStaticContants.STATIC_PAGE_DATA, RedisContants.KEY_DATATREE_COURSE_TYPE);
//页面静态化数据需要封装到那个key中
map.put(PageStaticContants.TEMPLATE_DATA_KEY, "courseTypes");
//调用页面静态化服务
pageClient.pageStatic(map);
}
2.页面静态化服务
2.1jar包
<!--velocity依赖包-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
</dependency>
<!--spirngboot集成rabbitmq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
...
2.2 application.yml
spring:
application:
name: page-server
datasource:
username: root
password: 123456
url: jdbc:mysql:///hrm_page
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
mybatis-plus:
mapper-locations: classpath:com/wal/hrm/mapper\/*Mapper.xml
server:
port: 2070
max-http-header-size: 102400000
tomcat:
max-http-header-size: 102400000
eureka:
instance:
hostname: page-server
instance-id: page-server:2070
prefer-ip-address: true
client:
service-url:
defaultZone: http://peer1:1010/eureka/
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtualHost: /
feign:
hystrix:
enabled: true #开启熔断支持
client:
config:
remote-service: #服务名,填写default为所有服务
connectTimeout: 3000
readTimeout: 3000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 50000
2.3.主配置类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableTransactionManagement
@Import({Swagger2.class, MyBatisPlusConfig.class})
public class PageServerApplication {
public static void main(String[] args) {
SpringApplication.run(PageServerApplication.class);
}
}
2.4页面静态化controller
@RestController
@RequestMapping("/static")
@CrossOrigin
public class PageStaticController {
@Autowired
private IPageStaticServer pageStaticServer;
@PostMapping("/page")
public AjaxResult pageStatic(@RequestBody Map map){
AjaxResult result = pageStaticServer.pageStatic(map);
return result;
}
}
2.5页面静态化service
public AjaxResult pageStatic(Map map) {
//根据页面名字从数据库中查找模板的存放路径
String name =(String) map.get(PageStaticContants.STATIC_PAGE_NAME);
Page page = pageMapper.findPageByName(name);
String templateUrl = page.getTemplateUrl();
//处理路径判断是不是 / 开头
String path = templateUrl.startsWith("/")?templateUrl.substring(1):templateUrl;
//拿到组名先找到第一个/的位置
int index = path.indexOf("/");
String groupName=path.substring(0, index);
String fileName=path.substring(index+1);
//从fastdfs下载模板
byte[] result = fastDfsClient.download(groupName, fileName);
if (result == null || result.length<1){
throw new MyException(ErrorCode.ERROR_CODE_TEMPLATE_DOWNLOAD_ERROR.getErrorMsg());
}
//将模板保存到本地:系统临时目录C:\Users\*****\AppData\Local\Temp\
String basicPath = System.getProperty("java.io.tmpdir")+"template\\";
String templateName =name+".zip";
String filepath = basicPath + templateName;
try {
File file = new File(filepath);
if (!file.exists()){
file.getParentFile().mkdirs();
file.createNewFile();
}
FileCopyUtils.copy(result, file);
//解压模板
ZipUtils.unZip(filepath, basicPath);
//生成html
//拿到数据需要包装的key
String dataKey = (String) map.get(PageStaticContants.TEMPLATE_DATA_KEY);
//拿到数据在redis中的key
String keyData = (String) map.get(PageStaticContants.STATIC_PAGE_DATA);
//从redis中查出数据
AjaxResult result1 = redisClient.get(keyData);
JSONArray dataRes = JSON.parseArray(result1.getResultObj().toString());
HashMap<String, Object> model = new HashMap<>();
model.put(dataKey, dataRes);
model.put("staticRoot", basicPath);
//模板的路径
String templatePath = basicPath+name+".vm";
//html输出路径
String htmlPath = basicPath+name+".html";
VelocityUtils.staticByTemplate(model, templatePath, htmlPath);
//将html上传到fastdfs
byte[] bytes = FileCopyUtils.copyToByteArray(new File(htmlPath));
AjaxResult result2 = fastDfsClient.uploadhtml(bytes, "html");
if(!result2.isSuccess()){
throw new MyException(ErrorCode.ERROR_CODE_HTML_UPLOAD_ERROR.getErrorMsg());
}
//保存配置信息
PageConfig pageConfig = new PageConfig();
pageConfig.setDataKey(keyData);
pageConfig.setDfsType(1l);
pageConfig.setPageId(page.getId());
pageConfig.setPageUrl(result2.getResultObj().toString());
pageConfig.setPhysicalPath(page.getPhysicalPath());
pageConfig.setTemplateUrl(page.getTemplateUrl());
pageConfigMapper.insert(pageConfig);
//查询routingKey
String routingKey = siteMapper.selectById(page.getSiteId()).getSn();
//准备发送的消息
HashMap<String, Object> map1 = new HashMap<>();
//html的存放地址
map1.put(MqContants.HTML_PATH, result2.getResultObj().toString());
//存放到nginx的地址
map1.put(MqContants.NGINX_PATH, page.getPhysicalPath().toString());
String msg = JSON.toJSONString(map1);
//向nginx代理发送mq消息
rabbitTemplate.convertAndSend(MqContants.EXCHANGE_NAME,routingKey ,msg);
} catch (Exception e) {
e.printStackTrace();
}
return AjaxResult.me();
}
2.6.velocityutils
public class VelocityUtils {
private static Properties p = new Properties();
static {
p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "");
p.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8");
p.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
p.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8");
}
/**
* 返回通过模板,将model中的数据替换后的内容
* @param model
* @param templateFilePathAndName
* @return
*/
public static String getContentByTemplate(Object model, String templateFilePathAndName){
try {
Velocity.init(p);
Template template = Velocity.getTemplate(templateFilePathAndName);
VelocityContext context = new VelocityContext();
context.put("model", model);
StringWriter writer = new StringWriter();
template.merge(context, writer);
String retContent = writer.toString();
writer.close();
return retContent;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* 根据模板,静态化model到指定的文件 模板文件中通过访问model来访问设置的内容
*
* @param model
* 数据对象
* @param templateFilePathAndName
* 模板文件的物理路径
* @param targetFilePathAndName
* 目标输出文件的物理路径
*/
public static void staticByTemplate(Object model, String templateFilePathAndName, String targetFilePathAndName) {
try {
Velocity.init(p);
Template template = Velocity.getTemplate(templateFilePathAndName);
VelocityContext context = new VelocityContext();
context.put("model", model);
FileOutputStream fos = new FileOutputStream(targetFilePathAndName);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8"));// 设置写入的文件编码,解决中文问题
template.merge(context, writer);
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 静态化内容content到指定的文件
*
* @param content
* @param targetFilePathAndName
*/
public static void staticBySimple(Object content, String targetFilePathAndName) {
VelocityEngine ve = new VelocityEngine();
ve.init(p);
String template = "${content}";
VelocityContext context = new VelocityContext();
context.put("content", content);
StringWriter writer = new StringWriter();
ve.evaluate(context, writer, "", template);
try {
FileWriter fileWriter = new FileWriter(new File(targetFilePathAndName));
fileWriter.write(writer.toString());
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.7.ziputils
package com.wal.hrm.utils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
/**
* <p>
* ZIP工具包
* </p>
* <p>
* 依赖:ant-1.7.1.jar
* </p>
*
* @author IceWee
* @date 2012-5-26
* @version 1.0
*/
public class ZipUtils {
/**
* 使用GBK编码可以避免压缩中文文件名乱码
*/
private static final String CHINESE_CHARSET = "GBK";
/**
* 文件读取缓冲区大小
*/
private static final int CACHE_SIZE = 1024;
/**
* <p>
* 压缩文件
* </p>
*
* @param sourceFolder 压缩文件夹
* @param zipFilePath 压缩文件输出路径
* @throws Exception
*/
public static void zip(String sourceFolder, String zipFilePath) throws Exception {
OutputStream out = new FileOutputStream(zipFilePath);
BufferedOutputStream bos = new BufferedOutputStream(out);
ZipOutputStream zos = new ZipOutputStream(bos);
// 解决中文文件名乱码
zos.setEncoding(CHINESE_CHARSET);
File file = new File(sourceFolder);
String basePath = null;
if (file.isDirectory()) {
basePath = file.getPath();
} else {
basePath = file.getParent();
}
zipFile(file, basePath, zos);
zos.closeEntry();
zos.close();
bos.close();
out.close();
}
/**
* <p>
* 递归压缩文件
* </p>
*
* @param parentFile
* @param basePath
* @param zos
* @throws Exception
*/
private static void zipFile(File parentFile, String basePath, ZipOutputStream zos) throws Exception {
File[] files = new File[0];
if (parentFile.isDirectory()) {
files = parentFile.listFiles();
} else {
files = new File[1];
files[0] = parentFile;
}
String pathName;
InputStream is;
BufferedInputStream bis;
byte[] cache = new byte[CACHE_SIZE];
for (File file : files) {
if (file.isDirectory()) {
zipFile(file, basePath, zos);
} else {
pathName = file.getPath().substring(basePath.length() + 1);
is = new FileInputStream(file);
bis = new BufferedInputStream(is);
zos.putNextEntry(new ZipEntry(pathName));
int nRead = 0;
while ((nRead = bis.read(cache, 0, CACHE_SIZE)) != -1) {
zos.write(cache, 0, nRead);
}
bis.close();
is.close();
}
}
}
/**
* <p>
* 解压压缩包
* </p>
*
* @param zipFilePath 压缩文件路径
* @param destDir 压缩包释放目录
* @throws Exception
*/
public static void unZip(String zipFilePath, String destDir) throws Exception {
ZipFile zipFile = new ZipFile(zipFilePath, CHINESE_CHARSET);
Enumeration<?> emu = zipFile.getEntries();
BufferedInputStream bis;
FileOutputStream fos;
BufferedOutputStream bos;
File file, parentFile;
ZipEntry entry;
byte[] cache = new byte[CACHE_SIZE];
while (emu.hasMoreElements()) {
entry = (ZipEntry) emu.nextElement();
if (entry.isDirectory()) {
new File(destDir + entry.getName()).mkdirs();
continue;
}
bis = new BufferedInputStream(zipFile.getInputStream(entry));
file = new File(destDir + entry.getName());
parentFile = file.getParentFile();
if (parentFile != null && (!parentFile.exists())) {
parentFile.mkdirs();
}
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos, CACHE_SIZE);
int nRead = 0;
while ((nRead = bis.read(cache, 0, CACHE_SIZE)) != -1) {
fos.write(cache, 0, nRead);
}
bos.flush();
bos.close();
fos.close();
bis.close();
}
zipFile.close();
}
}
2.7rabbitMq配置
@Configuration
public class MqConfig {
//配置交换机
@Bean(MqContants.EXCHANGE_NAME)
public Exchange exchange_name(){
return ExchangeBuilder.directExchange(MqContants.EXCHANGE_NAME).durable(true).build();
}
}
3.nginx代理服务,替nginx下载html
3.1application.yml
spring:
application:
name: proxy-server
server:
port: 2080
eureka:
instance:
hostname: proxy-server
prefer-ip-address: true
instance-id: proxy-server:2080
client:
service-url:
defaultZone: http://peer1:1010/eureka/
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtualHost: /
mq:
routingKey: hrmHomeSite
feign:
hystrix:
enabled: true #开启熔断支持
client:
config:
remote-service: #服务名,填写default为所有服务
connectTimeout: 30000
readTimeout: 30000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 30000
3.2接收端rabbitMq配置
接收端routingKey和发送短routingKey需要一致
@Configuration
public class MqConfig {
@Value("${mq.routingKey}")
private String routingKey;
//设置消息队列
@Bean
public Queue staticQueue(){
return new Queue(routingKey,true);
}
//设置交换机
@Bean(MqContants.EXCHANGE_NAME)
public Exchange exchange(){
return ExchangeBuilder.directExchange(MqContants.EXCHANGE_NAME)
.durable(true).build();
}
//将队列绑定到交换机
@Bean
public Binding binding(@Qualifier(MqContants.EXCHANGE_NAME) Exchange exchange,
@Qualifier("staticQueue") Queue queue){
return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
}
}
3.3 消息接收html上传
@Component
public class ReserveMsg {
@Autowired
FastDfsClient fastDfsClient;
//@Value("${mq.routingKey}")
private static final String routingKey = "hrmHomeSite";
@RabbitListener(queues =routingKey)
public void recive(String msg, Message message, Channel channel){
Map map = JSON.parseObject(msg, Map.class);
//拿到html地址
String htmlPath = map.get(MqContants.HTML_PATH).toString();
String path = htmlPath.startsWith("/")?htmlPath.substring(1):htmlPath;
int index = path.indexOf("/");
String groupName=path.substring(0, index);
String fileName = path.substring(index+1);
//从fastdfs下载html
byte[] html = fastDfsClient.download(groupName, fileName);
if (html==null || html.length<1){
throw new MyException(ErrorCode.ERROR_CODE_HTML_DOWNLOAD_ERROR.getErrorMsg());
}
//将html拷贝到指定目录
String nginxPath = map.get(MqContants.NGINX_PATH).toString();
File file = new File(nginxPath);
try {
if (!file.exists()){
file.getParentFile().mkdirs();
file.createNewFile();
}
FileCopyUtils.copy(html, file);
} catch (Exception e) {
e.printStackTrace();
}
}
}
4当通过feign调用服务超时
可以在application.yml上添加
##服务调用超时时间设置
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000