BT

Diffuser les Connaissances et l'Innovation dans le Développement Logiciel d'Entreprise

Contribuez

Sujets

Sélectionner votre région

Accueil InfoQ Articles Azure + Spring Boot = Serverless - Q&R Avec Julien Dubois

Azure + Spring Boot = Serverless - Q&R Avec Julien Dubois

Favoris

Points Clés

  • Microsoft fournit tous les outils nécessaires pour créer des applications serverless ainsi que des outils de déploiement continu. Actuellement, il existe une prise en charge à la fois dans Azure DevOps et dans les GitHub Actions.
  • Outre Spring Boot, Azure prend en charge Quarkus, Micronaut. JHipster prend également en charge clé en main le déploiement sur Azure.
  • En utilisant la technique du "lift and shift", on peut facilement transformer une application Spring Boot classique déployée sur Tomcat en une application Azure "serverless". Les contrôleurs MVC devront être réécrits, la partie complexe sera du côté de la base de données, où des efforts supplémentaires devraient être déployés pour obtenir un comportement similaire.
  • Java 8 et 11 sont correctement pris en charge sur Azure.
  • En choisissant soigneusement les outils utilisés, vous pouvez vous assurer que le passage d'Azure à un autre fournisseur cloud se fait sans stress

 

Microsoft semble prouver à maintes reprises que l'accent mis sur le cloud et l'écosystème Java est la nouvelle norme. Même si Java fait partie des langages pris en charge par Azure Functions depuis un certain temps déjà, Julien Dubois a expérimenté le support de Spring Boot sur Azure Functions, à la fois avec la JVM et avec GraalVM. InfoQ l'a contacté pour approfondir son expérience de la création d'une application Spring Boot sur Azure.

InfoQ : Merci d'avoir pris le temps de répondre à certaines questions de nos lecteurs. Pouvons-nous commencer par te demander de te présenter et de décrire tes rôles et tes fonctions au quotidien chez Microsoft ?

Julien Dubois : je suis dans la communauté Java depuis plus de 20 ans, et je suis surtout connu comme le créateur de JHipster et en tant que Java Champion. Sur le plan professionnel, je dirige l'équipe Java Cloud Developers Advocates chez Microsoft. Le rôle de mon équipe, au sein de l'organisation Developer Relations, est de contacter les développeurs Java afin de faire d'Azure la meilleure plateforme pour eux. Au quotidien, cela signifie travailler avec diverses communautés et partenaires Java pour recueillir des commentaires, améliorer la documentation et travailler avec nos équipes d'ingénierie pour améliorer nos produits et services.

InfoQ : Il semble que Microsoft attire de plus en plus de développeurs Java. Qu'est-ce qui t'a amené chez Microsoft ?

Julien Dubois : à ce jour, Microsoft emploie 11 Java Champions , et nous avons un excellent support et des projets pour Java. De plus, Microsoft n'est pas seulement Azure : nous avons de nombreux développeurs Java travaillant pour LinkedIn ou Minecraft !

Je suis arrivé chez Microsoft d'abord parce que c'est une entreprise qui m'a fait rêver quand j'étais enfant et que je voulais travailler à distance pour une entreprise américaine. J'adore les valeurs et l'esprit de l'entreprise, et en tant que personne qui aime la technologie, c'est l'un des meilleurs endroits pour évoluer et apprendre.

InfoQ : Qu'est-ce que ton arrivée chez Microsoft a signifié pour JHipster ? A-t-il été influencé d'une manière ou d'une autre ?

Julien Dubois : être le créateur de JHipster a été définitivement une force lors de mon embauche, mais j'ai une vie professionnelle chez Microsoft qui est très différente de ma vie Open Source avec JHipster. J'ai clairement moins de temps aujourd'hui pour travailler sur JHipster, mais ce n'est pas totalement la faute de mon rôle chez Microsoft. J'ai eu un quatrième enfant l'année dernière, et avec le COVID-19, cela signifie que je n'ai pas eu beaucoup de temps libre. Le point important avec JHipster est que cela a permis à d'autres personnes de grandir dans la communauté, et au final, le projet est probablement plus fort et plus stable.

InfoQ : Il semble que Microsoft est sur le point de devenir un contributeur de premier ordre de l'écosystème Java et cloud. Est-ce le cas ?

