5.5萤石云API播放接入指南介绍

海康威视的摄像头我们是可以自己编程开发进行远程控制的,海康威视提供了一套api供我们远程调用,也提供了一个网站去管理我们自己的网络摄像头,这个网站就是萤石云

https://www.ys7.com/

在这个网站可以对你购买的摄像头进行管理,进行开发

注册-登录后找到开发者服务,在这里可以添加你自己的摄像头并且进行管理

点击接口测试,就可以进行功能的调试了

详细开发api在https://open.ys7.com/doc/zh/

AccessToken:要根据这个token进行视频显示及控制,但是这个token七天 就会过期,所以需要每七天去调用接口重新获取一次

根据appKey和secret获取accessToken

  • 接口功能

    该接口用于管理员账号根据appKey和secret获取accessToken,appKey和secret可以在官网-开发者服务-我的应用中找到

    注意:获取到的accessToken有效期是7天,请在即将过期或者接口报错10002时重新获取,请勿频繁调用,频繁调用将会被拉入限制黑名单

  • 请求地址

    https://open.ys7.com/api/lapp/token/get

  • 请求方式

    POST

  • 请求参数

参数名 类型 描述 是否必选
appKey String appKey Y
appSecret String appSecret Y

UIKit简介
UI组件(UIKit)是萤石开放平台推出的含UI二次开发组件,全称Ezviz UIKit(简称EZUIKit),可以通过三行代码集成视频功能,有四个版本:iOS、Android、JS(Web/H5)、ActiveX(IE)。

UIKit自带播放器,只需使用“萤石播放链接(URL)”即可实现实时视频预览、录像回放,播放URL可以在“开发者服务中心”的“设备管理”中获取,也可以按照规则自己拼接

uikit非常方便,大部分情况下直接引入这个js,使用三行代码即可完成视频的展示

下载地址:https://open.ys7.com/mobile/download.html

代码示例——demo-live.html

// 首先引入js<script src="../ezuikit.js"></script><script src="../js/jquery.min.js"></script>
// 页面创建video标签<videoid="myplayer"src="ezopen://open.ys7.com/f01018a141094b7fa138b9d0b856507b.live"width="400"height="300"poster="[这里可以填入封面图片URL]"[autoplay]controlsplaysInlinewebkit-playsinline></video>
// 开始初始化直播源地址var player = new EZUIKit.EZUIPlayer('myPlayer');
// 播放player.play();
// 结束player.stop();

5.5.3.萤石云云台控制API介绍

http接口:—》设备:里面有很多大家会用到的接口

接口调试方法:使用接口调试工具postman,debugapi进行

前三个参数从萤石云-》我的设备中获取

后两个参数代表操作的方式

开始云台控制

  • 接口功能

    对设备进行开始云台控制,开始云台控制之后必须先调用停止云台控制接口才能进行其他操作,包括其他方向的云台转动

  • 请求地址

    https://open.ys7.com/api/lapp/device/ptz/start

  • 请求方式

    POST

  • 子账户token请求所需最小权限

    "Permission":"Ptz" "Resource":"Cam:序列号:通道号"

  • 请求参数

参数名 类型 描述 是否必选
accessToken String 授权过程获取的access_token Y
deviceSerial String 设备序列号,存在英文字母的设备序列号,字母需为大写 Y
channelNo int 通道号 Y
direction int 操作命令:0-上,1-下,2-左,3-右,4-左上,5-左下,6-右上,7-右下,8-放大,9-缩小,10-近焦距,11-远焦距 Y
speed int 云台速度:0-慢,1-适中,2-快 Y

预置点:可以设置摄像头开机后的初始位置,预置点序号,C6设备是1-12该参数需要开发者自行保存

如果开发者自己没有摄像头,需要甲方提供对应的设备,并且萤石云的账号是需要交个开发者的,因为开发需要的参数都在萤石云管理界面可以查询到。甲方要做的就是保持要测试的摄像头24小时开机

5.5.4.项目中对接海康威视摄像头

1)要有摄像头的数据库表

2)要有摄像头的管理界面

3)当用户登录以后,打开摄像头的监控页面时,去数据库根据用户找到对应的摄像头,并将摄像头数据返回

4)在页面根据摄像头的信息使用uikit进行渲染

购买摄像头之后,在萤石云申请对应的秘钥,方便下一步进行网络控制。

根据appKey和secret获取accessToken接口

而播放以及控制必须要的三个参数为accessToken,deviceSerial,channelNo

播放地址url

实现步骤:

1)

2)要从数据库把摄像头的信息取回来

3)页面上定义一个vedio标签,其中的src属性指定播放路径

4)在js中让代码执行var player1 = new EZUIPlayer(‘player1’);

可能会遇到的问题:通常是播放的token过期,需要重新去官网申请

5.5.5.使用阿里图标库制作摄像头云台对摄像头进行控制

这里主要想让大家学习的是如何使用第三方的图标库

阿里图标库:https://www.iconfont.cn/

使用说明:https://blog.csdn.net/sanlingwu/article/details/83097641

