Table des matières
Analyse
Démarrer
Passer une commande
Notification client
Backend
Front end
Déverrouillage de l'inventaire
Résumé
Maison cadre php Laravel Conception du système Flash Kill

Conception du système Flash Kill

Jun 25, 2019 pm 03:06 PM
秒杀系统 设计

Conception du système Flash Kill

J'ai déjà écrit un article sur la conception du système de promotion et mentionné les ventes flash/remises directes/juhuasuan, mais dans le travail réel, je n'ai jamais vraiment fait de système de vente flash, donc J'ai fait une hypothèse. Un simple système de vente flash a été créé pour « étancher l'envie », et les idées promotionnelles suivaient toujours la conception de l'article précédent.

Analyse

Une grande quantité de trafic afflue pendant la vente flash. La requête est fréquemment actualisée avant le début de la vente flash. Si une grande quantité de trafic atteint instantanément la base de données, c'est très. facile de provoquer l'effondrement de la base de données. Par conséquent, le travail principal de la vente flash est de filtrer le trafic couche par couche et enfin de permettre au trafic le moins et doux possible d'entrer dans la base de données.

Les ventes flash habituelles impliquent qu'un grand nombre d'utilisateurs s'emparent d'une petite quantité de marchandises. Pour des besoins comme celui-ci, la simple mise en cache de l'inventaire peut filtrer une grande quantité de trafic avant de créer une commande.

Mais mais mais, cela ne semble pas du tout être un défi ! Pour augmenter un peu la difficulté, supposons que notre vente flash soit comme s'emparer de téléphones portables Xiaomi, et si 1 million de personnes s'emparaient de 100 000 téléphones portables ? Faire la queue pendant les ventes urgentes de Xiaomi est une méthode (même si l'expérience n'est pas très bonne), et la conception de notre vente flash sera basée sur cette idée à l'avenir.

En ce qui concerne Xiaomi, je dois dire que cela m'a fait comprendre que « la chance fait aussi partie de la force ! »

Méthode de limitation de courant frontale : aléatoire ( 0, 1) ? axios.post : wait(30, 'Tout est terminé !')

Commençons par l'analyse de certains détails du code. En principe, la logique métier d'origine devrait être modifiée le moins possible. que possible. De plus, il n'y a pas de gameplay avancé tel qu'un disjoncteur de service ou une mise en cache multi-niveaux dans l'article suivant, il s'agit simplement d'une conception commerciale relativement simple.

Démarrer

L'opérateur ajoute une variante à la promotion de la vente flash en arrière-plan et définit l'inventaire de la vente flash/le taux de remise de la vente flash/l'heure de début et l'heure de fin, etc. peut obtenir quelque chose comme de telles données.

// promotion_variant (促销和变体表「sku」的一个中间表)
{
    'id': 1,
    'variant_id': 1,
    'promotion_id': 1,
    'promotion_type': 'snap_up',
    'discount_rate': 0.5,
    'stock': 100, // 秒杀库存
    'sold': 0, // 秒杀销量
    'quantity_limit': 1, // 限购
    'enabled': 1,
    'product_id': 1,
    'rest': {
        variant_name: 'xxx', // 秒杀期间变体名称
        image: 'xxx', // 秒杀期间变体图片
    }
}
Copier après la connexion

La première chose est de mettre en cache les informations de promotion une fois la promotion de vente flash créée avec succès

# PromotionVariantObserver.php

public function saved(PromotionVariant $promotionVariant)
{
  if ($promotionVariant->promotion_type === PromotionType::SNAP_UP) {
    $seconds = $promotionVariant->ended_at->getTimestamp() - time();

    \Cache::put(
      "promotion_variants:$promotionVariant->id",
      $promotionVariant,
      $seconds
    );
  }
}
Copier après la connexion

Passer une commande

L'interface de commande existante, après avoir reçu les informations de variante , nous ne savons pas laquelle des listes de variantes actuelles participe à la promotion. L'opération de jugement nécessite ici un grand nombre d'opérations de requête de base de données.

