后端开发小技巧 (七)
1. 自动更新任务
一、 启动程序 添加注解
@EnableScheduling
@SpringBootApplication
@MapperScan("cn.piesat.sar.dao")
public class MessageApplication {
public static void main(String[] args) {
SpringApplication.run(MessageApplication.class, args);
}
}
二、 quartz/ AutoScanTask 注入其他服务的Mapper
@Component
public class AutoScanTask {
private final Logger logger = LoggerFactory.getLogger(AutoScanTask.class);
@Autowired
private ThematicTaskMapper taskInfoMapper;
@Autowired
private RedisUtil redisUtil;
/**
* 每10分钟调用一次, 程序启动延迟10s执行 1000 毫秒fixedRate 每time 调用一次
* initalDelay 延迟多长时间
*/
@Scheduled(fixedRate = 10 * 60 * 1000, initialDelay = 10 * 1000)
public void updateTaskStatus() {
System.out.println("自动启动");
try {
//查询出数据库中指定时间段内待执行或者执行中的任务
Integer unfinishedTaskCount = taskInfoMapper.getUnfinishedTaskCount();
logger.info(String.format("自动任务扫描符合条件的[%d]条主任务", unfinishedTaskCount));
if (unfinishedTaskCount != null && unfinishedTaskCount > 0) {
List<ThematicTask> taskInfoList = taskInfoMapper.getUnfinishedTask();
if (taskInfoList != null && taskInfoList.size() > 0) {
for (ThematicTask taskInfo : taskInfoList) {
taskInfo.setStatus(1);
taskInfoMapper.updateSubTaskStatus(taskInfo);
System.out.println("更新状态成功"+taskInfo.toString());
// 更新任务状态
// updateSubTaskStatus(taskInfo);
}
}
// }
}
} catch (Exception e) {
logger.error("Update task workflow status error:", e);
}
}
}
三、 调用其他服务下的mapper.xml
<!--
// 任务未完成的条数
Integer getUnfinishedTaskCount();
// 未完成的任务
List<ThematicTask> getUnfinishedTask();
// 更新任务状态
int updateSubTaskStatus(ThematicTask taskInfo);
-->
<!-- <!– 查询未完成的主任务数量 –>-->
<select id="getUnfinishedTaskCount" resultType="Integer">
SELECT COUNT(1)
FROM sar_thematic_product_task
WHERE "status" != 3 AND "status" != 4;
</select>
<select id="getUnfinishedTask" parameterType="java.util.HashMap" resultType="cn.piesat.sar.entity.ThematicTask">
SELECT
s1.id, s1.require_task_id "requireTaskId", s1.priority, s1.special_product_level "specialProductLevel", s1.standard_product_list "standardProductList", s1.receive_time "receiveTime", s1.status, s1.rtask_result "rtaskResult", s1.rtask_progress "rtaskProgress",s1.msg_type "msgType"
FROM sar_thematic_product_task s1
where
s1.status=0
order by s1.receive_time desc
<if test="pageNum != null and pageSize != null and pageSize > 0">
limit #{pageSize} OFFSET #{pageNum}
</if>
</select>
<update id="updateSubTaskStatus">
UPDATE sar_thematic_product_task
<set>
<if test="status != null and status != ''">
status=#{status}
</if>
</set>
WHERE id = #{id}
</update>
2. 消息服务 message-service
一、 将消息接收储存 ServiceImpl
package cn.piesat.sar.service.impl;
import cn.piesat.sar.base.Config;
import cn.piesat.sar.entity.ServiceResult;
import cn.piesat.sar.service.TaskMessageService;
import cn.piesat.sar.utils.ClientConfiguration;
import cn.piesat.sar.utils.TestData;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Service
public class TaskMessageServicempl implements TaskMessageService {
@Autowired
TestData testData; // 工具类 测试数据
@Autowired
Config config; // 路径
@Autowired
ClientConfiguration clientConfiguration; //请求 工具类
@Value("${task.alg.param}")
private String taskAlgParam; //文件校验规则
public void sendDataNotifyMessage(String msg) {
Map <String,Object> param = new HashMap<>();
param.put("appKey","");
param.put("bizCode","");
param.put("message",msg);
//发送数据完成通知消息
String mqUrl = config.getMqApi()+"send";
clientConfiguration.sendPost(mqUrl,JSON.toJSONString(param, SerializerFeature.WriteMapNullValue));
}
@Override
public ServiceResult receiveThematicProductTaskMessage(String msg) {
ServiceResult result = new ServiceResult(false);
try {
String taskMessage = testData.getTaskMessage();//模拟任务跟踪消息
String url = config.getDataServiceUrl()+"thematictask/add";
JSONObject jsonObject = clientConfiguration.sendPost(url,taskMessage);
if(jsonObject.getBoolean("success")){
result.setMessage("消息处理成功");
result.setSuccess(true);
}
}catch (Exception e){
e.printStackTrace();
result.setMessage("消息处理失败");
}
return result;
}
private void taskMessageToStorage(String taskMessage, boolean flag) {
String thematicTaskUrl = config.getDataServiceUrl()+"thematictask/add";
JSONObject jsonObject = JSONObject.parseObject(taskMessage);
JSONObject param = new JSONObject();
param.put("requireTaskId",jsonObject.getString("requireTaskId"));
param.put("priority",jsonObject.getString("priority"));
param.put("targetLevel",jsonObject.getString("targetLevel"));
param.put("geoJsonString",jsonObject.getString("geoJsonString"));
param.put("standardProductList",jsonObject.getString("standardProductList"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
param.put("receiveTime",jsonObject.getString( sdf.format(new Date())));
if(flag){
param.put("status",0);
}else {
param.put("status",1);
}
clientConfiguration.sendPost(thematicTaskUrl,JSON.toJSONString(param));
}
}
3. pg sql 主键自增
一、 创建表时设置主键自增
mysql 主键自增使用AUTO_INCREMENT关键字,postgresql自增使用SERIAL关键字。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7y3ZzZX1-1649897596118)(C:\Users\HTHT\AppData\Roaming\Typora\typora-user-images\image-20220401131257039.png)]
二、修改menu表id字段为主键自增
1. 在Postgre sql当中,实现ID自增实现创建一个关联序列,一些sql语句是创建一个序列:
CREATE SEQUENCE menu_id_seq START 6000001;
序列名称是menu_id_seq,起始数为6000001
2. 然后在字段默认值里设 nextval(‘menu_id_seq’::regclass)即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pIFSvgzm-1649897596119)(C:\Users\HTHT\AppData\Roaming\Typora\typora-user-images\image-20220401131539241.png)]
3. 保存字段属性变更
ALTER TABLE public.menu ALTER COLUMN id SET DEFAULT nextval('menu_id_seq'::regclass);
三、 修改id的自增起始数
把当前最大的id作为当前的id自增起始数
select setval('gx_history_id_seq',(select max(id) from gx_history))
补充
UndefinedTable: relation “ ****_id_seq“ does not exist解决方法
查阅部分资料有的说是postgresql9版本的问题 10已经修复
相关技能
查询所有自增序列
SELECT "c"."relname" FROM "pg_class" "c" WHERE "c"."relkind" = 'S';
pgSQL导表或者创建表的时候,检查是否存在自增字段序列,然后创建
CREATE SEQUENCE IF NOT EXISTS tablename_id_seq;
创建自增序列
CREATE SEQUENCE tablename_id_seq
CREATE SEQUENCE tablename_id_seq START 10;
删除某个自增序列
DROP SEQUENCE tablename_id_seq
更新某个自增序列
alter sequence tablename_id_seq restart with 100
查询下一个序列
select nextval('tablename_id_seq ');
使用自增序列
nextval('tablename_id_seq'::regclass)
————————————————
版权声明:本文为CSDN博主「手心守贝」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35771567/article/details/103616842
4. 消息接收 ,以及验证消息完整ALG
一、 ServiceImpl
/**
* 校验规则
* 文件示例:JH0451_HT1A_0001_1024_1_20220211150123_20220211150223.dat
1、文件包数量和接口消息中的【文件包数量】是否一致;
2、每个文件包名称中的数据类型是否规范:如果文件包数量是1,数据类型应该为0;如果文件包数量是2,每个文件包中的数据类型不能相同,应该分别为1和2。
3、按照文件包进行分别校验:
(1)文件命名校验
每个文件包名字中的测站代号和接口消息中的【测站标识】是否一致;
每个文件包名字中的卫星代号和接口消息中的【卫星代号】是否一致;
每个文件包名称和接口消息中的【原始文件名】是否一致; (通过验证文件是否存在解决需求)
(2)文件大小校验
每个文件包大小和接口消息中的【原始文件长度】是否一致;
*
* */
public boolean verifyRule(String dataMessage) {
boolean flag = true;
JSONObject jsonObject = JSONObject.parseObject(dataMessage);
JSONArray jsonArray = jsonObject.getJSONArray("fileList");
int fileNumber = jsonObject.getInteger("fileNumber");
//文件数量验证
if(fileNumber!=jsonArray.size()){
return false;
}
// 文件包数量类型验证
if(fileNumber==1){
String fileName = jsonArray.getJSONObject(0).getString("fileName");
int dataType = Integer.parseInt(fileName.split("_")[4]);
if(dataType!=0){
return false;
}
}else if(fileNumber==2){
for(int i=0;i<jsonArray.size();i++){
String fileName = jsonArray.getJSONObject(i).getString("fileName");
int dataType = Integer.parseInt(fileName.split("_")[4]);
if(dataType!=2 && dataType!=1){
return false;
}
}
}
//文件命名验证
for(int i=0;i<jsonArray.size();i++){
JSONObject data = jsonArray.getJSONObject(i);
String path = config.getDataDir();
String fileName = data.getString("fileName");
Integer fileLength = data.getInteger("fileLength");
//文件命名校验
String antenna = data.getString("antenna");
String satellite = data.getString("satellite");
if(!antenna.equals(fileName.split("_")[0])){
return false;
}
if(!satellite.equals(fileName.split("_")[1])){
return false;
}
//文件大小校验
File f= new File(path+fileName);
if (f.exists() && f.isFile()){
if(f.length()!=fileLength){
return false;
}
} else {
return false;
}
}
return flag;
}
5. 杀死进程,重新启动该任务
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 8082 was already in use.
Action:
Identify and stop the process that's listening on port 8082 or configure this application to listen on another port.
使用 命令
netstat -ano |findstr 8082
找到
杀死 该进程
taskkill -PID 19772 -F
6. linux 部署常用命令
启动 .jar测试 是否成功
java -jar xxx.jar
标识 查看服务是否成功
ksar get svc
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QYiJY49n-1649897596119)(C:\Users\HTHT\AppData\Roaming\Typora\typora-user-images\image-20220402151921233.png)]
ls 目录 cd 进入文件 cat 查看 vi 编辑 :wq退出保存 i插入 exit 退出
遇到问题 先看报错 日志
常用 K8S 命令
kubectl get svc # 查看service服务
kubectl get po # 查看pod
kubectl logs xxxx # 查看某个pod的日志
kubectl apply -f xxx.yaml # 部署某个已经写好配置文件的服务
kubectl delete -f xxx.yaml # 删除某个已经写好配置文件的服务
kubectl describe pod xxx # 当容器启动报错时可以使用该方法查看报错细节
kubectl exec -ti xxx bash # 进入某个容器
kubectl get pv # 查看持久卷
kubectl get node # 查看集群状态
kubectl get po -o wide # 可以查看这些容器具体跑在哪些节点
7. 空网关
先看报错 日志
常用 K8S 命令
kubectl get svc # 查看service服务
kubectl get po # 查看pod
kubectl logs xxxx # 查看某个pod的日志
kubectl apply -f xxx.yaml # 部署某个已经写好配置文件的服务
kubectl delete -f xxx.yaml # 删除某个已经写好配置文件的服务
kubectl describe pod xxx # 当容器启动报错时可以使用该方法查看报错细节
kubectl exec -ti xxx bash # 进入某个容器
kubectl get pv # 查看持久卷
kubectl get node # 查看集群状态
kubectl get po -o wide # 可以查看这些容器具体跑在哪些节点