Julien Dubois : je pense que c'est déjà le cas. Nous utilisons Java dans un grand nombre de produits au sein de Microsoft, y compris Azure, où il s'agit définitivement d'une solution de premier ordre. Par exemple, nous avons le support de Java dans Azure App Service, qui est notre offre PaaS, ainsi que dans Azure Functions, qui est notre offre serverless. J'ai été largement impliqué dans Azure Spring Cloud, une offre spécifique à Spring que nous avons développée conjointement avec l'équipe Spring de VMware. Alors oui, nous sommes profondément impliqués dans Java, et cela va devenir beaucoup plus important dans un futur proche.

InfoQ : Le mouvement serverless prend de l'ampleur. Qu'en penses-tu ? À quel point est-il proche de la mise en production ?

Julien Dubois : nous avons déjà beaucoup de clients en production sur Azure Functions, y compris de gros clients exécutant des traitements Java. J'ai vu des choses très excitantes dans cet espace, mais je pense que ce n'est que le début. Au départ, Java n'est pas un outil idéal pour le serverless, et de nombreuses améliorations peuvent encore être apportées à l'avenir pour réduire le coût des fonctions serverless, améliorer leur temps de démarrage à froid, mieux les surveiller et mieux les mettre à l'échelle. L'une des parties préférées de mon travail est de travailler avec l'équipe d'ingénierie en charge du serverless sur Azure.

InfoQ : Quels sont les outils créés par Microsoft pour accéder à cet espace ? De quel outil auriez-vous besoin de l'offre d'autres fournisseurs de cloud ? Est-il possible de s'intégrer également avec d'autres fournisseurs ?

Julien Dubois : nous avons un runtime, des workers spécifiques au langage ( par exemple le Java worker), une CLI, ainsi que des plugins pour IDE. Ils sont tous open source. Celles-ci reposent sur les mêmes bases qu'Azure App Service, notre offre PaaS, qui fournit de nombreuses fonctionnalités éprouvées en production, ainsi que quelques limitations : cela nous a permis d'entrer dans cet espace très rapidement, avec une offre très complète, mais fonctionne d'une manière qui pourrait surprendre les personnes provenant d'autres fournisseurs cloud. La principale différence est que votre fonction peut être appelée par différents clients en même temps : nous vous permettons d'utiliser plusieurs threads, comme une application traditionnelle le fait. Cela améliore beaucoup les performances et les coûts, et est généralement une bonne chose pour les personnes utilisant Java, qui souffre de son temps de démarrage à froid.

Concernant l'interopérabilité avec d'autres fournisseurs de cloud, permettez-moi de parler comme un ancien utilisateur de Spring. Nous travaillons en étroite collaboration avec l'équipe Spring afin que vous puissiez déployer vos traitements à l'aide de Spring Cloud Function. Il y a de très petites différences entre les fournisseurs ici, et en fait, je travaille actuellement à en corriger un, mais en gros, vous devriez pouvoir exécuter le même code Spring Boot sur n'importe quel fournisseur cloud en utilisant cette abstraction. Spring fournit ici ce qu'ils ont toujours fait, comme au temps de J2EE : une abstraction qui vous permet de changer de fournisseur sans modifier votre code.

InfoQ : as-tu utilisé d'autres fournisseurs qu'Azure ? Comment se comparent-ils ?

Julien Dubois : dans ma vie passée de consultant, j'en ai beaucoup utilisé. Les plus évidents sont naturellement AWS, GCP et Heroku, mais j'ai également passé beaucoup de temps avec de petits fournisseurs qui fournissent des VM simples et des solutions réseau.

Tout d'abord, je suis clairement une personne PaaS : je n'aime pas gérer des VM, des clusters Kubernetes ou des bases de données SQL. Je pense qu'il existe aujourd'hui d'excellentes solutions PaaS, à des prix très bas, et faire quoi que ce soit à la main est une perte de temps, mais aussi un mauvais choix de production. Vous ne pouvez tout simplement pas rivaliser avec ce que les grands fournisseurs cloud peuvent proposer. Désormais, tous les grands fournisseurs cloud ont leurs propres avantages et j'ai eu d'excellentes expériences avec certains concurrents d'Azure. JHipster Online, fonctionne sur GCP car ils nous parrainent depuis le début. La plupart des utilisateurs de JHipster commencent généralement par utiliser Heroku, car leur niveau gratuit leur suffit, grâce à l'équipe Heroku qui a fait un travail formidable pour le peaufiner. Ce sont toutes d'excellentes options, et nous avons de la chance que Java fonctionne si bien sur tant de plates-formes différentes.