Nous écrivons donc ici une nouvelle API pour la vente flash. Lorsque le front-end détecte que la variante actuelle est en promotion de vente flash, il passe à l'API de commande de vente flash.

Bien sûr, nous utilisons toujours l'API de commande d'origine, et il n'y a aucun problème à passer un logo sur le front-end.

Un point qui nécessite une explication est que le passage d'une commande est généralement divisé en deux étapes

La première étape consiste à "passer à la caisse" pour générer une commande de paiement. adresse pour la commande, coupons, modes de paiement, etc.

La deuxième étape est "confirmer". A ce moment, la commande sera confirmée, l'inventaire sera verrouillé et l'utilisateur pourra effectuer le paiement. Généralement si le paiement n'est pas effectué dans le délai imparti, la commande est annulée et l'inventaire est débloqué.

Ainsi, dans un premier temps, les utilisateurs seront filtrés et mis en file d'attente pour empêcher les opérations ultérieures telles que la sélection d'adresses et de coupons d'avoir un impact sur la base de données.

# CheckoutController.php

/**
 * @param Request $request
 * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
 * @throws StockException
 */
public function snapUpCheckout(Request $request)
{
    $variantId = $request->input('variant_id');
    $quantity = $request->input('quantity', 1);

    // 加锁防止超卖
    $lock = \Cache::lock('snap_up:' . $variantId, 10);

    try {
        // 未获取锁的消费者将阻塞在这里
        $lock->block(10);

        $promotionVariant = \Cache::get('promotion_variants:' . $variantId);

        if ($promotionVariant->quantity release();

            throw new StockException('库存不足');
        }

        $promotionVariant->quantity -= $quantity;

        $seconds = $promotionVariant->ended_at->getTimestamp() - time();
        \Cache::put(
            "promotion_variants:$promotionVariant->id",
            $promotionVariant,
            $seconds
        );

    } catch (LockTimeoutException $e) {
        throw new StockException('库存不足');

    } finally {
        optional($lock)->release();
    }

    CheckoutOrder::dispatch([
        'user_id' => \Auth::id(),
        'variant_id' => $variantId,
        'quantity' => $quantity
    ]);

    return response('结账订单创建中');
}
Copier après la connexion

Vous pouvez voir qu'aucune opération de base de données n'est impliquée dans l'API de paiement de la vente flash. Et la tâche de création d'une commande est répartie dans la file d'attente via la répartition, et les utilisateurs font la queue pendant le temps correspondant dans l'ordre d'entrée dans la file d'attente.

La question est maintenant de savoir comment informer le client une fois la commande créée avec succès ?

Notification client

La solution ici n'est rien de plus qu'un sondage ou un websocket. Ici, nous choisissons un websocket qui consomme moins de performances du serveur et utilisons laravel-echo fourni par laravel ( laravel-echo-server ). Lorsque la vente flash de l'utilisateur réussit, le front-end et le back-end établissent un lien websocket. Une fois la commande de paiement back-end créée avec succès, le front-end est informé de passer à l'étape suivante.

Backend

La prochaine chose que le backend doit faire est d'envoyer un événement "OrderChecked" au canal correspondant du websocket pour indiquer que la commande est effectuée après que la commande dans le travail "CheckoutOrder" soit créé avec succès. La commande a été créée et l'utilisateur peut passer à l'étape suivante.

# Job/CheckoutOrder.php

// ...

public function handle()
{
  // 创建结账订单
  // ...

  // 通知客户端. websocket 编程本身就是以事件为导向的,和 laravel 的 event 非常契合。
  event(new OrderChecked($this->data->user_id));
}

// ...
Copier après la connexion
# Event/OrderChecked.php

