从 Spark 服务器执行 SFTP 时,大型机服务器上的记录级别数据截断

Posted

技术标签:

【中文标题】从 Spark 服务器执行 SFTP 时,大型机服务器上的记录级别数据截断【英文标题】:Record Level Data truncation on Mainframe server while doing SFTP from spark server 【发布时间】:2021-05-10 17:11:36 【问题描述】:

请完整阅读。

我正在通过 SFTP 将 csv 文件从 scala 开发的 spark 应用程序发送到大型机服务器。我正在使用 jsch(java 安全通道)包版本 0.1.53 版本来完成从 spark 服务器到大型机服务器的 SFTP 连接。我面临的问题是,在大型机服务器上,csv 文件被截断为 每记录行 1024 个字节。

经过研究,我发现在大型机上,我们可以使用“lrecl”和“recfm”等选项来控制文件中每条记录的长度以及该记录的格式。但我无法在 scala 上集成这些选项。我在 *** 上找到了this 答案,该答案旨在用 Java 实现。当我在 scala 上使用相同的逻辑时,我收到以下错误:

EDC5129I No such file or directory., file: /+recfm=fb,lrecl=3000 at
    at com.jcraft.jsch.ChannelSftp.throwStatusError(ChannelSftp.java:2846)
    at com.jcraft.jsch.ChannelSftp._stat(ChannelSftp.java:2198)
    at com.jcraft.jsch.ChannelSftp._stat(ChannelSftp.java:2215)
    at com.jcraft.jsch.ChannelSftp.ls(ChannelSftp.java:1565)
    at com.jcraft.jsch.ChannelSftp.ls(ChannelSftp.java:1526)

使用jsch库建立SFTP连接和传输文件的Scala代码块如下:

session = jsch.getSession(username, host, port)
session.setConfig("PreferredAuthentication","publickey")
session.setConfig("MaxAuthTries",2)
System.out.println("Created SFTP Session")

val sftpSessionConfig: Properties = new Properties()
sftpSessionConfig.put("StrictHostKeyChecking","no")
session.setConfig(sftpSessionConfig)
session.connect() //Connect to session
System.out.println("Connected to SFTP Session")
      
val channel = session.openChannel("sftp")
channel.connect()
val sftpChannel = channel.asInstanceOf[ChannelSftp]
sftpChannel.ls("/+recfm=fb,lrecl=3000") //set lrecl and recfm ---> THROWING ERROR HERE

sftpChannel.put(sourceFile, destinationPath,ChannelSftp.APPEND) //Push file from local to mainframe

有什么方法可以使用 jsch 库将这些选项设置为我的 scala 代码中的配置?我还尝试使用 spring-ml 的 spark-sftp 包。但是这个包在大型机服务器上也存在数据截断的问题。

请帮忙,因为这个问题已经成为我项目的非常关键的障碍。

编辑:使用 scala 代码块更新问题

【问题讨论】:

我已经使用 scala 代码测试了从我的 spark 服务器到 SFTP 的连接,连接建立正确,我什至可以推送文件。唯一的问题是我需要修复的每条记录行都被截断了。我没有尝试过 Java,因为我们的项目完全是在 scala 上开发的。 我想大型机正在运行 z/OS。您是否考虑过将文件发送到 z/OS 上的 UNIX 文件系统? 在我看来问题与 Spark 无关。出于调试目的,我会尝试纯粹用 Scala(没有 Spark)编写一个小程序来检查会发生什么 @werner 在没有 spark 服务器的本地系统上尝试此操作时,我遇到了同样的问题。问题是大型机服务器定义了每行记录的默认长度 (lrecl)。我们需要增加 scala 代码中的 lrecl 值,以便在使用 sftp 在大型机上写入文件时不会发生行级截断。 我建议您与您的客户交谈并询问他们正在运行什么 STFP 服务器。然后让他们与所述产品的供应商交谈,并寻求帮助指定 LRECL、RECFM 参数(如果完全支持)。我们似乎能在这里谈论的只是没有这些知识的猜测。 【参考方案1】:

来自幻灯片 21 上的 Dovetail SFTP Webinar 演示文稿:

ls /+recfm=fb,lrecl=80

在我看来,您的代码中有一个“/”太多了。

从错误信息来看,我认为 SFTP 服务器在 UNIX 文件系统中有当前路径。您没有为数据集设置数据集高级限定符 (HLQ),是吗?我在代码中看不到它。再次从上面的演示中,在ls 之前做一个cd

cd //your-hlq-of-choice

这将做两件事:

    将当前工作目录更改为 MVS 数据集端。 设置要使用的 HLQ。

对不起,我无法测试自己;我不知道斯卡拉。

【讨论】:

我仍然收到 EDC5129I No such file or directory., file: /+recfm=fb,lrecl=3000 2: EDC5129I No such file or directory., file: /+recfm=fb,lrecl = 3000 在 com.jcraft.jsch.ChannelSftp.throwStatusError(ChannelSftp.java:2846) 在 com.jcraft.jsch.ChannelSftp._stat(ChannelSftp.java:2198) 在 com.jcraft.jsch.ChannelSftp._stat(ChannelSftp.java :2215) at com.jcraft.jsch.ChannelSftp.ls(ChannelSftp.java:1565) at com.jcraft.jsch.ChannelSftp.ls(ChannelSftp.java:1526) 我将用我们使用的代码更新我的问题指定这些选项。 用 scala 代码块更新了我的问题。请查看并帮助我解决此问题。【参考方案2】:

首先,z/OS 上运行的是什么 SFTP 服务器?如果它是 z/OS(不是 Dovetail)提供的命令,则不支持您正在执行的命令,您将收到类似 Can't ls: "/+recfm=fb,lrecl=80" not found 的消息。这将是有效的,因为那不是有效的文件。 / 右侧的所有内容都将被视为文件名的一部分。

我将您的代码转换为 Java,因为我不熟悉 Scala,也没有时间学习它。这是我使用的代码示例。

import com.jcraft.jsch.JSch;
import java.util.Properties;
import java.util.Vector;

class sftptest 
  static public void main(String[] args) 

    String username = "ibmuser";
    String host = "localhost";
    int port = 10022;              // Note, my z/OS is running in a docker container so I map 10022 to 22
    JSch jsch = new JSch(); 
    String sourceFile = "/";
    String destinationPath ="/";
    String privateKey = "myPrivateKey";


    try 
      jsch.addIdentity(privateKey);                   //add private key path and file
      com.jcraft.jsch.Session session = jsch.getSession(username, host, port);
      session.setConfig("PreferredAuthentication","password");
      session.setConfig("MaxAuthTries", "2");
      System.out.println("Created SFTP Session");

      Properties sftpSessionConfig = new Properties();
      sftpSessionConfig.put("StrictHostKeyChecking","no");
      session.setConfig(sftpSessionConfig);
      session.connect(); //Connect to session
      System.out.println("Connected to SFTP Session");
      
      com.jcraft.jsch.ChannelSftp channel = (com.jcraft.jsch.ChannelSftp) session.openChannel("sftp");
      channel.connect();
      // com.jcraft.jsch.Channel sftpChannel = (ChannelSftp) channel;
      //    channel.ls("/+recfm=fb,lrecl=3000"); //set lrecl and recfm ---> THROWING ERROR HERE
      //    channel.ls("/"); //set lrecl and recfm ---> THROWING ERROR HERE
      Vector filelist = channel.ls("/");
      for(int i=0; i<filelist.size();i++)
          System.out.println(filelist.get(i).toString());
      


    //  channel.put(sourceFile, destinationPath, com.jcraft.jsch.ChannelSftp.APPEND);  //Push file from local to mainframe
     catch (Exception e) 
      System.out.println("Exception "+e.getMessage());
    
  

就我而言,我确实使用了 ssh 密钥而不是密码。 ls 方法的输出是:

Created SFTP Session
Connected to SFTP Session
Exception No such file

删除+ 和所有你得到的东西:

Created SFTP Session
Connected to SFTP Session
drwxr-xr-x    2 OMVSKERN SYS1         8192 May 13 01:18 .
drwxr-xr-x    7 OMVSKERN SYS1         8192 May 13 01:18 ..
-rw-r--r--    1 OMVSKERN SYS1            0 May 13 01:18 file 1
-rw-r--r--    1 OMVSKERN SYS1            0 May 13 01:18 file 2

主要问题是 z/OS 似乎不支持您使用的语法,该语法由 Dovetail 的特定 SFTP 实现提供。

如果您没有 Dovetail,我建议您发送的 CSV 文件通常长度可变,因此您将它们作为 USS 文件发送,以便正确翻译行并具有可变长度。将它们传输到 USS(z/OS 上的常规 Unix),然后将它们复制到具有 VB RECFM 的 MVS 文件。假设文件已经分配,​​你可以做一个cp myuploadedFile.csv "//'MY.MVS.FILE'"

【讨论】:

以上是关于从 Spark 服务器执行 SFTP 时,大型机服务器上的记录级别数据截断的主要内容,如果未能解决你的问题,请参考以下文章

sftp 到大型机主机

将文件从 SFTP 复制到 Blob 存储时并行执行 Azure 逻辑应用

从 SFTP 服务器将文件上传到 Azure 存储 Blob

JSch SFTP 可以支持 lrecl 或 blksize 等站点命令吗

JSch SFTP 可以支持 lrecl 或 blksize 等站点命令吗

从 Windows 机器触发大型机作业