<div class="layui-text-bottom layui-row vedio-button-select"><button lay-submit="" lay-filter="add" onclick="control(0)"></button><button  lay-submit="" lay-filter="add" onclick="control(1)"></button><button  lay-submit="" lay-filter="add" onclick="control(2)"></button><button  lay-submit="" lay-filter="add" onclick="control(3)"></button><button  lay-submit="" lay-filter="add" onclick="stop()">停止</button><button lay-submit="" lay-filter="add" onclick="control(8)">放大</button><button lay-submit="" lay-filter="add" onclick="control(9)">缩小</button><button lay-submit="" lay-filter="add" onclick="control(10)">近焦</button><button lay-submit="" lay-filter="add" onclick="control(11)">远焦</button><button lay-submit="" lay-filter="add" onclick="capture()">抓拍</button><div style="float: left;"><select id="speed3" name="speed" lay-verify="required" class='vedio-select layui-btn layui-btn-normal layui-col-sm2 form-control input-sm' data-bv-notempty='true'><option value="0">慢速</option><option value="1">正常</option><option value="2">快速</option></select><!--<select id="moving3" name="moving" lay-verify="required" class='vedio-select layui-btn layui-btn-normal layui-col-sm2 form-control input-sm' data-bv-notempty='true'><option value="0">步进</option><option value="1">连动</option></select>--></div>
</div>
//开始控制
//0-上,1-下,2-左,3-右,4-左上,5-左下,6-右上,7-右下,8-放大,9-缩小,10-近焦距,11-远焦距
//默认为步进,点一下就停止
var control=function (direction) {data.direction=direction;data.speed='0';layui.use(['jquery', 'layer'], function(){var $ = layui.$,layer = layui.layer;var speed = $("#speed").val();if (speed != null && speed != '') {data.speed=speed;}var moving = $("#moving").val();if (moving != null&& moving != '') {//连动if (moving == 1) {$.post('https://open.ys7.com/api/lapp/device/ptz/start',data,function(res){if(res.code==200){layer.msg(res.msg, {time: 2000});}else{layer.msg(res.msg, {time: 2000});}},'json');} else {//步进,动一下就停$.post('https://open.ys7.com/api/lapp/device/ptz/start',data,function(res){},'json');$.post('https://open.ys7.com/api/lapp/device/ptz/stop',data,function(res){if(res.code==200){layer.msg(res.msg, {time: 2000});}else{layer.msg(res.msg, {time: 2000});}},'json');}}return false;});
}

如果摄像头支持抓拍,可以使用如下代码:

//抓拍
var capture=function () {layui.use(['jquery', 'layer'], function(){var $ = layui.$,layer = layui.layer;$.post('https://open.ys7.com/api/lapp/device/capture',data,function(res){if(res.code==200){layer.msg(res.msg, {time: 2000});//抓图成功后将这个地址传递到后台,下载下来保存到阿里云console.log(res.data.picUrl)$.get('/vedios/savePic?picUrl='+res.data.picUrl,function (res) {ayer.msg('抓图成功', {time: 2000});})}else{layer.msg(res.msg, {time: 2000});}},'json');})
}

抓拍的后台逻辑:

/*** 抓取图片,并上传oss* https://open.ys7.com/doc/zh/book/index/device_option.html#device_option-api4* 请求地址:https://open.ys7.com/api/lapp/device/capture* 请求方式:POST* 请求参数:accessToken=at.12xp95k63bboast3aq0g5hg22q468929&deviceSerial=427734888&channelNo=2* 返回数据* {*     "data": {*         "picUrl": "http://img.ys7.com//group2/M00/74/22/CmGdBVjBVDCAaFNZAAD4cHwdlXA833.jpg"*     },*     "code": "200",*     "msg": "操作成功!"* }* @param* @return** http://localhost:9001/vedios/savePic?picUrl=111*/
@GetMapping("/savePic")
@ApiOperation(value = "将抓取的图片保存到阿里云")
public void capture(String picUrl)throws Exception{//System.out.println(picUrl);HttpGet httpGet = new HttpGet("https://img13.360buyimg.com/n5/jfs/t17446/352/1415143705/203490/68cdb4ee/5ac74800N0824a445.jpg");httpGet.setConfig(requestConfig);CloseableHttpResponse httpResponse = httpClient.execute(httpGet);HttpEntity httpEntity = httpResponse.getEntity();if (httpEntity!=null) {System.out.println("ContentType:"+httpEntity.getContentType().getValue());InputStream inputStream = httpEntity.getContent();FileService fileService = fileServiceFactory.getFileService("ALIYUN");//InputStream转换MultipartFileMultipartFile multipartFile = new MultipartFileDto("temp.jpg","temp.jpg",httpEntity.getContentType().getValue(), inputStream);fileService.upload(multipartFile);}
}

将file包装成MultipartFile

package com.topwulian.dto;import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;/*** @Author: szz* @Date: 2019/1/16 下午4:33* @Version 1.0** 负责将InputStream转换MultipartFile,可以少引一个jar包,本来用的是spring-test-4.3.9中的MockMultipartFile,直接提取出来使用*/
public class MultipartFileDto implements MultipartFile {private final String name;private String originalFilename;private String contentType;private final byte[] content;/*** Create a new MultipartFileDto with the given content.* @param name the name of the file* @param content the content of the file*/public MultipartFileDto(String name, byte[] content) {this(name, "", null, content);}/*** Create a new MultipartFileDto with the given content.* @param name the name of the file* @param contentStream the content of the file as stream* @throws IOException if reading from the stream failed*/public MultipartFileDto(String name, InputStream contentStream) throws IOException {this(name, "", null, FileCopyUtils.copyToByteArray(contentStream));}/*** Create a new MultipartFileDto with the given content.* @param name the name of the file* @param originalFilename the original filename (as on the client's machine)* @param contentType the content type (if known)* @param content the content of the file*/public MultipartFileDto(String name, String originalFilename, String contentType, byte[] content) {this.name = name;this.originalFilename = (originalFilename != null ? originalFilename : "");this.contentType = contentType;this.content = (content != null ? content : new byte[0]);}/*** Create a new MultipartFileDto with the given content.* @param name the name of the file* @param originalFilename the original filename (as on the client's machine)* @param contentType the content type (if known)* @param contentStream the content of the file as stream* @throws IOException if reading from the stream failed*/public MultipartFileDto(String name, String originalFilename, String contentType, InputStream contentStream)throws IOException {this(name, originalFilename, contentType, FileCopyUtils.copyToByteArray(contentStream));}@Overridepublic String getName() {return this.name;}@Overridepublic String getOriginalFilename() {return this.originalFilename;}@Overridepublic String getContentType() {return this.contentType;}@Overridepublic boolean isEmpty() {return (this.content.length == 0);}@Overridepublic long getSize() {return this.content.length;}@Overridepublic byte[] getBytes() throws IOException {return this.content;}@Overridepublic InputStream getInputStream() throws IOException {return new ByteArrayInputStream(this.content);}@Overridepublic void transferTo(File dest) throws IOException, IllegalStateException {FileCopyUtils.copy(this.content, dest);}}

5.5.6.摄像头不支持远程截图的另外一种玩法

如果你购买摄像头不支持远程截图,就需要其他办法

在摄像头购买后,可以在局域网中对它进行配置,我购买的摄像头支持配置截图并且上传到windows的ftp服务器这样一个功能,所以我只能迂回作战,先使用内置的上传ftp的功能将图片传到ftp上,再去监控ftp服务器,把最新截取的图片地址保存到云服务器的数据库,这样就能管理每个摄像头所截取的图片啦。

5.5.7.监控ftp中设备截图变化程序的制作

通过查看萤石接口平台:https://open.ys7.com/doc/zh/book/index/device_option.html#device_option-api4

得到摄像头截图的接口:

设备抓拍图片

  • 接口功能

    抓拍设备当前画面,该接口仅适用于IPC或者关联IPC的DVR设备,该接口并非预览时的截图功能。海康型号设备可能不支持萤石协议抓拍功能,使用该接口可能返回不支持或者超时。

    注意:设备抓图能力有限,请勿频繁调用,频繁调用将会被拉入限制黑名单,建议调用的间隔为4s左右

  • 请求地址

    https://open.ys7.com/api/lapp/device/capture

  • 请求方式

    POST

  • 子账户token请求所需最小权限

    "Permission":"Capture" "Resource":"Cam:序列号:通道号"

  • 请求参数

参数名 类型 描述 是否必选
accessToken String 授权过程获取的access_token Y
deviceSerial String 设备序列号,存在英文字母的设备序列号,字母需为大写 Y
channelNo int 通道号,IPC设备填写1 Y
  • HTTP请求报文
POST /api/lapp/device/capture HTTP/1.1
Host: open.ys7.com
Content-Type: application/x-www-form-urlencodedaccessToken=at.12xp95k63bboast3aq0g5hg22q468929&deviceSerial=427734888&channelNo=1
  • 返回数据
{"data": {"picUrl": "https://img.ys7.com//group2/M00/74/22/CmGdBVjBVDCAaFNZAAD4cHwdlXA833.jpg"},"code": "200","msg": "操作成功!"
}
  • 返回字段
字段名 类型 描述
picUrl String 抓拍后的图片路径,图片保存有效期为2小时
  • 返回码
返回码 返回消息 描述
200 操作成功 请求成功
10001 参数错误 参数为空或格式不正确
10002 accessToken异常或过期 重新获取accessToken
10005 appKey异常 appKey被冻结
10051 无权限进行抓图 设备不属于当前用户或者未分享给当前用户
20002 设备不存在
20006 网络异常 检查设备网络状况,稍后再试
20007 设备不在线 检查设备是否在线
20008 设备响应超时 操作过于频繁或者设备不支持萤石协议抓拍
20014 deviceSerial不合法
20032 该用户下该通道不存在 检查设备是否包含该通道
49999 数据异常 接口调用异常
60017 设备抓图失败 设备返回失败
60020 不支持该命令 确认设备是否支持抓图

但是,由于我们购买的摄像头经过测试,不支持远程抓图。

思路:打开摄像头的局域网配置中心,发现可以进行ftp截图,并且传到云服务器上,准备一台云服务器,安装nginx,将设备的截图定时传到nginx的文件目录下面,由nginx映射成http服务,同时把设备上传的图片跟摄像进行绑定(要在数据库维护摄像头跟上传图片的对应关系)。

前置工作:需要在摄像头的配置界面把你要上传到的ftp服务器地址配好

在云服务器上可以通过nginx配置一个ftp服务器,并且配置一个http服务器,可以在服务器上安装一个宝塔,通过宝塔可视化安装配置ftp,http服务器

宝塔网站:https://www.bt.cn/

我们服务器的程序,主要是监控ftp文件夹截图的变化,监控到变化后,要把文件的http地址保存到云平台的服务器的数据库中,跟摄像头建立对应关系

如何使用java程序监控文件夹下面文件的变化呢?

第一通过jdk内置的监控程序,第二通过apache的开源组件

程序ftp_image就可以监控任意文件夹文件的变化

<dependencies><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.version}</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId></dependency>
</dependencies>

application.yml

线程池

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {@Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 设置线程池核心容量executor.setCorePoolSize(10);// 设置线程池最大容量executor.setMaxPoolSize(20);// 设置任务队列长度executor.setQueueCapacity(200);// 设置线程超时时间executor.setKeepAliveSeconds(60);// 设置线程名称前缀executor.setThreadNamePrefix("taskExecutor-");// 设置任务丢弃后的处理策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());return executor;}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new AsyncUncaughtExceptionHandler() {@Overridepublic void handleUncaughtException(Throwable throwable, Method method, Object... objects) {System.out.println("线程异常");}};}
}

配置类

@Component
public class FtpConfig {// 自动注入业务服务@Autowiredprivate VedioImagesDao vedioImagesDao;@Autowiredprivate VedioDao vedioDao;@Beanpublic ReYoFileMonitor ftp(){//http://pic.topwulian.com/C32944171/2019_5_19-2019_5_19/C32944171_20190518171652865_TIMING.jpgReYoFileMonitor m =null;try {m=new ReYoFileMonitor(5000);m.monitor("/Users/szz/ftp", new ReYoFileListener(vedioImagesDao,vedioDao));m.start();}catch (Exception e){System.out.println("由于监听器在独立的线程中执行,一旦异常发生将导致线程退出,所以如果希望监听线程不中断,应在线程中捕获所有异常。");}return m;//}
}

监视器类

import java.io.File;import org.apache.commons.io.monitor.FileAlterationListener;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;/*** <B>创 建 人:</B>AdministratorReyoAut <BR>* <B>创建时间:</B>2017年12月23日 下午9:26:08<BR>** @author ReYo* @version 1.0*/public class ReYoFileMonitor {FileAlterationMonitor monitor = null;public ReYoFileMonitor(long interval) throws Exception {monitor = new FileAlterationMonitor(interval);}public void monitor(String path, FileAlterationListener listener) {FileAlterationObserver observer = new FileAlterationObserver(new File(path));monitor.addObserver(observer);observer.addListener(listener);}public void stop() throws Exception {monitor.stop();}public void start() throws Exception {monitor.start();}}

FileAlterationListener,FileAlterationMonitor,FileAlterationObserver都是commons-io包下面专门用来做文件监控的

监视到文件变化后要做的是事情,需要一个监听类

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import com.topwulian.dao.VedioDao;
import com.topwulian.dao.VedioImagesDao;
import com.topwulian.model.Vedio;
import com.topwulian.model.VedioImages;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationObserver;/*** <B>创 建 人:</B>AdministratorReyoAut <BR>* <B>创建时间:</B>2017年12月23日 下午9:25:21<BR>** @author ReYo* @version 1.0*/
//@Component
public class ReYoFileListener extends FileAlterationListenerAdaptor {private VedioImagesDao vedioImagesDao;private VedioDao vedioDao;public ReYoFileListener(VedioImagesDao vedioImagesDao, VedioDao vedioDao) {this.vedioImagesDao = vedioImagesDao;this.vedioDao = vedioDao;}ReYoFileMonitor monitor = null;@Overridepublic void onStart(FileAlterationObserver observer) {// System.out.println("onStart");}@Overridepublic void onDirectoryCreate(File directory) {System.out.println("onDirectoryCreate:" + directory.getAbsolutePath());}@Overridepublic void onDirectoryChange(File directory) {System.out.println("onDirectoryChange:" + directory.getAbsolutePath());}@Overridepublic void onDirectoryDelete(File directory) {System.out.println("onDirectoryDelete:" + directory.getAbsolutePath());}@Overridepublic void onFileCreate(File file) {try {String absolutePath = file.getAbsolutePath();System.out.println("onFileCreate:" + absolutePath);//c:\wwwroot\pic\C32944171\2019_5_19-2019_5_19\C32944171_20190518171652865_TIMING.jpg//System.out.println("将文件处理存入数据库。。。");String[] split = absolutePath.split("\\\\");Map<String, Object> params=new HashMap<>();params.put("deviceSerial",split[3]);List<Vedio> vedios = vedioDao.list(params, null, null);if (!vedios.isEmpty()&&split.length>5) {Vedio vedio = vedios.get(0);VedioImages vedioImages = new VedioImages();vedioImages.setPath(absolutePath);vedioImages.setDeviceSerial(vedio.getDeviceSerial());vedioImages.setFarmId(vedio.getFarmId());String urlPrefix = "http://pic.topwulian.com/";String url=urlPrefix+split[3]+"/"+split[4]+"/"+split[5];vedioImages.setUrl(url);vedioImages.setVedioId(vedio.getId().intValue());vedioImagesDao.save(vedioImages);System.out.println("文件存入数据库。。。");}}catch (Exception e){System.out.println("防止异常导致线程退出。。。");}}@Overridepublic void onFileChange(File file) {System.out.println("onFileCreate : " + file.getAbsolutePath());}@Overridepublic void onFileDelete(File file) {System.out.println("onFileDelete :" + file.getAbsolutePath());}@Overridepublic void onStop(FileAlterationObserver observer) {// System.out.println("onStop");}}

回顾:在commons-io包中有文件监控的类,主要的方法就是监控文件新增后做哪些事情

5.5.8.服务器可视化运维-宝塔面板的使用介绍

通常,服务于不懂linux命令的运维人员,对于小公司,小项目来说,项目完成了,就希望能稳定运行,不要出错,但是一些日常维护,比如程序重启,重新部署;如果购买了一台新的服务器,需要安装redis,安装tomcat,安装jdk环境,安装nginx,安装php环境,安装mysql。。。谁来安装?

可以进行服务器的监控:cpu,内存,硬盘(一定要关注硬盘空间,切记!!!)

可以通过宝塔无代码安装

他的免费版本非常强大,已经足够使用了,所以我们在运维过程中,不需要花钱。

宝塔可以安装在任意的服务器上

在宝塔面板的软件商店可以安装常用的软件,查看软件的运行状态,并且进行重启

5.5.9.使用SpringTask定时调用萤石云接口获取摄像头最新token

海康的摄像头要想在网页中播放并且进行控制,需要几个参数,其中token一周会过期一次,所以需要我们自己写代码,每周重新根据接口去获取最新的摄像头对应的token,并且更新到数据库,这样,当你刷新页面时,就会把最新的token查询出来,在页面渲染

1)如何去调接口?

https://open.ys7.com/doc/zh/book/index/user.html

注意:获取到的accessToken有效期是7天,请在即将过期或者接口报错10002时重新获取,请勿频繁调用,频繁调用将会被拉入限制黑名单

  • https://open.ys7.com/api/lapp/token/get

  • 请求方式

    POST

  • 请求参数

参数名 类型 描述 是否必选
appKey String appKey Y
appSecret String appSecret Y
  • 返回字段
字段名 类型 描述
accessToken String 获取的accessToken
expireTime long 具体过期时间,精确到毫秒

2)如何做定时?

定时框架可以用quartz,但项目中这个模块用的是SpringTask

/*** 定时为萤石云账号获取视频设备的accessToken,因为这个token一周会过期一次* 接口参考地址:https://open.ys7.com/doc/zh/book/index/user.html* 每周一23点15分执行任务* @param* @return*/
@Scheduled(cron = "0 15 23 ? * MON")
@Transactional
public void getAccessTokenAtEveryWeek()throws Exception{//萤石云获取摄像头控制key的接口,一周需要执行一次String url="https://open.ys7.com/api/lapp/token/get";//得到所有的萤石云开发账号List<SysYs7Account> sysYs7AccountList = sysYs7AccountDao.list(null, null, null);for (SysYs7Account sysYs7Account : sysYs7AccountList) {Map<String, Object> param=new HashMap<>();param.put("appKey",sysYs7Account.getAppKey());param.put("appSecret",sysYs7Account.getAppSecret());//这个接口不能频繁请求,为防止多账号情况下的请求次数过多,需要sleepThread.sleep(30000);HttpResult httpResult = httpAPIService.doPost(url, param);Map mapMsg = JSON.parseObject(httpResult.getBody(), Map.class);Data data = JSON.parseObject(mapMsg.get("data").toString(),Data.class);sysYs7Account.setAccessToken(data.getAccessToken());//更新数据库sysYs7AccountDao.update(sysYs7Account);//根据userId查询该用户所拥有的全部摄像头,并将它们的accessKey也一起更新Map<String, Object> vedioParam = new HashMap<>();vedioParam.put("", sysYs7Account.getUserId());List<Vedio> vedioList = vedioDao.list(vedioParam, null, null);for (Vedio vedio : vedioList) {vedio.setAccessToken(sysYs7Account.getAccessToken());vedio.setUpdateTime(new Date());vedioDao.update(vedio);}}
}

发送http请求的工具类

import com.topwulian.dto.HttpResult;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;
import java.util.Map;@Service
public class HttpAPIService {@Autowiredprivate CloseableHttpClient httpClient;@Autowiredprivate RequestConfig config;/*** 不带参数的get请求,如果状态码为200,则返回body,如果不为200,则返回null* * @param url* @return* @throws Exception*/public String doGet(String url) throws Exception {// 声明 http get 请求HttpGet httpGet = new HttpGet(url);// 装载配置信息httpGet.setConfig(config);// 发起请求CloseableHttpResponse response = this.httpClient.execute(httpGet);// 判断状态码是否为200if (response.getStatusLine().getStatusCode() == 200) {// 返回响应体的内容return EntityUtils.toString(response.getEntity(), "UTF-8");}return null;}/*** 带参数的get请求,如果状态码为200,则返回body,如果不为200,则返回null* * @param url* @return* @throws Exception*/public String doGet(String url, Map<String, Object> map) throws Exception {URIBuilder uriBuilder = new URIBuilder(url);if (map != null) {// 遍历map,拼接请求参数for (Map.Entry<String, Object> entry : map.entrySet()) {uriBuilder.setParameter(entry.getKey(), entry.getValue().toString());}}// 调用不带参数的get请求return this.doGet(uriBuilder.build().toString());}/*** 带参数的post请求* * @param url* @param map* @return* @throws Exception*/public HttpResult doPost(String url, Map<String, Object> map) throws Exception {// 声明httpPost请求HttpPost httpPost = new HttpPost(url);// 加入配置信息httpPost.setConfig(config);// 判断map是否为空,不为空则进行遍历,封装from表单对象if (map != null) {List<NameValuePair> list = new ArrayList<NameValuePair>();for (Map.Entry<String, Object> entry : map.entrySet()) {list.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));}// 构造from表单对象UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(list, "UTF-8");// 把表单放到post里httpPost.setEntity(urlEncodedFormEntity);}// 发起请求CloseableHttpResponse response = this.httpClient.execute(httpPost);return new HttpResult(response.getStatusLine().getStatusCode(), EntityUtils.toString(response.getEntity(), "UTF-8"));}/*** 不带参数post请求* * @param url* @return* @throws Exception*/public HttpResult doPost(String url) throws Exception {return this.doPost(url, null);}
}

5.6.1.IOS移动端适配的特殊处理

在页面进行h5适配的时候,iphone对于iframe不能很好地渲染,上下和左右的滚动条是无效的,这个只有在真机上才能发现,通过浏览器调试是发现不了的

操作步骤:以index.html页面进行

1)index页面中的iframe:

2)样式

<style>.scroll-wrapper{-webkit-overflow-scrolling: touch;overflow: scroll;}iframe{width: 100%;height: 100%;}</style>

3)js调用

<script>var device = layui.device();//可以获取设备的信息if (device.ios) {$("#scroll-wrapper").addClass("scroll-wrapper");}
</script>

5.6.2.没有硬件时如何模拟硬件端给程序发数据以方便测试?

开发快的官方提供了ssdk-demo程序,这个ssdk-demo程序既可以接收数据,也可以发送数据,我们启动项目后,可以使用这个demo给我们的程序发消息,接收到消息后就可以进行解析,调试了。

public static final String DOMAIN = "ip";//需要在开发快的官网申请
public static final String APP_KEY = "xxx";//需要在开发快的官网申请
public static final String SECRET_KEY = "xxx";//需要在开发快的官网申请
public static final String UID = "xxx";//发送消息的id
public static final String RECEIVE_UID = "xxx";//接收消息的id
//如果你只有一个id,就可以自己给自己发消息

5.6.3.将数据库导出成excel格式的设计文档

5.7.1.其他业务介绍

硬件传感器如何采购:传感器强烈建议大家使用成熟的产品,这种比较贵,但他很靠谱,一般不会出问题

你也可以在淘宝上采购,这个很便宜,几百块钱一套,但消息指令的发送,接收都需要自己编程处理,很坑的地方在于,天天出问题,无信号,无故重启,断电来电后不启动,风吹雨淋出故障,没有防雷击功能

6.ElasticSearch数据快速搜索

我们项目中的大量传感器的采集数据:每套传感器设备每天的采集量是7乘以12乘以24,一个农场仅仅部署一套是远远不够的 ,起码要3-5套。目前本项目已经实施的农场有5个,都是依赖于同一套系统,数据如果继续放在mysql,是承受不了的,需要能实时检索,并做大量的统计。

6.1.2.数据采集LogStash&数据可视化-Kibana的友好展示

ElasticSearch做搜索非常厉害,但是数据哪里来,这是需要使用LogStash进行采集的,可以从日志,消息队列,数据库进行数据的采集。官方也提供了开箱即用的安装包,我们只需要下载安装包,然后配置数据源,将数据保存到ES中即可。

数据的展示就需要使用Kibana,Kibana也可以做ES的管理。

需要注意的是,大家在安装配置ELK的时候,要统一他们三者之间的版本。

ELK运行是依赖于java运行时环境的,由于ELK消耗的服务器资源相对较多,需要服务器的配置相对较高,也需要配置多台服务器,建议搭建一个高可用的集群。

6.2.1.Solr跟ES比较以及不采用Solr的原因大揭秘

两个框架底层使用的都是lucene,而es出来的比较晚,他们的更新速度都相对比较快。

es的搜索速度回更快,他的变成也更友好,es可以做到近实时的搜索,当数据量大的时候,当有大量的新增数据,搜索服务都需要先做分词,然后再存入搜索库,在性能比较上,es表现地非常好。

solr完全免费,而es高级功能部分收费(权限)

6.3.1.邮件告警-JavaMail邮件收发

项目中是可以对环境监测的异常数据进行报警的,我们每次采集到的数据,都会跟对应传感器的阈值进行比对,如果超出范围,并且连续三次都超出,那我们就认为数据是异常的,这时需要进行预警,预警方式可以直接在系统中发邮件,发短信对于特别的紧急的又没人处理,也可以拨打电话。

对于发邮件,项目中使用了JavaMail进行处理,现在JavaMail有针对于springboot项目的起步依赖。

代码:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId>
</dependency>

在yml中配置邮件账号密码,服务器地址等信息

MailService中进行邮件的发送

@Override
public void sendMail(String toUser, String subject, String text) throws MessagingException {MimeMessage message = javaMailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(message, true);helper.setFrom(serverMail);helper.setTo(toUser);helper.setSubject(subject);helper.setText(text, true);javaMailSender.send(message);
}

6.3.2.使用Quartz实现邮件的定时收取

1)需要一个定时任务,本脚手架工程已经内置了quartz作为定时任务框架,你可以编写一个方法,然后在界面进行配置执行时间的规则,这样该方法就能运行了

quartz.properties

quartz要想使用详细的配置规则,是需要依赖数据库表的

JobServiceImpl

package com.topwulian.service.impl;import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;import com.topwulian.dao.JobDao;
import com.topwulian.job.SpringBeanJob;
import com.topwulian.model.JobModel;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;import com.topwulian.service.JobService;@Service
public class JobServiceImpl implements JobService {private static final Logger log = LoggerFactory.getLogger("adminLogger");@Autowiredprivate Scheduler scheduler;@Autowiredprivate ApplicationContext applicationContext;private static final String JOB_DATA_KEY = "JOB_DATA_KEY";@Autowiredprivate JobDao jobDao;@Overridepublic void saveJob(JobModel jobModel) {checkJobModel(jobModel);String name = jobModel.getJobName();JobKey jobKey = JobKey.jobKey(name);JobDetail jobDetail = JobBuilder.newJob(SpringBeanJob.class).storeDurably().withDescription(jobModel.getDescription()).withIdentity(jobKey).build();jobDetail.getJobDataMap().put(JOB_DATA_KEY, jobModel);CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobModel.getCron());CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(name).withSchedule(cronScheduleBuilder).forJob(jobKey).build();try {boolean exists = scheduler.checkExists(jobKey);if (exists) {scheduler.rescheduleJob(new TriggerKey(name), cronTrigger);scheduler.addJob(jobDetail, true);} else {scheduler.scheduleJob(jobDetail, cronTrigger);}JobModel model = jobDao.getByName(name);if (model == null) {jobDao.save(jobModel);} else {jobDao.update(jobModel);}} catch (SchedulerException e) {log.error("新增或修改job异常", e);}}private void checkJobModel(JobModel jobModel) {String springBeanName = jobModel.getSpringBeanName();boolean flag = applicationContext.containsBean(springBeanName);if (!flag) {throw new IllegalArgumentException("bean:" + springBeanName + "不存在,bean名如userServiceImpl,首字母小写");}Object object = applicationContext.getBean(springBeanName);Class<?> clazz = object.getClass();if (AopUtils.isAopProxy(object)) {clazz = clazz.getSuperclass();}String methodName = jobModel.getMethodName();Method[] methods = clazz.getDeclaredMethods();Set<String> names = new HashSet<>();Arrays.asList(methods).parallelStream().forEach(m -> {Class<?>[] classes = m.getParameterTypes();if (classes.length == 0) {names.add(m.getName());}});if (names.size() == 0) {throw new IllegalArgumentException("该bean没有无参方法");}if (!names.contains(methodName)) {throw new IllegalArgumentException("未找到无参方法" + methodName + ",该bean所有方法名为:" + names);}}@Overridepublic void doJob(JobDataMap jobDataMap) {JobModel jobModel = (JobModel) jobDataMap.get(JOB_DATA_KEY);String beanName = jobModel.getSpringBeanName();String methodName = jobModel.getMethodName();Object object = applicationContext.getBean(beanName);try {log.info("job:bean:{},方法名:{}", beanName, methodName);Method method = object.getClass().getDeclaredMethod(methodName);method.invoke(object);} catch (Exception e) {e.printStackTrace();}}/*** 删除job* * @throws SchedulerException*/@Overridepublic void deleteJob(Long id) throws SchedulerException {JobModel jobModel = jobDao.getById(id);if (jobModel.getIsSysJob() != null && jobModel.getIsSysJob()) {throw new IllegalArgumentException("该job是系统任务,不能删除,因为此job是在代码里初始化的,删除该类job请先确保相关代码已经去除");}String jobName = jobModel.getJobName();JobKey jobKey = JobKey.jobKey(jobName);scheduler.pauseJob(jobKey);scheduler.unscheduleJob(new TriggerKey(jobName));scheduler.deleteJob(jobKey);jobModel.setStatus(0);jobDao.update(jobModel);}}

2)要对邮箱中的邮件进行收取

https://www.cnblogs.com/a591378955/p/8031683.html

获取邮箱中的邮件

public class StoreMail {final static String USER = "robot"; // 用户名final static String PASSWORD = "password520"; // 密码public final static String MAIL_SERVER_HOST = "mail.***.com"; // 邮箱服务器public final static String TYPE_HTML = "text/html;charset=UTF-8"; // 文本内容类型public final static String MAIL_FROM = "[email protected]"; // 发件人public final static String MAIL_TO = "[email protected]"; // 收件人public final static String MAIL_CC = "[email protected]"; // 抄送人public final static String MAIL_BCC = "[email protected]"; // 密送人public static void main(String[] args) throws Exception {// 创建一个有具体连接信息的Properties对象Properties prop = new Properties();prop.setProperty("mail.debug", "true");prop.setProperty("mail.store.protocol", "pop3");prop.setProperty("mail.pop3.host", MAIL_SERVER_HOST);// 1、创建sessionSession session = Session.getInstance(prop);// 2、通过session得到Store对象Store store = session.getStore();// 3、连上邮件服务器store.connect(MAIL_SERVER_HOST, USER, PASSWORD);// 4、获得邮箱内的邮件夹Folder folder = store.getFolder("inbox");folder.open(Folder.READ_ONLY);// 获得邮件夹Folder内的所有邮件Message对象Message[] messages = folder.getMessages();for (int i = 0; i < messages.length; i++) {String subject = messages[i].getSubject();String from = (messages[i].getFrom()[0]).toString();System.out.println("第 " + (i + 1) + "封邮件的主题:" + subject);System.out.println("第 " + (i + 1) + "封邮件的发件人地址:" + from);}// 5、关闭folder.close(false);store.close();}
}

6.3.3.短信、电话平台介绍

阿里大于-》可以发国际短信,6毛一条,开通要求很严格,有段时间必须是企业

腾讯云短信:每个月送100条

网易短信:开通时送20条

其他平台:

大部分平台都支持语音验证码,对于收不到短信的用户,应该给他提供语音验证码的通道

具体的用法各个平台都有自己开发的sdk提供下载,可以进行参考,但一定要注意,需要使用最新的sdk,因为老版本的往往有安全漏洞,这种安全漏洞是涉及到钱的。

6.4.1.权限框架介绍

Shiro和SpringSecurity他们的使用场景

在开发中Shiro用的稍微多一点,其实这两个权限框架的原理都是类似的。

有两种权限控制的方式:

​ 粗粒度:我只判断是否登录,在访问页面之前判断该用户是否有权限,权限只判断一次

​ 细粒度:细粒度指的是在访问每个方法之前,都会进行权限校验,控制的非常精细

权限的具体实现:

​ 写代码做逻辑判断:用的很少

​ 把规则写在配置文件中:对于页面的访问,大部分情况下用于粗粒度

​ 规则定义在表中:用户表,角色表,权限表,菜单表,他们之间都是多对多的关系。

参考:https://blog.csdn.net/Qsir/article/details/72628127

6.4.2.项目中Shiro跟SpringBoot的整合

对于整合,一个项目甚至于说一个公司,只需要配置一次即可。表的设计也只需要最开始配置好。

大家在工作中,遇到最多的是如何使用权限控制?

​ 如何控制某些页面,某些方法只有特定的权限或角色才能访问?

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro.version}</version>
</dependency>

shiro-ehcache缓存jar包的作用:用户在访问某个方法时,需要查询数据库看是否拥有访问这个方法的权限,如果没有缓存,用户每访问一次该方法,都要去数据库查询,效率太低,这时我们可以把当前用户这次登陆时拥有的角色,权限存入内存,就不需要频繁地访问数据库了。

没有使用SpringBoot前,需要在xml文件中配置权限框架的拦截规则,有了SpringBoot,我们把配置改在了Bean中进行。

在com.topwulian.config.ShiroConfig类中定义了权限的配置规则,这个一般配好后不需要大家更改,而且每个项目的配置方法是一致的,代表着这个配置文件是通用的。

ShiroFilterFactoryBean是一个工厂类,项目启动创建一次,他里面配置了拦截规则
filterChainDefinitionMap包含了所要过滤的规则,key就是你要对哪些资源进行拦截,value代表拦截的规则
value中的anon代表不需要登录也可以访问。
authc这个代表必须登录后才能访问
SecurityManager权限管理器
用户拥有的权限要在访问页面,方法的时候进行判断,判断的工作我们都看不到,由权限管理器完成
MyShiroRealm是权限的作用域:我们的权限是在这个类中被管理的
HashedCredentialsMatcher凭证匹配器,你要使用的加密方式,比对数据库保存的加密后的密码和登录的时候输入的密码
常用的加密方式md5,sha1,sha5,BCrypt

通过配置文件或者配置类的方法进行的权限控制,我们通常叫他粗粒度的权限控制

那该如何进行细粒度的权限控制呢?

只需要在你要控制的方法上面加上对应的注解就可以了

@RequiresPermissions("sys:menu:query")

而这个权限就是配置在数据库中的一个字符串

6.5.1.代码生成器

最大的好处是可以把单表的增删改查一键生成,不需要自己写任何代码,包括页面都会生成

用到了模板(controller,service,dao,xml,html,js,css)

生成完毕拷贝到工程中对应的目录下面即可

com.topwulian.controller.GenerateController

com.topwulian.utils.TemplateUtil

原理就是模板加数据生成代码

模板的编写非常有难度

代码生成器有很多种,比较好的是vue+elementUI的模板

6.6.1.Docker

Docker教程:https://www.runoob.com/docker/docker-tutorial.html

对于我们的物联网项目,可能会有多个客户,这时候会要求他们之间的数据相对独立,程序开发保持一致,随着物联网设备的增加,如果只部署一个客户端可能会处理不了太多的请求,我们希望可以部署多份,要做到环境的隔离,每个程序都有自己的redis,自己的消息队列,可以使用docker来解决这个问题。

安装docker:需要注意的问题,在windows中安装docker后会跟虚拟机冲突

在docker中安装依赖,安装redis,mq,程序,数据库

6.7.1.MongoDB

MongoDB他是最像关系型数据库的非关系型数据库软件

对于我们采集来的数据,其实有部分不一定非要存到mysql中,而且采集到的数据没有太复杂的逻辑关系,可以把采集来的数据放到非关系型数据库中,非常推荐大家使用MongoDB,在跟SpringBoot整合后,很难发现他的操作方式跟mysql有什么区别

MongoDB的安装配置略微有些复杂,现在建议大家安装的版本是3.7,要注意的是安装完毕后需要配置数据的存储路径,还要注意磁盘的占用问题。

在安装过程中肯定会遇到各种各样的问题,大家遇到的问题越多,你能学到的也就越多。

数据采集表,设备表,设备阈值

6.8.1.实用爬虫程序制作

https://gitee.com/szz715/httpclient-xiecheng.git

有同学让我帮忙爬取一个医药网站所有的药品类目,爬取携程酒店数据,爬取网站的所有pdf资源

爬虫对于我们的农业项目的作用和意义?

农业项目是需要大量气象数据支撑的,某一地区的气象历史数据从何而来?来自于当地气象站的网站,我们可以爬取下来进行使用。

爬虫实际上就是模拟人类的浏览行为,打开每一个页面,并且把页面上的感兴趣的数据解析并保存下来

1)模拟用户行为

