在Windows下开发调试PySpark|牛气冲天新年征文

2,313 阅读3分钟
Spark版本:2.4.0+cdh6.3.1
Spark-Windows版本:spark-2.4.0-bin-hadoop2.7
WinUtils版本:hadoop-2.7.1
Python版本:3.7.2
Java版本:1.8.0_121
Scala版本:2.11.8 按需安装

前言

俗话说得好,Life is short, you need Python(人生苦短,我用python)。哈哈,这不是俗话,这是出自大名鼎鼎的python之父,Guido van Rossum(吉多•范罗苏姆),就是下图那位。Python作为目前最火的编程语言,而Spark作为目前最火的大数据计算引擎,它俩结合碰撞出了怎样的火花呢。 在这里插入图片描述

本文提供了两种在Windows下开发调试PySpark的方法,使用PyCharm和使用Jupyter。此外,本文还提供了使用IDEA通过JDBC方式远程连接HiveServer2。

1 使用PyCharm开发PySpark直连Hive metastore

1.1 下载相关包

Spark-2.4.0:CDH版本不支持Windows下运行,使用开源版本
Hadoop-2.7.1:使用winutils-master里的即可,不需要全量包
Python-3.7.2:python-3.7.2-amd64.exe
Java-1.8.0_121:jdk-8u121-windows-x64.exe
Scala-2.11.8:scala-2.11.8.msi 按需安装

想白嫖也可以自己找相关包哟,都是开源的~

1.2 配置环境变量

HADOOP_HOME = D:\hadoop-2.7.1
SPARK_HOME = D:\spark
JAVA_HOME = D:\java
SCALA_HOME = D:\scala
在这里插入图片描述

1.3 设置spark日志级别

修改{SPARK_HOME}\conf\log4j.properties

log4j.rootCategory=WARN, console

1.4 启动spark-sql

C:\Users\User>spark-sql

在这里插入图片描述
需注意的是,因为没有配置元数据库,默认使用derby,在哪里启动就会在哪里创建一个metastore_db,如果要复用表需在同一目录下启动。
在这里插入图片描述

1.5 创建测试表并插入数据

spark-sql> create table test_table(id string, age int);
spark-sql> insert into table test_table values('1', 18);
spark-sql> insert into table test_table values('2', 28);
spark-sql> select * from test_table;
2       28
1       18
Time taken: 0.129 seconds, Fetched 2 row(s)

1.6 安装相关包

将{SPARK_HOME}\python\下的pyspark文件夹拷到
{PYTHON_HOME}\Lib\site-packages下
安装py4j:pip install py4j-0.10.9.1-py2.py3-none-any.whl

1.7 编写脚本测试

在metastore_db所在目录下新建脚本test_local_conn.py,使用pycharm打开并执行。

from pyspark.sql import SparkSession
import os

os.environ["HADOOP_HOME"] = "D:\\hadoop-2.7.1"
spark = SparkSession.builder.appName('test_local_conn_hive')\
#.config("hive.metastore.uris", "thrift://xx:9083") \
        .master("local[*]")\
        .enableHiveSupport().getOrCreate()

spark.sql('show tables').show()
spark.sql('select distinct id from test_table').show()
#spark.sql('create table copy_table as select * from test_table')
spark.sql('show tables').show()
spark.sql("insert into table test_table values('3', 38)")
spark.sql("insert into table test_table values('4', 48)")
spark.sql('select * from test_table').show()

spark.stop()

执行部分结果见下:
在这里插入图片描述
注意:
1.执行脚本前需关闭spark-sql shell,不然会冲突报错
ERROR Schema: Failed initialising database.
Caused by: ERROR XSDB6: Another instance of Derby may have already booted the database D:\xx\metastore_db.
pyspark.sql.utils.AnalysisException: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient;'
2.连接远程集群没有开启Kerberos认证的hive,只需配置远程metastore uris即可连接
.config("hive.metastore.uris", "thrift://xx:9083")

2 使用Jupyter开发PySpark

2.1 放入公共脚本

在jupyter workspace放入公共脚本或者自己写的脚本,方便import

2.2 登录jupyter

http://xxx:19010/hub/login
在这里插入图片描述

2.3 新建ipynb

在这里插入图片描述

2.4 编写脚本

在这里插入图片描述

2.5 导出脚本

代码测试没有问题后,可直接导出.py文件
在这里插入图片描述

3 使用IDEA通过JDBC方式远程连接HiveServer2

使用这种方式可以远程连接开启了Kerberos认证的Hive,但是只能执行SQL,无法测试整个脚本。

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;

import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import com.cebbank.entity.AAATable;

/**
 * @author: drguo
 * @blog: drguo.blog.csdn.net
 * @date 2021/1/21 14:33
 */
public class JDBCHiveServer2Util {

    private static String CONNECTION_URL ="jdbc:hive2://xxx:10000/;principal=hive/xxx@BANKCHINA.COM";
    private static String principal ="xxx";
    private static String keytab ="d:\\xxx.keytab";
    private static PreparedStatement ps;
    private static Connection conn = null;
    private static ResultSet resultSet;

    static {
        // 或把 krb5.ini 放在 C:\Windows 下
        System.setProperty("java.security.krb5.conf", "C:\\ProgramData\\MIT\\Kerberos5\\krb5.conf");
        Configuration configuration = new Configuration();
        configuration.set("hadoop.security.authentication" , "Kerberos");
        UserGroupInformation.setConfiguration(configuration);
        try {
            UserGroupInformation.loginUserFromKeytab(principal, keytab);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static <T> ArrayList<T> query(Class<T> clazz, String sql, Object... args) throws SQLException, IOException {
        PreparedStatement ps;
        Connection conn = null;
        ResultSet resultSet;
        try {

            // 1.获得连接
            conn = DriverManager.getConnection(CONNECTION_URL);
            // 2.预编译
            ps = conn.prepareStatement(sql);
            // 填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            // 3.1执行
            resultSet = ps.executeQuery();
            // 3.2 获得元数据(数据库的字段)
            ResultSetMetaData metaData = resultSet.getMetaData();
            // 3.2 获得元数据的个数
            int columnCount = metaData.getColumnCount();
            ArrayList<T> list = new ArrayList<T>();
            // 4.返回结果集
            while (resultSet.next()) {
                // 反射实例化对象
                T t = clazz.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    // 获得值
                    Object columnValue = resultSet.getObject(i + 1);
                    // 获得列名
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    // 通过反射调用属性并赋值
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t, columnValue);
                }
                //将t对象加到集合
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            conn.close();
        }
        return null;
    }

    public static void execute(String sql) throws SQLException {
        try {

            // 1.获得连接
            conn = DriverManager.getConnection(CONNECTION_URL);
            // 2.预编译
            ps = conn.prepareStatement(sql);
            // 3. 执行
            ps.executeUpdate();
            System.out.println("execute success");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            conn.close();
        }
    }

    public static void main(String[] args) throws IOException, SQLException {
        String sql = "select a,b,c,d,e from aaa";
//        String sql = "select a,b,c,d,e from TestConnHiveServer2WithKerbs";
//        String sql = "insert overwrite table TestConnHiveServer2WithKerbs select d,e,c from aaa";
//        String sql = "drop table TestConnHiveServer2WithKerbs";
//        String sql = "create table TestConnHiveServer2WithKerbs as select b,d,a,e,c from aaa";
//        execute(sql);
        ArrayList<AAATable> alist = query(AAATable.class, sql);
        for (AAATable aaa : alist){
            System.out.println(aaa.toString());
        }
    }
}

执行结果见下:
在这里插入图片描述