• Noser.com
facebook
linkedin
twitter
youtube
  • NOSERmobile
    • Android
    • HTML 5
    • Hybrid Apps
    • iOS
    • Windows Phone
  • NOSERembedded
    • Medizintechnik
  • NOSERprojektmanagement
  • NOSERtesting
  • NOSERlifecycle
    • .NET Allgemein
    • Application Lifecylce Management
    • Apps
    • Architektur
    • ASP.NET
    • Azure
    • Cleancode
    • Cloud
    • Silverlight
    • Visual Studio / Team Foundation Server
    • Windows 8
    • Windows Presentation Foundation
  • NOSERinnovation
    • Big Data
    • Cloud
    • IoT
    • Operations Research
    • Augmented Reality
    • RFID, NFC, Bluetooth LE

Performanceoptimierung bei Social Media Integration

21. Februar 2013
Nathalie Kellenberger
Off
.NET, Caching, Facebook, Performance, Social Media, Social Media Integration, Twitter, Youtube

Wer Social Medias auf seiner Webseite integriert, stolpert als erste Hürde über die eigenen APIs der Social Medias, die sich jedoch leicht überwinden lässt wie mein letzter Post zeigt. Die zweite Hürde ist umso grösser: Die Performance der Zugriffe ist denkbar schlecht. Der Flaschenhals ist dabei das Social Media selbst. Facebook schneidet mit Abstand am schlechtesten ab. Selbst wenn die Abfrage stark eingeschränkt wird, kann sie immer noch bis zu 2 Sekunden dauern. Bei Youtube sind die Werte deutlich besser und liegen bei der ersten Abfrage für gewöhnlich unter einer Sekunde und bei einer wiederholten sogar unter einer halben.

Wenn der Channel asynchron geladen wird, ist das alles kein Problem. Wer jedoch auf seiner Webseite kein Busy Gif anstelle der Facebook Posts sehen will, sollte einen anderen Weg gehen. Ich möchte eine Caching Lösung vorstellen, an der ich mitgearbeitet habe. Das Prinzip ist denkbar einfach: Alle Social Media Channels werden jeweils in einem separaten Thread alle 2 Minuten aktualisiert. Die Webseite selbst zeigt die aktuellen Posts immer sofort an und hat so maximal einen veralteten Stand von 2 Minuten. Die Zeit lässt sich natürlich variieren. Der Vorteil liegt auf der Hand: Der Benutzer sieht die gewünschten Daten sofort und merkt nichts davon, dass sie geladen werden müssen.

// Base class for social media data providers
public abstract class SocialMediaDataProvider
{
    public SocialMediaDataProvider(string channelName, int n)
    {
        ChannelName = channelName;
        N = n;
    }

    // Data Providers are equal if their channel names are equal
    public override bool Equals(object obj)
    {
        if (obj is SocialMediaDataProvider)
        {
            if (obj != null)
            {
                return this.ChannelName.Equals(((SocialMediaDataProvider)obj).ChannelName);
            }
        }
        return base.Equals(obj);
    }
    public override int GetHashCode() { return base.GetHashCode(); }

    public abstract List GetTopNPostings();
    public int N { get; private set; }
    public string ChannelName { get; private set; }
}

Davon abgeleitet gibt es je einen Provider für bspw. Facebook, Twitter und Youtube, der die Daten entsprechend lädt. Eine Factory Klasse erstellt anhand einer ID den entsprechenden Subtyp.

// Factory for social media data providers
public static class SocialMediaDataProviderFactory
{
    public static SocialMediaDataProvider GetSocialMediaDataProvider(string socialID, string channelName, int n)
    {
        if (socialID == Constants.FACEBOOK)
	{
	    return new FacebookDataProvider(channelName, n);
	}
	else if (socialID == Constants.TWITTER)
	{
	    return new Linq2TwitterDataProvider(channelName, n);
	}
	else if (socialID == Constants.YOUTUBE)
	{
	    return new YouTubeDataProvider(channelName, n);
	}
	return null;
    }
}

Der eigentliche Cache funktioniert über einen Timer und lädt die Daten im Hintergrund regelmässig neu, so dass sie jederzeit abgerufen werden können. Der Zugriff über

GetLatestPostings

verursacht keine Verzögerung, wenn der Channel bereits vorgeladen wurde (beim Applikationsstart). Handelt es sich jedoch um einen neuen Channel, müssen die Daten beim ersten Mal in Echtzeit geladen werden.

// Holds the latest social media data and reloads them persistently
public class SocialMediaDataHolder
{
    private static object _lock = new object();

    private const int DELAY_MILLISECONDS = 120000;

    private static bool _isStarted = false;
    private static Timer _timer = null;
    private static List _dataProviders = new List();
    private static Dictionary> _latestPostings = new Dictionary>();

    public static void Start()
    {
        lock (_lock)
        {
            if (!_isStarted)
            {
                _isStarted = true;
                _timer = new Timer(TimerCallback, null, 0, DELAY_MILLISECONDS);
            }
        }
    }