用户的行为就是一个http请求:可以采用httpclient发送http请求

得到响应页面的数据

2)对页面进行解析

jsoup webmagic

3)把数据存到数据库

jdbc

要注意每个页面的页面布局不同,需要写不同的解析代码

7.1.1.Druid连接池可视化监控

主要用到了Druid内置的可视监控模块druid/index.html

大家重点关注的就是sql监控,如果项目在运行的过程中,出现了卡顿,延迟比较严重的情况,可以打开这个页面,实时查看哪些sql的执行比较缓慢,并且可以迅速定位问题。

7.1.2.Alibaba ToolKit一键上云跟持续集成的区别

项目开发完是需要部署到服务器上面的,通常的部署本地开发-》打包-》通过ftp工具将jar包上传到服务器-》用ssh工具登录到服务器-》运行这个程序。

这种方法不是很方便,操作的部署比较多,需要安装一些第三方工具,需要对linux的命令比较熟悉,以上部署一个比较熟练的开发,在5分钟之内可以搞定。

但是,我的需求经常发生变化,这就意味着需要经常重新部署这个项目,这样,你改完代码后,重新花5分钟做重复的事情,这样的事情做多了,你会很烦。

既然是程序员,可不可以把这个做成自动的,我只需要在本地点一个按钮,后续动作全部自动帮你完成?

