diff --git a/core/core.services.yml b/core/core.services.yml
index f5ed5db..ec97bfa 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -730,7 +730,7 @@ services:
     arguments: ['@state', '@current_user']
   maintenance_mode_subscriber:
     class: Drupal\Core\EventSubscriber\MaintenanceModeSubscriber
-    arguments: ['@maintenance_mode', '@config.factory', '@string_translation', '@url_generator', '@current_user']
+    arguments: ['@maintenance_mode', '@config.factory', '@string_translation', '@url_generator', '@current_user', '@messenger']
     tags:
       - { name: event_subscriber }
   path_subscriber:
@@ -775,7 +775,7 @@ services:
     class: Drupal\Core\EventSubscriber\DefaultExceptionSubscriber
     tags:
       - { name: event_subscriber }
-    arguments: ['@html_fragment_renderer', '@html_page_renderer', '@config.factory']
+    arguments: ['@html_fragment_renderer', '@html_page_renderer', '@config.factory', '@messenger']
   exception.logger:
     class: Drupal\Core\EventSubscriber\ExceptionLoggingSubscriber
     tags:
@@ -1045,3 +1045,6 @@ services:
     arguments: ['@module_handler']
     tags:
       - { name: mime_type_guesser }
+  messenger:
+    class: Drupal\Core\Messenger\Messenger
+    arguments: ['@page_cache_kill_switch']
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 553811f..ba3d51c 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -892,26 +892,15 @@ function watchdog($type, $message, array $variables = array(), $severity = WATCH
  *
  * @see drupal_get_messages()
  * @see theme_status_messages()
+ *
+ * @deprecated Deprecated as of Drupal 8.0.
+ *   Use \Drupal::service('messenger')->addMessage() instead.
  */
 function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) {
-  if ($message) {
-    if (!isset($_SESSION['messages'][$type])) {
-      $_SESSION['messages'][$type] = array();
-    }
-
-    if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
-      $_SESSION['messages'][$type][] = array(
-        'safe' => SafeMarkup::isSafe($message),
-        'message' => $message,
-      );
-    }
-
-    // Mark this page as being uncacheable.
-    \Drupal::service('page_cache_kill_switch')->trigger();
-  }
-
-  // Messages not set when DB connection fails.
-  return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
+  /* @var \Drupal\Core\Messenger\MessengerInterface $messenger */
+  $messenger = \Drupal::service('messenger');
+  $messenger->addMessage($message, $type, $repeat);
+  return $messenger->getMessages();
 }
 
 /**
@@ -938,20 +927,20 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE)
  *
  * @see drupal_set_message()
  * @see theme_status_messages()
+ *
+ *
+ * @deprecated Deprecated as of Drupal 8.0.
+ *   Use \Drupal::service('messenger')->getMessages() or
+ *   \Drupal::service('messenger')->getMessagesByType() instead.
  */
 function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