InfoQ : la documentation Azure Functions indique qu'un langage de programmation HTTP natif permettra le développement d'Azure Functions. Qu'est-ce que cela signifie réellement en Java ? Que devons-nous faire pour écrire une Function Azure avec Java ?

Julien Dubois : en fait, voici deux façons d'exécuter une fonction Java sur Azure. Il y a la manière classique : nous avons un worker Java officiel, avec une excellente documentation et une API. C'est ce que la plupart des gens utilisent, et c'est celui que je recommande car vous bénéficierez également du support pour le JDK et d'une meilleure surveillance. Vos performances de démarrage à froid devraient également être assez bonnes, car nous mettons en oeuvre un certain nombre d'astuces pour les améliorer, y compris le préchauffage de certaines machines à l'avance.

Ensuite, il y a la manière non officielle, qui est celle que vous évoquez en parlant de HTTP, avec une nouvelle fonctionnalité appelée «custom handlers for Azure Functions». Avec cette méthode, notre broker HTTP comprend seulement qu'il parle avec un serveur HTTP, donc il ne sait rien de votre application : cela supprime toutes les possibilités de réglage et de surveillance de notre côté, mais cela vous permet de développer votre application à la manière de votre choix. Ce que tout le monde essaie de faire aujourd'hui, moi y compris, c'est de créer des fonctions Java en utilisant des images natives GraalVM. Cela fonctionne déjà bien dans un certain nombre de cas, y compris l'utilisation de Spring Cloud Functions. Il y a encore un long chemin à parcourir avant de pouvoir l'utiliser en toute sécurité en production, mais si vous aimez utiliser les dernières et meilleures technologies, c'est certainement très amusant.

InfoQ : Java 8 est l'un des langages de programmation pris en charge pour Azure Functions. Comment se déroule l'expérience de son utilisation ?

Julien Dubois : Oh, nous sommes déjà sur Java 11, car c'est la nouvelle version LTS. S'il n'y avait que moi, nous aurions également le support de Java 14, mais la plupart des clients utilisent toujours Java 8, donc nous ne nous précipitons pas là-dessus. La plus grande amélioration que nous ayons eue de ce côté est d'avoir le support de Linux. Cela peut paraître surprenant, mais nous avons des clients qui effectuent des réglages spécifiques qui nécessitent Java sous Linux, et pour des raisons historiques, nous ne fournissions initialement que Java sous Windows.

Notez également que nous travaillons avec Azul Systems afin d'avoir un JDK avec support et patchs, ce qui est extrêmement important pour nos clients et nous-mêmes, car nous prenons la sécurité très au sérieux.

InfoQ : as-tu réussi à déployer une application Spring Boot serverless sur Azure. Peux-tu nous décrire ton expérience ?

Julien Dubois : en fait, je l'ai fait de deux manières différentes. Tout d'abord, j'ai utilisé la méthode officielle utilisant Spring Cloud Function pour déployer une application Spring Boot sur Azure Functions. Ce code est devenu l'exemple d'application officiel sur Azure. Cela fonctionne bien, mais cette solution peut être améliorée car nous avons besoin d'une classe de configuration spécifique pour cela, et je travaille actuellement avec l'équipe d'ingénierie Azure et l'équipe Spring Boot pour améliorer cela.

Deuxièmement, j'ai essayé d'utiliser le nouveau «custom handlers for Azure Functions» pour créer la même fonction avec GraalVM. Je l'ai fait avec l'aide de mes amis des équipes Spring et GraalVM, et cela fonctionne très bien. Veuillez noter que je ne l'ai pas beaucoup utilisé en production, je ne le recommanderais donc pas si vous souhaitez utiliser une charge de traitements sérieuse. Ensuite, cela m'a permis de découvrir quelques comportements étranges dans Azure Functions, et je l'ai signalé en interne, donc nous les corrigeons activement.

InfoQ : Cela a-t-il fonctionné immédiatement ou des efforts supplémentaires ont-ils été nécessaires ?

