问题:
在使用sparkstreaming的过程中,我们经常有一个需求是把中间变量写到redis,然后在流程序中去读redis的中间变量。在sparkstreaming中,我们只需要用个foreach算子就能实现这种需求,那flink中没有foreach算子,我们该如何做到流程中读redis的数据呢?
方法:
我们可以用flink的异步IO访问redis完成这种操作
注意问题:
我们要用flink的异步IO访问redis的这种操作,要有一个前提:
对数据库(或key/value存储)实现适当的异步I/O需要客户端访问支持异步请求的数据库。
在没有这样的客户端的情况下,可以通过创建多个客户端并使用线程池处理同步调用来尝试将同步客户端转变为有限的并发客户端,但是,这种方法通常比适当的异步客户端效率低。
我下面的实现就是用jedis实现的,因为jedis客户端实例不是线程安全的,他不支持异步操作,所以我们用了连接池来模拟“异步”。
踩坑提示:
我这里是直接用redis.clients进行的读取redis,我们可以看到网上有许多用vertx-redis-client来实现的,如下博文:
https://blog.csdn.net/rlnLo2pNEfx9c/article/details/103692000
但是,这里要注意
vertx目前在scala中只支持2.12版本!!!
本人用的是scala2.11,所以用这个包怎么都跑不通,因为网上的案例大多都是java实现的,所以当时以为是自己在从java转scala中出现的问题,所以这个坑一定要注意
实现
import java.util.concurrent.TimeUnit
import net.clickwifi.writer.RedisWriter
import org.apache.flink.api.java.utils.ParameterTool
import org.apache.flink.runtime.concurrent.Executors
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.scala.async.{AsyncFunction, ResultFuture}
import org.apache.flink.streaming.connectors.redis.RedisSink
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisPoolConfig
import redis.clients.jedis.Jedis
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
object WriteToHbase {
def main(args: Array[String]): Unit = {
val parameter = ParameterTool.fromArgs(args)
val host = parameter.get("host")
val port = parameter.getInt("port")
println(host)
//创建环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
//env.setParallelism(1)
//读取数据source
val socketDs = env.socketTextStream(host, port)
val resultDS = socketDs.map(str => {
val arrayStr = str.split(",")
val id = arrayStr(0)
val name = arrayStr(1)
val age = arrayStr(2)
(id, name, age)
})
//
val EndDS = AsyncDataStream.orderedWait(resultDS, new AsyncDatabaseRequest, 100000L, TimeUnit.MILLISECONDS)
EndDS.print()
env.execute("flink")
}
/**
* An implementation of the 'AsyncFunction' that sends requests and sets the callback.
*/
class AsyncDatabaseRequest extends RichAsyncFunction[(String, String, String), (String, String, String, String)] {
/** The database specific client that can issue concurrent requests with callbacks */
println("jedis连接")
@transient
private var jedis: Jedis = _
private lazy val pool = new JedisPool(new JedisPoolConfig,"hadoop102",6379)
/** The context used for the future callbacks */
implicit lazy val executor: ExecutionContext = ExecutionContext.fromExecutor(Executors.directExecutor())
override def asyncInvoke(input: (String, String, String), resultFuture: ResultFuture[(String, String, String, String)]): Unit = {
var result = ""
val resultFutureRequested: Future[String] = Future {
jedis = pool.getResource
result = jedis.hget(input._1,input._2)
result
}
resultFutureRequested.onComplete {
case Success(result) =>
resultFuture.complete(Iterable((input._1,input._2,input._3,result)))
jedis.close()
case Failure(error) => case e: Exception => println(s"报错了,报错信息是: ${e.getMessage}")
}
println("invoke")
}
override def close(): Unit = {
if(jedis != null) jedis.close()
}
}
}
结果
我们redis中存储了这样一条hash数据:
1,jack,beijing
然后我们输入一条数据:
1,jack,18
我们可以看到控制台输出了:
1,jack,18,beijing