mirror of
https://github.com/Microsoft/sql-server-samples.git
synced 2025-12-08 14:58:54 +00:00
29 KiB
29 KiB
In [0]:
USE master
IF EXISTS(select * from sys.databases where name='TwitterData')
DROP DATABASE TwitterData;
GO
CREATE DATABASE TwitterData;
GO
USE TwitterData;
GOIn [0]:
SELECT @@Servername;In [0]:
USE TwitterData
GO
IF NOT EXISTS(SELECT * FROM sys.external_data_sources WHERE name = 'TweetsDataSource')
CREATE EXTERNAL DATA SOURCE TweetsDataSource
WITH (LOCATION = 'sqldatapool://controller-svc/default');In [0]:
IF NOT EXISTS(SELECT * FROM sys.external_tables WHERE name = 'Tweets')
CREATE EXTERNAL TABLE [Tweets]
("screen_name" NVARCHAR(MAX), "createdAt" DATETIME , "num_followers" BIGINT, "text" NVARCHAR(MAX))
WITH
(
DATA_SOURCE = TweetsDataSource,
DISTRIBUTION = ROUND_ROBIN
);In [0]:
import java.io.{BufferedReader, File, FileNotFoundException, InputStream, InputStreamReader}
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.util.Base64
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import scala.collection.JavaConverters._
import org.apache.commons.io.IOUtils
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClients
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.fs.FileSystem
import org.apache.hadoop.fs.Path
import java.io.PrintWriter
import org.apache.spark.sql.{SparkSession, SaveMode, Row, DataFrame}In [0]:
// Twitter app autnentication keys
val consumerKey = ""
val consumerSecret = ""
val accessToken = ""
val accessTokenSecret = ""
// Provide username/password to SQL Server master instance
// Refer mssql_spark_connector_user_creation.ipynb in samples repo on how to create this user.
val user = "user"
val password = "password"
// Spark-SQL connector parameters
val hostname = "master-0.master-svc"
val port = 1433
val database = "TwitterData"
val url = s"jdbc:sqlserver://${hostname}:${port};database=${database};user=${user};password=${password};"
val dbtable = "Tweets"
val datasource_name = "TweetsDataSource"
// Twitter stream object parameters
val filters = Array("tennis", "nadal", "federer", "murray", "djokovic")
val path = "/user/twitter/"
val savingInterval = 2000In [0]:
class TwitterStream(
consumerKey: String,
consumerSecret: String,
accessToken: String,
accessTokenSecret: String,
path: String,
savingInterval: Long,
filters: Array[String]) {
private val threadName = "tweet-downloader"
{
val hasActiveStream = Thread.getAllStackTraces().keySet().asScala.map(_.getName).contains(threadName)
if (hasActiveStream) {
throw new RuntimeException(
"There is already an active stream that writes tweets to the configured path. " +
"Please stop the existing stream first (using twitterStream.stop()).")
}
}
@volatile private var thread: Thread = null
@volatile private var isStopped = false
@volatile var isDownloading = false
@volatile var exception: Throwable = null
private var httpclient: CloseableHttpClient = null
private var input: InputStream = null
private var httpGet: HttpGet = null
private def encode(string: String): String = {
URLEncoder.encode(string, StandardCharsets.UTF_8.name)
}
def start(): Unit = synchronized {
isDownloading = false
isStopped = false
thread = new Thread(threadName) {
override def run(): Unit = {
httpclient = HttpClients.createDefault()
try {
requestStream(httpclient)
} catch {
case e: Throwable => exception = e
} finally {
//TwitterStream.this.stop()
}
}
}
thread.start()
}
private def requestStream(httpclient: CloseableHttpClient): Unit = {
val url = "https://stream.twitter.com/1.1/statuses/filter.json"
val timestamp = System.currentTimeMillis / 1000
val nonce = timestamp + scala.util.Random.nextInt
val oauthNonce = nonce.toString
val oauthTimestamp = timestamp.toString
val oauthHeaderParams = List(
"oauth_consumer_key" -> encode(consumerKey),
"oauth_signature_method" -> encode("HMAC-SHA1"),
"oauth_timestamp" -> encode(oauthTimestamp),
"oauth_nonce" -> encode(oauthNonce),
"oauth_token" -> encode(accessToken),
"oauth_version" -> "1.0"
)
val requestParams = List(
"track" -> encode(filters.mkString(","))
)
val parameters = (oauthHeaderParams ++ requestParams).sortBy(_._1).map(pair => s"""${pair._1}=${pair._2}""").mkString("&")
val base = s"GET&${encode(url)}&${encode(parameters)}"
val oauthBaseString: String = base.toString
val signature = generateSignature(oauthBaseString)
val oauthFinalHeaderParams = oauthHeaderParams ::: List("oauth_signature" -> encode(signature))
val authHeader = "OAuth " + ((oauthFinalHeaderParams.sortBy(_._1).map(pair => s"""${pair._1}="${pair._2}"""")).mkString(", "))
httpGet = new HttpGet(s"https://stream.twitter.com/1.1/statuses/filter.json?${requestParams.map(pair => s"""${pair._1}=${pair._2}""").mkString("&")}")
httpGet.addHeader("Authorization", authHeader)
println("Downloading tweets!")
val response = httpclient.execute(httpGet)
val entity = response.getEntity()
input = entity.getContent()
if (response.getStatusLine.getStatusCode != 200) {
throw new RuntimeException(IOUtils.toString(input, StandardCharsets.UTF_8))
}
isDownloading = true
val reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))
var line: String = null
var lineno = 1
line = reader.readLine()
var lastSavingTime = System.currentTimeMillis()
val s = new StringBuilder()
val conf = new Configuration()
val fs= FileSystem.get(conf)
while (line != null && !isStopped) {
lineno += 1
line = reader.readLine()
s.append(line + "\n")
val now = System.currentTimeMillis()
if (now - lastSavingTime >= savingInterval) {
val df = spark.read.json(spark.sparkContext.parallelize(Seq(s.toString)))
df.write.json(path + now.toString)
lastSavingTime = now
s.clear()
}
}
}
private def generateSignature(data: String): String = {
val mac = Mac.getInstance("HmacSHA1")
val oauthSignature = encode(consumerSecret) + "&" + encode(accessTokenSecret)
val spec = new SecretKeySpec(oauthSignature.getBytes, "HmacSHA1")
mac.init(spec)
val byteHMAC = mac.doFinal(data.getBytes)
return Base64.getEncoder.encodeToString(byteHMAC)
}
def stop(): Unit = synchronized {
isStopped = true
isDownloading = false
try {
if (httpGet != null) {
httpGet.abort()
httpGet = null
}
if (input != null) {
input.close()
input = null
}
if (httpclient != null) {
httpclient.close()
httpclient = null
}
if (thread != null) {
thread.interrupt()
thread = null
}
} catch {
case _: Throwable =>
}
}
}
println("class defined")In [0]:
val twitterStream = new TwitterStream(consumerKey, consumerSecret, accessToken, accessTokenSecret, path, savingInterval, filters)In [0]:
twitterStream.start()
if (twitterStream.exception != null) { throw twitterStream.exception }In [0]:
val tweets = spark.read.json(path + "*")
val tweets_schema = tweets.schema
val tweetStream = spark.readStream.
|schema(tweets_schema).
|json(path + "*").
|filter($"lang" === "en").
|withColumn("screen_name", $"user.screen_name").
|withColumn("num_followers", $"user.followers_count").
|withColumn("createdAt", from_utc_timestamp(from_unixtime(unix_timestamp($"created_at", "EEE MMM dd HH:mm:ss ZZZZ yyyy")),"EST")).
|select("screen_name","createdAt","num_followers", "text") In [0]:
val query = tweetStream.writeStream.outputMode("append").foreachBatch{ (batchDF: DataFrame, batchId: Long) =>
batchDF.write
.format("com.microsoft.sqlserver.jdbc.spark")
.mode("append")
.option("url", url)
.option("dbtable", dbtable)
.option("user", user)
.option("password", password)
.option("dataPoolDataSource",datasource_name).save()
}.start()
query.processAllAvailable()
//query.awaitTermination(40000)In [0]:
def df_read(dbtable: String,
url: String,
dataPoolDataSource: String=""): DataFrame = {
spark.read
.format("com.microsoft.sqlserver.jdbc.spark")
.option("url", url)
.option("dbtable", dbtable)
.option("user", user)
.option("password", password)
.option("dataPoolDataSource", datasource_name)
.load()
}In [0]:
val new_df = df_read(dbtable, url, dataPoolDataSource=datasource_name)
new_df.countIn [0]:
twitterStream.stop()