Julien Dubois : pour la méthode officielle prise en charge, il existe un fichier de configuration spécifique à Azure à connaître lorsque vous utilisez Spring Cloud Function, mais dans l'ensemble, cela n'a pas nécessité d'effort spécifique.

Pour utiliser GraalVM c'est une autre histoire, comme personne ne l'avait fait auparavant. La plupart du travail difficile avait déjà été fait par l'équipe Spring et GraalVM, mais je pense qu'il m'a fallu quelques jours pour que tout se passe bien. Bien sûr, vous pouvez maintenant utiliser mon dépôt GitHub, qui est entièrement documenté, et vous devriez avoir le même résultat beaucoup plus rapidement.

InfoQ : Il semble que Microsoft dispose désormais de tout le nécessaire pour un flux de déploiement continu. Est-ce le cas ?

Julien Dubois : en fait, vous avez deux options proposées par Microsoft. La principale offre actuelle s'appelle Azure DevOps, et elle est très complète et très puissante, c'est donc celle que nous recommandons à tout le monde. Sur JHipster, nous l'avons utilisé pendant plusieurs mois, car il dispose d'un niveau gratuit très généreux pour les projets OSS, et cela a été un énorme succès. Il est bien sûr entièrement intégré à Azure et à l'ensemble de la pile Microsoft.

Ensuite, vous savez peut-être que GitHub a un nouveau service appelé GitHub Actions. Celui-ci montre une croissance énorme car tout le monde sur GitHub l'utilise. Il est encore actuellement moins puissant qu'Azure DevOps, mais il évolue très rapidement et il présente l'énorme avantage d'être intégré à GitHub. C'est la raison principale pour laquelle nous avons migré le pipeline JHipster CI/CD vers GitHub Actions : nous avons un grand nombre de contributeurs, avec de nombreuses autorisations à mettre en place, et en tant qu'administrateur de l'organisation, c'était beaucoup plus facile pour moi. De plus, c'est totalement suffisant pour nos cas d'utilisation, et aujourd'hui nous sommes très satisfaits de cette solution.

InfoQ : cela semble assez similaire à Quarkus et Amazon Lambda. Avez-vous également eu l'opportunité d'expérimenter cette combinaison ? Comment se comparent-ils à Spring Boot et Azure ?

Julien Dubois : je n'ai pas essayé Quarkus sur Lambda, mais j'ai essayé Quarkus sur Azure ! En fait, nous effectuons un travail similaire avec les équipes Spring, Quarkus et Micronaut afin que les trois principaux frameworks Java soient également pris en charge sur nos services. Maintenant, tous fonctionnent de manière très similaire sur la JVM, et l'expérience devrait être assez proche. Le principal domaine d'expérimentation pour le moment, et où le potentiel est énorme, consiste à exécuter ces frameworks avec GraalVM sur Azure Functions. Cela changerait certainement la donne, mais il est vraiment compliqué d'avoir le niveau de support que nous fournissons avec la JVM, ce qui signifie qu'au moins à court terme, il y aura beaucoup plus de responsabilités du côté des développeurs. Permettez-moi de donner un exemple : nous fournissons à nos utilisateurs de JVM un OS et une JVM gérés, donc s'il y a un problème de sécurité sur la JVM, nous allons le corriger pour vous : vous ne remarquerez rien, et comme nous utilisons des versions de la JVM d'Azul Systems avec support, nous pourrions même la corriger avant que le problème de sécurité ne soit public. Au contraire, si vous voulez faire de même avec GraalVM, pour le moment vous êtes seul : il sera de votre responsabilité de mettre à jour et de patcher votre binaire, car tout ce que nous verrons côté Azure est un binaire.

InfoQ : quel serait le scénario le mieux adapté pour une application serverless avec Azure ? L'utiliseriez-vous si vous deviez créer quelque chose à partir de zéro maintenant ? Pourquoi ?