    public static void Stop()
    {
        lock (_lock)
        {
            if (_isStarted)
            {
                _timer.Dispose();
            }
            _isStarted = false;
        }
    }

    public static void AddProvider(ID socialId, string channelName, int n)
    {
        var provider = SocialMediaDataProviderFactory.GetSocialMediaDataProvider(socialId, channelName, n);
        AddProvider(provider);
    }

    public static void AddProvider(SocialMediaDataProvider provider)
    {
        if (provider == null)
        {
            return;
        }

        lock (_lock)
        {
            if (!_dataProviders.Contains(provider))
            {
                _dataProviders.Add(provider);
            }
        }
    }

    public static void ClearProviders()
    {
        lock (_lock)
        {
            _dataProviders.Clear();
            _latestPostings.Clear();
        }
    }

    public static void RemoveProvider(string channelName)
    {
        var provider = _dataProviders.Where(p => p.ChannelName.Equals(channelName)).FirstOrDefault();
        RemoveProvider(provider);
    }

    public static void RemoveProvider(SocialMediaDataProvider provider)
    {
        if (provider == null)
        {
            return;
        }

        lock (_lock)
        {
            if (_dataProviders.Contains(provider))
            {
                _dataProviders.Remove(provider);
            }
            if (_latestPostings.ContainsKey(provider.ChannelName))
            {
                _latestPostings.Remove(provider.ChannelName);
            }
        }
    }

    public static List GetLatestPostings(ID socialId, string channelName, int n)
    {
        // provider added on runtime, load data first
        if (!_latestPostings.ContainsKey(channelName))
        {
            // add new provider
            var provider = SocialMediaDataProviderFactory.GetSocialMediaDataProvider(socialId, channelName, n);
            AddProvider(provider);

            try
            {
                List newPostings = provider.GetTopNPostings();
                _latestPostings.Add(channelName, newPostings);
            }
            catch (Exception exc)
            {
                // do not touch cached list -> return empty list
                return new List();
            }
        }
        return _latestPostings[channelName];
    }

    private static void TimerCallback(object state)
    {
        foreach (var provider in _dataProviders)
        {
            List newPostings = null;
            try
            {
                newPostings = provider.GetTopNPostings();
                if ((newPostings.Count == 1) && (newPostings[0] is PostingError))
                {
                    continue;
                }
            }
            catch (Exception exc)
            {
                //Do nothing - do not touch cached list
            }

            if (newPostings != null)
            {
                if (_latestPostings.ContainsKey(provider.ChannelName))
                {
                    _latestPostings[provider.ChannelName] = newPostings;
                }
                else
                {
                    _latestPostings.Add(provider.ChannelName, newPostings);
                }
            }
        }
    }
}

Der DataHolder kann bereits beim Applikationsstart (im Global.asax) angestossen werden, wenn die zu ladenden Channels bekannt sind. Andernfalls können neue Channels auch zur Laufzeit hinzugeladen werden, wobei es sich empfiehlt möglichst alle Channels schon beim Applikationsstart zu laden.

// Global.asax
public void Application_Start()
{
    // Start loading 5 social media posts from each facebook and twitter channel repeatedly
    SocialMediaDataHolder.AddProvider(Constants.FACEBOOK, "MyFacebookChannel", 5);
    SocialMediaDataHolder.AddProvider(Constants.TWITTER, "MyTwitterChannel", 5);
    SocialMediaDataHolder.Start();
}

Social Media Integration mit .NET

21. Februar 2013
Nathalie Kellenberger
Off
.NET, Facebook, LINQ, Linq2Twitter, Social Media, Social Media Integration, Twitter, Youtube

Social Media hat sich längst zum Schlagwort gemausert und auch eine noch so kleine Webseite bietet plötzlich eine Integration der gängisten Social Medias in einer Form an, sei es, dass Artikel oder Beiträge “geliked” werden können oder dass der eigene Twitter Channel auf der Webseite gleich mit integriert wird.

Social Medias bieten zum Zugriff auf die Daten eigene Schnittstellen an, die nicht immer ganz angenehm zu verwenden sind. Für .NET gibt es verschiedenste APIs, die diese Funktionalität wiederum kapseln und den Zugriff wesentlich angenehmer gestalten. Ich möchte hier je ein Framework für Facebook, Twitter und Youtube vorstellen.

Twitter

Für Twitter existiert eines der elegantesten Frameworks, Linq2Twitter, das Twitter Abfragen als Linq Queries ermöglicht: http://linqtotwitter.codeplex.com/.

Der Zugriff erfolgt anonym, es kann daher jeder beliebige Channel geladen werden.

Beispiel:

TwitterContext twitterCtx = new TwitterContext(); 
var publicTweets = (from tweet in twitterCtx.Status
    where tweet.Type == StatusType.User
        && tweet.ScreenName == CHANNELNAME
        && !(bool)tweet.Retweeted
        // only add text messages
        && !string.IsNullOrEmpty(tweet.Text)
    select tweet).Take(N);

Facebook

Die Graph API von Facebook lässt sich direkt über Facebook testen: http://developers.facebook.com/tools/explorer/.

