From bf9f235464d3783846093f4ca532c85e2edc5755 Mon Sep 17 00:00:00 2001 From: coco11563 Date: Thu, 25 Jul 2019 11:24:36 +0800 Subject: [PATCH 1/4] done the redis persist --- piflow-bundle/piflow-bundle.iml | 4 +- .../bundle/nsfc/distinct/HiveDistinct.scala | 47 +++++++++ .../distinct/RedisDistinctCachePersist.scala | 98 +++++++++++++++++++ .../cn/piflow/bundle/util/NSFCUtil.scala | 12 ++- .../cn/piflow/bundle/util/RedisUtil.scala | 35 ++++++- piflow-core/piflow-core.iml | 2 +- piflow-server/piflow-server.iml | 2 +- 7 files changed, 191 insertions(+), 9 deletions(-) create mode 100644 piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala create mode 100644 piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/RedisDistinctCachePersist.scala diff --git a/piflow-bundle/piflow-bundle.iml b/piflow-bundle/piflow-bundle.iml index 7a2fa97..ad2e5fa 100644 --- a/piflow-bundle/piflow-bundle.iml +++ b/piflow-bundle/piflow-bundle.iml @@ -73,7 +73,6 @@ - @@ -233,7 +232,7 @@ - + @@ -398,5 +397,6 @@ + \ No newline at end of file diff --git a/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala new file mode 100644 index 0000000..ac29206 --- /dev/null +++ b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala @@ -0,0 +1,47 @@ +package cn.piflow.bundle.nsfc.distinct + +import cn.piflow.{JobContext, JobInputStream, JobOutputStream, ProcessContext} +import cn.piflow.conf.{ConfigurableStop, PortEnum, StopGroup} +import cn.piflow.conf.bean.PropertyDescriptor +import cn.piflow.conf.util.ImageUtil +import org.apache.spark.sql.SparkSession + +class HiveDistinct extends ConfigurableStop{ + override val authorEmail: String = "xiaomeng7890@gmail.com" + override val description: String = "" + override val inportList: List[String] = List(PortEnum.DefaultPort.toString) + override val outportList: List[String] = List(PortEnum.DefaultPort.toString) +// val primaryKey:String = _ +// val subPrimaryKey:String = _ +// val idKeyName : String = _ +// val processKey = _ +// val timeFields = _ +// val subTimeFields = _ + var tableName : String = _ //after wash + var noChangeSource : String = _ + var sourceField : String = _ + var timeField : String = _ + var noChange : Boolean = _ + var baseOnTime : Boolean = _ + var baseOnField : Boolean = _ + var distinctRule : String = _ + var distinctFields : String = _ + // val subTableName = _ + override def setProperties(map: Map[String, Any]): Unit = ??? + + override def getPropertyDescriptor(): List[PropertyDescriptor] = ??? + + override def getIcon(): Array[Byte] = ImageUtil.getImage("icon/hive/SelectHiveQL.png") + + override def getGroup(): List[String] = { + List(StopGroup.NSFC.toString, "sha0w", "distinct") + } + + override def initialize(ctx: ProcessContext): Unit = { + + } + + override def perform(in: JobInputStream, out: JobOutputStream, pec: JobContext): Unit = { + val spark = pec.get[SparkSession]() + } +} diff --git a/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/RedisDistinctCachePersist.scala b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/RedisDistinctCachePersist.scala new file mode 100644 index 0000000..93a2857 --- /dev/null +++ b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/RedisDistinctCachePersist.scala @@ -0,0 +1,98 @@ +package cn.piflow.bundle.nsfc.distinct + +import cn.piflow.bundle.util.JedisClusterImplSer +import cn.piflow.{JobContext, JobInputStream, JobOutputStream, ProcessContext} +import cn.piflow.conf.{ConfigurableStop, PortEnum, StopGroup} +import cn.piflow.conf.bean.PropertyDescriptor +import cn.piflow.conf.util.{ImageUtil, MapUtil} +import redis.clients.jedis.HostAndPort + +class RedisDistinctCachePersist extends ConfigurableStop { + override val authorEmail: String = "xiaomeng7890@gmail.com" + override val description: String = "persist the df into redis server, which will be used to distinct" + override val inportList: List[String] = List(PortEnum.DefaultPort.toString) + override val outportList: List[String] = List(PortEnum.DefaultPort.toString) + + var persist_needed_fields : String = _ + var persist_primary_field : String = _ + var distinct_rule : String = _ + var redis_server_ip : String = _ + var redis_server_port : Int = _ + + override def setProperties(map: Map[String, Any]): Unit = { + persist_needed_fields = MapUtil.get(map,"distinct field").asInstanceOf[String] + persist_primary_field = MapUtil.get(map,"primary field").asInstanceOf[String] + distinct_rule = MapUtil.get(map,"distinct rule").asInstanceOf[String] + redis_server_ip = MapUtil.get(map,"redis ip").asInstanceOf[String] + redis_server_port = MapUtil.get(map,"redis port").asInstanceOf[Int] + } + + override def getPropertyDescriptor(): List[PropertyDescriptor] = { + var descriptor : List[PropertyDescriptor] = List() + + val redis_port = new PropertyDescriptor(). + name("redis port"). + displayName("redis port"). + description("redis server port"). + required(true) + descriptor = redis_port :: descriptor + + val redis_server = new PropertyDescriptor(). + name("redis server"). + displayName("redis server"). + description("redis server ip"). + required(true) + descriptor = redis_server :: descriptor + + val primary_field = new PropertyDescriptor(). + name("primary field"). + displayName("primary field"). + description("the primary key"). + defaultValue("psn_code"). + required(true) + descriptor = primary_field :: descriptor + + val distinct_field = new PropertyDescriptor(). + name("distinct field"). + displayName("distinct field"). + description("the fields needed in distinct and distinct rule"). + defaultValue("zh_name,email,tel"). + required(true) + descriptor = distinct_field :: descriptor + + val distinct_rule = new PropertyDescriptor(). + name("distinct rule"). + displayName("distinct rule"). + description("the rule to organize distinct"). + defaultValue("zh_name&email,zh_name&tel"). + required(true) + descriptor = distinct_rule :: descriptor + + + descriptor + } + + override def getIcon(): Array[Byte] = ImageUtil.getImage("icon/hive/SelectHiveQL.png") + + override def getGroup(): List[String] = List(StopGroup.NSFC.toString, "sha0w", "distinct") + + override def initialize(ctx: ProcessContext): Unit = {} + + override def perform(in: JobInputStream, out: JobOutputStream, pec: JobContext): Unit = { + val jedisCluster : JedisClusterImplSer = + new JedisClusterImplSer(new HostAndPort(redis_server_ip, redis_server_port)) + val df = in.read() + val mPrimaryKeyIndex = df.schema.fieldIndex(persist_primary_field) //PSNCODE + val df_mperson_fields = persist_needed_fields.split(",").+:(persist_primary_field) + val s1 = df_mperson_fields.map(i => (i, { + df.schema.fieldIndex(i) + })).toMap[String, Int] //生产字段名 -》 index的键值对 + df.rdd.foreach( + row => { + cn.piflow.bundle.util.RedisUtil.putRedis( + row, s1, distinct_rule, mPrimaryKeyIndex, jedisCluster.getJedisCluster) // create the redis dataset + } + ) + out.write(df) + } +} diff --git a/piflow-bundle/src/main/scala/cn/piflow/bundle/util/NSFCUtil.scala b/piflow-bundle/src/main/scala/cn/piflow/bundle/util/NSFCUtil.scala index 98861fc..c2a2a8f 100644 --- a/piflow-bundle/src/main/scala/cn/piflow/bundle/util/NSFCUtil.scala +++ b/piflow-bundle/src/main/scala/cn/piflow/bundle/util/NSFCUtil.scala @@ -4,7 +4,7 @@ import java.text.SimpleDateFormat import java.util.{Date, UUID} import org.apache.spark.sql.Row -import org.apache.spark.sql.types.{StringType, StructField, StructType} +import org.apache.spark.sql.types.{LongType, StringType, StructField, StructType} object NSFCUtil { val dateFormat: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") @@ -35,8 +35,10 @@ object NSFCUtil { for (index <- 0 until beforeSchema.length) { val name = beforeSchema(index).name name match { - case `idTypeField` => if (beforeRowSeq(index) == null) card_type = "null" else card_type = String.valueOf(beforeRowSeq(index)) - case `idField` => if (beforeRowSeq(index) == null) card_code = "null" else card_code = String.valueOf(beforeRowSeq(index)) + case `idTypeField` => if (beforeRowSeq(index) == null) + card_type = "null" else card_type = String.valueOf(beforeRowSeq(index)) + case `idField` => if (beforeRowSeq(index) == null) + card_code = "null" else card_code = String.valueOf(beforeRowSeq(index)) case _ => afterMap.put(beforeSchema(index).name, beforeRowSeq(index)) } } @@ -59,6 +61,7 @@ object NSFCUtil { afterMap.put("mainland_travel_permit_for_taiwan_residents", ifNUll(six)) afterMap.put("source", source) // 加入source字段 afterMap.put("uuid", UUID.randomUUID().toString) + afterMap.put("id_hash", (card_code + card_type).##.toString) for (index <- 0 until afterSchema.length) { if (!afterMap.keySet.contains(afterSchema(index).name)) { afterMap.put(afterSchema(index).name, null) @@ -81,7 +84,8 @@ object NSFCUtil { afterSchema = afterSchema.add(f.name, f.dataType,nullable = true) }) afterSchema = afterSchema.add("source", StringType, nullable = true) - afterSchema.add("uuid", StringType, nullable = true) + afterSchema = afterSchema.add("uuid", StringType, nullable = true) + afterSchema.add("id_hash", StringType, nullable = true) } diff --git a/piflow-bundle/src/main/scala/cn/piflow/bundle/util/RedisUtil.scala b/piflow-bundle/src/main/scala/cn/piflow/bundle/util/RedisUtil.scala index e0035e4..2b71865 100644 --- a/piflow-bundle/src/main/scala/cn/piflow/bundle/util/RedisUtil.scala +++ b/piflow-bundle/src/main/scala/cn/piflow/bundle/util/RedisUtil.scala @@ -2,6 +2,7 @@ package cn.piflow.bundle.util import java.util import org.apache.spark.sql.{Dataset, Row} +import redis.clients.jedis.JedisCluster object RedisUtil extends Serializable { def manipulateRow(row:Row,column_name:String,jedisClusterImplSer: JedisClusterImplSer):Unit={ var hm:util.HashMap[String,String]=new util.HashMap() @@ -18,5 +19,37 @@ object RedisUtil extends Serializable { jedisClusterImplSer.getJedisCluster.hmset(key,hm) } - + /** + * + * @param row 存入redis的行 + * @param map 字段名称与位置索引 + * @param build 字段名 <- 用以去重的字段 + * @param valueIndex PSNCODE的位置 + * @param jedisCluster + */ + def putRedis(row: Row, map: Map[String, Int], build :String, valueIndex:Int, jedisCluster: JedisCluster): Unit = { + var hasNull = false + var value = row.getString(valueIndex) //get the primary key + build.split(",").foreach(idKey => { //name&tel,name&email + var field = idKey + var key = "" + if (idKey.contains("&")) { //name&tel + val sl = idKey.split("&") + sl.foreach(s_ => { + if (!row.isNullAt(map(s_))) { + key += row.getString(map(s_)) + } else {hasNull = true} + }) + } + else { + if (!row.isNullAt(map(idKey))) { + key += row.getString(map(idKey)) + } else {hasNull = true} + } + if (!hasNull) { + jedisCluster.hset(key,field,value) // combined keys - fields - psn_code + // println(key + ":" + field + ":" + value) test pass + } + }) + } } diff --git a/piflow-core/piflow-core.iml b/piflow-core/piflow-core.iml index 9452783..93a39a0 100644 --- a/piflow-core/piflow-core.iml +++ b/piflow-core/piflow-core.iml @@ -166,7 +166,6 @@ - @@ -207,5 +206,6 @@ + \ No newline at end of file diff --git a/piflow-server/piflow-server.iml b/piflow-server/piflow-server.iml index 34720d7..7ec0f8b 100644 --- a/piflow-server/piflow-server.iml +++ b/piflow-server/piflow-server.iml @@ -65,7 +65,6 @@ - @@ -408,5 +407,6 @@ + \ No newline at end of file From fa8c203207b6b51117fb4324896fcbf07d0cf4a7 Mon Sep 17 00:00:00 2001 From: coco11563 Date: Thu, 25 Jul 2019 11:34:59 +0800 Subject: [PATCH 2/4] adding passwd config in redis --- .../cn/piflow/bundle/util/JedisClusterImplSer.java | 8 ++++++++ .../cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala | 4 ++++ .../nsfc/distinct/RedisDistinctCachePersist.scala | 10 +++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/piflow-bundle/src/main/java/cn/piflow/bundle/util/JedisClusterImplSer.java b/piflow-bundle/src/main/java/cn/piflow/bundle/util/JedisClusterImplSer.java index 3fbdbb6..c0bfdea 100644 --- a/piflow-bundle/src/main/java/cn/piflow/bundle/util/JedisClusterImplSer.java +++ b/piflow-bundle/src/main/java/cn/piflow/bundle/util/JedisClusterImplSer.java @@ -53,4 +53,12 @@ public class JedisClusterImplSer implements Serializable { private void setJedisCluster(JedisCluster jedisCluster) { this.jedisCluster = jedisCluster; } + + private void close() { + try { + this.jedisCluster.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala index ac29206..3e272fd 100644 --- a/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala +++ b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala @@ -42,6 +42,10 @@ class HiveDistinct extends ConfigurableStop{ } override def perform(in: JobInputStream, out: JobOutputStream, pec: JobContext): Unit = { + if (noChange){ + out.write(in.read()) + return + } val spark = pec.get[SparkSession]() } } diff --git a/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/RedisDistinctCachePersist.scala b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/RedisDistinctCachePersist.scala index 93a2857..ea06972 100644 --- a/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/RedisDistinctCachePersist.scala +++ b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/RedisDistinctCachePersist.scala @@ -18,6 +18,7 @@ class RedisDistinctCachePersist extends ConfigurableStop { var distinct_rule : String = _ var redis_server_ip : String = _ var redis_server_port : Int = _ + var redis_server_passwd : String = _ override def setProperties(map: Map[String, Any]): Unit = { persist_needed_fields = MapUtil.get(map,"distinct field").asInstanceOf[String] @@ -25,10 +26,17 @@ class RedisDistinctCachePersist extends ConfigurableStop { distinct_rule = MapUtil.get(map,"distinct rule").asInstanceOf[String] redis_server_ip = MapUtil.get(map,"redis ip").asInstanceOf[String] redis_server_port = MapUtil.get(map,"redis port").asInstanceOf[Int] + redis_server_passwd = MapUtil.get(map,"redis passwd").asInstanceOf[String] } override def getPropertyDescriptor(): List[PropertyDescriptor] = { var descriptor : List[PropertyDescriptor] = List() + val redis_passwd = new PropertyDescriptor(). + name("redis passwd"). + displayName("redis passwd"). + description("redis server passwd"). + required(true) + descriptor = redis_passwd :: descriptor val redis_port = new PropertyDescriptor(). name("redis port"). @@ -80,7 +88,7 @@ class RedisDistinctCachePersist extends ConfigurableStop { override def perform(in: JobInputStream, out: JobOutputStream, pec: JobContext): Unit = { val jedisCluster : JedisClusterImplSer = - new JedisClusterImplSer(new HostAndPort(redis_server_ip, redis_server_port)) + new JedisClusterImplSer(new HostAndPort(redis_server_ip, redis_server_port), redis_server_passwd) val df = in.read() val mPrimaryKeyIndex = df.schema.fieldIndex(persist_primary_field) //PSNCODE val df_mperson_fields = persist_needed_fields.split(",").+:(persist_primary_field) From 7c6ce8313fbb90afaefb5244ba03333378cdcea1 Mon Sep 17 00:00:00 2001 From: coco11563 Date: Thu, 25 Jul 2019 15:41:28 +0800 Subject: [PATCH 3/4] Merge branch 'master' of C:\Users\coco1\IdeaProjects\piflow with conflicts. --- .../src/main/resources/mongoDB/mongoDB.png | Bin 2986 -> 36689 bytes .../bundle/nsfc/distinct/HiveDistinct.scala | 51 ------ .../nsfc/distinct/HivePRDDistinct.scala | 5 + .../nsfc/distinct/HivePSNDistinct.scala | 167 ++++++++++++++++++ piflow-server/piflow-server.iml | 2 +- 5 files changed, 173 insertions(+), 52 deletions(-) delete mode 100644 piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala create mode 100644 piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HivePRDDistinct.scala create mode 100644 piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HivePSNDistinct.scala diff --git a/piflow-bundle/src/main/resources/mongoDB/mongoDB.png b/piflow-bundle/src/main/resources/mongoDB/mongoDB.png index 9901a5867f3c45091cc1e7695b5b5911d1a1cd6e..4c034c678477ad1b080926fa29089f23cb71f17b 100644 GIT binary patch literal 36689 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 PIPg4O{an^LB{Ts51PZt+ literal 2986 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 diff --git a/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala deleted file mode 100644 index 3e272fd..0000000 --- a/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HiveDistinct.scala +++ /dev/null @@ -1,51 +0,0 @@ -package cn.piflow.bundle.nsfc.distinct - -import cn.piflow.{JobContext, JobInputStream, JobOutputStream, ProcessContext} -import cn.piflow.conf.{ConfigurableStop, PortEnum, StopGroup} -import cn.piflow.conf.bean.PropertyDescriptor -import cn.piflow.conf.util.ImageUtil -import org.apache.spark.sql.SparkSession - -class HiveDistinct extends ConfigurableStop{ - override val authorEmail: String = "xiaomeng7890@gmail.com" - override val description: String = "" - override val inportList: List[String] = List(PortEnum.DefaultPort.toString) - override val outportList: List[String] = List(PortEnum.DefaultPort.toString) -// val primaryKey:String = _ -// val subPrimaryKey:String = _ -// val idKeyName : String = _ -// val processKey = _ -// val timeFields = _ -// val subTimeFields = _ - var tableName : String = _ //after wash - var noChangeSource : String = _ - var sourceField : String = _ - var timeField : String = _ - var noChange : Boolean = _ - var baseOnTime : Boolean = _ - var baseOnField : Boolean = _ - var distinctRule : String = _ - var distinctFields : String = _ - // val subTableName = _ - override def setProperties(map: Map[String, Any]): Unit = ??? - - override def getPropertyDescriptor(): List[PropertyDescriptor] = ??? - - override def getIcon(): Array[Byte] = ImageUtil.getImage("icon/hive/SelectHiveQL.png") - - override def getGroup(): List[String] = { - List(StopGroup.NSFC.toString, "sha0w", "distinct") - } - - override def initialize(ctx: ProcessContext): Unit = { - - } - - override def perform(in: JobInputStream, out: JobOutputStream, pec: JobContext): Unit = { - if (noChange){ - out.write(in.read()) - return - } - val spark = pec.get[SparkSession]() - } -} diff --git a/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HivePRDDistinct.scala b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HivePRDDistinct.scala new file mode 100644 index 0000000..441a40d --- /dev/null +++ b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HivePRDDistinct.scala @@ -0,0 +1,5 @@ +package cn.piflow.bundle.nsfc.distinct + +class HivePRDDistinct { + +} diff --git a/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HivePSNDistinct.scala b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HivePSNDistinct.scala new file mode 100644 index 0000000..0079825 --- /dev/null +++ b/piflow-bundle/src/main/scala/cn/piflow/bundle/nsfc/distinct/HivePSNDistinct.scala @@ -0,0 +1,167 @@ +package cn.piflow.bundle.nsfc.distinct + +import cn.piflow.bundle.util.JedisClusterImplSer +import cn.piflow.{JobContext, JobInputStream, JobOutputStream, ProcessContext} +import cn.piflow.conf.{ConfigurableStop, PortEnum, StopGroup} +import cn.piflow.conf.bean.PropertyDescriptor +import cn.piflow.conf.util.{ImageUtil, MapUtil} +import org.apache.spark.rdd.RDD +import org.apache.spark.sql.types.{StringType, StructField, StructType} +import org.apache.spark.sql.{Column, DataFrame, Row, SaveMode, SparkSession, TypedColumn} +import redis.clients.jedis.HostAndPort + +class HiveDistinct extends ConfigurableStop{ + override val authorEmail: String = "xiaomeng7890@gmail.com" + override val description: String = "" + override val inportList: List[String] = List(PortEnum.DefaultPort.toString) + override val outportList: List[String] = List("Relation", "Entity") +// val primaryKey:String = _ +// val subPrimaryKey:String = _ +// val idKeyName : String = _ +// val processKey = _ +// val timeFields = _ +// val subTimeFields = _ + var tableName : String = _ //after wash + var noChangeSource : String = _ + var sourceField : String = _ + var timeField : String = _ + var idKeys : String = _ + var noChange : Boolean = _ + var baseOnTime : Boolean = _ + var baseOnField : Boolean = _ + var distinctRule : String = _ + var distinctFields : String = _ + var primaryKey : String = _ + var distinctTableType : String = _ + var rel_fields : String = _ //"id,prj_code,psn_code" + var used_fields : String = _ //"id,psn_code,zh_name,id_type,id_no,birthday,gender,degree_code,nation,tel,email,org_code,zero,identity_card,military_id,passport,four,home_return_permit,mainland_travel_permit_for_taiwan_residents" + + + var redis_server_ip : String = _ + var redis_server_port : Int = _ + var redis_server_passwd : String = _ + + var jedisCluster : JedisClusterImplSer = _ + // val subTableName = _ + override def setProperties(map: Map[String, Any]): Unit = { + redis_server_ip = MapUtil.get(map,"redis ip").asInstanceOf[String] + redis_server_port = MapUtil.get(map,"redis port").asInstanceOf[Int] + redis_server_passwd = MapUtil.get(map,"redis passwd").asInstanceOf[String] + } + + override def getPropertyDescriptor(): List[PropertyDescriptor] = { + var descriptor : List[PropertyDescriptor] = List() + val redis_passwd = new PropertyDescriptor(). + name("redis passwd"). + displayName("redis passwd"). + description("redis server passwd"). + required(true) + descriptor = redis_passwd :: descriptor + + val redis_port = new PropertyDescriptor(). + name("redis port"). + displayName("redis port"). + description("redis server port"). + required(true) + descriptor = redis_port :: descriptor + + val redis_server = new PropertyDescriptor(). + name("redis server"). + displayName("redis server"). + description("redis server ip"). + required(true) + descriptor = redis_server :: descriptor + descriptor + } + + override def getIcon(): Array[Byte] = ImageUtil.getImage("icon/hive/SelectHiveQL.png") + + override def getGroup(): List[String] = { + List(StopGroup.NSFC.toString, "sha0w", "distinct") + } + + override def initialize(ctx: ProcessContext): Unit = { + jedisCluster = new JedisClusterImplSer(new HostAndPort(redis_server_ip, redis_server_port), redis_server_passwd) + } + + override def perform(in: JobInputStream, out: JobOutputStream, pec: JobContext): Unit = { + if (noChange){ + out.write(in.read()) + return + } + val inDF = in.read().select(used_fields.split(",").map(s => new Column(s)) : _*) + + val spark = pec.get[SparkSession]() + val inSchema = inDF.schema + val primaryIndex = inDF.schema.fieldIndex(primaryKey) + var pairRDD = inDF.rdd.map(row => (row.getString(primaryIndex), { + row + })) + var processKeyArray = distinctFields.split(",") + processKeyArray += idKeys + processKeyArray.foreach(key => { //对这里的每一组key + pairRDD = pairRDD.map(row => (cn.piflow.bundle.util.NSFCUtil.mkRowKey(inSchema, row._2, key), row)) //生成key pair, 若key不存在则生成UUID + .groupByKey + .map(i => (i._1 , { + cn.piflow.bundle.util.RedisUtil.recordMKDup(i._2, tableName ,jedisCluster.getJedisCluster) //Mark需要合并的key + })) //opt:R/W deliver this part record the mk Dup + .values + .filter(r => { + r._2 != null + }) //reduce can combine two duplicate value) + }) + var keykeyRDD = pairRDD.map(r => (r._1, cn.piflow.bundle.util.RedisUtil.checkRedis(r, inSchema, tableName,distinctTableType, distinctRule.split(",") , jedisCluster.getJedisCluster),r._2)) + + var backToHiveRdd = keykeyRDD.map(r => (r._1, { + var row = r._3 + var psn = r._2 + var id = r._1 + var rowArray = row.toSeq + jedisCluster.getJedisCluster.hset(tableName + "@" + id, distinctTableType + "IdPSN" , psn) + Row.fromSeq(rowArray.updated(primaryIndex,psn)) + })).filter(r => { + !jedisCluster.getJedisCluster.hexists(tableName + "@" + r._1, distinctTableType + "PSNExist") + }).values + println("=====================" + backToHiveRdd.count + "========================") //active + val df_backToHive = spark.sqlContext.createDataFrame( + backToHiveRdd, inSchema + ) + val rel_fields_arr = rel_fields.split(",") + if (rel_fields_arr.size == 3) { + var df_relationship : DataFrame = inDF.select(rel_fields_arr.map(x => new Column(x)) : _*) + //此处应对pj_member进行映射 + var rela_schema = df_relationship.schema + var relation_rdd = df_relationship.rdd + val id_1 = rel_fields_arr(0) //id + val id_2 = rel_fields_arr(1) //prj + val id_3 = rel_fields_arr(2) //psn + + var backToHiveRelation = relation_rdd.map(row => (row.getString(rela_schema.fieldIndex(id_1)) + ,row.getString(rela_schema.fieldIndex(id_2)))) + .map(r => (r._1, r._2, cn.piflow.bundle.util.RedisUtil.getMKFather(r._1, tableName, distinctTableType, jedisCluster.getJedisCluster) + )).map(i => Row.fromSeq(Array(i._1,i._2,i._3))) + var df_backToHiveRelation = spark.sqlContext.createDataFrame(backToHiveRelation, rela_schema) + jedisCluster.close() + out.write("Relation", df_backToHiveRelation) + } + else { + var df_relationship : DataFrame = inDF.select(rel_fields_arr.map(x => new Column(x)) : _*) + if (rel_fields_arr.size != 4) throw new Exception("wrong input rel schema size, should be 3 or 4") + var rela_schema = StructType(rel_fields_arr.map(StructField(_, StringType, nullable = true))) + var relation_rdd = df_relationship.rdd.map(i => {Row.fromSeq(i.toSeq.+:(cn.piflow.bundle.util.NSFCUtil.generateUUID()))}) + //id,prp_code,psn_code,seq_no + var backToHiveRelation = + relation_rdd. + map(row => (row.getString(rela_schema.fieldIndex(rel_fields_arr(0))), + row.getString(rela_schema.fieldIndex(rel_fields_arr(1))), + row.getString(rela_schema.fieldIndex(rel_fields_arr(2))), + row.getString(rela_schema.fieldIndex(rel_fields_arr(3))))) + .map(r => (r._1, r._2, cn.piflow.bundle.util.RedisUtil.getMKFather(r._3, tableName,distinctTableType,jedisCluster.getJedisCluster), r._4)) + .map(i => Row.fromSeq(Array(i._1,i._2,i._3,i._4))) + var df_backToHiveRelation = spark.sqlContext.createDataFrame(backToHiveRelation, rela_schema) + jedisCluster.close() + out.write("Relation", df_backToHiveRelation) + } + out.write("Entity", df_backToHive) + } +} diff --git a/piflow-server/piflow-server.iml b/piflow-server/piflow-server.iml index 7ec0f8b..34720d7 100644 --- a/piflow-server/piflow-server.iml +++ b/piflow-server/piflow-server.iml @@ -65,6 +65,7 @@ + @@ -407,6 +408,5 @@ - \ No newline at end of file From abf19acc0fe2000e68d77b9cd404172773d2df8a Mon Sep 17 00:00:00 2001 From: judy_0131 Date: Thu, 25 Jul 2019 16:04:27 +0800 Subject: [PATCH 4/4] fix bug: can not connect to hive metastore --- conf/config.properties | 23 ------------------ .../src/main/resources/hive-site.xml | 8 +++++++ .../src/main/resources/log4j.properties | 24 +++++++++++++++++++ readMe.txt | 8 +++---- 4 files changed, 36 insertions(+), 27 deletions(-) delete mode 100644 conf/config.properties create mode 100644 piflow-server/src/main/resources/hive-site.xml create mode 100644 piflow-server/src/main/resources/log4j.properties diff --git a/conf/config.properties b/conf/config.properties deleted file mode 100644 index 1806c0d..0000000 --- a/conf/config.properties +++ /dev/null @@ -1,23 +0,0 @@ -server.ip=10.0.86.98 -server.port=8001 - -#spark.master=spark://10.0.86.89:7077 -#spark.master=spark://10.0.86.191:7077 -spark.master=yarn -spark.deploy.mode=cluster -yarn.resourcemanager.hostname=10.0.86.191 -yarn.resourcemanager.address=10.0.86.191:8032 -yarn.access.namenode=hdfs://10.0.86.191:9000 -yarn.stagingDir=hdfs://10.0.86.191:9000/tmp/ -yarn.jars=hdfs://10.0.86.191:9000/user/spark/share/lib/*.jar - -hive.metastore.uris=thrift://10.0.86.191:9083 - -#piflow.bundle=/opt/project/piflow/out/artifacts/piflow_bundle/piflow-bundle.jar -piflow.bundle=/opt/project/piflow/piflow-server/target/piflow-server-0.9.jar - -yarn.url=http://10.0.86.191:8088/ws/v1/cluster/apps/ -checkpoint.path=hdfs://10.0.86.89:9000/xjzhu/piflow/checkpoints/ - -log.path=/opt/project/piflow/logs -icon.path=/opt/project/piflow/icon \ No newline at end of file diff --git a/piflow-server/src/main/resources/hive-site.xml b/piflow-server/src/main/resources/hive-site.xml new file mode 100644 index 0000000..cba701c --- /dev/null +++ b/piflow-server/src/main/resources/hive-site.xml @@ -0,0 +1,8 @@ + + + + hive.metastore.schema.verification + true + + + \ No newline at end of file diff --git a/piflow-server/src/main/resources/log4j.properties b/piflow-server/src/main/resources/log4j.properties new file mode 100644 index 0000000..3ce467c --- /dev/null +++ b/piflow-server/src/main/resources/log4j.properties @@ -0,0 +1,24 @@ + +# Set everything to be logged to the console +log4j.rootCategory=INFO, console +log4j.appender.console=org.apache.log4j.ConsoleAppender +log4j.appender.console.target=System.err +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n + +# Set the default spark-shell log level to WARN. When running the spark-shell, the +# log level for this class is used to overwrite the root logger's log level, so that +# the user can have different defaults for the shell and regular Spark apps. +log4j.logger.org.apache.spark.repl.Main=WARN + +# Settings to quiet third party logs that are too verbose +log4j.logger.org.spark_project.jetty=WARN +log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR +log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=INFO +log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=INFO +log4j.logger.org.apache.parquet=ERROR +log4j.logger.parquet=ERROR + +# SPARK-9183: Settings to avoid annoying messages when looking up nonexistent UDFs in SparkSQL with Hive support +log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=FATAL +log4j.logger.org.apache.hadoop.hive.ql.exec.FunctionRegistry=ERROR \ No newline at end of file diff --git a/readMe.txt b/readMe.txt index c0a23f0..e6cbf00 100644 --- a/readMe.txt +++ b/readMe.txt @@ -1,9 +1,9 @@ 1.maven error apt-get install maven -mvn install:install-file -Dfile=/root/Desktop/dblp/piflow-bundle/lib/spark-xml_2.11-0.4.2.jar -DgroupId=com.databricks -DartifactId=spark-xml_2.11 -Dversion=0.4.2 -Dpackaging=jar -mvn install:install-file -Dfile=/root/Desktop/dblp/piflow-bundle/lib/java_memcached-release_2.6.6.jar -DgroupId=com.memcached -DartifactId=java_memcached-release -Dversion=2.6.6 -Dpackaging=jar -mvn install:install-file -Dfile=/root/Desktop/dblp/piflow-bundle/lib/ojdbc6.jar -DgroupId=jdbc_oracle -DartifactId=ojdbc -Dversion=6.0.0 -Dpackaging=jar -mvn install:install-file -Dfile=/root/Desktop/dblp/piflow-bundle/lib/edtftpj.jar -DgroupId=ftpClient -DartifactId=edtftp -Dversion=1.0.0 -Dpackaging=jar +mvn install:install-file -Dfile=/opt/project/piflow/piflow-bundle/lib/spark-xml_2.11-0.4.2.jar -DgroupId=com.databricks -DartifactId=spark-xml_2.11 -Dversion=0.4.2 -Dpackaging=jar +mvn install:install-file -Dfile=/opt/project/piflow/piflow-bundle/lib/java_memcached-release_2.6.6.jar -DgroupId=com.memcached -DartifactId=java_memcached-release -Dversion=2.6.6 -Dpackaging=jar +mvn install:install-file -Dfile=/opt/project/piflow/piflow-bundle/lib/ojdbc6.jar -DgroupId=jdbc_oracle -DartifactId=ojdbc -Dversion=6.0.0 -Dpackaging=jar +mvn install:install-file -Dfile=/opt/project/piflow/piflow-bundle/lib/edtftpj.jar -DgroupId=ftpClient -DartifactId=edtftp -Dversion=1.0.0 -Dpackaging=jar 2.packaging