以往的做法,使用持续集成的CI工具,比如jenkins去部署,但是使用jenkins需要服务器资源,需要在服务器安装maven仓库,安装git,安装jenkins及它的插件,要在linux服务器上装这些程序花的时间成本更高,而且还要保证这些程序不能挂掉。

对于一个人战斗的项目来说,成本实在太高了!

引入一键上云神器!

对于独立开发者,没有那么多的运维资料,这种方案是最方便的,整个过程只需要半分钟

7.1.3.阿里云监控配置实战

项目上线后,如果保证程序不挂掉,数据库一直可以访问,以及比如域名一直可以访问

7.1.4.SpringBoot系列-配置多环境配置文件

我们的项目是在本地开发,服务器部署,本地和服务器的配置肯定是不一样的,数据库,redis,开发快,阿里云,mq这些配置本地跟服务器肯定是不一样的,前面说了,可以通过上云神器一键部署,但是在部署前,需要更改这些配置,改完之后再部署,这个时间又要几分钟。而且部署完后我又要继续开发,意味着我又要把配置信息改回来,麻烦不?

这个时候就要使用多配置文件了,开发环境使用开发环境的配置,部署使用服务器的配置,测试使用测试配置

7.1.5.Linux MySQL自动备份和数据恢复-Crontab

