摘要:记录一下工作中学到的知识点
前言
提示:这里可以添加本文要记录的大概内容:
先简单写写
一、Kafka定时任务推送数据
code:performance-alarm-access
1.1 Kafka的使用
1.1.1 本地测试(Windows)
1)下载及配置
- kafka只需要解压下载的压缩包就行了;kafka当前版本已经内置了zookeeper,所以不需要再安装zookeeper。
- kafka服务端配置在server.properties中,kafka配置需要修改两处配置文件:listeners 和 log.dirs。
# listeners:服务器监听的地址
listeners=PLAINTEXT://localhost:9092
# log.dirs:日志文件修改为自定义的日志目录
log.dirs=D:/softFiles/kafka/kafka_2.13-3.2.1/logs
- zookeeper配置文件为zookeeper.properties,只需修改dataDir
# dataDir:zookeeper存储数据的路径修改自定义的目录
dataDir=D:/softFiles/kafka/kafka_2.13-3.2.1/data
2)本地测试
- 进入kafka安装根目录下,地址栏输入cmd,然后回车,通过命令行启动之后都不要关闭窗口(每步打开新窗口)
- 启动zookeeper
.\bin\windows\zookeeper-server-start.bat .\config\zookeeper.properties
- 启动kafka-server
.\bin\windows\kafka-server-start.bat .\config\server.properties
# 如果启动kafka失败,并出现以下异常,删除logs文件夹下的meta.properties文件即可。
# The Cluster ID xxxx doesn’t match stored clusterId Some(finN2zUTRWaXMomXCknRew) in meta.properties. The broker is trying to join the wrong cluster. Configured zookeeper.connect may be wrong.
- 启动kafka-topics(创建一个名为test的topic)
.\bin\windows\kafka-topics.bat --create --bootstrap-server localhost:2181 --replication-factor 1 --partitions 1 --topic test
# 启动之后,kafka-topics处于等待创建topic状态,一段时间内如果不createTopic,kafka-topics将自动断开
- 启动生产者
.\bin\windows\kafka-console-producer.bat --broker-list localhost:9092 --topic test
- 启动消费者
.\bin\windows\kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic test
- 在生产端发送消息,可以看到消费端就收到了
1.1.2 代码测试
依赖
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>3.2.0</version>
</dependency>
代码
// 定义主题
public static String topic = "test";
// 配置kafka(本地)
Properties p = new Properties();
p.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");//kafka地址,多个地址用逗号分割
p.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
p.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(p);
// Kafka推送
ProducerRecord<String, String> recordTest = new ProducerRecord<String, String>(topic, jsonArray.toJSONString());
kafkaProducer.send(recordTest);
logger.info("消息发送成功:" + jsonArray.toJSONString());
1.2 定时任务SpringTask
注解
@Component
@Scheduled(cron = "0 */15 * * * ?") //每15分钟执行一次
public void execute() throws Exception {}
@EnableScheduling
@SpringBootApplication
public class PerformanceAlarmAccessApplication {}
1.3 其他
1.3.1 如何写log
代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendKafkaMsgTimer {
private static final Logger logger = LoggerFactory.getLogger(SendKafkaMsgTimer.class);
logger.info("消息发送成功");
}
关于报错:可能是因为系统配置?改完就好了
Settings->Maven和Java Compiler
Project Structure
1.3.2 数据库连接及配置
application.properties中配置数据库
# PostgreSql连接信息
driver=org.postgresql.Driver
url=jdbc:postgresql://ip:port/数据库
user=
password=
依赖(我用的PostgreSql)
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
代码
public class JdbcUtils {
/**
* @Description: 连接数据库
* @return: Connection
*/
public static Connection getConnection() throws ClassNotFoundException, SQLException {
ResourceBundle jdbc = ResourceBundle.getBundle("application");
String driver = jdbc.getString("driver");
String url = jdbc.getString("url");
String username = jdbc.getString("user");
String password = jdbc.getString("password");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, username, password);
if (connection != null) {
System.out.println("数据库连接成功! ");
} else {
System.out.println("数据库连接失败! ");
}
return connection;
}
}
1.3.3 JSONArray的使用
依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
代码
JSONArray jsonArray = new JSONArray();
JSONObject json = new JSONObject();
json.put("value", value);
jsonArray.add(json);
1.3.4 sql语句
- 占位符
?
的使用 或 用+
按字符串进行拼接 - rs.next() 和 rs.getString()
- 代码
Connection con = JdbcUtils.getConnection();
PreparedStatement pstmt = con.prepareStatement("SQL"+"语句");
ResultSet rs = pstmt.executeQuery();
while (rs.next()) { rs.getString("列名"); //得到值 }
1.3.5 字符串的分割
代码
// 添加原数据
dataMap.put("tt", "a,aa,aaa");
// 分割字段
String[] columns = dataMap.get(key).split(",");
1.3.6 kafka启动报错
参考:ERROR Fatal error during KafkaServer startup. Prepare to shutdown (kafka.server.KafkaServer) kafka.c
二、FTP读取excel数据并解析入库
code:emergency-data-upload
2.1 FTP的使用
2.1.1 FTP服务器本地搭建(用于测试)
2.1.2 FTP配置及使用
1.本地配置+配置类+服务(此次demo)
因为只有读取功能,所以这次直接写在服务中实现了;其实也可以写一个工具类,再在服务中调用
本地配置application-dev.yml
#ftp服务器配置
ftp :
ftpHost: 110.110.110.110
#ftp服务器端口号
ftpPort: 21
#ftp用户名
ftpUserName: user
#ftp密码
ftpPassword: 123
#ftp文件在服务器上存放物理路径
ftpPath: /path
#ftp下载下来的文件存放本地的地址
localPath: D://test
#文件名称
excelFileName: 模版.xlsx
配置类代码
@Component
@ConfigurationProperties(prefix = "ftp")
@Data
public class FtpConfig {
/**
* ip
*/
private String ftpHost;
/**
* 端口
*/
private int ftpPort;
/**
* 用户名
*/
private String ftpUserName;
/**
* 密码
*/
private String ftpPassword;
/**
* ftp文件位置
*/
private String ftpPath;
/**
* 本地文件位置
*/
private String localPath;
private List<String> excelFileName;
}
服务实现类代码
注意:避免中文名乱码问题
@Service
public class ReadFtpFileServiceImpl implements ReadFtpFileService {
// 本地字符编码 UTF-8/GBK
private static String localCharset = "GBK";
@Autowired
private BaseStationService baseStationService;
@Autowired
private EmergencyDataService emergencyDataService;
/**
* @param filePath 文件路径
* @param user 用户名
* @param password 密码
* @param ip 主机名
* @param port 端口号
* @param fileName 文件名
* @Description: 通过ftp连接解析并上传文件数据
*/
@Override
public Map<String, Object> read(String filePath, String user, String password, String ip, int port, String fileName) {
Map<String, Object> map = new HashMap<>();
FTPClient ftp = new FTPClient();
try {
// 连接ftp服务器
ftp.connect(ip, port);
// 登陆
ftp.login(user, password);
// 检验登陆操作的返回码是否正确
if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
ftp.disconnect();
}
// 设置被动模式
ftp.enterLocalActiveMode();
// 设置文件类型为二进制,与ASCII有区别
ftp.setFileType(FTP.BINARY_FILE_TYPE);
// 设置编码格式
ftp.setControlEncoding(localCharset);
// 获得ftp文件路径
String dir = filePath.substring(0, filePath.lastIndexOf(File.separatorChar));
// 进入文件所在目录,注意编码格式,以能够正确识别中文目录
ftp.changeWorkingDirectory(new String(dir.getBytes(localCharset), FTP.DEFAULT_CONTROL_ENCODING));
// 拆分文件名和后缀
String ext = "";
String ftpFileName = "";
if (fileName.contains(".")) {
ftpFileName = fileName.split("\\.")[0];
ext = fileName.split("\\.")[1];
}
// 遍历寻找文件
FTPFile[] files = ftp.listFiles();
// 记录入口文件数量
int num = 0;
String uploadFileName = "";
for (FTPFile file1 : files) {
if (file1.getName().contains(ftpFileName)) {
// 两个sheet,两个文件流
int i = 2;
while (i > 0) {
// 检验文件是否存在
filePath = filePath + file1.getName();
String file = filePath.substring(filePath.lastIndexOf(File.separatorChar) + 1);
InputStream is = ftp.retrieveFileStream(new String(file.getBytes(localCharset), FTP.DEFAULT_CONTROL_ENCODING));
filePath = "";
// 若存在,解析文件
if (is != null) {
// 处理excel数据
if (ext.equals("xls") || ext.equals("xlsx") || ext.equals("csv")) {
if (i == 2) {
EasyExcel.read(is, BaseStationTemplate.class, new BaseStationListener(baseStationService)).sheet("模板1").doRead();
} else {
EasyExcel.read(is, EmergencyDataTemplate.class, new EmergencyDataListener(emergencyDataService)).headRowNumber(0).sheet("模板2").doRead();
}
num++;
uploadFileName += file1.getName() + ",";
}
if (is != null) {
is.close();
ftp.completePendingCommand();
}
}
i--;
}
}
}
map.put("num", num);
map.put("uploadFileName", uploadFileName);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ftp != null) {
try {
ftp.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return map;
}
}
2.动态配置+工具类+服务(SFTP)
Linux默认是不提供ftp的;使用nacos配置中心进行配置;在服务中通过@Value注解将外部配置文件的值注入到Bean中
-
FTPUtil可包含的功能
- 1.连接服务器
- 2.断开连接
- 3.上传文件
- 4.下载文件
- 5.其他……
添加SFTP依赖
<!-- SFTP -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
<scope>compile</scope>
</dependency>
FTP工具类代码
import com.jcraft.jsch.*;
import org.apache.commons.net.ftp.FTP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.*;
/**
* @Description: FTP工具类
* @Date: 2022/9/15 15:15
*/
public class FtpUtil {
private static final Logger log = LoggerFactory.getLogger(FtpUtil.class);
private String username;
private String password;
private String privateKey;
private String host;
private int port;
public ChannelSftp sftp;
private Session session;
public FtpUtil(String username, String password, String host, int port) {
this.username = username;
this.password = password;
this.host = host;
this.port = port;
}
// 使用私钥而不是密码
public FtpUtil(String username, String host, int port, String privateKey) {
this.username = username;
this.host = host;
this.port = port;
this.privateKey = privateKey;
}
public FtpUtil() {
}
/**
* 连接服务器
*
* @param
*/
public void connect() {
try {
JSch e = new JSch();
if (this.privateKey != null) {
e.addIdentity(this.privateKey);
}
this.session = e.getSession(this.username, this.host, this.port);
if (this.password != null) {
this.session.setPassword(this.password);
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
this.session.setConfig(config);
this.session.connect();
Channel channel = this.session.openChannel("sftp");
channel.connect();
this.sftp = (ChannelSftp) channel;
} catch (JSchException var4) {
log.error("连接sftp服务器异常", var4);
}
}
/**
* 断开连接
*
* @param
*/
public void disconnect() {
if (this.sftp != null && this.sftp.isConnected()) {
this.sftp.disconnect();
}
if (this.session != null && this.session.isConnected()) {
this.session.disconnect();
}
}
/**
* 上传文件
*
* @param basePath 上传路径
* @param sftpFileName 文件名
* @param input 输入流
*/
public void upload(String basePath, String sftpFileName, InputStream input) throws SftpException {
if (!isDirExist(basePath)) {
this.createDir(basePath, this.sftp);
}
this.sftp.cd(basePath);
this.sftp.put(input, sftpFileName);
}
/**
* 下载文件
*
* @param downloadFilePath 下载路径
* @param saveFile 文件名
*/
public File download(String downloadFilePath, String saveFile) {
FileOutputStream fileOutputStream = null;
try {
int i = downloadFilePath.lastIndexOf('/');
if (i == -1) {
return null;
}
sftp.cd(downloadFilePath.substring(0, i));
File file = new File(saveFile);
fileOutputStream = new FileOutputStream(file);
sftp.get(downloadFilePath.substring(i + 1), fileOutputStream);
fileOutputStream.close();
return file;
} catch (Exception e) {
log.error(e.getMessage());
return null;
} finally {
if (null != fileOutputStream) {
try {
fileOutputStream.close();
} catch (IOException e) {
log.info("关闭资源失败");
}
}
}
}
/**
* 删除文件
*
* @param directory 文件路径
* @param deleteFile 文件名
*/
public void delete(String directory, String deleteFile) throws SftpException {
this.sftp.cd(directory);
this.sftp.rm(deleteFile);
}
/**
* 读取文件(不完善)
*
* @param directory 文件路径
* @param readFile 文件名
*/
public void read(String directory, String readFile) {
InputStream in = null;
try {
this.sftp.cd(directory);
in = sftp.get(readFile);
if (in != null) {
BufferedReader br = new BufferedReader(new InputStreamReader(in, FTP.DEFAULT_CONTROL_ENCODING));
String str = null;
//这里写逻辑,在控制台进行按行输出
while ((str = br.readLine()) != null) {
System.out.println(str);
}
} else {
log.error("in为空,不能读取");
}
} catch (Exception e) {
log.error(e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.info("关闭资源失败");
}
}
}
}
/**
* 读取所有文件名
*
* @param directory 文件路径
* @param sign
*/
public List<String> listFiles(String directory, boolean sign) throws SftpException {
Vector vector = sftp.ls(directory);
Iterator iterator = vector.iterator();
List<String> fileList = new ArrayList<>();
while (iterator.hasNext()) {
ChannelSftp.LsEntry file = (ChannelSftp.LsEntry) iterator.next();
//文件名称
String fileName = file.getFilename();
if (file.getAttrs().isDir()) {
if (!fileName.equals(".") && !fileName.equals("..")) {
if (sign) {
String path = directory + "/" + fileName;
List<String> files = listFiles(path, sign);
fileList.addAll(files);
}
}
} else {
fileList.add(fileName);
}
}
return fileList;
}
/**
* 创建一个文件目录
*/
public void createDir(String createPath, ChannelSftp sftp) {
try {
String pathArray[] = createPath.split("/");
StringBuffer filePath = new StringBuffer("/");
for (String path : pathArray) {
if (path.equals("")) {
continue;
}
filePath.append(path + "/");
if (isDirExist(filePath.toString())) {
sftp.cd(filePath.toString());
} else {
// 建立目录
sftp.mkdir(filePath.toString());
// 进入并设置为当前目录
sftp.cd(filePath.toString());
}
}
this.sftp.cd(createPath);
} catch (SftpException e) {
e.printStackTrace();
}
}
/**
* 判断目录是否存在
*/
public boolean isDirExist(String directory) {
boolean isDirExistFlag = false;
try {
SftpATTRS sftpATTRS = sftp.lstat(directory);
isDirExistFlag = true;
return sftpATTRS.isDir();
} catch (Exception e) {
if (e.getMessage().toLowerCase().equals("no such file")) {
isDirExistFlag = false;
}
}
return isDirExistFlag;
}
/**
* 获取文件数量
*/
public long getFileSize(String srcSftpFilePath) {
long fileSize;//文件大于等于0则存在
try {
SftpATTRS sftpATTRS = sftp.lstat(srcSftpFilePath);
fileSize = sftpATTRS.getSize();
} catch (Exception e) {
fileSize = -1;//获取文件大小异常
if (e.getMessage().toLowerCase().equals("no such file")) {
fileSize = -2;//文件不存在
}
}
return fileSize;
}
}
服务示例
public CommonModel uploader(MultipartFile file) {
CommonModel result = ModelTool.createModel();
if (file.isEmpty()) {
throw new CustomException("上传文件不能为空");
}
if (file.getSize() > MAX_SIZE) {
throw new CustomException("上传文件过大, 请选择大小在2MB以内的文件");
}
String fileName = file.getOriginalFilename();
String suffixName = fileName.substring(fileName.lastIndexOf("."));
String newFileName = System.currentTimeMillis() + suffixName;
FTPUtil sftpUtils = new FTPUtil(
sftpUsername,
sftpPassword,
sftpIp,
(sftpPort != null ? Integer.parseInt(sftpPort) : 22)
);
sftpUtils.login();
if (!sftpUtils.isDirExist(diskFilePath)) {
sftpUtils.createDir(diskFilePath, sftpUtils.sftp);
}
try {
sftpUtils.upload(diskFilePath, newFileName, file.getInputStream());
File sysFile = new File();
sysFile.setFileName(fileName);
sysFile.setPath(diskFilePath + "/" + newFileName);
sysFile.setFileType(suffixName);
sysFile.setDel(0);
FieldUtil.setCreatedField(sysFile);
try {
fileMapper.insert(sysFile);
result.success(sysFile);
} catch (Exception e) {
sftpUtils.delete(diskFilePath, newFileName);
result.fail("上传失败");
log.error("文件保存失败");
}
} catch (SftpException e) {
log.error("SFTP异常");
} catch (IOException e) {
log.error("IO异常");
}
return result;
}
2.2 spring boot + mybatis
参考:
项目源码参考
MyBatis代码生成器及源码
2.3 easyexcel使用
2.4 其他
2.4.1 生成sql文件方法
2.4.2 EasyExcel从指定位置开始读数据
EasyExcel从指定位置开始读数据——headRowNumber()
2.4.3 FTP中文文件名乱码问题
FTP中文文件名乱码问题以及本地编码
另外,在.properties文件配置会存在中午乱码问题,改成.yml文件会解决
三、配置相关问题
真正工作起来全靠百度,用的多忘得快,根本没时间记捏,就记点百度不容易查到的吧
- 因为项目用到了微服务,调用其他服务时一开始居然直接加了依赖,耦合性太高,其他服务一崩自己的服务也就崩了,应该用
@FeignClient(name = "xxx")
注解调用其他服务,用到的实体类和函数再写一份就行 - es用到了ik分词器,忘记跟运维说了,又报错。。。
- 配置文件,其实是最重点,但因为同事帮忙搞了,一知半解的;简单来说,bootstrap相关配置复制其他服务的(nacos和jasypt解密,服务名之类的之前已经写好了,一定注意看看服务在nacos注册成功了没,jasypt还有个依赖要加也容易忘捏),application相关配置之前也写好了,但是es配置要改一下,其他一些报错完全靠同事(有个ip字段多余了可能?反正去掉就不报错了,application.yml还有个
profiles: active: ${APPLICATION_URL:dev}
要加,反正我自己是发现不了的),其他的靠运维,告诉他端口(原话:A:你要起XXX肯定得给他一个端口吧,然后这个服务与其他服务之间是怎么关联的,B:都注册到nacos上的,正常启动就可以,你配置一下nacos就行,端口按照配置文件或者让他随便映射一个),运维自己指定环境(原话:升级的时候测试环境让他指定test环境,生产prod环境)
ok了,愚蠢的我
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容。