Julien Dubois : nous avons actuellement de nombreux clients différents, avec des scénarios très différents, qui ont une expérience réussie. Il est donc difficile de parler du «meilleur» scénario, mais au moins je peux parler de l'un de nos grands et intéressants cas d'utilisation. C'est une entreprise française très connue qui propose un service qui est utilisé par beaucoup de gens, littéralement des dizaines de millions, mais dont l'utilisation varie beaucoup selon l'heure ou le jour. En règle générale, les gens utilisent beaucoup ce service le soir, et cette utilisation peut augmenter considérablement en fonction de certains événements publics ou annonces. J'ai eu l'occasion de leur rendre visite avant le COVID-19, car leur bureau est proche du nôtre et pendant les heures de pointe, leur utilisation est incroyable. Ces personnes exécutent Java et Spring en plus d'Azure Functions, et leur pool d'instances ne fait qu'augmenter et se réduire automatiquement en fonction de l'utilisation, sans qu'elles ne nécessitent de maintenance. En fin de compte, ils se concentrent sur leur code et sur leurs fonctionnalités métier, puis tout fonctionne et gère la charge sans rien faire, c'est donc un excellent scénario. Je pense que cela leur enlève beaucoup de problèmes et leur fait gagner beaucoup d'argent.

InfoQ : dans quelle mesure serait-il compliqué de migrer une application prête pour la production vers le modèle serverless avec Azure ?

Julien Dubois : en tant que personne aimant Java, parlons d'une application Spring Boot classique fonctionnant sur Tomcat, ce qui est un scénario très courant. Il y a deux façons de migrer cela vers un modèle serverless : vous pouvez faire ce que nous appelons le «lift and shift», ou vous pouvez tout restructurer.

Avec le «lift and shift», l'un des problèmes que vous aurez est que vous devrez transformer tous vos contrôleurs Spring MVC pour qu'ils deviennent des fonctions Spring Cloud. Il existe différentes façons et astuces pour y parvenir, et vous aurez certaines limites, mais cela devrait fonctionner sans dépenser trop d'argent. De plus, vous ne toucherez pas trop au code métier, donc rien d'important ne devrait être cassé.

Ensuite, une Azure Functions fonctionne d'une manière très différente d'une application Spring Boot classique : vous pouvez toujours bénéficier d'un cache de deuxième niveau Hibernate ou d'un pool de connexion à une base de données, mais vous pouvez facilement comprendre qu'ils ne seront pas aussi efficaces, car leur temps de vie sera beaucoup plus court. L'utilisation d'un cache distribué sera un énorme problème ici. De plus, votre fonction peut évoluer beaucoup mieux que votre serveur Tomcat à un nœud, donc peut-être que votre base de données ne fonctionnera pas aussi bien qu'avant, car elle n'a pas été conçue pour ce type de charge. Vous pouvez utiliser à la place une base de données comme CosmosDB ou d'une solution de mise en cache comme Redis, qui sont deux options très utilisées sur Azure. C'est beaucoup plus de travail, mais c'est la seule façon d'obtenir tous les avantages d'une plate-forme serverless. C'est ce que nous appelons la «réarchitecture», la nécessité de transformer l'ensemble de votre application et de ses services afin de tirer pleinement parti de la plate-forme cloud.

InfoQ : que diriez-vous d'un autre fournisseur cloud ?

Julien Dubois: je peux principalement parler d'Amazon Lambda ici, mais je pense que de nombreux autres fournisseurs serverless fonctionnent de la même manière. Il existe une énorme différence entre Azure Functions et Amazon Lambda : avec Azure, votre application est démarrée et s'exécute pendant quelques minutes, et pendant ce temps, plusieurs clients peuvent y accéder, et ses threads d'arrière-plan fonctionnent comme d'habitude. Sur Amazon, vous auriez une exécution par client, et les threads d'arrière-plan ne fonctionneront probablement pas très bien. Ainsi, avec Azure, le comportement que vous avez est comme une application «normale», à la différence qu'elle ne s'exécute pas très longtemps. Cela signifie que la plupart des applications existantes fonctionneront de la même manière, et comme vous avez dû concevoir votre application pour gérer les pannes, le fait qu'elle ne s'exécute que quelques minutes (et non des jours ou des semaines) ne devrait pas être un gros problème. C'est pourquoi le scénario «lift and shift» fonctionne assez bien avec Azure, alors que d'autres fournisseurs cloud vous demanderont probablement de procéder à une réarchitecture complète de votre application. Ensuite, même avec Azure, la réarchitecture est le seul moyen de tirer le meilleur parti de votre application serverless, il n'y a donc pas de solution magique ici.

InfoQ : dans quelle mesure il serait facile de déplacer votre application vers un autre fournisseur ? Le développement d'une application dans l'espace Azure signifierait-il un verrouillage du fournisseur ?