项目上线后,需要定时对数据库备份,切记切记,数据库一般不会出问题,一旦出问题,就是大问题,如果没有备份,想哭都哭不出来!!!

一定要有好的意识,好的习惯,必须备份

7.1.6.代码管理GitLab、码云、github

小项目可以使用码云或github,做的项目的代码不希望公开,可以使用私有项目

对于github,在2019年之前私有项目是收费的,但是被微软收购后,私有项目也免费

对于大的项目,或者你认为代码放在公有库不安全,可以自己搭建gitlab私有仓库

像我们的项目,最多的时候,开发也有五个

7.1.7.域名申请和备案的流程

项目在上线后,还是要做域名访问,通常也需要我们来帮客户申请,建议大家使用阿里云的万网域名申请

注意百度搜索的阿里云地址,不要进入假网站https://cn.aliyun.com/

进行注册登录,搜索域名注册

个人注册,需要准备身份证,公司注册,需要营业执照和法人身份证

搜索你想要注册的域名,并且进行购买,花钱的事情非常容易,支付宝直接扫码付款,新用户有优惠券

注册完的域名不能使用,为啥,根据监管局要求,注册后必须要备案,很痛苦,我备案都是7-20天左右

需要阿里云给你邮寄一个备案的背景贴纸,你站在贴纸下,拍照,上传,打印承诺书,签字,上传,然后等待阿里云客服给你打电话确认

