学成在线(第14天)

视频处理

需求分析

原始视频通常需要经过编码处理,生成m3u8和ts文件方可基于HLS协议播放视频。通常用户上传原始视频,系统
自动处理成标准格式,系统对用户上传的视频自动编码、转换,最终生成m3u8文件和ts文件,处理流程如下:
1、用户上传视频成功
2、系统对上传成功的视频自动开始编码处理
3、用户查看视频处理结果,没有处理成功的视频用户可在管理界面再次触发处理
4、视频处理完成将视频地址及处理结果保存到数据库

视频处理流程如下:

视频处理进程的任务是接收视频处理消息进行视频处理,业务流程如下:
1、监听MQ,接收视频处理消息。
2、进行视频处理。
3、向数据库写入视频处理结果。

视频处理进程属于媒资管理系统的一部分,考虑提高系统的扩展性,将视频处理单独定义视频处理工程。

视频处理实现

处理流程

1)接收视频处理消息
2)判断媒体文件是否需要处理(本视频处理程序目前只接收avi视频的处理)
当前只有avi文件需要处理,其它文件需要更新处理状态为“无需处理”。
3)处理前初始化处理状态为“未处理”
4)处理失败需要在数据库记录处理日志,及处理状态为“处理失败”
5)处理成功记录处理状态为“处理成功”

数据模型

在MediaFile类中添加mediaFileProcess_m3u8属性记录ts文件列表,代码如下:

//处理状态
private String processStatus;
//hls处理
private MediaFileProcess_m3u8 mediaFileProcess_m3u8;
@Data
@ToString
public class MediaFileProcess_m3u8 extends MediaFileProcess {
    //ts列表
    private List<String> tslist;
}

视频处理生成Mp4

1、创建Dao
视频处理结果需要保存到媒资数据库,创建dao如下:

public interface MediaFileRepository extends MongoRepository<MediaFile,String> {
}

2、在application.yml中配置ffmpeg的位置及视频目录的根目录:

xc‐service‐manage‐media:
  video‐location: F:/develop/video/
  ffmpeg‐path: D:/Program Files/ffmpeg‐20180227‐fa0c9d6‐win64‐static/bin/ffmpeg.exe

3、处理任务类
在mq包下创建MediaProcessTask类,此类负责监听视频处理队列,并进行视频处理。
整个视频处理内容较多,这里分两部分实现:生成Mp4和生成m3u8,下边代码实现了生成mp4。

@Component
public class MediaProcessTask {
    private static final Logger LOGGER = LoggerFactory.getLogger(MediaProcessTask.class);
    //ffmpeg绝对路径
    @Value("${xc‐service‐manage‐media.ffmpeg‐path}")
    String ffmpeg_path;
    //上传文件根目录
    @Value("${xc‐service‐manage‐media.upload‐location}")
    String serverPath;
@Autowired
    MediaFileRepository mediaFileRepository;
    @RabbitListener(queues = "${xc‐service‐manage‐media.mq.queue‐media‐processtask}")
    public void receiveMediaProcessTask(String msg) throws IOException {
        Map msgMap = JSON.parseObject(msg, Map.class);
        LOGGER.info("receive media process task msg :{} ",msgMap);
        //解析消息
        //媒资文件id
        String mediaId = (String) msgMap.get("mediaId");
        //获取媒资文件信息
        Optional<MediaFile> optional = mediaFileRepository.findById(fileMd5);
        if(!optional.isPresent()){
            return ;
        }
        MediaFile mediaFile = optional.get();
        //媒资文件类型
        String fileType = mediaFile.getFileType();
        if(fileType == null || !fileType.equals("avi")){//目前只处理avi文件
            mediaFile.setProcessStatus("303004");//处理状态为无需处理
            mediaFileRepository.save(mediaFile);
            return ;
        }else{
            mediaFile.setProcessStatus("303001");//处理状态为未处理
            mediaFileRepository.save(mediaFile);
        }
        //生成mp4
        String video_path = serverPath + mediaFile.getFilePath()+mediaFile.getFileName();
        String mp4_name = mediaFile.getFileId()+".mp4";
        String mp4folder_path = serverPath + mediaFile.getFilePath();
        Mp4VideoUtil videoUtil = new
Mp4VideoUtil(ffmpeg_path,video_path,mp4_name,mp4folder_path);
        String result = videoUtil.generateMp4();
        if(result == null || !result.equals("success")){
            //操作失败写入处理日志
            mediaFile.setProcessStatus("303003");//处理状态为处理失败
            MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
            mediaFileProcess_m3u8.setErrormsg(result);
            mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);
            mediaFileRepository.save(mediaFile);
            return ;
        }
        //生成m3u8...
    }
}