Julien Dubois : cela dépend des services que vous utilisez, et du framework que vous utilisez ! En ce qui concerne les frameworks, comme nous l'avons vu précédemment, il existe des solutions comme Spring Cloud Function, Quarkus et Micronaut qui vous donnent une abstraction au-dessus de votre fournisseur cloud. En Java, vous utiliseriez probablement l'un de ces frameworks de toute façon, ce qui signifie que la plupart de votre code devrait fonctionner de la même manière sur toutes les plates-formes. Ensuite, concernant les services, il est clair que si vous utilisez CosmosDB, qui est une excellente solution mais uniquement disponible chez Microsoft, vous aurez du mal à passer à un autre fournisseur cloud. C'est pourquoi, lorsque je fais des démos avec JHipster, j'utilise des services comme MySQL, qui fonctionnent partout de la même manière. Et lorsque je souhaite utiliser CosmosDB, j'utilise leur API MongoDB : de cette façon, je peux facilement migrer vers un autre fournisseur cloud prenant en charge MongoDB.

Ensuite, essayer d'être indépendant du fournisseur vous limitera beaucoup et vous ne pourrez probablement pas utiliser de nombreuses fonctionnalités avancées de votre fournisseur cloud. C'est comme des gens qui essaient d'utiliser une base de données en utilisant uniquement du SQL standard. Vous perdez beaucoup, et pour une mauvaise raison : le coût de la migration ne sera pas d'utiliser une autre API, mais de déplacer toutes vos données d'un service à l'autre.

InfoQ : penchons-nous ensemble sur les deux projets auxquels tu es impliqué : serait-il possible de mélanger JHipster et Azure à des fins de développement ? Comment envisages-tu cela ?

Julien Dubois : Oh, nous soutenons déjà le déploiement de JHipster sur Azure ! Nous prenons officiellement en charge Azure Spring Cloud et Azure App Service, qui sont les deux principales options pour les développeurs Java. Ensuite, le support Kubernetes de JHipster fonctionne sur Azure Kubernetes Service sans que nous n'effectuions de travail spécifique. Bien sûr, j'aimerais aller plus loin, et l'une des idées avec lesquelles je joue est d'avoir un bien meilleur support de Terraform dans JHipster : cela permettrait de générer des infrastructures beaucoup plus complexes sur Azure.

InfoQ : Des conseils pour ceux qui souhaitent commencer avec Azure Functions ?

Julien Dubois : je recommanderais de créer une simple application serverless comme vous pouvez le faire avec Azure Static Web Apps. Cela ne fonctionne qu'avec JavaScript pour le moment, mais c'est gratuit et simple à utiliser, c'est donc un excellent moyen de tester et d'apprendre lorsque vous débutez. De plus, vous avez une excellente documentation sur notre site Web Microsoft Learn.

Les choses les plus compliquées ici sont les concepts, puis appliquer cela à un autre langage ne devrait pas être un gros problème.

[NdT : Tous mes remerciements à Julien Dubois pour la relecture de cette traduction]

A propos de l'interviewé

Julien Dubois, responsable de l'équipe Java developer advocates chez Microsoft, a expérimenté Spring Boot et Azure pour voir ce que cette combinaison signifie pour la plateforme de calcul serverless d'Azure. Julien Dubois est le créateur et développeur principal du projet JHipster et est Java Champion. Au cours des 20 dernières années, il a principalement travaillé avec les technologies Java et Spring en tant qu'architecte et consultant, travaillant pour de nombreux clients différents dans tous les secteurs. Aimant partager sa passion, Julien Dubois a écrit un livre sur le framework Spring, a été speaker pour plus de 100 conférences internationales et a créé plusieurs projets open-source populaires.

Evaluer cet article

Pertinence
Style

Contenu Éducatif

Bonjour étranger!

Vous devez créer un compte InfoQ ou cliquez sur pour déposer des commentaires. Mais il y a bien d'autres avantages à s'enregistrer.

Tirez le meilleur d'InfoQ

Html autorisé: a,b,br,blockquote,i,li,pre,u,ul,p

Commentaires de la Communauté

Html autorisé: a,b,br,blockquote,i,li,pre,u,ul,p

Html autorisé: a,b,br,blockquote,i,li,pre,u,ul,p

BT