急不得,出了什么问题就按他要求的去整改即可

8.1.1.消息队列的重试机制

项目中使用了mq,mq他发送消息可能会失败,失败了之后会进行重试操作,尽量避免消息的丢失,这里以activemq为例,会首先发送一次,如果失败,重试六次,如果六次后依然失败,就认为消息发不出去了,会把消息放到死信队列,等待用户手动处理。

8.1.2.ActiveMQ死信产生的原因及使用方案

https://blog.csdn.net/m0_37609579/article/details/82216332

比较熟悉,以上部署一个比较熟练的开发,在5分钟之内可以搞定。

但是,我的需求经常发生变化,这就意味着需要经常重新部署这个项目,这样,你改完代码后,重新花5分钟做重复的事情,这样的事情做多了,你会很烦。

既然是程序员,可不可以把这个做成自动的,我只需要在本地点一个按钮,后续动作全部自动帮你完成?

以往的做法,使用持续集成的CI工具,比如jenkins去部署,但是使用jenkins需要服务器资源,需要在服务器安装maven仓库,安装git,安装jenkins及它的插件,要在linux服务器上装这些程序花的时间成本更高,而且还要保证这些程序不能挂掉。

对于一个人战斗的项目来说,成本实在太高了!

引入一键上云神器!

对于独立开发者,没有那么多的运维资料,这种方案是最方便的,整个过程只需要半分钟