-  if ($messages = drupal_set_message()) {
-    foreach ($messages as $message_type => $message_typed_messages) {
-      foreach ($message_typed_messages as $key => $message) {
-        if ($message['safe']) {
-          $message['message'] = SafeMarkup::set($message['message']);
-        }
-        $messages[$message_type][$key] = $message['message'];
-      }
-    }
+  /** @var \Drupal\Core\Messenger\MessengerInterface $messenger */
+  $messenger = \Drupal::service('messenger');
+
+  if ($messages = $messenger->getMessages()) {
     if ($type) {
       if ($clear_queue) {
-        unset($_SESSION['messages'][$type]);
+        $messenger->deleteMessagesByType($type);
       }
       if (isset($messages[$type])) {
         return array($type => $messages[$type]);
@@ -959,7 +948,7 @@ function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
     }
     else {
       if ($clear_queue) {
-        unset($_SESSION['messages']);
+        $messenger->deleteMessages();
       }
       return $messages;
     }
diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php
index e35eff4..a5e0e7f 100644
--- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php
@@ -11,6 +11,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\ContentNegotiation;
+use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Core\Page\DefaultHtmlPageRenderer;
 use Drupal\Core\Page\HtmlFragment;
 use Drupal\Core\Page\HtmlFragmentRendererInterface;
@@ -64,6 +65,13 @@ class DefaultExceptionSubscriber implements EventSubscriberInterface {
   protected $configFactory;
 
   /**
+   * The messenger.
+   *
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $messenger;
+
+  /**
    * Constructs a new DefaultExceptionHtmlSubscriber.
    *
    * @param \Drupal\Core\Page\HtmlFragmentRendererInterface $fragment_renderer
@@ -72,11 +80,14 @@ class DefaultExceptionSubscriber implements EventSubscriberInterface {
    *   The page renderer.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The configuration factory.
+   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+   *   The messenger.
    */
-  public function __construct(HtmlFragmentRendererInterface $fragment_renderer, HtmlPageRendererInterface $page_renderer, ConfigFactoryInterface $config_factory) {
+  public function __construct(HtmlFragmentRendererInterface $fragment_renderer, HtmlPageRendererInterface $page_renderer, ConfigFactoryInterface $config_factory, MessengerInterface $messenger) {
     $this->fragmentRenderer = $fragment_renderer;
     $this->htmlPageRenderer = $page_renderer;
     $this->configFactory = $config_factory;
+    $this->messenger = $messenger;
   }
 
   /**
@@ -141,7 +152,7 @@ protected function onHtml(GetResponseForExceptionEvent $event) {
         // Generate a backtrace containing only scalar argument values.
         $message .= '<pre class="backtrace">' . Error::formatFlattenedBacktrace($backtrace) . '</pre>';
       }
-      drupal_set_message(SafeMarkup::set($message), $class, TRUE);
+      $this->messenger->addMessage(SafeMarkup::set($message), $class, TRUE);
     }
 
     $content = $this->t('The website has encountered an error. Please try again later.');
diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
index f09e3e3..4062879 100644
--- a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Core\Page\DefaultHtmlPageRenderer;
 use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Routing\UrlGeneratorInterface;
@@ -58,6 +59,13 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
   protected $urlGenerator;
 
   /**
+   * The messenger.
+   *
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $messenger;
+
+  /**
    * Constructs a new MaintenanceModeSubscriber.
    *
    * @param \Drupal\Core\Site\MaintenanceModeInterface $maintenance_mode
@@ -70,13 +78,16 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
    *   The url generator.
    * @param \Drupal\Core\Session\AccountInterface $account
    *   The current user.
+   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+   *   The messenger.
    */
-  public function __construct(MaintenanceModeInterface $maintenance_mode, ConfigFactoryInterface $config_factory, TranslationInterface $translation, UrlGeneratorInterface $url_generator, AccountInterface $account) {
+  public function __construct(MaintenanceModeInterface $maintenance_mode, ConfigFactoryInterface $config_factory, TranslationInterface $translation, UrlGeneratorInterface $url_generator, AccountInterface $account, MessengerInterface $messenger) {
     $this->maintenanceMode = $maintenance_mode;
     $this->config = $config_factory;
     $this->stringTranslation = $translation;
     $this->urlGenerator = $url_generator;
     $this->account = $account;
+    $this->messenger = $messenger;
   }
 
   /**
@@ -108,10 +119,10 @@ public function onKernelRequestMaintenance(GetResponseEvent $event) {
         // settings page.
         if ($route_match->getRouteName() != 'system.site_maintenance_mode') {
           if ($this->account->hasPermission('administer site configuration')) {
-            $this->drupalSetMessage($this->t('Operating in maintenance mode. <a href="@url">Go online.</a>', array('@url' => $this->urlGenerator->generate('system.site_maintenance_mode'))), 'status', FALSE);
+            $this->messenger->addMessage($this->t('Operating in maintenance mode. <a href="@url">Go online.</a>', array('@url' => $this->urlGenerator->generate('system.site_maintenance_mode'))), 'status', FALSE);
           }
           else {
-            $this->drupalSetMessage($this->t('Operating in maintenance mode.'), 'status', FALSE);
+            $this->messenger->addMessage($this->t('Operating in maintenance mode.'), 'status', FALSE);
           }
         }
       }
@@ -119,13 +130,6 @@ public function onKernelRequestMaintenance(GetResponseEvent $event) {
   }
 
   /**
-   * Wraps the drupal_set_message function.
-   */
-  protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) {
-    return drupal_set_message($message, $type, $repeat);
-  }
-
-  /**
    * {@inheritdoc}
    */
   public static function getSubscribedEvents() {
diff --git a/core/lib/Drupal/Core/Messenger/Messenger.php b/core/lib/Drupal/Core/Messenger/Messenger.php
new file mode 100644
index 0000000..21ac4c8
--- /dev/null
+++ b/core/lib/Drupal/Core/Messenger/Messenger.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Messenger\Messenger.
+ */
+
+namespace Drupal\Core\Messenger;
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\PageCache\ResponsePolicy\KillSwitch;
+
+/**
+ * Provides a session-based messenger.
+ *
+ * @todo Use Symfony's session components once http://drupal.org/node/1858196
+ *   has been fixed.
+ */
+class Messenger implements MessengerInterface {
+
+  /**
+   * The page caching kill switch.
+   *
+   * @var \Drupal\Core\PageCache\ResponsePolicy\KillSwitch
+   */
+  protected $pageCacheKillSwitch;
+
+  /**
+   * Constructs a new instance.
+   *
+   * @param \Drupal\Core\PageCache\ResponsePolicy\KillSwitch $page_cache_kill_switch
+   *   The page caching kill switch.
+   */
+  public function __construct(KillSwitch $page_cache_kill_switch) {
+    $this->pageCacheKillSwitch = $page_cache_kill_switch;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addMessage($message, $type = self::STATUS, $repeat = FALSE) {
+    if ($repeat || !array_key_exists('messages', $_SESSION) || !array_key_exists($type, $_SESSION['messages']) || !in_array($message, $_SESSION['messages'][$type])) {
+      $_SESSION['messages'][$type][] = array(
+        'safe' => SafeMarkup::isSafe($message),
+        'message' => $message,
+      );
+    }
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMessages() {
+    $messages = isset($_SESSION['messages']) ? $_SESSION['messages'] : array();
+    foreach ($messages as $type => $messages_by_type) {
+      $messages[$type] = $this->processMessages($messages_by_type);
+    }
+
+    return $messages;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMessagesByType($type) {
+    $messages = isset($_SESSION['messages']) && isset($_SESSION['messages'][$type]) ? $_SESSION['messages'][$type] : array();
+
+    return $this->processMessages($messages);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteMessages() {
+    unset($_SESSION['messages']);
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteMessagesByType($type) {
+    unset($_SESSION['messages'][$type]);
+
+    return $this;
+  }
+
+  /**
+   * Proccesses safe markup.
+   *
+   * @param array[]
+   *   An array of arrays with the following keys:
+   *   - message: the message string.
+   *   - safe: a boolean indicating whether the message contains safe markup.
+   *
+   * @return string[]
+   *   The messages.
+   */
+  protected function processMessages(array $messages) {
+    $processed_messages = array();
+    foreach ($messages as $message_data) {
+      $processed_messages[] = $message_data['safe'] ? SafeMarkup::set($message_data['message']) : $message_data['message'];
+    }
+
+    return $processed_messages;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Messenger/MessengerInterface.php b/core/lib/Drupal/Core/Messenger/MessengerInterface.php
new file mode 100644
index 0000000..044b16c
--- /dev/null
+++ b/core/lib/Drupal/Core/Messenger/MessengerInterface.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Messenger\MessengerInterface.
+ */
+
+namespace Drupal\Core\Messenger;
+
+interface MessengerInterface {
+
+  /**
+   * A status message.
+   */
+  const STATUS = 'status';
+
+  /**
+   * A warning.
+   */
+  const WARNING = 'warning';
+
+  /**
+   * An error.
+   */
+  const ERROR = 'error';
+
+  /**
+   * Adds a new message to the queue.
+   *
+   * @param string $message
+   *   (optional) The translated message to be displayed to the user. For
+   *   consistency with other messages, it should begin with a capital letter
+   *   and end with a period.
+   * @param string $type
+   *   (optional) The message's type. Either self::STATUS, self::WARNING, or
+   *   self::ERROR.
+   * @param bool $repeat
+   *   (optional) If this is FALSE and the message is already set, then the
+   *   message won't be repeated. Defaults to FALSE.
+   *
+   * @return $this
+   */
+  public function addMessage($message, $type = self::STATUS, $repeat = FALSE);
+
+  /**
+   * Gets all messages.
+   *
+   * @return array[]
+   *   Keys are message types and values are indexed arrays of messages. Message
+   *   types are either self::STATUS, self::WARNING, or self::ERROR.
+   */
+  public function getMessages();
+
+  /**
+   * Gets all messages of a certain type.
+   *
+   * @param string $type
+   *   The messages' type. Either self::STATUS, self::WARNING, or self::ERROR.
+   *
+   * @return string[]
+   */
+  public function getMessagesByType($type);
+
+  /**
+   * Deletes all messages.
+   *
+   * @return $this
+   */
+  public function deleteMessages();
+
+  /**
+   * Deletes all messages of a certain type.
+   *
+   * @param string $type
+   *   The messages' type. Either self::STATUS, self::WARNING, or self::ERROR.
+   *
+   * @return $this
+   */
+  public function deleteMessagesByType($type);
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Messenger/MessengerTest.php b/core/tests/Drupal/Tests/Core/Messenger/MessengerTest.php
new file mode 100644
index 0000000..d954532
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Messenger/MessengerTest.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Messenger\MessengerTest.
+ */
+
+namespace Drupal\Tests\Core\Messenger;
+
+use Drupal\Core\Messenger\Messenger;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Messenger\Messenger
+ */
+class MessengerTest extends UnitTestCase {
+
+  /**
+   * A copy of any existing session data to restore after the test.
+   *
+   * @var array
+   */
+  protected $existingSession;
+
+  /**
+   * The messenger under test.
+   *
+   * @var \Drupal\Core\Messenger\Messenger
+   */
+  protected $messenger;
+
+  /**
+   * The page caching kill switch.
+   *
+   * @var \Drupal\Core\PageCache\ResponsePolicy\KillSwitch|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $pageCacheKillSwitch;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'description' => '',
+      'name' => '\Drupal\Core\Messenger\Messenger unit test',
+      'group' => 'Messenger',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @covers ::__construct
+   */
+  public function setUp() {
+    $this->pageCacheKillSwitch = $this->getMockBuilder('\Drupal\Core\PageCache\ResponsePolicy\KillSwitch')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $this->messenger = new Messenger($this->pageCacheKillSwitch);
+
+    $this->existingSession = isset($_SESSION) ? $_SESSION : NULL;
+    $_SESSION = array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function tearDown() {
+    if ($this->existingSession !== NULL) {
+      $_SESSION = $this->existingSession;
+    }
+    else {
+      unset($_SESSION);
+    }
+  }
+
+  /**
+   * @covers ::addMessage
+   * @covers ::getMessages
+   * @covers ::getMessagesByType
+   * @covers ::deleteMessages
+   * @covers ::deleteMessagesByType
+   */
+  public function testMessenger() {
+    $message_a = $this->randomMachineName();
+    $type_a = $this->randomMachineName();
+    $message_b = $this->randomMachineName();
+    $type_b = $this->randomMachineName();
+
+    // Test that if there are no messages, the default is an empty array.
+    $this->assertEquals($this->messenger->getMessages(), array());
+
+    // Test that adding a message returns the messenger and that the message can
+    // be retrieved.
+    $this->assertEquals($this->messenger->addMessage($message_a, $type_a), $this->messenger);
+    $this->messenger->addMessage($message_a, $type_a);
+    $this->messenger->addMessage($message_a, $type_a, TRUE);
+    $this->messenger->addMessage($message_b, $type_b, TRUE);
+    $this->assertEquals($this->messenger->getMessages(), array(
+        $type_a => array(
+          array(
+            'safe' => FALSE,
+            'message' => $message_a,
+          ),
+          array(
+            'safe' => FALSE,
+            'message' => $message_a,
+          ),
+          array(
+            'safe' => FALSE,
+            'message' => $message_a,
+          ),
+        ),
+        $type_b => array(
+          array('safe' => FALSE, 'message' => $message_b),
+        ),
+      )
+    );
+
+    // Test deleting messages of a certain type.
+    $this->assertEquals($this->messenger->deleteMessagesByType($type_a), $this->messenger);
+    $this->assertEquals($this->messenger->getMessages(), array(
+      $type_b => array(array('safe' => FALSE, 'message' => $message_b)),
+    ));
+
+    // Test deleting all messages.
+    $this->assertEquals($this->messenger->deleteMessages(), $this->messenger);
+    $this->assertEquals($this->messenger->getMessages(), array());
+  }
+
+}