视频处理生成m3u8

下边是完整的视频处理任务类代码,包括了生成m3u8及生成mp4的代码。

@Component
public class MediaProcessTask {
    private static final Logger LOGGER = LoggerFactory.getLogger(MediaProcessTask.class);
    //ffmpeg绝对路径
    @Value("${xc‐service‐manage‐media.ffmpeg‐path}")
    String ffmpeg_path;
    //上传文件根目录
    @Value("${xc‐service‐manage‐media.upload‐location}")
    String serverPath;
    @Autowired
    MediaFileRepository mediaFileRepository;
    @RabbitListener(queues = "${xc‐service‐manage‐media.mq.queue‐media‐processtask}")
    public void receiveMediaProcessTask(String msg) throws IOException {
        Map msgMap = JSON.parseObject(msg, Map.class);
        LOGGER.info("receive media process task msg :{} ",msgMap);
        //解析消息
        //媒资文件id
        String mediaId = (String) msgMap.get("mediaId");
        //获取媒资文件信息
        Optional<MediaFile> optional = mediaFileRepository.findById(fileMd5);
        if(!optional.isPresent()){
            return ;
        }
        MediaFile mediaFile = optional.get();
        //媒资文件类型
        String fileType = mediaFile.getFileType();
        if(fileType == null || !fileType.equals("avi")){//目前只处理avi文件
            mediaFile.setProcessStatus("303004");//处理状态为无需处理
            mediaFileRepository.save(mediaFile);
            return ;
        }else{
            mediaFile.setProcessStatus("303001");//处理状态为未处理
            mediaFileRepository.save(mediaFile);
        }
        //生成mp4
        String video_path = serverPath + mediaFile.getFilePath()+mediaFile.getFileName();
        String mp4_name = mediaFile.getFileId()+".mp4";
        String mp4folder_path = serverPath + mediaFile.getFilePath();
        Mp4VideoUtil videoUtil = new
Mp4VideoUtil(ffmpeg_path,video_path,mp4_name,mp4folder_path);
        String result = videoUtil.generateMp4();
 if(result == null || !result.equals("success")){
            //操作失败写入处理日志
            mediaFile.setProcessStatus("303003");//处理状态为处理失败
            MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
            mediaFileProcess_m3u8.setErrormsg(result);
            mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);
            mediaFileRepository.save(mediaFile);
            return ;
        }
        //生成m3u8
        video_path = serverPath + mediaFile.getFilePath()+mp4_name;//此地址为mp4的地址
        String m3u8_name = mediaFile.getFileId()+".m3u8";
        String m3u8folder_path = serverPath + mediaFile.getFilePath()+"hls/";
        HlsVideoUtil hlsVideoUtil = new
HlsVideoUtil(ffmpeg_path,video_path,m3u8_name,m3u8folder_path);
        result = hlsVideoUtil.generateM3u8();
        if(result == null || !result.equals("success")){
            //操作失败写入处理日志
            mediaFile.setProcessStatus("303003");//处理状态为处理失败
            MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
            mediaFileProcess_m3u8.setErrormsg(result);
            mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);
            mediaFileRepository.save(mediaFile);
            return ;
        }
        //获取m3u8列表
        List<String> ts_list = hlsVideoUtil.get_ts_list();
        //更新处理状态为成功
        mediaFile.setProcessStatus("303002");//处理状态为处理成功
        MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
        mediaFileProcess_m3u8.setTslist(ts_list);
        mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);
        //m3u8文件url
        mediaFile.setFileUrl(mediaFile.getFilePath()+"hls/"+m3u8_name);
        mediaFileRepository.save(mediaFile);
    }
}

说明:
mp4转成m3u8如何判断转换成功?
第一、根据视频时长来判断,同mp4转换成功的判断方法。
第二、最后还要判断m3u8文件内容是否完整。

发送视频处理消息

当视频上传成功后向 MQ 发送视频 处理消息。
修改媒资管理服务的文件上传代码,当文件上传成功向MQ发送视频处理消息。

RabbitMQ配置

1、将media-processor工程下的RabbitmqConfig配置类拷贝到media工程下。
2、在media工程下配置mq队列等信息
修改application.yml