7.1.3.阿里云监控配置实战

项目上线后,如果保证程序不挂掉,数据库一直可以访问,以及比如域名一直可以访问

7.1.4.SpringBoot系列-配置多环境配置文件

我们的项目是在本地开发,服务器部署,本地和服务器的配置肯定是不一样的,数据库,redis,开发快,阿里云,mq这些配置本地跟服务器肯定是不一样的,前面说了,可以通过上云神器一键部署,但是在部署前,需要更改这些配置,改完之后再部署,这个时间又要几分钟。而且部署完后我又要继续开发,意味着我又要把配置信息改回来,麻烦不?

这个时候就要使用多配置文件了,开发环境使用开发环境的配置,部署使用服务器的配置,测试使用测试配置

7.1.5.Linux MySQL自动备份和数据恢复-Crontab

项目上线后,需要定时对数据库备份,切记切记,数据库一般不会出问题,一旦出问题,就是大问题,如果没有备份,想哭都哭不出来!!!

一定要有好的意识,好的习惯,必须备份

7.1.6.代码管理GitLab、码云、github

小项目可以使用码云或github,做的项目的代码不希望公开,可以使用私有项目

对于github,在2019年之前私有项目是收费的,但是被微软收购后,私有项目也免费

对于大的项目,或者你认为代码放在公有库不安全,可以自己搭建gitlab私有仓库

