课程预览功能通过模板引擎技术在页面模板中填充数据,生成html页面,这个过程是当客户端请求服务器时服务器才开始渲染生成html页面,最后响应给浏览器,服务端渲染的并发能力是有限的。
页面静态化则强调将生成html页面的过程提前,提前使用模板引擎技术生成html页面,当客户端请求时直接请求html页面,由于是静态页面可以使用nginx、apache等高性能的web服务器,并发性能高。
当数据变化不频繁,一旦生成静态页面很长一段时间内很少变化,此时可以使用页面静态化。因为如果数据变化频繁,一旦改变就需要重新生成静态页面,导致维护静态页面的工作量很大。
根据课程发布的业务需求,虽然课程发布后仍可以修改课程信息,但需要经过课程审核,且修改频度不大,所以适合使用页面静态化。
我们使用代码将页面进行静态化之后存储在minio然后我们访问指定域名就能够访问到我们minio中的静态页面
#访问minio文件系统的网址
upstream fileserver{
server 192.168.101.65:9000 weight=10;
}server {
listen 80;
server_name file.51xuecheng.cn;
#charset koi8-r;
ssi on;
ssi_silent_errors on;
#access_log logs/host.access.log main;
location /video {
proxy_pass http://fileserver;
}location /mediafiles {
proxy_pass http://fileserver;
}
}
这样我们可以直接访问 http://192.168.101.65:9000/mediafiles/course/test.html或者我们可以访问域名file.51xuecheng.cn/mediafiles/course/test.html这样Nginx就会自动代理到我们要访问的文件了。
@SpringBootTest(classes = XuechengPlusContentServiceApplication.class) public class FreemarkerTest { @Autowired CoursePublishService coursePublishService; //测试页面静态化 @Test public void testGenerateHtmlByTemplate() throws IOException, TemplateException { //配置freemarker Configuration configuration = new Configuration(Configuration.getVersion()); //加载模板 //选指定模板路径,classpath下templates下 //得到classpath路径 String classpath = this.getClass().getResource("/").getPath(); configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates/")); //设置字符编码 configuration.setDefaultEncoding("utf-8"); //指定模板文件名称 Template template = configuration.getTemplate("course_template.ftl"); //准备数据 CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(2L); Mapmap = new HashMap<>(); map.put("model", coursePreviewInfo); //静态化 //参数1:模板,参数2:数据模型 String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); System.out.println(content); //将静态化内容输出到文件中 InputStream inputStream = IOUtils.toInputStream(content); //输出流 FileOutputStream outputStream = new FileOutputStream("D:\\upload\\test.html"); IOUtils.copy(inputStream, outputStream); } }
我们现在是在一个content微服务中,我们需要调用media微服务,并且这两个微服务互相是独立的,(注意我们content内部的api service model微服务具有相互依赖关系这样我们的就可以直接使用里面的东西)独立的微服务之间相互访问要使用feign进行,下面对feign进行配置。
发送请求的接口类
@FeignClient(value = "media-api",configuration = MultipartSupportConfig.class) public interface MediaServiceClient { @RequestMapping(value = "/media/upload/coursefile",consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String uploadFile(@RequestPart("filedata") MultipartFile upload,@RequestParam(value = "objectName",required=false) String objectName); }
启动类使用注解
@EnableFeignClients(basePackages={"com.xuecheng.content.feignclient"})
feignClient调用
@SpringBootTest(classes = XuechengPlusContentServiceApplication.class) public class FeignUploadTest { @Autowired MediaServiceClient mediaServiceClient; //远程调用,上传文件 @Test public void test() { MultipartFile multipartFile = MultipartSupportConfig.getMultipartFile(new File("D:\\upload\\test.html")); mediaServiceClient.uploadFile(multipartFile,"course/test.html"); } }
最终上传完文件,之后就直接访问之前的域名即可访问我们minio中存储的静态页面。
注意:
feign本身是不支持文件格式的传递参数,要使用要进行配置
坐标
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-openfeign
io.github.openfeign
feign-httpclient
io.github.openfeign.form
feign-form
3.8.0
io.github.openfeign.form
feign-form-spring
3.8.0
配置
feign:
hystrix:
enabled: true
circuitbreaker:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 30000 #熔断超时时间
ribbon:
ConnectTimeout: 60000 #连接超时时间
ReadTimeout: 60000 #读超时时间
MaxAutoRetries: 0 #重试次数
MaxAutoRetriesNextServer: 1 #切换实例的重试次数
配置feign支持Multipart 在config包下面类
package com.xuecheng.content.config; import feign.codec.Encoder; import feign.form.spring.SpringFormEncoder; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.cloud.openfeign.support.SpringEncoder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Scope; import org.springframework.http.MediaType; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.commons.CommonsMultipartFile; import java.io.File; import java.io.FileInputStream; import java.io.OutputStream; /** * @author Mr.M * @version 1.0 * @description TODO * @date 2022/10/15 22:13 */ @Configuration public class MultipartSupportConfig { @Autowired private ObjectFactorymessageConverters; @Bean @Primary//注入相同类型的bean时优先使用 @Scope("prototype") public Encoder feignEncoder() { return new SpringFormEncoder(new SpringEncoder(messageConverters)); } //将file转为Multipart public static MultipartFile getMultipartFile(File file) { FileItem item = new DiskFileItemFactory().createItem("file", MediaType.MULTIPART_FORM_DATA_VALUE, true, file.getName()); try (FileInputStream inputStream = new FileInputStream(file); OutputStream outputStream = item.getOutputStream();) { IOUtils.copy(inputStream, outputStream); } catch (Exception e) { e.printStackTrace(); } return new CommonsMultipartFile(item); } }
注意:
直接访问 http://192.168.101.65:9000/mediafiles/course/test.html或者我们可以访问域名file.51xuecheng.cn/mediafiles/course/test.html这样Nginx就会自动代理到我们要访问的文件了。
但是大家看我上面的Nginx配置,我们的域名下的虚拟主机并没有访问我们静态样式文件的接口,这些接口都在我们www.51xuecheng.cn这个域名下面,因此要通过这个域名访问我们的minio中的文件,在这个域名下加上 就会代理到我们的minio
location /course/ {
proxy_pass http://fileserver/mediafiles/course/;
}
整体Nginx配置如下·
#user nobody;
worker_processes 1;#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;#keepalive_timeout 0;
keepalive_timeout 65;#gzip on;
#访问minio文件系统的网址
upstream fileserver{
server 192.168.101.65:9000 weight=10;
}
#后台网关
upstream gatewayserver{
server 127.0.0.1:63010 weight=10;
}
server {
listen 80;
server_name www.51xuecheng.cn localhost;
#rewrite ^(.*) https://$server_name$1 permanent;
#charset koi8-r;
ssi on;
ssi_silent_errors on;
#access_log logs/host.access.log main;location / {
alias E:/code/xc-ui-pc-static-portal/;
index index.html index.htm;
}
#静态资源
location /static/img/ {
alias E:/code/xc-ui-pc-static-portal/img/;
}
location /static/css/ {
alias E:/code/xc-ui-pc-static-portal/css/;
}
location /static/js/ {
alias E:/code/xc-ui-pc-static-portal/js/;
}
location /static/plugins/ {
alias E:/code/xc-ui-pc-static-portal/plugins/;
add_header Access-Control-Allow-Origin http://ucenter.51xuecheng.cn;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods GET;
}
location /plugins/ {
alias E:/code/xc-ui-pc-static-portal/plugins/;
}
location /course/preview/learning.html {
alias E:/code/xc-ui-pc-static-portal/course/learning.html;
}
location /course/search.html {
root E:/code/xc-ui-pc-static-portal;
}
location /course/learning.html {
root E:/code/xc-ui-pc-static-portal;
}
#api
location /api/ {
proxy_pass http://gatewayserver/;
}
#openapi 这里不走网关
location /open/content/ {
proxy_pass http://gatewayserver/content/open/;
}
location /open/media/ {
proxy_pass http://gatewayserver/media/open/;
}
location /course/ {
proxy_pass http://fileserver/mediafiles/course/;
}#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
server {
listen 80;
server_name file.51xuecheng.cn;
#charset koi8-r;
ssi on;
ssi_silent_errors on;
#access_log logs/host.access.log main;
location /video {
proxy_pass http://fileserver;
}location /mediafiles {
proxy_pass http://fileserver;
}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;# location / {
# root html;
# index index.html index.htm;
# }
#}}
之后我们访问www.51xuecheng.cn/course/test.html