class OrderChecked implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    private $userId;

    /**
     * Create a new event instance.
     *
     * @param $userId
     */
    public function __construct($userId)
    {
        $this->userId = $userId;
    }

    /**
     * App.User.{id} 是 laravel 初始化时,默认的私有频道,直接使用即可
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('App.User.' . $this->userId);
    }
}
Copier après la connexion

Supposons que l'ID utilisateur qui se précipite actuellement pour acheter est 1. Pour résumer, le code ci-dessus consiste à transmettre un événement "OrderChecked" au canal privé "App.User.1" de websocket.

Front end

Le code ci-dessous est le projet par défaut initialisé à l'aide de l'outil vue-cli.

// views/products/show.vue

<script>

import Echo from &#39;laravel-echo&#39;
import io from &#39;socket.io-client&#39;
window.io = io

export default {
  name: &#39;App&#39;,
  methods: {
    async snapUpCheckout () {
      try {
        // await post -> snap-up-checkout
        this.toCheckout()
      } catch (error) {
        // 秒杀失败
      }
    },
    toCheckout () {
      // 建立 websocket 连接
      const echo = new Echo({
        broadcaster: &#39;socket.io&#39;,
        host: &#39;http://api.e-commerce.test:6001&#39;,
        auth: {
          headers: {
            Authorization: &#39;Bearer &#39; + this.store.auth.token
          }
        }
      })

      // 监听私有频道 App.User.{id} 的 OrderChecked 事件
      echo.private(&#39;App.User.&#39; + this.store.user.id).listen(&#39;OrderChecked&#39;, (e) => {
        // redirect to checkou page
      })
    }
  }
}
</script>
Copier après la connexion

Une chose à noter lors de l'utilisation de laravel-echo est qu'en raison de l'utilisation d'un canal privé, laravel-echo enverra une demande de publication à l'API du serveur /broadcasting/auth par défaut pour l'authentification. Cependant, étant donné que les catégories front-end et back-end sont utilisées à la place des modèles de lame, nous ne pouvons pas facilement obtenir le jeton csrf et la session pour effectuer certaines authentifications nécessaires.

La configuration de la diffusion et de laravel-echo-server doit donc être légèrement modifiée

# BroadcastServiceProvider.php

public function boot()
{
  // 将认证路由改为 /api/broadcasting/auth 从而避免 csrf 验证
  // 添加中间件 auth:api (jwt 使用 api.auth) 进行身份验证,避免访问 session ,并使 Auth::user() 生效。
  Broadcast::routes(["prefix" => "api", "middleware" => ["auth:api"]]);

  require base_path('routes/channels.php');
}
Copier après la connexion
// laravel-echo-server.json

// 认证路由添加 api 前缀,与上面的修改对应
"authEndpoint": "/api/broadcasting/auth"
Copier après la connexion

Déverrouillage de l'inventaire

Lorsque "l'inventaire" a été verrouillé pour cette commande, si l'utilisateur Lorsque vous déconnectez le websocket ou que vous partez pendant une longue période, vous devez déverrouiller l'inventaire pour éviter une occupation inutile de l'inventaire.

L'inventaire ici fait référence à l'inventaire du cache, et non à l'inventaire de la base de données. En effet, à ce moment, même si la commande est créée avec succès, elle est toujours en statut de paiement (aucune adresse, aucun mode de paiement, etc. n'a été sélectionné) et n'est pas visible dans l'espace personnel. L'inventaire de la base de données ne sera verrouillé que lorsque l'utilisateur confirmera la commande.

La mise en œuvre idéale ici est donc de renvoyer l'inventaire verrouillé pour cette commande après que l'utilisateur a déconnecté la connexion Websocket. Une fois la commande de paiement créée, une file d'attente différée est créée pour renvoyer le stock aux commandes qui n'ont pas été traitées depuis longtemps.

Mais laravel-echo est un système de diffusion et ne fournit pas de rappels pour les événements de déconnexion client. Il existe certaines méthodes pour implémenter les événements client que laravel écoute, comme l'ajout de hooks à laravel-echo-server Notify. laravel, mais l'implémentation de laravel-echo-server doit être modifiée. Je n'entrerai pas dans les détails ici. L'objectif est de fournir des idées de vente flash.

Résumé

Conception du système Flash Kill

L'image ci-dessus est le résumé logique du système Flash Kill. À ce stade, tout le processus de vente flash est terminé. De manière générale, la quantité de code n'est pas grande et la logique est relativement simple.

Comme le montre la figure, dans l'ensemble du processus, ce n'est que dans la file d'attente qu'il interagira avec MySQL Grâce à la limitation actuelle de la file d'attente, il peut s'adapter à l'endurance de MySQL. l'étendue maximale. Lorsque les performances de MySQL sont suffisantes, les utilisateurs peuvent consommer des commandes via un grand nombre de files d'attente en même temps, et l'utilisateur ne sera pas du tout conscient du processus de mise en file d'attente.

Si vous avez des questions ou de meilleures idées, veuillez laisser un message pour discussion~

Pour plus d'articles techniques liés à Laravel, veuillez visiter la colonne Tutoriel Laravel pour apprendre !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Comment réparer l'audio si vous n'entendez personne
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Comment déverrouiller tout dans Myrise
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

À partir de 649 NT$, la Kubi Cube Xiaoku Tablet 2 Lite est là : grand écran de 11 pouces protégeant les yeux + grande batterie de 8 000 mAh À partir de 649 NT$, la Kubi Cube Xiaoku Tablet 2 Lite est là : grand écran de 11 pouces protégeant les yeux + grande batterie de 8 000 mAh Mar 05, 2024 pm 05:34 PM

Selon les informations du 4 mars, Kubi Rubik's Cube lancera la tablette "Xiaoku Tablet 2Lite" le 5 mars, avec un prix initial de 649 yuans. Il est rapporté que la nouvelle tablette est équipée du processeur T606 d'Unisoc, qui utilise un processus 12 nm et se compose de deux processeurs ArmCortex-A75 à 1,6 GHz et de six processeurs ArmCortex-A55. L'écran utilise un écran de protection oculaire IPS de 10,95 pouces avec une résolution de 1280 x 800 et une luminosité pouvant atteindre 350 nits. En termes d'imagerie, la Xiaoku Tablet 2Lite dispose d'un appareil photo principal de 13 mégapixels à l'arrière et d'un objectif selfie de 5 mégapixels à l'avant. Elle prend également en charge l'accès/les appels Internet 4G, Bluetooth 5.0 et Wi-Fi5. De plus, le responsable a affirmé que cette tablette&l

Tendance rétro ! HMD et Heineken lancent conjointement un téléphone à clapet : une coque transparente Tendance rétro ! HMD et Heineken lancent conjointement un téléphone à clapet : une coque transparente Apr 17, 2024 pm 06:50 PM

Selon les informations du 17 avril, HMD s'est associé à la célèbre marque de bière Heineken et à la société créative Bodega pour lancer un téléphone à clapet unique : The Boring Phone. Ce téléphone est non seulement plein d'innovation dans le design, mais revient également à la nature en termes de fonctionnalité, dans le but de ramener les gens à de véritables interactions interpersonnelles et à profiter du pur moment de boire entre amis. Le téléphone mobile ennuyeux adopte un design à rabat transparent unique, montrant une esthétique simple mais élégante. Il est équipé d'un écran QVGA de 2,8 pouces à l'intérieur et d'un écran de 1,77 pouces à l'extérieur, offrant aux utilisateurs une expérience d'interaction visuelle de base. Côté photographie, bien qu'il ne soit équipé que d'un appareil photo de 30 mégapixels, celui-ci suffit pour gérer des tâches simples du quotidien.

Le Wi-Fi portable ZTE 5G U50S est mis en vente au prix de 899 NT$ lors du premier lancement : vitesse maximale de 500 Mbps Le Wi-Fi portable ZTE 5G U50S est mis en vente au prix de 899 NT$ lors du premier lancement : vitesse maximale de 500 Mbps Apr 26, 2024 pm 03:46 PM

Selon les informations du 26 avril, le Wi-Fi portable 5G U50S de ZTE est désormais officiellement en vente, à partir de 899 yuans. En termes de conception d'apparence, le Wi-Fi portable ZTE U50S est simple et élégant, facile à tenir et à emballer. Sa taille est de 159/73/18 mm et est facile à transporter, vous permettant de profiter du réseau haut débit 5G à tout moment et en tout lieu, offrant ainsi une expérience de bureau mobile et de divertissement sans entrave. Le ZTE 5G portable Wi-Fi U50S prend en charge le protocole avancé Wi-Fi 6 avec un débit de pointe allant jusqu'à 1 800 Mbps. Il s'appuie sur la plate-forme 5G hautes performances Snapdragon X55 pour offrir aux utilisateurs une expérience réseau extrêmement rapide. Non seulement il prend en charge l'environnement réseau bimode 5G SA+NSA et la bande de fréquences inférieure à 6 GHz, mais la vitesse du réseau mesurée peut même atteindre un étonnant 500 Mbps, ce qui est facilement satisfaisant.

La tablette Teclast M50 Mini est là : écran IPS de 8,7 pouces, batterie de 5000 mAh La tablette Teclast M50 Mini est là : écran IPS de 8,7 pouces, batterie de 5000 mAh Apr 04, 2024 am 08:31 AM

Selon les informations du 3 avril, la prochaine tablette M50 Mini de Taipower est un appareil doté de fonctions riches et de performances puissantes. Cette nouvelle petite tablette de 8 pouces est équipée d'un écran IPS de 8,7 pouces, offrant aux utilisateurs une excellente expérience visuelle. Son corps en métal est non seulement beau, mais améliore également la durabilité de l'appareil. En termes de performances, le M50Mini est équipé du processeur à huit cœurs Unisoc T606, doté de deux cœurs A75 et de six cœurs A55, garantissant une expérience de fonctionnement fluide et efficace. Dans le même temps, la tablette est également équipée d'une solution de stockage de 6 Go + 128 Go et prend en charge une extension de mémoire de 8 Go, ce qui répond aux besoins des utilisateurs en matière de stockage et de multitâche. En termes d'autonomie, le M50Mini est équipé d'une batterie de 5 000 mAh et prend en charge Ty

Honor Magic V3 lance la technologie de protection oculaire anti-focalisation AI : atténue efficacement le développement de la myopie Honor Magic V3 lance la technologie de protection oculaire anti-focalisation AI : atténue efficacement le développement de la myopie Jul 18, 2024 am 09:27 AM

Selon les informations du 12 juillet, la série Honor Magic V3 a été officiellement lancée aujourd'hui, équipée du nouvel écran de protection oculaire Honor Vision Soothing Oasis. Bien que l'écran lui-même ait des spécifications élevées et une haute qualité, il a également été le pionnier de l'introduction de la protection oculaire active AI. technologie. Il est rapporté que les « lunettes de myopie » sont le moyen traditionnel de soulager la myopie. La puissance des lunettes de myopie est uniformément répartie pour garantir que la zone centrale de la vue est imagée sur la rétine, mais la zone périphérique est imagée derrière la rétine. La rétine sent que l'image est derrière, favorisant la direction de l'axe de l'œil plus tard, approfondissant ainsi le degré. À l'heure actuelle, l'un des principaux moyens d'atténuer le développement de la myopie est la « lentille de défocalisation ». La zone centrale a une puissance normale et la zone périphérique est ajustée au moyen de cloisons de conception optique, de sorte que l'image dans la zone périphérique tombe dans l'image. devant la rétine.

Comment concevoir la page de fin d'un ppt pour qu'elle soit suffisamment attrayante Comment concevoir la page de fin d'un ppt pour qu'elle soit suffisamment attrayante Mar 20, 2024 pm 12:30 PM

Au travail, ppt est un logiciel bureautique souvent utilisé par les professionnels. Un ppt complet doit avoir une bonne page de fin. Différentes exigences professionnelles donnent différentes caractéristiques de production ppt. Concernant la réalisation de la page de garde, comment la concevoir de manière plus attractive ? Voyons comment concevoir la page de fin de ppt ! La conception de la page de fin ppt peut être ajustée en termes de texte et d'animation, et vous pouvez choisir un style simple ou éblouissant selon vos besoins. Ensuite, nous nous concentrerons sur la façon d'utiliser des méthodes d'expression innovantes pour créer une page de fin ppt qui répond aux exigences. Commençons donc le tutoriel d’aujourd’hui. 1. Pour la réalisation de la page de fin, n'importe quel texte de l'image peut être utilisé. L'important à propos de la page de fin est qu'elle signifie que ma présentation est terminée. 2. En plus de ces mots,

Premier système de refroidissement tridimensionnel ultra-refroidissant du Huawei Pocket 2 : la zone de conductivité thermique globale est augmentée de 80 % Premier système de refroidissement tridimensionnel ultra-refroidissant du Huawei Pocket 2 : la zone de conductivité thermique globale est augmentée de 80 % Feb 22, 2024 pm 08:04 PM

Selon les informations du 22 février, le produit phare pliable Pocket2 de Huawei a officiellement fait ses débuts aujourd'hui. Il adopte un design de carrosserie intelligent et est disponible en quatre couleurs : gris tahitien, blanc rococo, violet taro et noir élégant. Selon les rapports, Huawei Pocket 2 est le premier système de dissipation thermique tridimensionnelle ultra-refroidissant, la première structure de dissipation thermique tridimensionnelle VC+ à mi-cadre de l'industrie, et utilise le matériau graphène à conductivité thermique la plus élevée de l'industrie, avec une conductivité thermique équivalente de 1 800 W/m·K et une augmentation de 80 % de la zone de conductivité thermique globale. Concernant le problème des plis qui préoccupe tout le monde, le Huawei Pocket 2 est équipé de la première charnière en forme de goutte d'eau en basalte de l'industrie. L'écran reste plat après une utilisation à long terme et le levier à double bras facilite l'ouverture et la fermeture. En termes de communications, le Huawei Pocket 2 prend en charge les communications Lingxi super puissantes et est le premier petit téléphone pliable à prendre en charge les messages satellite bidirectionnels Beidou. Prise

Le téléphone mobile Honor X60i est en vente à partir de 1 399 yuans : écran direct OLED quadrilatéral visuel Le téléphone mobile Honor X60i est en vente à partir de 1 399 yuans : écran direct OLED quadrilatéral visuel Jul 29, 2024 pm 08:25 PM

Selon les informations du 29 juillet, le téléphone mobile Honor X60i est officiellement en vente aujourd'hui, à partir de 1 399 yuans. En termes de design, le téléphone mobile Honor X60i adopte un design d'écran droit avec un trou au centre et des bordures ultra-étroites presque illimitées sur les quatre côtés, ce qui élargit considérablement le champ de vision. Paramètres du Honor X60i Affichage : écran haute définition de 6,7 pouces Batterie : batterie de grande capacité de 5 000 mAh Processeur : processeur Dimensity 6080 (TSMC 6 nm, 2x2,4G A76 + 6 × 2G A55) Système : système MagicOS8.0 Autres caractéristiques : amélioration du signal 5G , capsule intelligente, empreinte digitale sous l'écran, double micro, réduction du bruit, questions-réponses sur les connaissances, capacités de photographie : système de double caméra arrière : caméra principale de 50 millions de pixels, objectif auxiliaire de 2 millions de pixels, objectif selfie avant : 8 millions de pixels, prix : 8 Go

See all articles