像我们的项目,最多的时候,开发也有五个

7.1.7.域名申请和备案的流程

项目在上线后,还是要做域名访问,通常也需要我们来帮客户申请,建议大家使用阿里云的万网域名申请

注意百度搜索的阿里云地址,不要进入假网站https://cn.aliyun.com/

进行注册登录,搜索域名注册

个人注册,需要准备身份证,公司注册,需要营业执照和法人身份证

搜索你想要注册的域名,并且进行购买,花钱的事情非常容易,支付宝直接扫码付款,新用户有优惠券

注册完的域名不能使用,为啥,根据监管局要求,注册后必须要备案,很痛苦,我备案都是7-20天左右

需要阿里云给你邮寄一个备案的背景贴纸,你站在贴纸下,拍照,上传,打印承诺书,签字,上传,然后等待阿里云客服给你打电话确认

急不得,出了什么问题就按他要求的去整改即可

8.1.1.消息队列的重试机制

项目中使用了mq,mq他发送消息可能会失败,失败了之后会进行重试操作,尽量避免消息的丢失,这里以activemq为例,会首先发送一次,如果失败,重试六次,如果六次后依然失败,就认为消息发不出去了,会把消息放到死信队列,等待用户手动处理。

8.1.2.ActiveMQ死信产生的原因及使用方案

https://blog.csdn.net/m0_37609579/article/details/82216332

java物联网第四天 智慧农业物联网下相关推荐

  1. STM32--ESP8266物联网WIFI模块(贝壳物联)--温湿度数据上传服务器显示

    本文适用于STM32F103C8T6等MCU,其他MCU可以移植,完整资源见文末链接 一.简介 随着移动物联网的发展,各场景下对于物联控制.数据上传.远程控制的诉求也越来越多,基于此乐鑫科技推出了便宜 ...

  2. STM32--ESP8266物联网WIFI模块(贝壳物联)--远程无线控制点灯

    本文适用于STM32F103C8T6等MCU,其他MCU可以移植,完整资源见文末链接 一.简介 随着移动物联网的发展,各场景下对于物联控制.数据上传.远程控制的诉求也越来越多,基于此乐鑫科技推出了便宜 ...

  3. 物联网卡会锁卡吗_4G物联网监控摄像机为什么要用物联网卡?

     在我们生活中监控系统已经成为了城市建设的配套设备,许多的城市通过搭建监控系统提高城市治安,个人和企业则通过监控系统来保证企业财产的安全,一般情况下4G监控摄像机消耗的流量也是比较大,许多的场地通常都 ...

  4. 联通物联卡为什么没有网络_联通物联网卡怎么样?联通物联卡的查询官网是什么?...

    原标题:联通物联网卡怎么样?联通物联卡的查询官网是什么? 物联网时代的来临为我们生活中带来了许许多多的智能应用,移动物联网卡.电信物联网卡.联通物联网卡作为物联网最基础的通讯产品,在物联网应用中发挥着 ...

  5. 2021IOTE国际物联网展深圳站核芯物联科技正式发布全球第一款无线全网通国产蓝牙AOA高精度定位基站GA30

    2021IOTE国际物联网展深圳站核芯物联科技正式发布全球第一款无线全网通国产蓝牙AOA高精度定位基站GA30-W 核芯物联岳毅恒 深圳核芯物联科技有限公司 战略合作拓展总监 2021IOTE国际物联 ...

  6. Arduino--ESP8266物联网WIFI模块(贝壳物联)--数据上传服务器(单数据接口)

    一.简介 随着移动物联网的发展,各场景下对于物联控制.数据上传.远程控制的诉求也越来越多,基于此乐鑫科技推出了便宜好用性价比极高的wifi物联模块--ESP8266,话不多少我们先来看看这个神奇的模块 ...

  7. 物联网卡显示无服务器,联通物联网卡信号不好(物联卡一直显示无服务)

    你打开手机设置.[联通物联网卡信号不好(物联卡一直显示无服务)].找到网络设置.在把网络设置为4中国电信物联卡充值G优先.还是不能上网,就联系卖家. 您好,联通物联卡网速慢设置APN的步骤知是:1.找 ...

  8. hawk物联网组态工具_万德物联平台|智能供电整体解决方案之物联网在线组态软件...

    原标题:万德物联平台|智能供电整体解决方案之物联网在线组态软件 万德物联网在线组态软件是一款采用先进的实时动态渲染技术及实时数据处理引擎技术作为内核的通用人机可视化监控组态软件.软件提供易用的配置工具 ...

  9. 物联网 - 听说你设备更换物联卡后无法正常使用了?

    嗯- 是的,接触售货机差不多约俩年的时间,遇到过不少问题,不论是简单的硬件问题,还是常见的软件问题,都有很多值得记录的地方,故此抽时间记录一下自己这部分的经历~ 一般情况下,在公司首次购入售货机时,都 ...

最新文章

  1. ❤️手撕这十道HiveSQL题还不能吊打面试官,却能保你不被吊打❤️【推荐收藏】
  2. 锐界机器人_看着就很酸爽,2.7T V6双涡轮,车则试驾新福特锐界ST
  3. 小程序·云开发实战 - 迷你微博
  4. 简述java的线程_Java多线程的简述
  5. zookeeper使用和原理探究
  6. [转]Android截屏及图片解析
  7. 标准C程序设计七---120
  8. CODEVS-1215迷宫
  9. zooInspector下载
  10. 读书笔记:余华--《活着--这是一本书》
  11. python问卷星微信登录_python
  12. Android笔记:多开/分身检测
  13. 如何在计算机修改wifi密码,wifi修改密码,教您电脑怎么修改wifi密码
  14. 产品读书《长尾理论》
  15. 高考志愿填报选专业,女孩子适合的十大职业
  16. 浅谈动感歌词:网易云歌词分析
  17. airpods pro是按压还是触摸_使用AirPods Pro一个月后,我是这么看待它的
  18. 边缘提取——Prewitt算子和Sobel算子
  19. 多态之父类引用指向子类对象
  20. English Word —— Day 11(discipline——editorial)

热门文章

  1. C#:使用海康SDK解码回调函数DecCallbackFUN()
  2. Oracle JAVA SORCE and BLOB OBJECT
  3. KEPServerEX 6 之 lot Gateway MQTT Clinet 配置使用方式
  4. ipxe u盘启动linux内核,[转载]笔记: SYSLINUX PXE gPXE iPXE PXELINUX D
  5. 【软件】电脑店U盘启动盘v7.01(DIY无广告版)
  6. GB8410和FMVSS302测试方法和要求是否相同
  7. 易语言miniblink交互教程——第三课 易语言与 Miniblink 交互
  8. 团队-石头剪刀布-最终程序
  9. java 父类转子类失败_父类不能转换成子类
  10. css背景图片(第五次课)