xc‐service‐manage‐media:
  mq:
    queue‐media‐video‐processor: queue_media_video_processor
    routingkey‐media‐video: routingkey_media_video

修改Service

在文件合并方法中添加向mq发送视频处理消息的代码:

 //向MQ发送视频处理消息
    public ResponseResult sendProcessVideoMsg(String mediaId){
       Optional<MediaFile> optional = mediaFileRepository.findById(fileMd5);
        if(!optional.isPresent()){
          return new ResponseResult(CommonCode.FAIL);
        }
       MediaFile mediaFile = optional.get();
        //发送视频处理消息
        Map<String,String> msgMap = new HashMap<>();
        msgMap.put("mediaId",mediaId);
        //发送的消息
        String msg = JSON.toJSONString(msgMap);
        try {
           
this.rabbitTemplate.convertAndSend(RabbitMQConfig.EX_MEDIA_PROCESSTASK,routingkey_media_video,
msg);
            LOGGER.info("send media process task msg:{}",msg);
        }catch (Exception e){
            e.printStackTrace();
            LOGGER.info("send media process task error,msg is:{},error:{}",msg,e.getMessage());
            return new ResponseResult(CommonCode.FAIL);
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

在mergechunks方法最后调用sendProcessVideo方法。

......
        //状态为上传成功
        mediaFile.setFileStatus("301002");
        mediaFileRepository.save(mediaFile);
        String mediaId = mediaFile.getFileId();
        //向MQ发送视频处理消息
        sendProcessVideoMsg(mediaId);
......

视频处理测试

测试流程:
1、上传avi文件
2、观察日志是否发送消息
3、观察视频处理进程是否接收到消息进行处理
4、观察mp4文件是否生成
5、观察m3u8及 ts文件是否生成

我的媒资

通过我的媒资可以查询本教育机构拥有的媒资文件,进行文件处理、删除文件、修改文件信息等操作,具体需求如
下:
1、分页查询我的媒资文件
2、删除媒资文件
3、处理媒资文件
4、修改媒资文件信息

API

@Api(value = "媒体文件管理",description = "媒体文件管理接口",tags = {"媒体文件管理接口"})
public interface MediaFileControllerApi {
   
    @ApiOperation("查询文件列表")
    public QueryResponseResult findList(int page, int size, QueryMediaFileRequest
queryMediaFileRequest) ;
}

服务端开发

Dao

@Repository
public interface MediaFileDao extends MongoRepository<MediaFile,String> {
}

Service

定义findList方法实现媒资文件查询列表。

@Service
public class MediaFileService {
    private static Logger logger = LoggerFactory.getLogger(MediaFileService.class);
    @Autowired
    MediaFileRepository mediaFileRepository;
    //文件列表分页查询
    public QueryResponseResult findList(int page,int size,QueryMediaFileRequest
queryMediaFileRequest){
        //查询条件
        MediaFile mediaFile = new MediaFile();
        if(queryMediaFileRequest == null){
            queryMediaFileRequest = new QueryMediaFileRequest();
        }
        //查询条件匹配器
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withMatcher("tag", ExampleMatcher.GenericPropertyMatchers.contains())//tag字段
模糊匹配
                .withMatcher("fileOriginalName",
ExampleMatcher.GenericPropertyMatchers.contains())//文件原始名称模糊匹配
                .withMatcher("processStatus", ExampleMatcher.GenericPropertyMatchers.exact());//
处理状态精确匹配(默认)
        //查询条件对象
        if(StringUtils.isNotEmpty(queryMediaFileRequest.getTag())){
     mediaFile.setTag(queryMediaFileRequest.getTag());
        }
        if(StringUtils.isNotEmpty(queryMediaFileRequest.getFileOriginalName())){
            mediaFile.setFileOriginalName(queryMediaFileRequest.getFileOriginalName());
        }
        if(StringUtils.isNotEmpty(queryMediaFileRequest.getProcessStatus())){
            mediaFile.setProcessStatus(queryMediaFileRequest.getProcessStatus());
        }
        //定义example实例
        Example<MediaFile> ex = Example.of(mediaFile, matcher);
        page = page‐1;
        //分页参数
        Pageable pageable = new PageRequest(page, size);
        //分页查询
        Page<MediaFile> all = mediaFileRepository.findAll(ex,pageable);
        QueryResult<MediaFile> mediaFileQueryResult = new QueryResult<MediaFile>();
        mediaFileQueryResult.setList(all.getContent());
        mediaFileQueryResult.setTotal(all.getTotalElements());
        return new QueryResponseResult(CommonCode.SUCCESS,mediaFileQueryResult);
    }
}

Controller

@RestController
@RequestMapping("/media/file")
public class MediaFileController implements MediaFileControllerApi {
    @Autowired
    MediaFileService mediaFileService;
    @Autowired
    MediaUploadService mediaUploadService;
    @Override
    @GetMapping("/list/{page}/{size}")
    public QueryResponseResult findList(@PathVariable("page") int page, @PathVariable("size")
int size, QueryMediaFileRequest queryMediaFileRequest) {
     //媒资文件查询    
        return mediaFileService.findList(page,size,queryMediaFileRequest);
    }
}

媒资与课程计划关联

操作的业务流程如下:
1、进入课程计划修改页面
2、选择视频
打开媒资文件查询窗口,找到该课程章节的视频,选择此视频。
点击“选择媒资文件”打开媒资文件列表

3、 选择成功后,将在课程管理数据库保存课程计划对应在的课程视频地址。
在课程管理数据库创建表 teachplan_media 存储课程计划与媒资关联信息

保存视频信息

需求分析

用户进入课程计划页面,选择视频,将课程计划与视频信息保存在课程管理数据库中。
用户操作流程:
1、进入课程计划,点击”选择视频“,打开我的媒资查询页面
2、为课程计划选择对应的视频,选择“选择”
3、前端请求课程管理服务保存课程计划与视频信息。

数据模型

创建teachplanMedia 模型类:

@Data
@ToString
@Entity
@Table(name="teachplan_media")
@GenericGenerator(name = "jpa‐assigned", strategy = "assigned")
public class TeachplanMedia implements Serializable {
    private static final long serialVersionUID = ‐916357110051689485L;
    @Id
    @GeneratedValue(generator = "jpa‐assigned")
    @Column(name="teachplan_id")
    private String teachplanId;
    @Column(name="media_id")
    private String mediaId;
    @Column(name="media_fileoriginalname")
    private String mediaFileOriginalName;
    
  @Column(name="media_url")
    private String mediaUrl;
    
    @Column(name="courseid")
    private String courseId;
}

API接口

此接口作为前端请求课程管理服务保存课程计划与视频信息的接口:
在课程管理服务增加接口:

@ApiOperation("保存媒资信息")
public ResponseResult savemedia(TeachplanMedia teachplanMedia);

服务端开发

DAO

创建 TeachplanMediaRepository用于对TeachplanMedia的操作。

public interface TeachplanMediaRepository extends JpaRepository<TeachplanMedia, String> {
}

Service

//保存媒资信息
public ResponseResult savemedia(TeachplanMedia teachplanMedia) {
    if(teachplanMedia == null){
        ExceptionCast.cast(CommonCode.INVALIDPARAM);
    }
    //课程计划
    String teachplanId = teachplanMedia.getTeachplanId();
    //查询课程计划
    Optional<Teachplan> optional = teachplanRepository.findById(teachplanId);
    if(!optional.isPresent()){
        ExceptionCast.cast(CourseCode.COURSE_MEDIA_TEACHPLAN_ISNULL);
    }
    Teachplan teachplan = optional.get();
    //只允许为叶子结点课程计划选择视频
    String grade = teachplan.getGrade();
    if(StringUtils.isEmpty(grade) || !grade.equals("3")){
        ExceptionCast.cast(CourseCode.COURSE_MEDIA_TEACHPLAN_GRADEERROR);
    }
    TeachplanMedia one = null;
    Optional<TeachplanMedia> teachplanMediaOptional =
teachplanMediaRepository.findById(teachplanId);
    if(!teachplanMediaOptional.isPresent()){
        one = new TeachplanMedia();
    }else{
        one = teachplanMediaOptional.get();
    }
    //保存媒资信息与课程计划信息
    one.setTeachplanId(teachplanId);
    one.setCourseId(teachplanMedia.getCourseId());
    one.setMediaFileOriginalName(teachplanMedia.getMediaFileOriginalName());
    one.setMediaId(teachplanMedia.getMediaId());
    one.setMediaUrl(teachplanMedia.getMediaUrl());
    teachplanMediaRepository.save(one);
    return new ResponseResult(CommonCode.SUCCESS);
}

Controller

@Override
@PostMapping("/savemedia")
public ResponseResult savemedia(@RequestBody TeachplanMedia teachplanMedia) {
    return courseService.savemedia(teachplanMedia);
}

查询视频信息

需求分析

课程计划的视频信息保存后在页面无法查看,本节解决课程计划页面显示相关联的媒资信息。
解决方案:
在获取课程计划树结点信息时将关联的媒资信息一并查询,并在前端显示,下图说明了课程计划显示的区域。

Dao

修改课程计划查询的Dao:
1、修改模型
在课程计划结果信息中添加媒资信息

@Data
@ToString
public class TeachplanNode extends Teachplan {
    List<TeachplanNode> children;
    //媒资信息
    private String mediaId;
    private String mediaFileOriginalName;
}

2、修改sql语句,添加关联查询媒资信息
添加mediaId、mediaFileOriginalName

<resultMap type="com.xuecheng.framework.domain.course.ext.TeachplanNode" id="teachplanMap" >
    <id property="id" column="one_id"/>
    <result property="pname" column="one_name"/>
    <result property="grade" column="one_grade"/>
    <collection property="children"
ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
<id property="id" column="two_id"/>
        <result property="pname" column="two_name"/>
        <result property="grade" column="two_grade"/>
        <collection property="children"
ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
            <id property="id" column="three_id"/>
            <result property="pname" column="three_name"/>
            <result property="grade" column="three_grade"/>
            <result property="mediaId" column="mediaId"/>
            <result property="mediaFileOriginalName" column="mediaFileOriginalName"/>
        </collection>
    </collection>
</resultMap>
<select id="selectList" resultMap="teachplanMap" parameterType="java.lang.String" >
    SELECT
    a.id one_id,
    a.pname one_name,
    a.grade one_grade,
    a.orderby one_orderby,
    b.id two_id,
    b.pname two_name,
    b.grade two_grade,
    b.orderby two_orderby,
    c.id three_id,
    c.pname three_name,
    c.grade three_grade,
    c.orderby three_orderby,
    media.media_id mediaId,
    media.media_fileoriginalname mediaFileOriginalName
    FROM
    teachplan a LEFT JOIN teachplan b
    ON a.id = b.parentid
    LEFT JOIN teachplan c
    ON b.id = c.parentid
    LEFT JOIN teachplan_media media
    ON c.id = media.teachplan_id
    WHERE  a.parentid = ‘0‘
    <if test="_parameter!=null and _parameter!=‘‘">
        and a.courseid=#{courseId}
    </if>
    ORDER BY a.orderby,
    b.orderby,
    c.orderby
</select>

页面查询视频

<el‐button style="font‐size: 12px;" type="text" on‐click={ () => this.querymedia(data.id) }>
{data.mediaFileOriginalName}&nbsp;&nbsp;&nbsp;&nbsp;选择视频</el‐button>

选择视频后立即刷新课程计划树,在提交成功后,添加查询课程计划代码:this.findTeachplan(),完整代码如下:

choosemedia(mediaId,fileOriginalName,mediaUrl){
  this.mediaFormVisible = false;
  //保存课程计划与视频对应关系
  let teachplanMedia = {};
  teachplanMedia.teachplanId = this.activeTeachplanId;
  teachplanMedia.mediaId = mediaId;
  teachplanMedia.mediaFileOriginalName = fileOriginalName;
  teachplanMedia.mediaUrl = mediaUrl;
  teachplanMedia.courseId = this.courseid;
  //保存媒资信息到课程数据库
  courseApi.savemedia(teachplanMedia).then(res=>{
      if(res.success){
          this.$message.success("选择视频成功")
        //查询课程计划
        this.findTeachplan()
      }else{
        this.$message.error(res.message)
      }
  })
},

原文地址:https://www.cnblogs.com/anan-java/p/12293413.html

时间: 2024-11-11 04:26:56

学成在线(第14天)的相关文章

学成在线(第1天)

第一次用博客,笔者大学毕业即将走向社会,从事Java开发,有个博客是很有必要的.博客用于记录学习工作中遇到的问题,可以很好的总结经验.同时我在遇到bug的时候也是通过他人博客解决的,我也想通过博客帮助到遇到同样问题的人. 年前面试过几家,现在企业都要求分布式,微服务springcloud的项目经验,为了能找到更好的工作,我选择了自学黑马<学成在线>这个微服务项目,这个博客就用于记录每天看视频做这个项目遇到的问题和总结吧! 1.项目的功能架构 1.1  项目背景 学成在线借鉴了MOOC(大型开放

学成在线(第21天)项目总结

1  学成在线是个什么样的项目? 1.1  项目背景 学成在线借鉴了MOOC(大型开放式网络课程,即MOOC(massive open online courses))的设计思想,是一个提供IT职业课程在线学习的平台,它为即将和已经加入IT领域的技术人才提供在线学习服务,用户通过在线学习.在线练习.在线考试等学习内容,最终掌握所学的IT技能,并能在工作中熟练应用.当前市场的在线教育模式多种多样,包括:B2C.C2C.B2B2C等业务模式,学成在线采用B2B2C业务模式,即向企业或个人提供在线教育

学成在线(第15天)

学习页面查询课程计划 到目前为止,我们已可以编辑课程计划信息并上传课程视频,下一步我们要实现在线学习页面动态读取章节对应的视频并进行播放.在线学习页面所需要的信息有两类:一类是课程计划信息.一类是课程学习信息(视频地址.学习进度等),如下图: 在线学习集成媒资管理的需求如下:1.在线学习页面显示课程计划2.点击课程计划播放该课程计划对应的视频 Api 接口 课程计划信息从哪里获取?目前课程计划信息在课程管理数据库和ES索引库中存在,考虑性能要求,课程发布后对课程的查询统一从ES索引库中查询.前端

学成在线(第3天)

新增页面 新增页面接口定义 1.定义响应模型 @Data public class CmsPageResult extends ResponseResult {     CmsPage cmsPage;     public CmsPageResult(ResultCode resultCode,CmsPage cmsPage) {         super(resultCode);         this.cmsPage = cmsPage;     } } 2.定义添加Api @ApiO

学成在线(第4天)

页面静态化需求 1.为什么要进行页面管理?本项目cms系统的功能就是根据运营需要,对门户等子系统的部分页面进行管理,从而实现快速根据用户需求修改页面内容并上线的需求. 2.如何修改页面的内容?在开发中修改页面内容是需要人工编写html及JS文件,CMS系统是通过程序自动化的对页面内容进行修改,通过页面静态化技术生成html页面. 3.如何对页面进行静态化? 采用页面模板+数据 = 输出html页面的技术实现静态化. 4.静态化的html页面存放在哪里? 生成对的静态化的页面,由cms程序自动发布

学成在线(第8天)

FastDFS 研究 什么是分布式文件系统 什么是文件系统 文件系统是负责管理和存储文件的系统软件,它是操作系统和硬件驱动之间的桥梁,操作系统通过文件系统提供的接口去存取文件,用户通过操作系统访问磁盘上的文件.如下图: 什么是分布式文件系统 为什么会有分布文件系统呢?分布式文件系统是面对互联网的需求而产生,互联网时代对海量数据如何存储?靠简单的增加硬盘的个数已经满足不了我们的要求,因为硬盘传输速度有限但是数据在急剧增长,另外我们还要要做好数据备份.数据安全等.采用分布式文件系统可以将多个地点的文

学成在线(第11天)

课程搜索需求分析 需求分析 1.根据分类搜索课程信息.2.根据关键字搜索课程信息,搜索方式为全文检索,关键字需要匹配课程的名称. 课程内容.3.根据难度等级搜索课程.4.搜索结点分页显示. 搜索流程 1.课程管理服务将数据写到MySQL数据库2.使用Logstash将MySQL数据库中的数据写到ES的索引库.3.用户在前端搜索课程信息,请求到搜索服务.4.搜索服务请求ES搜索课程信息. 课程索引 如何维护课程索引信息?1.当课程向MySQL添加后同时将课程信息添加到索引库.采用Logstach实

学成在线_nginx遇到的问题

下载nginx:http://nginx.org/en/download.html 下载了最新的稳定版本 安装目录内运行nginx.exe 一直运行不起来.进程里面也没有这个进程.我把本机的IIS的80端口关闭了也起作用. 后来查询,发现可以在logs文件夹下面看错误日志. An attempt was made to access a socket in a way forbidden by its access permissions https://www.cnblogs.com/hope

学成在线(第6天)

页面发布课程管理 技术方案 本项目使用MQ实现页面发布的技术方案如下: 技术方案说明:1.平台包括多个站点,页面归属不同的站点.2.发布一个页面应将该页面发布到所属站点的服务器上.3.每个站点服务部署cms client程序,并与交换机绑定,绑定时指定站点Id为routingKey.指定站点id为routingKey就可以实现cms client只能接收到所属站点的页面发布消息.4.页面发布程序向MQ发布消息时指定页面所属站点Id为routingKey,将该页面发布到它所在服务器上的cmscli