Für .NET Entwickler gibt es das Facebook C# SDK for ASP.NET: http://csharpsdk.org/docs/web/getting-started.

Das SDK lässt sich bequem im Visual Studio über den Extension Manager installieren.

Für den Facebook Zugriff muss jedoch ein Zugriffstoken der Facebookseite gelöst werden, das den Zugriff sichert. Ein Token kann vom Administrator der Seite über folgenden Link generiert werden (muss als Administrator der Seite in Facebook eingeloggt sein): https://developers.facebook.com/tools/access_token/.

Beispiel:

string postsUrl = string.Format("{0}/posts", CHANNELNAME);
FacebookData fbData = null;
FacebookClient fbClient = new FacebookClient(ACCESSTOKEN);
dynamic returnedData = fbClient.Get(postsUrl, new { limit = N });
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(returnedData.ToString())))
{
    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(FacebookData));
    fbData = serializer.ReadObject(stream) as FacebookData;
}

Facebook liefert das Resultat in Form von JSON. Die Klasse FacebookData bildet dabei lediglich den von Facebook gelieferten JSON Datentyp nach, mit den Eigenschaften, die benötigt werden. Dies erleichtert den Zugriff auf das Resultat.

[DataContract]
public class FacebookData
{
    [DataMember(Name = "data")]
    public List Posts{ get; set; }

    [DataContract]
    public class FacebookPosting
    {
        [DataMember(Name = "created_time")]
        public string Created { get; set; }

        [DataMember(Name = "message")]
        public string Message { get; set; }

        [DataMember(Name = "picture")]
        public string Photo { get; set; }

        [DataMember(Name = "link")]
        public string Link { get; set; }

        [DataMember(Name = "name")]
        public string LinkName { get; set; }

        [DataMember(Name = "from")]
        public Originator From { get; set; }

        [DataMember(Name = "comments")]
        public FacebookData Comments { get; set; }

        [DataContract]
        public class Originator
        {
            [DataMember(Name = "name")]
            public string Name { get; set; }

            [DataMember(Name = "category")]
            public string Category { get; set; }
        }
    }
}

Youtube

Für Youtube gibt es von Google das YouTube Data API: https://developers.google.com/youtube/2.0/developers_guide_dotnet

Das API selbst kann über folgenden Link geladen werden: http://code.google.com/p/google-gdata/downloads/detail?name=Google_Data_API_Setup_2.1.0.0.msi

Damit das API genutzt werden kann, muss ein Entwicklerschlüssel bei Google gelöst werden. Dazu wird ein Google Account benötigt. Schlüssel können über folgenden Link gelöst werden (dazu muss mit dem Google Account eingeloggt werden): https://code.google.com/apis/youtube/dashboard.

Beispiel:

YouTubeRequestSettings settings = new YouTubeRequestSettings(CHANNELNAME, YOUTUBE_DEVKEY);
YouTubeRequest request = new YouTubeRequest(settings);
var feed = request.GetPlaylistsFeed(CHANNELNAME);
var lastPlaylist = feed.Entries.First();
1234

Tag Cloud

.NET android Angular AngularJs Arduino ASP.Net automated testing Azure Big Data C# C++ Cloud continuous integration Elm Embedded Führung gRPC Internet of Things IoT Java Javascript M2M OWASP Projektmanagement protobuf Python Raspberry Pi Reactive Programming REST Scrum Security Softwarequalität SPA Testen testing Testmanagement Teststrategie UX Visual Studio WebAPI windows WPF Xamarin Xamarin.Android Xamarin.Forms

Archive

Current Posts

  • Akzente setzen mit der Android Splash Screen API unter .NET MAUI
  • Do You have Your Personal Space?
  • Automated provisioning with ARM Templates
  • Asynchrone Beobachtungen und Versprechungen in Angular
  • Simplify Your Automated Tests With Fluent Syntax

Last Comments

  • Hans Reinsch bei Der Safety-Plan: Die wichtigsten Antworten mit Checkliste
  • George H. Barbehenn bei Modeling Optocouplers with Spice
  • Noser Blog Touch-Actions in Xamarin.Forms - Noser Blog bei Mach mehr aus Animationen in Xamarin.Forms mit SkiaSharp
  • Noser Blog Focus on the Secure Storage service of Trusted Firmware (TFM) - Noser Blog bei First run of the Trusted Firmware (TFM) application
  • Noser Blog First run of the Trusted Firmware (TFM) application - Noser Blog bei Focus on the Secure Storage service of Trusted Firmware (TFM)

Popular Posts

Xamarin.Android Code Obfuscation

6 Comments

ManuScripts: Wenn jemand eine Reise tut... Funktionale Programmierung mit Elm - Teil 1 - Aufbruch

5 Comments

ManuScripts: Wenn jemand eine Reise tut... Funktionale Programmierung mit Elm - Teil 2 - Kein Picknick

4 Comments

Contact us

  1. Name *
    * Please enter your name
  2. Email *
    * Please enter a valid email address
  3. Message *
    * Please enter message
© 2013 NOSER ENGINEERING AG. All rights reserved. Datenschutz | Cookie-Richtlinie