0、前言
在前后端分离架构中,服务层被拆分成了很多的微服务,Spring Cloud中提供服务注册中心来管理微服务信息
为什么要用注册中心?
1、微服务数量众多,要进行远程调用就需要知道服务端的IP地址和端口号,注册中心帮助我们管理这些服务IP和端口
2、微服务会实时上报自己的状态,注册中心统一管理这些微服务的状态,将存在问题的服务踢出服务列表,客户端获取到可用的服务进行调用
如果不用注册中心就只能在各个模块的配置文件中配置固定的IP地址跟端口号,管理起来比较麻烦
Spring Cloud Eureka是对Eureka的二次封装,它实现了服务治理的功能,Spring Cloud Eureka提供服务端与客户端,服务端即Eureka服务注册中心,客户端完成向Eureka服务端的注册与发现,服务端和客户端均采用Java语言编写,下图显示了Eureka Server与Eureka Client的关系:
1、Eureka Server是服务端,负责管理各个微服务结点的信息和状态
2、在微服务上部署Eureka Client程序,远程访问Eureka Client时将自己注册在Eureka Server
3、微服务需要调用另一个微服务时从Eureka Server中获取服务调用地址,进行远程调用
Eureka Server搭建:
创建xc-govern-center工程:
1、父工程中添加cloud依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring‐cloud‐dependencies</artifactId> <version>Finchley.SR1</version> <type>pom</type> <scope>import</scope> </dependency>
服务端工程添加eureka‐server依赖:
<dependencies> <!‐‐ 导入Eureka服务的依赖 ‐‐> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring‐cloud‐starter‐netflix‐eureka‐server</artifactId> </dependency> </dependencies>
2、启动类添加注解:
@EnableEurekaServer//标识这是一个Eureka服务
3、yml配置文件中添加eureka相关配置
eureka: client: registerWithEureka: false #服务注册,是否将自己注册到Eureka服务中 fetchRegistry: false #服务发现,是否从Eureka中获取注册信息 serviceUrl: #Eureka客户端与Eureka服务端的交互地址,高可用状态配置对方的地址, #单机状态配置自己(如果 不配置则默认本机8761端口) defaultZone: http://localhost:50101/eureka/ server: enable‐self‐preservation: false #是否开启自我保护模式 eviction‐interval‐timer‐in‐ms: 60000 #服务注册表清理间隔(单位毫秒,默认是60*1000)
registerWithEureka:被其它服务调用时需向Eureka注册
fetchRegistry:需要从Eureka中查找要调用的目标服务时需要设置为true
serviceUrl.defaultZone:配置上报Eureka服务地址高可用状态配置对方的地址,单机状态配置自己
enable-self-preservation:自保护设置,下边有介绍
eviction-interval-timer-in-ms:清理失效结点的间隔,在这个时间段内如果没有收到该结点的上报则将结点从服务列表中剔除
4、启动Eureka Server即可
启动Eureka Server,访问:http://localhost:50101/
上图红色提示信息: THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT
PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
自我保护模式被关闭。在网络或其他问题的情况下可能不会保护实例失效。
Eureka Server有一种自我保护模式,当微服务不再向Eureka Server上报状态,Eureka Server会从服务列表将此服务删除,如果出现网络异常情况(微服务正常),此时Eureka server进入自保护模式,不再将微服务从服务列表删除
在开发阶段建议关闭自保护模式
高可用环境搭建:
高 可 用 配 置 只 需 要 将 两 个 服 务 端 配 置 文 件 中 的 d e f a u l t Z o n e 标 签 改 成 对 方 的 地 址 即 可 \color{#FF0000}{高可用配置只需要将两个服务端配置文件中的defaultZone标签改成对方的地址即可}高可用配置只需要将两个服务端配置文件中的defaultZone标签改成对方的地址即可
Eureka Server 高可用环境需要部署两个Eureka server,它们互相向对方注册。如果在本机启动两个Eureka需要注意两个Eureka Server的端口要设置不一样,这里我们部署一个Eureka Server工程,将端口可配置,制作两个 Eureka Server启动脚本,启动不同的端口,如下图:
1、在实际使用时Eureka Server至少部署两台服务器,实现高可用
2、两台Eureka Server互相注册
3、微服务需要连接两台Eureka Server注册,当其中一台Eureka死掉也不会影响服务的注册与发现
4、微服务会定时向Eureka server发送心跳,报告自己的状态
5、微服务从注册中心获取服务地址以RESTful方式发起远程调用
配置如下:
1、端口可配置
server: port: ${PORT:50101} #服务端口
2、Eureka服务端的交互地址可配置
3、配置hostname Eureka 组成高可用,两个Eureka互相向对方注册,这里需要通过域名或主机名访问,这里我们设置两个Eureka服务的主机名分别为 eureka01、eureka02。
2和3的完整配置如下:
eureka: client: registerWithEureka: true #服务注册,是否将自己注册到Eureka服务中 false:单机使用,true:高可用使用 fetchRegistry: true #服务发现,是否从Eureka中获取注册信息 false:单机使用,true:高可用使用 serviceUrl: #Eureka客户端与Eureka服务端的交互地址,高可用状态配置对方的地址,单机状态配置自己(如果不配置则默认本机8761端口) #defaultZone: http://localhost:50101/eureka/ #单机环境使用 defaultZone: ${EUREKA_SERVER:http://eureka02:50102/eureka/} #高可用环境使用 server: enable-self-preservation: false #是否开启自我保护模式 eviction-interval-timer-in-ms: 60000 #服务注册表清理间隔(单位毫秒,默认是60*1000) instance: hostname: ${EUREKA_DOMAIN:eureka01}
4、在IDEA中制作启动脚本
配置参数:-DPORT=50101 -DEUREKA_SERVER=http://eureka02:50102/eureka/ -DEUREKA_DOMAIN=eureka01
启动1:
启动2:
运行两个启动脚本,分别浏览:
http://localhost:50101/
http://localhost:50102/
Eureka主画面如下:
Eureka Client的配置
将cms注册到Eureka Server:
1、在manager-cms服务中添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
2、在application.yml配置
eureka: client: registerWithEureka: true #服务注册开关 fetchRegistry: true #服务发现开关 serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址,多个中间用逗号分隔 defaultZone: ${EUREKA_SERVER:http://localhost:50101/eureka/,http://localhost:50102/eureka/} instance: prefer-ip-address: true #将自己的ip地址注册到Eureka服务中 ip-address: ${IP_ADDRESS:127.0.0.1} instance-id: ${spring.application.name}:${server.port} #指定实例id
3、在启动类上添加注解
在启动类上添加注解 @EnableDiscoveryClient ,表示它是一个Eureka的客户端
4、启动cms工程和eureka01,然后刷新Eureka Server查看注册情况
说明cms已将服务注册到Eureka工程
Eureka客户端和服务器的注册流程
1、微服务客户端cms添加所属依赖并在配置文件中配置相关配置信息,路径指向服务器工程的路径
2、Eureka服务器工程添加所属依赖和配置文件,如果是高可用就地址就配置成相互注册的地址
3、客户端和服务器都启动成功后,可以看到客户端向服务器注册了相关信息
Feign远程调用
在前后端分离架构中,服务层被拆分成了很多的微服务,微服务远程调用用到了Feign
下图是课程管理服务远程调用CMS服务的流程图:
课程管理请求cms服务的工作流程:
1、cms服务将自己注册到注册中心
2、课程管理服务从注册中心获取cms服务的地址
3、课程管理服务远程调用cms服务
Feign是Netflix公司开源的轻量级rest客户端,使用Feign可以非常方便的实现Http客户端,Spring Cloud引入 Feign并且集成了Ribbon实现客户端负载均衡调用
Feign测试:
1、在客户端course管理服务添加依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign‐okhttp</artifactId> </dependency>
2、定义FeignClient接口
在课程管理服务中创建client包,定义查询cms页面的客户端接口:
@FeignClient(value = XcServiceList.XC_SERVICE_MANAGE_CMS) public interface CmsPageClient { //根据页面id查询页面信息,远程调用cms请求数据 @GetMapping("/cms/page/get/{id}")//用GetMapping标识远程调用的http的方法类型 public CmsPage findCmsPageById(@PathVariable("id") String id); }
3、启动类添加@EnableFeignClients注解
4、测试类进行测试
@Autowired CmsPageClient cmsPageClient; //接口代理对象,由Feign生成代理对象 @Test public void testFeign() { //发起远程调用 CmsPage cmsPage = cmsPageClient.findCmsPageById("5a795ac7dd573c04508f3a56"); System.out.println(cmsPage); }
测试结果:
可 以 看 到 : 在 课 程 管 理 系 统 中 通 过 F e i g n 远 程 调 用 c m d 管 理 系 统 中 的 接 口 \color{#FF0000}{可以看到:在课程管理系统中通过Feign远程调用cmd管理系统中的接口}可以看到:在课程管理系统中通过Feign远程调用cmd管理系统中的接口
Feign工作原理如下:
1、 启动类添加@EnableFeignClients注解,Spring会扫描标记了@FeignClient注解的接口,并生成此接口的代理对象
2、 @FeignClient(value = XcServiceList.XC_SERVICE_MANAGE_CMS)即指定了cms的服务名称,Feign会从注册中心获取cms服务列表,并通过负载均衡算法进行服务调用
3、在接口方法中使用注解@GetMapping("/cms/page/get/{id}"),指定调用的url,Feign将根据url进行远程调用
Feign注意点
SpringCloud对Feign进行了增强,兼容了SpringMVC的注解 ,我们在使用SpringMVC注解时需要注意:
1、feignClient接口的参数必须加@PathVariable("XXX")和@RequestParam("XXX")
2、feignClient返回值为复杂对象时其类型必须有无参构造函数
Ribbon
Ribbon是一个基于HTTP、 TCP的客户端负载均衡器
负载均衡是微服务架构中必须使用的技术,通过负载均衡来实现系统的高可用、集群扩容等功能
一次请求太多,就需要负载均衡将请求合理的分配给不同的服务器去调用
负载均衡可通过硬件设备和软件来实现,硬件比如:F5、Array等,软件比如:LVS、Nginx等
负载均衡的架构图:
负载均衡算法有:轮训、随机、加权轮训、加权随机、地址哈希等方法,负载均衡器维护一份服务列表,根据负载均衡算法将请求转发到相应的微服务上,所以负载均衡可以为微服务集群分担请求,降低系统的压力
什么是客户端负载均衡?
上图是服务端负载均衡,客户端负载均衡与服务端负载均衡的区别在于客 户 端 要 维 护 一 份 服 务 列 表 \color{#FF0000}{客户端要维护一份服务列表}客户端要维护一份服务列表,Ribbon从 Eureka Server获取服务列表,Ribbon根据负载均衡算法直接请求到具体的微服务,中间省去了负载均衡服务,说 白 了 微 服 务 中 用 R i b b o n 来 代 替 负 载 均 衡 N g i n x , N g i n x 是 服 务 端 负 载 均 衡 , 现 在 换 做 客 户 端 负 载 均 衡 R i b b o n 来 维 护 一 份 服 务 列 表 \color{#FF0000}{说白了微服务中用Ribbon来代替负载均衡Nginx,Nginx是服务端负载均衡,现在换做客户端负载均衡Ribbon来维护一份服务列表}说白了微服务中用Ribbon来代替负载均衡Nginx,Nginx是服务端负载均衡,现在换做客户端负载均衡Ribbon来维护一份服务列表
如下图是Ribbon负载均衡的流程图:
1、在微服务中使用Ribbon实现负载均衡,Ribbon先从EurekaServer中获取服务列表
2、Ribbon根据负载均衡的算法去调用微服务
Spring Cloud引入Ribbon配合 restTemplate 实现客户端负载均衡
创 建 的 独 立 模 块 相 当 于 服 务 端 , 管 理 中 心 模 块 相 当 于 客 户 端 \color{#FF0000}{创建的独立模块相当于服务端,管理中心模块相当于客户端}创建的独立模块相当于服务端,管理中心模块相当于客户端
环境搭建:
1、在客户端课程管理添加Ribbon依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> </dependency>
由于依赖了spring-cloud-starter-eureka,会自动添加spring-cloud-starter-ribbon依赖,当引入eureka后可以不添加ribbon依赖
2、在yml中配置ribbon参数:
ribbon: MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试 MaxAutoRetriesNextServer: 3 #切换实例的重试次数 OkToRetryOnAllOperations: false #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作 没有实现幂等的情况下是很危险的,所以设置为false ConnectTimeout: 5000 #请求连接的超时时间 ReadTimeout: 6000 #请求处理的超时时间
3、负载均衡测试
1)启动两个cms服务,注意端口要不一致,启动完成观察Eureka Server的服务列表
2)定义RestTemplate,使用@LoadBalanced注解
在course管理启动类ManageCourseApplication中添加以下代码:
@Bean @LoadBalanced//开始客户端负载均衡 public RestTemplate restTemplate(){ return new RestTemplate(new OkHttp3ClientHttpRequestFactory()); }
在课程管理服务工程创建单元测试代码,远程调用cms的查询页面接口:
@Autowired RestTemplate restTemplate; @Test public void testRibbon(){ //确定要获取的服务名 String serviceId = "XC-SERVICE-MANAGE-CMS"; for (int i=0;i<10;i++){ //ribbon客户端从eurekaServer中获取服务列表,根据服务名获取服务列表 ResponseEntity<Map> forEntity = restTemplate.getForEntity("http://"+serviceId+"/cms/page/get/5a754adf6abb500ad05688d9", Map.class); Map body = forEntity.getBody(); System.out.println(body); } }
4)负载均衡测试
添加@LoadBalanced注解后,restTemplate会走LoadBalancerInterceptor拦截器,此拦截器中会通过 RibbonLoadBalancerClient查询服务地址,可以在此类打断点观察每次调用的服务地址和端口,两个cms服务会轮流被调用
返回的body数据如下:
课程预览
课程预览是为了保证课程发布后的正确性,通过课程预览可以直观的通过课程详情页面看到课程的信息是否正确, 通过课程预览看到的页面内容和课程发布后的页面内容是一致的
课程详情页面是向用户展示课程信息的窗口,课程相当于网站的商品,本页面的访问量会非常大。此页面的内容设计不仅要展示出课程核心重要的内容而且用户访问页面的速度要有保证,有统计显示打开一个页面超过4秒用户就走掉了,所以本页面的性能要求是本页面的重要需求
本页面另一个需求就是SEO,要非常有利于爬虫抓取页面上信息,并且生成页面快照,利于用户通过搜索引擎搜索课程信息
如何在保证SEO的前提下提高页面的访问速度
对于不会频繁改变的信息可以采用页面静态化的技术,提前让页面生成html静态页面存储在nginx服务器,用户直接访问nginx即可,对于一些动态信息可以访问服务端获取json数据在页面渲染
优点:使用Nginx作为web服务器,并且直接访问html页面,性能出色
缺点:需要维护大量的静态页面,增加了维护的难度
操作流程:
1、制作课程详情页面模板
2、开发课程详情页面数据模型的查询接口(为静态化提供数据)
3、调用cms课程预览接口通过浏览器浏览静态文件
静态页面加载思路:
打开课程资料中的“静态页面目录”中的课程详情模板页面,模板页面路径如下:
静态页面目录\static\course\detail\course_main_template.html
1、主页面
我们需要在主页面中通过SSI加载:页头、页尾、教育机构、教师信息
2、异步加载课程统计与教育机构统计信息
课程统计信息(json)、教育机构统计信息(json)
3、马上学习按钮事件
用户点击“马上学习”会根据课程收费情况、课程购买情况执行下一步操作。
静态资源虚拟主机
1、配置静态资源虚拟主机
静态资源虚拟主机负责处理课程详情、公司信息、老师信息、统计信息等页面的请求:
将课程资料中的“静态页面目录”中的目录拷贝到
D:\Workspace\webstorm\xcEduUI\
xc-ui-pc-static-portal下
在Nginx配置文件nginx.conf的http标签下配置以下内容:
server { listen 91; server_name localhost; location /static/company/ { alias D:/Workspace/webstorm/xcEduUI/xc-ui-pc-static-portal/company/; } location /static/teacher/ { alias D:/Workspace/webstorm/xcEduUI/xc-ui-pc-static-portal/teacher/; } location /static/stat/ { alias D:/Workspace/webstorm/xcEduUI/xc-ui-pc-static-portal/stat/; } location /course/detail/ { alias D:/Workspace/webstorm/xcEduUI/xc-ui-pc-static-portal/course/detail/; } location /static/category/ { alias D:/Workspace/webstorm/xcEduUI/xc-ui-pc-static-portal/category/; } }
2、通过www.xuecheng.com虚拟主机转发到静态资源
由于课程页面需要通过SSI加载页头和页尾所以需要通过www.xuecheng.com虚拟主机转发到静态资源。
在server_name为www.xuecheng.com的server中加入如下配置:
location /static/company/ { proxy_pass http://static_server_pool; } location /static/teacher/ { proxy_pass http://static_server_pool; } location /static/stat/ { proxy_pass http://static_server_pool; } location /course/detail/ { proxy_pass http://static_server_pool;
3、配置upstream实现请求转发到资源服务虚拟主机,在http标签中配置如下:
upstream img_server_pool{ server 192.168.25.133:80 weight=10; }
门户静态资源路径
门户中的一些图片、样式等静态资源统一通过/static路径对外提供服务,在server_name为www.xuecheng.com的server中加入如下配置:
location /static/img/ { alias D:/Workspace/webstorm/xcEduUI/xc-ui-pc-static-portal/img/; } location /static/css/ { alias D:/Workspace/webstorm/xcEduUI/xc-ui-pc-static-portal/css/; } location /static/js/ { alias D:/Workspace/webstorm/xcEduUI/xc-ui-pc-static-portal/js/; } location /static/plugins/ { alias D:/Workspace/webstorm/xcEduUI/xc-ui-pc-static-portal/plugins/; add_header Access-Control-Allow-Origin http://ucenter.xuecheng.com; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Methods GET; }
cors跨域参数:
Access-Control-Allow-Origin:允许跨域访问的外域地址
如果允许任何站点跨域访问则设置为*,通常这是不建议的。
Access-Control-Allow-Credentials: 允许客户端携带证书访问
Access-Control-Allow-Methods:允许客户端跨域访问的方法
页面测试
请求:http://www.xuecheng.com/course/detail/course_main_template.html测试课程详情页面模板是否可以正常浏览。
页面动态脚本
为了方便日后的维护,我们将javascript实现的动态部分单独编写一个html 文件,在门户的include目录下定义 course_detail_dynamic.html文件,此文件通过ssi包含在课程详情页面中.
文件地址:D:\Workspace\webstorm\xcEduUI\xc-ui-pc-static-portal\include\course_detail_dynamic.html
所有的课程公用一个 页面动态脚本。
在course_main_template.html主页面下端添加如下代码,通过SSI技术包含课程详情页面动态脚本文件:
<script> //课程id var courseId = "template" </script> <!--#include virtual="/include/course_detail_dynamic.html"--> </body> </html>
课程数据模型查询接口
静态化操作需要模型数据方可进行静态化,课程数据模型由课程管理服务提供,仅供课程静态化程序调用使用。
1、实体CourseView
@Data @NoArgsConstructor @ToString public class CourseView implements java.io.Serializable { private CourseBase courseBase;//基础信息 private CoursePic coursePic;//课程营销 private CourseMarket courseMarket;//课程图片 private TeachplanNode teachplanNode;//教学计划 }
2、API定义
@ApiOperation("课程视图查询") public CourseView courseview(String id);
3、Controller
@Override @GetMapping("/courseview/{id}") public CourseView courseview(@PathVariable("id") String id) { return courseService.getCoruseView(id); }
4、Service
public CourseView getCoruseView(String id) { }
5、Dao
public interface CourseMarketRepository extends JpaRepository<CourseMarket,String> { }
课程信息模板设计
在确定了静态化所需要的数据模型之后,就可以编写页面模板了,课程详情页面由多个静态化页面组成,所以我们 需要创建多个页面模板,本章节创建课程详情页面的主模板,即课程信息模板。
测试:
使用swagger-ui或postman测试本接口。
课程信息模板设计
在确定了静态化所需要的数据模型之后,就可以编写页面模板了,课程详情页面由多个静态化页面组成,所以我们 需要创建多个页面模板,本章节创建课程详情页面的主模板,即课程信息模板。
使用test-freemarker工程测试模板
1、将course.ftl拷贝到test-freemarker工程的resources/templates下,并在test-freemarker工程的controller中添加测试方法
2、Controller中方法:
@RequestMapping("/course") public String course(Map<String, Object> map){ //使用restTemplate请求轮播图的模型数据 ResponseEntity<Map> forEntity = restTemplate.getForEntity("http://localhost:31200/course/courseview/4028e581617f945f01617f9dabc40000", Map.class); Map body = forEntity.getBody(); //设置模型数据 map.putAll(body); return "course"; }
测试:http://localhost:8088/freemarker/course
结果:
模板保存
模板编写并测试通过后要在数据库保存:
1、模板信息保存在xc_cms数据库(mongodb)的cms_template表
2、模板文件保存在mongodb的GridFS中
第一步:将模板文件上传到GridFS中
调用manage-cms中GridFsTest类中testStore方法:
@Test public void testStore() throws FileNotFoundException { //定义file File file =new File("D:/Workspace/xcEduService01/test-freemarker/src/main/resources/templates/course.ftl"); //定义fileInputStream FileInputStream fileInputStream = new FileInputStream(file); ObjectId objectId = gridFsTemplate.store(fileInputStream, "course.ftl"); System.out.println(objectId); }
保存成功需要记录模板文件的id,即上边代码中的fileId。5ecd0a0cda11e33814bfa410
第二步:向cms_template表添加模板记录(请不要重复添加)
使用Studio 3T连接mongodb,向cms_template添加记录:
{ "_class" : "com.xuecheng.framework.domain.cms.CmsTemplate", "siteId" : "5a751fab6abb5044e0d19ea1", "templateName" : "课程详情页面", "templateParameter" : "courseid", "templateFileId" : "5ecd0a0cda11e33814bfa410" }
其它模板
除了课程详情主页面需要设计模板所有静态化的页面都要设计模板,
如下: 教育机构页面模板、教师信息页面模板、课程统计信息json模板、教育机构统计信息json模板。
本项目我们实现课程详情主页面模板的制作和测试,其它页面模板的开发参考课程详情页面去实现。
课程预览功能开发
1、向cms_page表插入一条页面记录或者从cms_page找一个页面进行测试。
注意:页面配置一定要正确,需设置正确的模板id和dataUrl,例如:
{ "_id" : ObjectId("5b3469f794db44269cb2bff1"), "siteId" : "5a751fab6abb5044e0d19ea1", "pageName" : "4028e581617f945f01617f9dabc40000.html", "pageAliase" : "课程详情页面测试01", "pageWebPath" : "/course/detail/", "pagePhysicalPath" : "D:\\Workspace\\webstorm\\xcEduUI\\xc-ui-pc-static-portal\\include\\", "pageType" : "1", "pageCreateTime" : ISODate("2018-02-06T07:34:21.255+0000"), "templateId" : "5ecd0a0cda11e33814bfa410", "htmlFileId" : "5eb964ccee1e35162c69c2d9", "dataUrl" : "http://localhost:31200/course/courseview/4028e581617f945f01617f9dabc40000", "_class" : "com.xuecheng.framework.domain.cms.CmsPage" }
2、课程详细页面 使用ssi注意
由于Nginx先请求cms的课程预览功能得到html页面,再解析页面中的ssi标签,这里必须保证cms页面预览返回的 页面的Content-Type为text/html;charset=utf-8
在cms页面预览的controller方法中添加:
response.setHeader("Content-type","text/html;charset=utf-8");
3、测试:http://www.xuecheng.com/cms/preview/5b3469f794db44269cb2bff1
结果:
具体逻辑:
通过id到cmsPage表中找到DataUrl字段;
通过id到cmsPage表中找到TemplateId字段;
通过TemplateId到CmsTemplate表中匹配id找到模板文件TemplateFileId字段;
通过TemplateFileId到fs.files表获取到模板文件;
CMS添加页面接口
cms服务对外提供添加页面接口,实现:如果不存在页面则添加,否则就更新页面信息。
此接口由课程管理服务在课程预览时调用。
1、API
@ApiOperation("保存页面") public CmsPageResult save(CmsPage cmsPage);
2、Controller
在CmsPageController中添加:
@Override @PostMapping("/save") public CmsPageResult save(@RequestBody CmsPage cmsPage) { return pageService.save(cmsPage); }
课程预览服务端
此Api是课程管理前端请求服务端进行课程预览的Api
1、API
@ApiOperation("课程预览") public CoursePublishResult preview(String id);
2、创建 Feign Client
在课程管理工程创建CMS服务的Feign Client,通过此Client远程请求cms添加页面。
@FeignClient(value = "XC-SERVICE-MANAGE-CMS") //指定远程调用的服务名 public interface CmsPageClient { //根据页面id查询页面信息,远程调用cms请求数据 @GetMapping("/cms/page/get/{id}")//用GetMapping标识远程调用的http的方法类型 public CmsPage findCmsPageById(@PathVariable("id") String id); //添加页面,用于课程预览 @PostMapping("/cms/page/save") public CmsPageResult saveCmsPage(@RequestBody CmsPage cmsPage); }
3、在course工程的yml文件中添加:
course-publish: siteId: 5a751fab6abb5044e0d19ea1 templateId: 5ecd0a0cda11e33814bfa410 previewUrl: http://www.xuecheng.com/cms/preview/ pageWebPath: /course/detail/ pagePhysicalPath: /course/detail/ dataUrlPre: http://localhost:31200/course/courseview/
4、Controller
CourseController中添加以下方法:
@Override @PostMapping("/preview/{id}") public CoursePublishResult preview(@PathVariable("id") String id) { return courseService.preview(id); }
5、编写前端预览页面
操作步骤:启动x前端项目c-ui-pc-teach,点击我的课程-发布课程-课程预览-点我查看课程预览页面即可