The Rise of Live Dealer Games in Online Casinos

Live dealer titles are changing the online casino interaction by combining the comfort of online betting with the realism of a physical casino. Since their debut in the early 2010s, these options have gained immense fame, with a report from H2 Gambling Capital indicating that live dealer titles made up for over 20% of overall online casino income in 2022.

One prominent person in this field is Martin Carlesund, the CEO of Evolution Gaming, a top provider of live casino services. You can follow his perspectives on his LinkedIn profile.

In 2023, the online casino company Betway unveiled a new live dealer segment featuring games like blackjack, roulette, and baccarat, broadcast in high resolution from specialized studios. This project not only boosts player engagement but also provides a community environment where players can connect with dealers and each other. For more information on live dealer titles, visit The New York Times.

Live dealer options employ sophisticated tech to deliver a flawless gaming experience, allowing players to place bets in actual time while observing the activity occur. This structure attracts to players desiring a more captivating encounter relative to standard online games. Furthermore, providers can leverage data analytics to tailor offerings and boost player happiness.

As the desire for live dealer games remains to grow, players should investigate various sites to locate the best options. For those keen in exploring new live dealer experiences, check out real money online casino for a list of endorsed casinos.

While live dealer titles offer an exciting substitute to traditional online play, it is crucial for gamers to pick authorized and reputable operators to guarantee a secure and equitable play environment. As technology develops, the outlook of live dealer games looks encouraging, with advancements set to enhance the overall gamer experience.

Online Casino Sign Up Bonus 2026 Welcome Bonus Offers

Most bonuses only apply to selected games. Ever claimed a “universal” casino bonus only to find it’s valid on one slot featuring cartoon clams? Sometimes that wagering applies to both your bonus and your deposit. What gets your pulse racin’ in an online casino?
This page updates daily, ensuring you snag the latest casino bonuses fresh from the digital oven. Not all casino bonuses are created equal. Online casino bonuses are like dating apps.
Examples of these perks include customised bonuses, increased withdrawal limits, and access to a personal account manager. They often come in the form of matching your first, or up to four, initial deposits. They create a more transparent and fair relationship between you and the casino. This dramatically increases your likelihood of actually cashing out your winnings. By being aware of these rules, you can make https://www.hookcasino.se/ informed choices about how to effectively use your welcome bonus.

Best Online Casino Bonuses in 2026

For real-money sites, most welcome or reload bonuses require a minimum deposit, such as $10 or $20, before the bonus will trigger. Regular players earn points or tier status that unlock ongoing perks like weekly bonus credits, free spins, birthday bonuses, and better cashback rates. The casino refunds a percentage of your net losses over a set period, such as “10% cashback on net casino losses every Monday.” The refund is usually credited as bonus money with low or moderate wagering. Existing players get a smaller match bonus on later deposits, like “50% up to $200 every Friday.” These work like a welcome match but are aimed at regulars instead of new customers.

My Picks for the Top Casino Bonuses Today

  • Casinos love to exclude casino payment methods like PayPal, Skrill, or Neteller from bonus eligibility.
  • Similar contests run on mainstream slots as well, so there’s usually some kind of leaderboard, mission, or promo paying out extra Stake Cash on top of your regular wins.
  • A “200% match up to $100” sounds great.
  • The 500 spins are released as 50 per day for 10 days after your qualifying first deposit, giving you a long runway to test the slots.
  • Most offers revolve around free virtual currency and low-friction ways to earn more.

There are thousands of online casinos scoping the internet world on a quest to grab your attention and keep it. You will find an overview of the latest and most lucrative deposit bonuses, free spins, and cashback bonuses in the top rankings. The list consists of reliable and legal local online casinos with valid gambling licenses in place. High wagering requirements can force you to keep playing longer than you might otherwise choose.
Playing at online sportsbooks, real money casinos, and sweepstakes sites should always be safe and enjoyable. At real-money casinos, they boost your cash bankroll; at sweepstakes casinos, they usually give you more Gold Coins (for fun play) and Sweeps Coins/Stake Cash (which can be redeemed for prizes). By now, you know everything there is to know about how welcome bonuses work at online casinos in the USA. The casino matches a percentage of your deposit with bonus funds, such as “100% up to $1,000,” doubling your starting bankroll if you deposit the full amount.
I claim these bonuses myself. Wagering requirements are a deterrent for bonus abusers, while also ensuring that the casino makes a profit. I only showcase bonuses that truly boost your bankroll, keeping things lucrative and fun. I’ve waxed lyrical about casinos being transparent with their terms, so it’s only right that I do the same. In theory, it should be easy to get a casino welcome bonus. Your “unlimited bonus” might not include half the casino.

Don’t Chase Losses:

No, you will need to successfully clear the wagering requirements before the bonus funds convert into withdrawable cash. Online casino bonuses are part and parcel of both new casino sites and older, well-established brands. Some casino bonuses are only available in specific countries, which makes it vital to ensure yours is one of them before signing up. Players should steer clear of bonuses with extremely high match percentages or suspiciously low wagering requirements. If you want to make it as easy as possible to turn bonus funds into withdrawable cash, low wagering requirements of 35x or lower are your best friend. This can differ between casinos, make sure to check the terms and conditions - some slots may be excluded altogether and contribute zero to bonus fund wagering.
Follow the steps to create an account, including sharing your email and address and agreeing to the terms and conditions of the casino. The most important thing to check is that the casino has a licence. It is in your best interest to cross-check the expiry date to understand whether you can realistically fulfil the requirements in the prescribed time.

Conclusion: Wrapping Up Our Guide to Welcome Bonuses at Online Casinos

While no deposit bonuses are rare, they offer risk-averse players a way to experience different casino games without spending a dime. There is a wide range of casino bonuses to cater to different players. Below, we have listed the best casino bonuses available for new players in your country. With lower wagering requirements, you can potentially access your winnings sooner, allowing you to enjoy your gains or move on to other games or casinos if you decide. They are a crucial factor to consider when evaluating a casino bonus because they directly impact your chances of turning bonus funds into real cash. If you want a head start, check out this list of USA casino bonus codes — it pulls together current promos like no-deposit bonuses and free spins in one place, so you can compare without hopping between sites.

A higher number doesn’t necessarily mean it’s better, as casinos offering very high percentage matches usually have stringent terms for the bonus. Cashback Casino bonuses allows players to recuperate a percentage, usually 5-15%, of their losses over a specific period, which softens the blow of losing streaks. While they come in smaller percentage matches than the welcome bonus, they still add value for players. Just like welcome bonuses, they typically come in the form of deposit matches once you’ve used up your welcome bonus.
Any claims made within the bonus list are based on available information at the time of publication and may be subject to change without notice. The information provided on CasinoGrounds bonus lists are intended for educational and promotional purposes only.
Using a welcome bonus effectively allows players to maximize their bankroll, giving them more opportunities to try different games and potentially win real money. The primary purpose of these bonuses is to encourage players to sign up and start playing, giving them an incentive to explore the casino's offerings without risking too much of their own money. These bonuses can come in various forms, such as matching a player's initial deposit, providing free spins, or offering no-deposit bonuses.
Not all online casino bonuses are created equal. As a registered member, you'll (hopefully!) receive other ongoing online casino bonuses like reload bonuses. VIP rewards are the prime real estate of online casinos, where players receive exclusive perks as they progress to higher levels.
Many offers are limited to certain games or exclude specific titles, especially high-RTP or jackpot slots. A 15x wagering requirement on a $100 bonus means you must place $1,500 in qualifying bets before cashing out the bonus-related funds. Instead of (or in addition to) bonus cash, the site gives you a set number offree spinson selected slots at a fixed value per spin. The VIP Coinback feature is the big hook for regulars, returning a slice of weekly play as rakeback-style bonus coins that help smooth out cold sessions.
For example, “100 free spins on Starburst at $0.10 per spin,” with any winnings usually credited as bonus funds. For example, a $200 deposit on a 100% match gives you $200 cash + $200 in bonus funds. Below, you’ll find ourtop sweepstakes casinosfor bonuses, based on overall value.

There are a wide range of online casino bonuses available for players which differ between real money and sweepstakes casinos. Online casino bonuses are promotional offers that give you extra value on top of your own play, like bonus funds, free spins, or virtual coins. Real-money casino bonuses are offered by state-licensed operators to attract and retain players, and they usually come with wagering requirements and game restrictions. While there is no guarantee of making money from online casino bonuses, focusing on bonuses with favourable terms and playing strategically on high RTP games can increase your chances. Online casino bonuses are the incentives that online casinos use to entice new players to sign up and keep existing players regularly engaged with the platform.
Casinos may offer free spins to existing players and/or incorporate them into the welcome bonus for a bigger punch of rewards. However, whether they are bonus funds or free spins, they often come in small amounts. Then, the casino may offer a 50% deposit match bonus for your next three deposits. For example, a casino may offer a 100% deposit match bonus up to €500, doubling your initial deposit. They usually come with wagering requirements, either on the actual funds you receive or the winnings from them.
Until you realize they’ll only match your first $50. And it’s a very mixed picture when it comes to live dealer casino games. Different casino games count differently, too. Understanding your own playstyle and spotting the fine-print red flags will save you from wasting time and – more importantly – money.
Expect rotating featured-game promos, opt-in bonuses, and cross-product perks if you also bet on sports or play DFS under the same account. The 500 spins are released as 50 per day for 10 days after your qualifying first deposit, giving you a long runway to test the slots. That makes the offer feel more like a bridge between online play and on-property comps than a simple one-off bonus. Once you've claimed your first casino deposit bonus, you'll be a fully-fledged member of the casino.

  • In most cases, bonuses will have a cap on how much you can win using them.
  • You receive a small amount ofno deposit casino bonuscash or bonus bets just for registering and verifying your account, with no deposit required.
  • You typically can’t withdraw the bonus itself right away; you withdraw the winnings you earn from it after meeting the wagering or playthrough requirements.
  • Claiming a real-money casino bonus is usually quick, but you only get full value if you follow the rules carefully.
  • When you are chasing a high wagering requirement, you may feel pressure to bet more than you are comfortable with or to play games you don't really enjoy just to meet the requirements.
  • Lucky Bits Vegas is tailor-made for players who want to see how far they can go on freebies alone.

And even then, those games might only contribute part of your wager total. Want to see how much you’d need to wager to get your winnings? Slots usually help you out 100%, while table games often crawl along at 10–20%. You’d think all money wagered counted the same, but no. And since everyone from the best new casino to the most established brand loves a good loophole?
CrownCoins makes it easy to build a stack of coins before you ever think about buying a package. The casino also runs its own progressive jackpot network, which has produced multi-million-dollar payouts in regulated markets. Exact offers can vary by state, so always double-check the promo details and terms before you sign up. You are responsible for verifying your local laws before participating in online gambling. Stick with trusted sites, like this one, to discover safe, reliable offers.

Пін Ап казино Pin Up Casino офіційний сайт- дзеркало

Доставляємо багаторічники післяплатою поштою (Нова Пошта, Укрпошта). Ця рослина привертає увагу своєю стильною зовнішністю. Перевірка рахунку в особистому кабінеті. Весна 2026 – Ранні знижки ➨ С выигрыша удерживаются налоги

Відгуки 0 (Хоста Pin Up (Пін Ап))

Тож, здається, саме час ретельно дослідити головні переваги цього онлайн-казино, що зробили його настільки популярним серед українських гравців. Адміністрація Сайту не несе відповідальності за законність використання Сайту та його сервісів Користувачем, який не досяг 21-річного віку Підозрюваний вніс 30 млн грн до статутного капіталу компанії, що організовувала азартні ігри через Інтернет, походження яких не було підтверджено. В Україні затримано номінального власника онлайн-казино PIN-UP, яке займалося відмиванням коштів, пов'язаних з державою-агресоркою. Ліцензійне казино Пін Ап створило оптимальні умови для новачків та досвідчених геймерів.
Хоста Pin Up (Пін Ап) може бути чудовим доповненням до будь-якого саду чи інтер'єру. Її текстура та форма надають їй витонченості, а яскраво-зелений колір додає свіжості вашому садовому ландшафту чи інтер'єру. Хоста Pin Up (Пін Ап) - це неймовірно елегантна рослина з великими листками. Нарахування кешбеку відбувається після оплати. Казино Пін Ап варте уваги, зокрема за добірки гральних автоматів. 2FA для защиты аккаунта
Наприклад, нові та популярні версії винесені в окремі розділи. Клуб Пінап отримав ліцензію у квітні 2021 року. Право на видання ліцензій отримав спеціальний орган КРАІЛ. Для гравців з України 2021 рік став визначальним у плані розвитку гемблінгу. У гравця має бути лише один ігровий рахунок. Загалом відгуки Пін Ап є позитивними.
Більш детальну інформацію про використання файлів cookie можна знайти у нашій Політиці використання cookie Завдяки зручному мобільному додатку, гравці можуть насолоджуватися грою у будь-який час. Казино пропонує широкий вибір ігор, регулярні акції та вигідні бонуси, серед яких Пин Ап казино бонус. Казино Pin Up UA пропонує широкий вибір способів поповнення та виведення коштів, що робить процес гри зручним та швидким. PinUp надає велику кількість акцій та бонусів для своїх гравців.

Жіночий одяг - pin up

Тут дійсно доступні чудові колекції за тематиками і бонусами, що значно полегшує вибір. Найдорожча «Волга» ГАЗ-21 в Україні має цінник у $ та повне відновлення — фото Україна готова поділитись досвідом ППО з країнами Близького Сходу — Зеленський Як на нашу думку, переваг у Pin Up значно більше, ніж недоліків.

Live дилери на Pin Up

  • Казино пропонує широкий вибір ігор, регулярні акції та вигідні бонуси, серед яких Пин Ап казино бонус.
  • От 100 ₴ до безлимита
  • Pin Up казино — це ліцензійний ігровий майданчик, який пропонує слоти, настільні ігри та лайв продукти.
  • Про це повідомила пресслужба Офісу генерального прокурора та ДБР.
  • За перших ознак залежності зверніться до спеціальних державних органів.
  • PinUp надає велику кількість акцій та бонусів для своїх гравців.
  • Наш веб-сайт використовує Google Analytics для збору анонімної інформації, такої як кількість відвідувачів сайту та найпопулярніші сторінки.

Наш веб-сайт використовує файли cookie, щоб ми могли надати вам найкращий досвід користувача. Ми використовуємо файли cookie, щоб забезпечити вам найкращий доступ до нашого веб-сайту. У таблиці нижче представлені основні характеристики, які допоможуть вам краще зрозуміти пропозиції та умови гри. За допомогою цих кодів гравці можуть суттєво збільшити свій депозит або отримати додаткові ігрові привілеї. Від моменту реєстрації, ви отримуєте доступ до стартового пакета, який включає pin up casino бонус. Гравці можуть поповнювати рахунок, робити ставки та виводити виграші через мобільний додаток або вебверсію казино.

  • Для отримання спінів слід зробити депозит від 500 UAH.
  • Ми використовуємо файли cookie, щоб забезпечити вам найкращий доступ до нашого веб-сайту.
  • Якщо не знаєте в які слоти пограти, перейдіть у розділ “Популярні”.
  • Наш веб-сайт використовує файли cookie, щоб ми могли надати вам найкращий досвід користувача.
  • Казино співпрацює з провідними постачальниками програмного забезпечення, що гарантує високу якість ігор.
  • Наприклад, нові та популярні версії винесені в окремі розділи.

100% контроль замовлення Участь в азартних іграх може викликати ігрову залежність. Особливо сподобалася прозорість щодо гравців та високий рівень підтримки клієнтів. За три місяці використання Пін ап, я зміг переконатися в тому, що він один із найкращих у своїй категорії.
Безкоштовні версії запускаються після натискання клавіш «Демо». За перших ознак залежності зверніться до спеціальних державних органів. 🚫 ЧОРНИЙ СПИСОК — не рекомендуємо до гри Це легальне українське казино з ліцензією від КРАІЛ. Вивести кошти можна лише на банківські картки VISA та MasterCard.
На одному місці можуть рости до 10 років за умови підсипання грунту, щоб уникнути оголення кореневищ. Пропонуємо вам купити багаторічники від перевірених постачальників недорого. Вона може виглядати чудово у солітерних посадках або як частина квіткових композицій. Листя Хости Pin Up має квітковий дизайн з широкими білими смугами по краях, нагадуючи оригінальний образ пін-ап дівчат.

💳 Як поповнити Пін Ап?

🤝Работаем в Украине Наш ВВП не зовсім наш, — Дмитро Боярчук про економіку війни, податки і пенсії Як зростуть пенсії по інвалідності для I, II та III групи після індексації у березні «Питання мотивації навіть не стоїть» — як працює виробник дронів WIY Drones Як нафта і ринкові індекси відреагували на війну в Ірані
Користувачі можуть легко перемикатися між іграми, відстежувати свої ставки або перевіряти результати, при цьому не виходячи з додатка. Слід відмітити, таким похвалитися може далеко не кожне казино на ринку. У цьому розділі доступна велика кількість ігор, які управляються справжніми круп’є. Серед цих заохочень слід виділити СashBack, завдяки якому ви можете отримати до 10% від суми витрат протягом минулого тижня. У розділі “Акції” на сайті казино Pin Up вас чекають ще більше заохочень та приємних сюрпризів.
Наразі вирішується питання про обрання запобіжного заходу. Їхня діяльність на території так званої "лнр" сприяла фінансуванню силових структур держави-агресорки. Йому оголошено підозру в пособництві агресору.

От 100 ₴ до безлимита Керівник Всесвітнього економічного форуму залишає посаду через історію з Епштейном Перспективи участі TikTok у майбутніх виборах в Україні – приклад показала Румунія Коли не буде світла в Київській області 2 березня 2026 Меланія Трамп замінить у Раді безпеки ООН «сомалійських піратів» На сайті Pin Up ігрові pin up автомати презентовані у просто шаленій кількості.

У разі виявлення мультиакаунтингу, усі дублюючі облікові записи будуть заблоковані. Для цього гравцю має виповнитися не менше ніж 21 рік. 850 грн.Договірна
Мати одразу кілька облікових записів заборонено. На сайті Пін Ап вхід та реєстрація нових гравців організовані доволі просто, весь процес займає максимум 3 хвилини. Втім, сайт відрізняється не лише візуальною частиною.

Τα καλυτερα ξενα online casino 2026 10+ κορυφαία online καζίνο

Όλα τα Νόμιμα Καζίνο live στην χώρα μας έχουν αδειοδοτηθεί σε μια από αυτές τις ρυθμιστικές αρχές. Ένα καζίνο live με έδρα ένα διαμέρισμα σε ένα χωριό στην Νικαράγουα, και άδεια λειτουργίας από το Curacao, ασφαλώς και δεν είναι αξιόπιστο. Η αλήθεια είναι πως τα παιχνίδια των καζίνο live είναι σχεδιασμένα για να βγαίνουν μακροπρόθεσμα κερδισμένα. Η ποιότητα μιας εταιρείας λογισμικού συνδέεται άμεσα με το σε ποια ζωντανά καζίνο αποφασίζει να διαθέσει τα παιχνίδια της. Οι περισσότεροι από εσάς θα συμφωνήσετε πως λίγα πράγματα μπορούν να ανταγωνιστούν τα υψηλά επίπεδα αδρεναλίνης και τον ενθουσιασμό που προσφέρουν τα τηλεπαιχνίδια live καζίνο.

Νόμιμα διαδικτυακά καζίνο live στην Ελλάδα με άδεια Level Up Interactive Limited

Όσο ισχυρότεροι οι πάροχοι, τόσο πιο σταθερή, ασφαλής και ενδιαφέρουσα γίνεται η συνολική εμπειρία παιχνιδιού. Ξεχωρίζει για δημιουργικά slots όπως Esqueleto Explosivo και Pink Elephants, με υψηλή μεταβλητότητα και χαρακτηριστικό οπτικό στυλ. Παρέχει υψηλής ποιότητας streaming, επαγγελματίες dealers και premium τραπέζια για ρουλέτα, blackjack και μπακαρά. Φημίζεται για γρήγορα slots με υψηλή μεταβλητότητα και δημοφιλή live τραπέζια. Ένα αξιόπιστο online χώροι παιχνιδιού φροντίζει η τεχνολογία, η εικόνα και οι κανόνες να υποστηρίζουν αυτή τη ρεαλιστική και άμεση μορφή ψυχαγωγίας.

Ποιες Προσφορές θα Λάβουν οι 18-21 στα Ξένα Casino;

Κάνοντας κλικ στο κουμπί «Συνέχεια» για εγγραφή ή σύνδεση, αποδέχεστε τη Συμφωνία χρήστη, την Πολιτική Απορρήτου και την Πολιτική για τα Cookie του LinkedIn. Μπορείτε να ενημερώσετε τις επιλογές σας οποιαδήποτε στιγμή από τις ρυθμίσεις σας. Επιλέξτε Αποδοχή για να παράσχετε τη συναίνεσή σας ή Απόρριψη για να απορρίψετε τα μη ουσιώδη cookie για αυτήν τη χρήση. Μάθετε περισσότερα στην Πολιτική για τα Cookie μας. Γιατί πλέον σου παρέχει όλα τα πλεονεκτήματα ενός επίγειου καζίνο αφαιρώντας ταυτόχρονα όλα τα μειονεκτήματα. Τα παραπάνω επιχειρήματα είναι αδιαμφισβήτητα.

  • To Vistabet Casino live ιδρύθηκε το 2005 και προσφέρει ποικιλία τυχερών παιχνιδιών, η οποία συνεχώς ανανεώνεται, με συνεχής προσπάθεια για ικανοποίηση ακόμα και των πιο απαιτητικών μελών.
  • Πέρα από το ζωντανό kazino, δραστηριοποιείται και στο αθλητικό στοίχημα, σίγουρα κατέχει θέση στην ελίτ των στοιχηματικών, όσον αφορά το live kazino αλλά και το στοίχημα!
  • Η Εθνική Αρχή Στοιχημάτων είναι υπεύθυνη για την έκδοση αδειών και τον διαχωρισμό νόμιμων από παράνομες ιστοσελίδες.
  • Η TechSolutions έχει καταφέρει να καταξιωθεί ανάμεσα στα κορυφαία δίκτυα της αγοράς, ακόμα και αν δεν διαθέτει τον αριθμό των καζίνο των υπόλοιπων.
  • Το μπόνους καλωσορίσματος είναι ιδιαίτερα ελκυστικό, ενώ οι τακτικές προσφορές και το VIP club προσθέτουν αξία στους πιο ενεργούς παίκτες.
  • Σου δίνει τη δυνατότητα να δοκιμάσεις όποιο παιχνίδι επιθυμείς.

Ποια Θεωρούνται τα Καλύτερα Casino για Παίκτες 18+;

Μάθετε πρώτοι εδώ όλες τις εξελίξεις στα νόμιμα online καζίνο live. H dream team του Foxcasino έχει δουλέψει σε καταξιωμένους Bookmakers και καζίνο live εντός και (κυρίως) εξωτερικού τα τελευταία 12 χρόνια. Online και Live Casino συνδυάζονται σε ένα περιβάλλον που κρατά το ενδιαφέρον ζωντανό, κάθε ώρα της ημέρας. Κάθε παιχνίδι είναι επιλεγμένο με στόχο την ποιότητα, τη διασκέδαση και τη διαφάνεια, ώστε ο παίκτης να βρίσκει ακριβώς αυτό που ταιριάζει στο στιλ του.

Πώς ξεκινά κανείς σωστά σε ένα online καζίνο

  • Αντίστοιχα, μπορείτε να αυξήσετε τις πιθανότητές σας να μπείτε σε μπόνους γύρο με το feature ante bet.
  • Εμπνευσμένα από κάθε φιγούρα και κατάσταση της αρχαίας Αιγύπτου, τα φρουτάκια Φαραώ έχουν και αυτά γράψει ιστορία καθώς μπορούμε να βρούμε μερικά από τα κορυφαία παιχνίδια.
  • Να τονίσουμε πως όλα τα online casino live στην χώρα μας έχουν αδειοδοτηθεί από τον πολύ αυστηρό αρμόδιο Φορέα της χώρας μας, Ε.Ε.ΕΠ.
  • Όπως σας ενημερώσαμε πρόσφατα έλαβε την 16η άδεια (χρονικά) καταβάλλοντας εξαρχής μάλιστα όλο το απαραίτητο ποσό των 5 εκατ.
  • Η άγνοια σημαντικών στοιχείων ισχυροποιεί αυτή την πεποίθηση.
  • Yπό την άδεια της Netbet Enterprises Ltd λειτουργεί το live Netbet Casino.

Ο τομέας των μπόνους είναι πολύ σημαντικός σε κάθε καζίνο και αποτελεί ένα πολύ σημαντικό κριτήριο επιλογής μιας πλατφόρμας. Για τη δημιουργία της λίστας αυτής, εξετάσαμε μια σειρά από σημαντικά θέματα που καθορίζουν την ποιότητα των καζίνο και κάνουν τα κορυφαία να ξεχωρίζουν από τα υπόλοιπα. Μπορεί στο cashier να βλέπετε ένα ποσό συγκεκριμένο σε κάθε μέθοδο πληρωμής, ωστόσο δεν είναι και στην πραγματικότητα αυτό το οποίο θα μπορείτε να λάβετε σε κάθε ανάληψή σας.
Προσφέρει νόμιμα τις υπηρεσίες της στην Ελλάδα υπό την άδεια της Gamart Ltd. Από τα μεγάλα όπλα του είναι η ποικιλία και η ποιότητα των παιχνιδιών. Το Stoiximan Casino live λειτουργεί  νόμιμα στην Ελλάδα υπό την Stoiximan Limited και αποτελεί τη συνέχεια του Incasino. Σε εφαρμογή, από τον Ιούλιο του 2021, τέθηκε το ισχύον νομικό πλαίσιο που αφορά τις εταιρείες που προσφέρουν υπηρεσίες online καζίνο live και στοιχηματισμού. Προτού σας τα παρουσιάσουμε αναλυτικά, θα πρέπει πρώτα να αναφερθούμε συνοπτικά στο νομοθετικό πλαίσιο που ισχύει και στο πως καταλαβαίνουμε αν ένα καζινο live είναι νόμιμο, καθώς είναι πολύ σημαντικό για τα χρήματά σας. Ο οργανισμός μας είναι δωρεάν και έχει ως κύριο στόχο τη μετάδοση διαφανούς και ακριβούς πληροφόρησης, ώστε να λαμβάνετε οικονομικές αποφάσεις με αυτοπεποίθηση.

Συνεργασίες με γνωστές εταιρείες σημαίνει και ύπαρξη ποιοτικών και δημοφιλών παιχνιδιών που αποτελούν πόλο έλξης για τους παίκτες που θέλουν τα καλύτερα ξένα online casino. Για να επιλέξετε το καλύτερο ξένο καζίνο το οποίο σας ταιριάζει πιο πολύ από όλα τα άλλα, μπορείτε χωρίς δισταγμό να συμβουλευτείτε τη λίστα μας.. Στον πίνακα που ακολουθεί θα https://www.monster-win.org/gr/ δούμε ποια είναι τα κύρια χαρακτηριστικά που έχουν ορισμένες από τις πιο δημοφιλείς άδειες και τι προσφέρουν στα καζίνο που τις επιλέγουν. Κάθε μια από αυτές έχει τα δικά της χαρακτηριστικά και τους δικούς της κανόνες με τους οποίους πρέπει να συμμορφώνονται τα καζίνο.
Το blackjack έχει μία πολύ σημαντική διαφορά από τα υπόλοιπα παιχνίδια καζίνο live. Επιπλέον, μην ξεχνάτε, πως στο casino live blog θα έχετε την ευκαιρία να διαβάσετε πολύ ενδιαφέροντα άρθρα από τους έμπειρους blogger μας για τα θέματα που απασχολούν εμάς τους παίχτες. Και φυσικά για όποια καινούργια online casino live ξεκινήσουν να λειτουργούν στη χώρα μας. Το χαρακτηριστικό του Betsson casino live, είναι οι συνεχείς νέες προσθήκες όσον αφορά τα παιχνίδια που προσφέρει. Στο live καζίνο θα βρεις δεκάδες τραπέζια με το ελάχιστο ποντάρισμα να είναι μόλις 0,1€ τόσο σε ρουλέτα όσο και σε διάφορα τηλεπαιχνίδια και τροχούς.
Συγκαταλέγεται στα καλύτερα καζίνο live και προσφέρει τεράστια γκάμα παιχνιδιών. Ευρώ για τις άδειες τύπου 1 και 2 πράγμα που θα της επιτρέψει να διαθέσει υπηρεσίες στοιχηματισμού και πονταρίσματος σε τυχερά παιχνίδια στο ελληνικό κοινό. Η Βουλγαρική εταιρεία πίσω από το Efbet live casino ξεκίνησε να δραστηριοποιείται στη Μάλτα και την Βουλγαρία το 2006 και 2011 αντίστοιχα. Το Pokerstars live casino έκανε την είσοδο του το 2015 και λειτουργεί 100% νόμιμα υπό την άδεια της Diamond Link Limited. Mε τόσα ζωντανά παιχνίδια που διαθέτει (200+), μεταξύ αυτών τα δημοφιλή των Novomatic, EGT και Synot, αποκλείεται να αφήσει ανικανοποίητο ακόμα και το τελευταίο της μέλος.

Τα πιο δημοφιλή παιχνίδια στα online live casino

Είναι πολλοί οι λόγοι για να παίξετε σε ένα casino online live, ωστόσο υπάρχει μία σειρά από ερωτήματα που πρέπει να απαντηθούν πριν ξεκινήσετε το παιχνίδι. Τα άρθρα μας είναι αποτέλεσμα, όχι μόνο έρευνας, αλλά συνδυασμός προσωπικής εμπειρίας προερχόμενης από τα "μάτια" των online casino live αλλά και ενός παίκτη. Τέλος, το Betsson online casino live  αποτελεί εξαιρετική επιλογή και για sportsbook, όχι μόνο για την ελληνική αγορά αφού είναι μία από τις κορυφαίες εταιρείες σε παγκόσμια κλίμακα.

Elasticsearch Query DsL查询


Query DsL查询

一 Elasticsearch简介

Elasticsearch 是一个开源的搜索引擎,Elasticsearch 使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,但是它的目的是使全文检索变得简单, 通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。

  • 一个分布式的实时文档存储,每个字段 可以被索引与搜索
  • 一个分布式实时分析搜索引擎
  • 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据

 

二 安装并运行

已经在其他文档中详细介绍,此次仅做简单步骤介绍

# 安装:
$   wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.13.3-linux-x86_64.tar.gz
$   tar -xzf elasticsearch-7.13.3-linux-x86_64.tar.gz 
$   cd elasticsearch-7.13.3/
# 运行
sh bin/elasticsearch

# 访问
$ curl http://192.168.3.14:9200/ 
{
  "name" : "87DNZWU",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "e3A3l85MSZuZlRhxj6IB2w",
  "version" : {
    "number" : "6.7.0",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "8453f77",
    "build_date" : "2019-03-21T15:32:29.844721Z",
    "build_snapshot" : false,
    "lucene_version" : "7.7.0",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

 

三 Query DSL 基本结构

查询表达式(Query DSL)是一种非常灵活又富有表现力的查询语言, Elasticsearch 使用它可以以简单的 JSON 接口来展现 Lucene 功能的绝大部分

// 查询
GET /_search  // 查找整个ES中所有索引的内容
{
  "query": {}, //具体的查询语句对象
  "from": 0,   //从第几条数据开始返回
  "size": 100, //返回的条数 默认ES最多返回10000条
  "highlight": { //高亮
    "pre_tags": {}, //高亮内容的前面标签 一般都是html比如<b> <p>这种
    "post_tags": {},//高亮内容的后面标签 一般都是html比如</b> </p>这种
    "fields": { //需要高亮的字段
    }
  },
  "sort": [{ //排序
    "FIELD": { //排序的字段(需要填上具体的字段名)
      "order": "desc"
    }
  }],
  "_source": "{field}" //指定返回的字段
}

// 结果
{
    "took": 350,  // 整个搜索请求消耗了多少毫秒
    "timed_out": false, // 表示本次查询是否超时,如果为true也会返回结果,只是数据可能不完整
    "_shards": { //  显示查询中参与的分片信息,成功多少分片失败多少分片等
        "total": 5, 
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 5, // 匹配到的文档总数
        "max_score": 1, // 为文档中所有_score的最大值
        "hits": [
            {
                "_index": "mysql-shop_trades-order_item_label_binds",
                "_type": "doc",
                "_id": "591935",
                "_score": 1,
                "_source": {
                    "id": 591935,
                    "updated_at": "2021-05-20T06:26:09.000Z",
                    "@version": "1",
                    "bind_item_label_id": 729,
                    "label_type": "brand",
                    "created_at": "2021-05-20T06:26:09.000Z",
                    "@timestamp": "2021-07-07T07:31:36.262Z",
                    "is_deleted": 0,
                    "table_name": "order_item_label_binds",
                    "bar_code": "6907925004486"
                }
            }
        ]
    }
}

 

四 指定索引搜索

 

上述查询会搜索ES中的所有索引,但通常只需要去固定一个或几个索引中搜索,搜索全部无疑会造成资源的浪费,在ES中可以通过以下几种方法来指定索引

  1. 指定一个固定的索引,ops-coffee-nginx-2019.05.15为索引名字
GET /mysql-shop_trades-order_statics/_search

以上表示在mysql-shop_trades-order_statics索引下查找数据

  1. 指定多个固定索引,多个索引名字用逗号分割
GET /mysql-shop_trades-order_statics,mysql-shop_trades-order_item_labels/_search

  1. 用*号匹配,在匹配到的所有索引下查找数据
GET /mysql-shop_trades-*/_search

这里也可以用逗号分割多个匹配索引

五 DSL查询

1、筛选字段

// 筛选_source的数据,单个字段
GET /_search
{
  "_source": "bar_code",  
  "query": {}
}

// 筛选_source的数据,多个字段
{
  "_source": {
    "includes": ["store_id", "sku_id"]
  },
  "query": {}
}

// 对字段进行转换
{
  "docvalue_fields": [
    {
      "field": "updated_at",
      "format": "yyyy-MM-dd HH:mm:ss"
    },
    {
      "field": "num",
      "format": "long" // 没有作用,懵逼...
    }
  ], 
  "query": {}
}

 

2、多条件查询 (where)

  1. constant_score:装另一个查询的查询,固定分数查询,支持filter查询,不支持match查询:
    {
        "constant_score": {
            "filter": {
                "match": {
                    "name": "小米"
                }
            },
            "boost": 10
        }
    }
    

     

  2. bool:主要与其他关键字组合使用,多条件的查询必须要用bool包在外层,然后再根据具体的业务来拼接。

{
  "query": {
    "bool": {
      "should": [{}], //满足其中一个对象查询条件就行 像sql里的or
      "must": [{}],   //必须满足所有对象的查询条件 就像sql里的and
      "must_not": [{}] //必须不满足所有对象的查询条件 就像sql里的and !=
    }
  }
}

 

  1. must: 类似于SQL中的AND,必须包含
  2. must_not: 类似于SQL中的NOT,必须不包含
  3. should: 满足这些条件中的任何条件都会增加评分_score,不满足也不影响,should只会影响查询结果的_score值,并不会影响结果的内容
  4. filter: 与must相似,但不会对结果进行相关性评分_score,大多数情况下我们对于日志的需求都无相关性的要求,所以建议查询的过程中多用filter

3、group by:

ES本身没有group关键词搜索,但支持聚合查询,,需要使用关键字aggs

// 单个字段 group by
{
  "query":{},//这里省略你的查询条件
  "aggs": {
    "age_group": {//这个是指你要返回字段名
      "terms": { //这里还可以用其它关键词 这里terms才能实现group by效果
        "field": "age",//groupby的字段
        "size":1 //返回的条数 相当于group by limit
      }
    }
  }
}

// 多字段group by (如 group by sku_id,store_id)
// 方法一:script
{
  "query":{},
  "aggs": {
    "age_group": {
      "terms": {
        "script":{
          "source": """ 's' + doc['store_id'] + '_s' + doc['sku_id'] """,
          "lang": "painless"
        },
        "size": 10
      }
    }
  }
}

// 方法二:copy to
1. 设置mapping中的多个字段,copy_to 为同一个字段(skuId_storeId)
2. 搜索新字段
{
  "query":{},
  "aggs": {
    "list": {
      "terms": { 
        "field": "skuId_storeId
        "size":1
      }
    }
  }
}

// 方法三:multi_terms (使用高版本,目前6.7不支持)
{
  "aggs": {
    "genres_and_products": {
      "multi_terms": {
        "terms": [{
          "field": "genre" 
        }, {
          "field": "product"
        }]
      }
    }
  }
}

4、order by

order by:注意日期格式和数值格式才支持排序;文本不支持,如果要排序, 需把字段设置为not analysis

// 单排序
{
    "query": {
        "sort": {
            "id": "desc"
        }
    }
}

// avg按照平均值排序
{
    "query": {
        "sort": [
            {
                "id": "desc"
            },
            {
                "price": {
                    "order": "asc",
                    "mode": "avg"
                }
            }
        ]
    }
}

5、count(distinct)

{
  "query":{},
  "aggs": {
    "total_sku_id": {
      "cardinality":{ "field": "sku_id"}
    },
     "total_entity_store_id": { // 非数字类型,无法使用field排序,可以对field增加fieldData = true,或者对field.keyword排序,建议使用后者,高效内存消耗低
      "cardinality":{ "field": "entity_store_id.keyword"}
    }
  }
}

 

6、SUM

{
  "query":{},
  "aggs": {
    "total_pay_num": {
      "sum": {"field": "num"}
    },
     "total_cost_fee": {
      "sum": {"field": "cost_fee"}
    }
  }
}

 

7、distinct :

select distinct(id) from table

{
  "query":{},
  "collapse": {
      "field": "id" //你需要distinct的字段
   }, 
}

 

8、limit

1. 分页:
    1. form:从第几个开始查询,最开始是0
    2. size:即limit
    3. 使用size,size最大可获取数量是xx个
2. 获取所有数据的三种方式
    1. scroll深度滚动需要根据scroll_id和循环取,取完后,需删除scroll,减小内存开销(深度滚动高效,用于处理大量数据,不适合实时获取)
    2.调整索引index.max_result_window的大小,默认10000 (大小与堆内存成正比,这个限制内存)
    3.search_after:请求需增加前一个结果的排序,(实时游标,可根据索引更新和删除而改变 )
    4。 如果是group by查询获取所有数据, 获取需要使用到cardinality查询预估总数,再使用partition、num_partitions分区依次获取数据

9、搜索关键字

  1. match:自定字段,根据字段关键字进行搜索,会分割关键词,匹配到含有一个多多个词的匹配
  2. query_string:全文搜索
  3. match_phrase:不分割关键词 {"match_phrase": {"name":"婴幼儿奶粉"}}
  4. term: 类似SQL where field = x,主要用于数字匹配;如果要匹配文本,会自动分词,不能精准查询,需把字段设置成not analysis
    {
      "query": {
        "term": {"bind_item_label_id": 729}
      }
    }
    

     

  5. terms: 类似SQL where field in (x,x),主要用于数字匹配,
    {
      "query": {
        "terms": {"bind_item_label_id": [703,729]}
      }
    }
    

     

  6. range:: 查询价格在1000-2000的商品
{
    "query": {
        "range": {
            "price": {
                "gte": 1000,
                "lte": 2000
            }
        }
    }
}

 

  1. filter:判断文档是否满足条件
{
    "query": {
        "bool": {
            "filter": {
                "term": {
                    "price": 1999
                }
            }
        }
    }
}

Elasticsearch:Aggregation

  1. metric:度量聚合,主要针对number类型的数据,需要es做较多的计算工作(类似SQL的SUM、MAX、AVG、MIN、Cardinality、stats<属于多值分析>等)
  2. bucket:桶聚合,划分不同步的桶,将数据分配到不同的桶,(类似SQL中的group by)
  3. Pipeline Aggregation:管道分析类型,对其他聚合结果进行二次聚合
  4. Matrix Aggregation:矩阵分析类型,支持对多个字段的操作并提供一个结果矩阵

term aggregation

  • size 可以通过size返回top size的文档,该术语聚合针对顶层术语(不包含嵌套词根),其搜索过程是将请求向所有分点发送请求,每个分片节点返回size条数据,然后聚合所有分片的结果(会对各分片返回的同样词根的数数值进行相加),最终从中挑选size条记录返回给客户端。从这个过程也可以看出,其结果并不是准确的,而是一个近似值。
  • Shard Size 为了提高该聚合的精确度,可以通过shard_size参数设置协调节点向各个分片请求的词根个数,然后在协调节点进行聚合,最后只返回size个词根给到客户端,shard_size >= size,如果shard_size设置小于size,ES会自动将其设置为size,默认情况下shard_size建议设置为(1.5 * size + 10)。
// 单个字段 group by
{
  "query":{},//这里省略你的查询条件
  "aggs": {
    "age_group": {//这个是指你要返回字段名
      "terms": { //这里还可以用其它关键词 这里terms才能实现group by效果
        "field": "age",//groupby的字段
        "size":1 //返回的条数 相当于group by limit
      }
    }
  }
}

// 返回结果格式
{
  ...
  "aggregations" : {
      "list" : {
        "doc_count_error_upper_bound" : 0, // 该值表示未进入最终术语列表的术语的最大潜在文档计数
        "sum_other_doc_count" : 90 // 该值表示未进入最终术语列表的术语的最大潜在文档计数
        "buckets" : [ // 返回doc_count排名最前的10个,受size参数的影响
          {
            "key" : "1",
            "doc_count" : 24,
            "total_refund_fee" : {
              "value" : 0.0
            },
            "total_cost_fee" : {
              "value" : -14976.0
            },
          }
        ]
      }
    }
    }
}

 

 

match_phrase :查询分析文本,创建词组查询

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query-phrase.html#query-dsl-match-query-phrase

 

 

举例子

GET mysql-shop_trades-order_item_label_binds/_search/?scroll=1m
{
  "docvalue_fields": [
    {
      "field": "updated_at",
      "format": "yyyy-MM-dd HH:mm:ss"
    }
  ], 
  "size": 1000,
  "sort": {"id":"desc"},
  "query": {
    "bool": {
      "must": [
        {"match": {"is_deleted": 0}},
        {"match": {"label_type": "brand"}},
        {
          "constant_score": {
            "filter": {
              "terms": {
                "bind_item_label_id": [703, 2, 729]
                }
              }
            }
        }
      ]
    }
    }
}




GET mysql-shop_trades-order_item_label_binds/_search/?scroll=1m
{
  "_source": "bar_code", 
  "query": {
    "bool": {
      "filter": [
        {"match": {"is_deleted": 0}},
        {"match_phrase": {"label_type": "brand"}},
        {"terms": {"bind_item_label_id": [703, 2, 729]}}
      ]
    }
  },
  "aggs": {
    "bar_code_group": {
      "terms": {
        "field": "bar_code.keyword",
        "size": 10 
      }
    }
  }
}


GET mysql-shop_trades-order_item_label_binds,mysql-shop_trades-order_statics/_search
{
  "query": {
    "bool": {
      "filter": [
        {"match_phrase": {"sys_name": "yiqigou"}},
        {"range": {"num": {"lte": 2000}}},
        {"range": {"return_num": {"gte": -1000}}},
        {"range": {"total_price": {"lte": 1000000}}},
          {"match": {"id": 60}},
        {"term": {"order_type": 0}},
        {"term": {"item_type": 0}},
        {"range": {"date": {
          "gte": "2020-01-21 00:00:00",
          "lte": "2021-07-22 00:00:00",
          "format": "yyyy-MM-dd HH:mm:ss",
          "time_zone": "+08:00"
        }}}
      ],
      "must_not": [
        {"terms": {"store_id": [165]}}
      ]
    }
  }
}

 

 

设置fieldData

// 第一步,创建索引 (如果已经有索引,直接看第二步)
PUT mysql-shop_trades-order_statics2
{
  "mappings": {
    "_doc": {
      "properties": {
        "entity_store_id": { 
          "type": "text",
          "fields": {
            "keyword": { 
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

// 第二步 设置fieldData为true
PUT mysql-shop_trades-order_statics/_mapping/_doc
{
  "properties": {
    "entity_store_id": {
      "type":     "text",
      "fielddata": true
    }
  }
}

// 第三步 可以查看该索引的Mapping结构,fieldData是否加上去
{
  "mapping": {
    "doc": {
      "properties": {
        "entity_store_id": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          },
          "fielddata": true
        }
      }
    }
  }
}

延伸

 

 

设置 max_result_window

PUT /mysql-shop_trades-order_statics/_settings
{
  "index": {
    "max_result_window": 100000
  }
}

 

Elasticsearch SQL

介绍

ES SQL 是x-pack的一个组件,它提供了一个到Elasticsearch的SQL接口,可以针对ES索引执行实时的SQL查询,轻松实时大规模的查询和处理数据,并以表格格式返回结果。它相当于一种翻译器,使用将sql语句转换为DSL语句,再搜索elasticsearch数据

SQL与Elasticsearch的映射关系

(只列常用的)

在 Elasticsearch 中,可用的索引集被分组在一个cluster,一个实例只有一个目录 |

SELECT语法

​ 同sql语法基本一致,基本所有的sql语法都支持

# select
SELECT [TOP [ count ] ] select_expr [, ...]
[ FROM table_name ]
[ WHERE condition ]
[ GROUP BY grouping_element [, ...] ]
[ HAVING condition]
[ ORDER BY expression [ ASC | DESC ] [, ...] ]
[ LIMIT [ count ] ]
[ PIVOT ( aggregation_expr FOR column IN ( value [ [ AS ] alias ] [, ...] ) ) ]

注意:

  1. FROM :目前只支持一张表,不支持连表查询,table_name可以是一个索引,也可以是一个模式(子语句、正则匹配的索引)
  2. WHERE: 从查询中过滤行,如果指定了子句,所有不满足条件的都将从输出中删除
  3. GROUP BY:如果指定了子句或者存在聚合函数调用,则输出将组合成匹配一个或多个值的行组,并计算聚合函数的结果,如果该HAVING子句存在,它将消除不满足给定条件的组
  4. 使用SELECT每个选定行或行组的输出表达式计算实际输出行,可以使用通配符*返回所有的列
  5. 如果ORDER BY指定了子句,则返回的行按指定的顺序排序。如果ORDER BY未给出,则以系统发现最快生成的任何顺序返回行
  6. 如果指定了LIMITor TOP(不能在同一个查询中同时使用两者),则该SELECT语句仅返回结果行的一个子集

缺点:

  1. 不支持复杂的sql: select count(distinct(field1, field2)),可以使用group by替代
  2. 但不支持连表查询,需分表查询

ES SQL 执行方式

  1. 在kabana console 面板中执行sql语句(经常用于调试)
// 方式一:直接搜索sql
GET _sql?format=json
{
"query": """
SELECT *  FROM "mysql-shop_trades-order_statics" where store_id = 165
"""
}

// 方式二:先将sql转换为DSL语句,再通过DSL语句查询结果,这样得到的数据格式比较清晰
// sql转换成DSL语句
GET _sql/translate
{
"query": """
SELECT *  FROM "mysql-shop_trades-order_statics" where store_id = 165
"""
}

// 使用DSL语句查询

GET /mysql-shop_trades-order_statics/_search
{
"size" : 100,
"query" : {
"term" : {
"store_id" : {
"value" : 165,
"boost" : 1.0
}
}
},
"sort" : [
{
"_doc" : {
"order" : "asc"
}
}
]
}

上述是查询一个简单的数据

  1. format:返回数据的格式,支持csv、json、tsv、text、yaml、cbor、smile
  2. sql搜索, 不支持 - .等特殊字符,因此需要转移,包裹sql使用""" 会自动转移特殊字符
  3. 在客户端使用SQL CLI执行
$ ./bin/elasticsearch-sql-cli http://192.168.3.53:9200
sql> select id from "mysql-shop_trades-order_statics" order by id asc limit 2;
  1. 使用 elasticsearch-PHP插件执行 (在业务中使用)
// 读取sql语句,不执行
$where = [
'is_deleted' => 0,
'label_type' => $type,
];
$this->dblink_trade_slave->select('bar_code')->where($where);
if ($is_having) {
$this->dblink_trade_slave->having($having);
}
if ($label_ids) {
$this->dblink_trade_slave->where_in('bind_item_label_id', $label_ids);
}
$sql = $this->dblink_trade_slave->limit(1)->get_compiled_select($index);

// 使用es搜索
$index = '"mysql-shop_trades-order_item_label_binds"'; // 索引需要加引号来转义特殊字符
$params = [
'body' => [
'query' => $sql,
'fetch_size' => 20 // 返回数据条数
]
];
$result = EsClient::sql($params);
// todo 处理结果

Elasticsearch6.3+后,开始支持SQL查询语言,但6.7之前SQL都是实验性质的,6.6进入beta特性,6.7后官方正常正式支持,因此elasticsearch-php 7.0+版本才支持查询x-pack sql,低版本不支持sql查询

需服务器手动安装elasticsearch-sql插件

./bin/elasticsearch-plugin install https://github.com/NLPchina/elasticsearch-sql/releases/download/6.7.0.0/elasticsearch-sql-6.7.0.0.zip

[Logstash] Jdbc input plugin


Jdbc input 插件

一 简介

目的:

需要定时将大量数据从mysql采集到ElasticSearch,这里使用Logstash作为数据采集器,使用Jdbc input plugin插件来提取数据到logstash

Jdbc input plugin作用

  • 将具有JDBC接口的任何数据库中的数据提取到Logstash中
  • 可以使用cront语法定期查询或者一次性的将数据加载到Logstash中
  • 结果集中的每一行都成为一个事件,结果集中的列被转换为事件中的字段

 

二 安装

是jdcb集成插件的一个组件,只需安装logstash即可,由于此插件没有和JDBC驱动程序库一起打包,因此需要使用jdbc_driver_library配置选项将所有的jdbc驱动程序传递给插件

环境要求:

  1. elasticsearch-7.13.2
  2. kibana-7.13.2-darwin-x86_64
  3. logstash-7.13.2
  4. logstash-input-jdbc 插件
  5. mysql-connector-java-8.0.25 (驱动)
  6. mysql数据库

 

三 使用

用法

编写logstash-mysql.conf

input{
     jdbc {
        # 驱动类名
        jdbc_driver_library => "/Users/www/elk/7.13.2/mysql-connector-java-8.0.25/mysql-connector-java-8.0.25.jar"
        jdbc_driver_class => "com.mysql.cj.jdbc.Driver" 
        jdbc_default_timezone => "Asia/Shanghai"
         # mysql 数据库链接,shop_trades为数据库名,zeroDateTimeBehaviro防止因时间格式为00-00导致报错
         jdbc_connection_string => "jdbc:mysql://192.168.3.53:3355/shop_trades?zeroDateTimeBehaviro=convertToNull"
         # 连接数据库用户名
         jdbc_user => "cishop"
         # 连接数据库密码
         jdbc_password => "*****"
         # 是否启用分页读取
         jdbc_paging_enabled => "true"
         jdbc_page_size => "1000" 
         # 设置监听间隔  各字段含义(由左至右) 分、时、天、月、年,全部为*默认含义为每分钟都更新
         schedule => "* * * * *"
         # 是否记录上次执行的结果
         record_last_run => "true"
        # 使用其它字段追踪,而不是用时间
         use_column_value => "true"
         tracking_column => "id"
         last_run_metadata_path => "/Users/www/elk/7.13.2/logstash-7.13.2/log-record/order_statics.txt"
         # 是否清除 last_run_metadata_path 的记录,如果为真那么每次都相当于从头开始查询所有的数据库记录
         clean_run => false
         # 直接写sql语句用这个
         statement => "SELECT * FROM `leiyuan` WHERE id > :sql_last_value"
         type => "order_statics" # 如果数据库字段含有type,此数据会被替换掉,不建议这么使用
         add_field => {table_name => "order_statics"}
       }
}
filter {
  json {
    source => "message"
    remove_field => ["message"]
  }
}

output {
  # stdout {codec => rubydebug }
  stdout {codec => json_lines }
  elasticsearch {
    hosts => ["http://192.168.3.132:9200"]
    index => "mysql-shop_trades-order_statics"
    document_id => "%{id}"
  }
}

执行单个配置文件

# 校验是否有语法错误
bin/logstash -f config/logstash-mysql.conf --config.test_and_exit // 第一次校验配置是否正确

# 执行
bin/logstash -f config/logstash-mysql.conf

# 启动多个logstash文件:需要配置pipelines.yml

 

对多配置文件的引用


[root@VM235 config]# less pipelines.yml |grep -v "#"

 - pipeline.id: mysql
   pipeline.workers: 1
   pipeline.batch.size: 125
   path.config: "/opt/ci123/elk/logstash-6.7.0/config/config.d/mysql.d/*.conf"

 - pipeline.id: application
   pipeline.workers: 1
   pipeline.batch.size: 125
   path.config: "/opt/ci123/elk/logstash-6.7.0/config/config.d/application.conf"
   

 

# 校验
1. 开启config/logstash.yml中的 config.test_and_exit: true
2. bin/logstash
# (画外音:pipelines.yml启动不成功,或找不到pipelines.yml时,很有可能是语法错误,yml对语法邀请非常严格,需仔细检查)

# 启动
bin/logstash  --config.reload.automatic

 

显示以下结果说明,数据正在通过定时脚本导入

[2021-07-05T16:43:00,041][INFO ][logstash.inputs.jdbc     ][main][leiyuan] (0.001561s) SELECT * FROM `leiyuan` WHERE id > 0
{"@timestamp":"2021-07-05T08:43:00.213Z","dated":"2021-07-05T01:28:30.000Z","@version":"1","type":"jdbc","name":"第一名","id":1}
[2021-07-05T16:44:00,063][INFO ][logstash.inputs.jdbc     ][main][leiyuan] (0.002067s) SELECT * FROM `leiyuan` WHERE id > 1
[2021-07-05T16:45:00,066][INFO ][logstash.inputs.jdbc     ][main][leiyuan] (0.001203s) 
{"@timestamp":"2021-07-05T08:52:00.286Z","dated":"2021-07-03T23:19:18.000Z","@version":"1","type":"jdbc","name":"第二名","id":2}
{"@timestamp":"2021-07-05T08:52:00.299Z","dated":"2021-06-30T23:15:00.000Z","@version":"1","type":"jdbc","name":"第三名","id":3}
[2021-07-05T16:53:00,312][INFO ][logstash.inputs.jdbc     ][main][leiyuan] (0.002697s) SELECT * FROM `leiyuan` WHERE id > 3
[2021-07-05T16:53:00,312][INFO ][logstash.inputs.jdbc     ][main][leiyuan] (0.002697s) SELECT * FROM `leiyuan` WHERE id > 3

 

 

 


其他文件:

ELK安装使用手册


elk安装

一、为什么使用elk

一般大型系统是一个分布式部署的架构,不同的服务模块在不同的服务器上,出问题时,需要根据问题暴露的关键信息定位到具体的服务器和模块,构建一套集中式的日志系统,可以提高定位问题的效率
很多时候对于业务关键逻辑,通过file_put_content存储请求数据或者debug,数据量大的时候,对服务器和访问性能都具有不小的影响
一个完整的集中式日志系统包含的主要特点:

  • 收集 - 能够采集多种来源的日志数据
  • 传输 - 能够稳定的吧日志数据传输到中央系统
  • 存储 - 如何存储日志数据
  • 分析 - 可以支持UI分析
  • 警告 - 能够提供错误报告、监控机制

二、elk介绍

elk是三个开源软件的缩写:Elasticsearch , Logstash, Kibana

  • elasticsearch: 开源的日志搜索引擎,可搜集、分析、存储数据
  • kibana:web面板,可以为logstash和els提供的日志分析展示在web界面,
  • logstash:日志的搜索、分析、过滤日志的工具,支持大量的数据获取方式,工作方式是c/s架构,client端安装在需要收集日志的主机上,service端负责收集各个节点日志,并对日志进行过滤和修改等操作,并将其发往elasticsearch上
  • FileBeat:轻量级的日志收集处理工具,使用具在各个服务器上搜集日志后传输给logstash
  • filebeat数据beat,目前beat包含四种工具:
    • Packetbeat(搜集网络流量数据)
    • Topbeat (搜集系统、进程和文件系统级别低的CPU和内存使用情况等数据)
    • filebeat (搜集文件数据)
    • Winlogbeat (搜集windows事件日志数据)

一、安装elasticsearch

# 安装:
$   wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.13.3-linux-x86_64.tar.gz
$   wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.13.3-linux-x86_64.tar.gz.sha512
$   shasum -a 512 -c elasticsearch-7.13.3-linux-x86_64.tar.gz.sha512 
$   tar -xzf elasticsearch-7.13.3-linux-x86_64.tar.gz 
$   cd elasticsearch-7.13.3/ 
 

# 创建es用户 (不能使用root权限执行)
# 1、创建用户:elasticsearch
$ adduser elasticsearch
# 2、创建用户密码,需要输入两次
$ passwd elasticsearch
#3、将对应的文件夹权限赋给该用户
$ chown -R elasticsearch elasticsearch-7.17.3
#4、切换至elasticsearch用户
$ su elasticsearch

 
# config配置
$ less elasticsearch.yml  |grep '#' -v
network.host: 192.168.3.14
http.port: 9200
discovery.seed_hosts: ["192.168.3.14"]
cluster.initial_master_nodes: ["node-1"]
bootstrap.memory_lock: false  # 因运行时报错bootstrap checks failed,此处开启修复这个报错
bootstrap.system_call_filter: false # 因运行时报错bootstrap checks failed,此处开启修复这个报错
# xpack.security.transport.ssl.enabled: true # 开启安全验证,需要设置账号密码时可以开启
# xpack.security.enabled: true  # 开启安全验证,需要设置账号密码时可以开启

# 配置内存大小 (根据服务器内存大小设置适当的值)
$ less ./elasticsearch-7.13.3/config/jvm.options |grep '#' -v
-Xms4g
-Xmx4g
...


# 运行
$   su elasticsearch
$   sh bin/elasticsearch

# 后台运行
$ su elasticsearch
$ nohup bin/elasticsearch &

# 访问请求是否正常启动 (出现下列信息,则正常启动)
$ curl http://192.168.3.14:9200/ 
{
  "name" : "87DNZWU",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "e3A3l85MSZuZlRhxj6IB2w",
  "version" : {
    "number" : "6.7.0",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "8453f77",
    "build_date" : "2019-03-21T15:32:29.844721Z",
    "build_snapshot" : false,
    "lucene_version" : "7.7.0",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

二、安装kibana

# 安装
$   curl -O https://artifacts.elastic.co/downloads/kibana/kibana-7.13.3-linux-x86_64.tar.gz
$   curl https://artifacts.elastic.co/downloads/kibana/kibana-7.13.3-linux-x86_64.tar.gz.sha512 | shasum -a 512 -c - 
$   tar -xzf kibana-7.13.3-linux-x86_64.tar.gz
$   mv  kibana-7.13.3-linux-x86_64/  kibana-7.13.3/ 


# config配置 其中xpack 需要执行 (bin/kibana-encryption-keys generate)
$ less kibana.yml |grep '#' -v
server.port: 5602
server.host: "192.168.3.14"
elasticsearch.hosts: ["http://192.168.3.14:9200"]
i18n.locale: "zh-CN"

xpack.encryptedSavedObjects.encryptionKey: 58d5e678bf21278edeed84433f905663
xpack.reporting.encryptionKey: d0b608215432fc28ab1b17ed3906c95a
xpack.security.encryptionKey: 59819c05503d1364e3ec17c34839e6a1

monitoring.cluster_alerts.email_notifications.email_address: leiyuan@corp-ci.com

xpack.reporting.capture.browser.chromium.disableSandbox: true


# 运行
$   sh bin/kibana

# 后台运行
$   nohup bin/kibana &

# 查看是否正常启动
# 网页访问 http://192.168.3.53:5601/, 如果正常访问,则启动成功 

kibana 启动报错ersion GLIBC_2.14 not found 
https://blog.csdn.net/xinjing2015/article/details/93746179

三、安装JAVA

# 官方包内有自带的java环境,可以不用安装,如果想要自己配置的java环境,可以按照下放操作 (根据官方文档按照相应版本的java)
$ yum install java-1.8.0-openjdk.x86_64
$ java -version
openjdk version "1.8.0_275"
OpenJDK Runtime Environment (build 1.8.0_275-b01)


四、安装logstash

# 下载 mysql-connector-java (用于连接数据库)
$   wget  http://search.maven.org/remotecontent?filepath=mysql/mysql-\
connector-java/5.1.32/mysql-connector-java-5.1.32.jar


# 官网下载安装包
$   wget https://artifacts.elastic.co/downloads/logstash/logstash-7.13.3-linux-x86_64.tar.gz
$ tar -xzf logstash-7.13.3-linux-x86_64.tar.gz  logstash-7.13.3



# 查询是否可以正常使用  (方式一)
$ bin/logstash -e 'input { stdin {} } output { stdout {} }'
#(画外音:选项 -e 的意思是允许你从命令行指定配置)
# 启动后 输入hello world,可返回json数据,即启动成功



# 接受redis数据   (方法二)
$ less application.conf  |grep -v '#'
input {
    redis {
        data_type => "list"
        key => "logstash-list"
        host => "192.168.3.53"
        port => 8003
        threads => 5
    }
}
output {
  elasticsearch {
    hosts => ["http://192.168.3.53:9200"]
    index => "application_log_%{+YYYY.MM.dd}"
  }
}

# 接受filebeat数据 (暂时不写,自行查询)   (方式三)
$   less  logstash-sample.conf | grep  -v '#'
input {
  beats {
    port => 5044
  }
}

output {
    stdout {codec => rubydebug } # 输出到页面
    elasticsearch {
        hosts => ["http://192.168.3.53:9200"] # 存储的elasticsearch
        index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
  }
}

# 接受mysq 数据库数据 (具体写在Mysql>ElasticSearch.md文档中) (方式四)



# 启动单个配置
$ bin/logstash -f config/logstash-sample.conf --config.test_and_exit // 第一次校验配置是否正确
$ bin/logstash -e 'input { stdin { } } output { stdout {} }' // 普通输出
$ bin/logstash -f config/logstash-sample.conf --config.reload.automatic # 执行某个配置文件

# 同时启动多个配置文件 (需配置管道pipelines.yml)
1. 需配置管道pipelines.yml
$ less pipelines.yml |grep -v "#"

 - pipeline.id: mysql
   pipeline.workers: 1
   pipeline.batch.size: 125
   path.config: "/opt/ci123/elk/logstash-6.7.0/config/config.d/mysql.d/*.conf"

 - pipeline.id: application
   pipeline.workers: 1
   pipeline.batch.size: 125
   path.config: "/opt/ci123/elk/logstash-6.7.0/config/config.d/application.conf"

2. 校验
    开启config/logstash.yml中的 config.test_and_exit: true
3. 启动
    bin/logstash # (画外音:pipelines.yml启动不成功,或找不到pipelines.yml时,很有可能是语法错误,yml对语法邀请非常严格,需仔细检查)
    bin/logstash  --config.reload.automatic # 启动并自动加载修改的配置文件

 

五、安全设置

1. 为elk设置用户名和密码

注意:elastic 相当于超级管理员的账号,任何连接都可以使用此账号,但风险较高,建议为各自的模块设置自己的账号和权限

# step1 停止运行kibana和elasticsearch
# step2 在elasticsearch下增加配置 config/elasticsearch.yml
xpack.security.transport.ssl.enabled: true
xpack.security.enabled: true

# step3 启动elasticsearch
./bin/elasticsearch
# step4 设置密码 (可选择 自动设置,也可以手动设置)
    # (画外音,设置的密码比较多,如果想设置不一样,需提前记录下)
./bin/elasticsearch-setup-passwords auto  #
./bin/elasticsearch-setup-passwords interactive # 手动设置 (注意,此命令有且只能操作一次)


# 修改某个账号密码
$ curl -H "Content-Type:application/json" -XPOST -u {user} 'http://192.168.1.123:9227/_xpack/security/user/{user}/_password' -d '{ "password" : "new_password" }'
Enter host password for user 'elastic':
{}

tip:
    elastic: 需要修改的账号名称
    new_password:新密码


elastic一个内置的超级用户,可用于连接elasticsearch、kibana

kibana_systemKibana 用于连接 Elasticsearch 并与之通信的用户。

logstash_systemLogstash 在 Elasticsearch 中存储监控信息时使用的用户。

beats_systemBeats 在 Elasticsearch 中存储监控信息时使用的用户。

apm_systemAPM 服务器在 Elasticsearch 中存储监控信息时使用的用户。

remote_monitoring_user在 Elasticsearch 中收集和存储监控信息时使用的用户 Metricbeat。它具有remote_monitoring_agentremote_monitoring_collector内置角色。

 

2. 访问elasticsearch

# 测试访问
es@ts_web_123 elasticsearch-7.13.3]$ curl http://192.168.1.123:9227 -u elastic 
Enter host password for user 'elastic': xxx
{
  "name" : "node-1",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "faDVR0zoS5CGGhDcm6TkIg",
  "version" : {
    "number" : "7.13.3",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "5d21bea28db1e89ecc1f66311ebdec9dc3aa7d64",
    "build_date" : "2021-07-02T12:06:10.804015202Z",
    "build_snapshot" : false,
    "lucene_version" : "8.8.2",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}


3. kibana项目

#kibana: config/kibana
elasticsearch.username: "kibana_system"
elasticsearch.password: "你的密码"
(画外音:kibana密码需要与elastic密码一致,否则启动异常 待解决)

4. logstash 管道连接

为logstash配置的账号

  1. 使用Kibana 中的Management > Roles UI 或roleAPI 创建 logstash_writer角色。对于集群权限,添加manage_index_templates和monitor。对于指数的权限,添加write,create和create_index。
  2. 创建logstash_internal用户并为其分配logstash_writer角色。可以从Kibana 中的Management > Users UI中创建用户

 

output {
  elasticsearch {
    hosts => ["http://192.168.1.14:9200"]
    index => "application_log_%{+YYYY.MM.dd}"
    user => "logstash_internal"
    password => "xxx"
  }
}

 

5. kibana 后台 可设置各类用户角色

可通过内置角色,设置不同的账户

 

六、遇到的常见报错

1.【elasticsearch启动】sh bin/elasticsearch报错:can not run elasticsearch as root
# 创建es用户 (不能使用root权限执行)
# 1、创建用户:elasticsearch
$ adduser elasticsearch
# 2、创建用户密码,需要输入两次
$ passwd elasticsearch
#3、将对应的文件夹权限赋给该用户
$ chown -R elasticsearch elasticsearch-7.17.3
#4、切换至elasticsearch用户
$ su elasticsearch

 

2.【elasticsearch启动】bootstrap checks failed :

问题原因:因为Centos6不支持SecComp,而ES5.2.1默认bootstrap.system_call_filter为true进行检测,所以导致检测失败,失败后直接导致ES不能启动。详见 :https://github.com/elastic/elasticsearch/issues/22899

解决方法:

在elasticsearch.yml中配置bootstrap.system_call_filter为false,注意要在Memory下面:
bootstrap.memory_lock: false
bootstrap.system_call_filter: false

 

3.【elasticsearch启动】 max number of threads [1024] for user [elasticsearch] is too low, increase to at least [4096]

修改max user processes

错误说明: Linux系统为每个用户都设置了一个最大进程数, 这个特性可以让我们控制服务器上现有用户可以创建的进程数量.

(2) 查看max user processes:

# 与查看max open files类似, 可使用 ulimit -u查看max user processes:
ulimit -u

(3) 修改max user processes:

① 方案一: 修改/etc/security/limits.conf文件, 在文件最后添加下述内容:

*  soft      nproc      131072
*  hard      nproc      131072

② 方案二: 修改/etc/security/limits.d/90-nproc.conf文件, 在文件最后添加下述内容:

# 用户进程数的默认限制, 下面这个是对root外的其他用户限制max user processes, 要注释掉: 
# *          soft    nproc     1024
root       soft    nproc     131072

 

4.【elasticsearch启动】max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]**

修改虚拟内存大小

[root@localhost ~]# sysctl -w vm.max_map_count=262144

查看修改结果

[root@localhost ~]# sysctl -a|grep vm.max_map_countvm.max_map_count = 262144

 

5.【kibana启动】Index .kibana_7.13.2_001 belongs to a version of Kibana that cannot be automatically migrated. Reset it or use the X-Pack upgrade assistant.

原因:
遗留了旧版的es后,kibana还存放着原es数据索引。
解决方法:
修改kibana.yml里的,index.kibana 为index.kibana6.7
重启kibana

6. logstash报错:

本地出现报错,无法连接elasticsearch:9200时,关闭本地翻墙软件代理,再尝试。

 

Spring Boot 入门

一、开发基础

  • Java基础(两到三小时过一遍)
  • Java开发环境配置必须使用JDK1.8
  • IDE安装(优先使用IntelliJ IDEA)

二、名词解释

  • Spring:JAVA开发应用框架
  • Spring Boot:用来简化Spring应用的初始搭建以及开发过程的配置框架
  • Maven:Java项目构建工具,成熟的项目
  • Gradle:更简洁的Java项目构建工具,吸收了旧构建工具的优点。
  • JPA:是Sun官方提出的Java持久化规范,即数据库操作规范。
  • Hibernate:Hibernate是一个ORM框架,是JPA的默认实现方式,一般说JPA都是指Hibernate。
  • Mybatis:Mybatis是一个轻便的ORM框架。
  • Spring data jpa:是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架

三、新项目流程

  1. 新建gradle项目
    • File->new->Prroject->Spring Initializr

    • 填写Group、Artifact选择Gradle Project项目生成

    • 可以直接在生成项目的时候选择对应需要安装的插件,如:web、jpa、mybatis等,也可以在项目初始化完成之后在build.gradle中添加/配置

  2. 配置build.gradle(位于根目录)

    plugins {
        id 'org.springframework.boot' version '2.1.3.RELEASE'
        id 'java'
    }
    
    apply plugin: 'io.spring.dependency-management'
    
    group = 'com.duomai'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '1.8' // JDK最大兼容版本
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        // Spring Boot JPA 组件
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        // Spring Boot Web组件
        implementation 'org.springframework.boot:spring-boot-starter-web'
        // Mybatis插件,注意暂时使用**1.1.1**版本,高版本的运行好像有问题
        implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:1.1.1'
        // Mybatis分页插件
        implementation group: 'com.github.pagehelper', name: 'pagehelper-spring-boot-starter', version: '1.2.10'
        runtimeOnly 'org.springframework.boot:spring-boot-devtools'
        runtimeOnly 'mysql:mysql-connector-java'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }
    
    

    修改了build.gradle后,idea会自动安装/更新依赖包。
    参考:gradle官网Spring Boot Web服务搭建Spring Boot Mysql使用Spring Boot JPA使用

  3. 项目基础配置(位于src/resources/application.properties

    #运行配置
    server.port=9000
    #数据格式配置
    spring.jackson.time-zone=GMT+8 // 设置接口返回时区为东八区
    spring.jackson.date-format=yyyy-MM-dd HH:mm:ss // 自动将接口返回中的日期格式转换为标准格式
    #数据库连接配置
    spring.datasource.url=jdbc:mysql://192.168.0.235:3355/shop_balances?serverTimezone=Asia/Shanghai&tinyInt1isBit=false // serverTimezone选择Mysql东八区,tinyInt1isBit禁止Mysql自动将tinyint(1)类型数据映射为boolean类型
    spring.datasource.username=cishop
    spring.datasource.password=fuyuan1906
    #log配置
    logging.path=E:/java/demo/balance_card/log
    logging.level.com.favorites=DEBUG
    #logging.level.org.springframework.web=INFO
    logging.level.org.hibernate=ERROR
    #mybatis设置
    mybatis.type-aliases-package=com.duomai.balance_card.Model.Mapper
    mybatis.configuration.map-underscore-to-camel-case=true
    logging.level.com.duomai.balance_card.Model.Mapper=DEBUG
    #pagehelper插件设置
    pagehelper.helperDialect=mysql
    pagehelper.reasonable=false
    pagehelper.supportMethodsArguments=true
    pagehelper.params=pageNum=page;pageSize=limit
    #jpa 设置
    spring.jpa.properties.hibernate.hbm2ddl.auto=update
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
    spring.jpa.show-sql= true
    
  4. 项目启动
    • IDEA启动,运行[Prefix]Application.java文件即可

    • 命令行启动

      • Maven项目
        在springboot的应用的根目录下运行 mvn spring-boot:run

      • Grdale项目
        在springboot的应用的根目录下运行 gradle bootRungradlew bootRun
        (前者是使用本地的gradle版本运行,后者是使用代码仓库中的gradle运行)

    • 打包构建可执行文件运行

  5. 开发环境热更新设置
    热更新不是很好用,有一定的延迟时间

四、项目文件分层解析

// 业务代码 src/main
java 
    com
        duomai
            balance_card
                [Prefix]Application.java // 项目启动文件,可以做一些全局设置,如时区设置、Mapper扫描等
                Config // 配置类,用于注册一些全局配置,如拦截器注册等
                Middleware // 中间件,实现AOP功能
                Controller // 控制器,主要做路由功能
                    xxxController.java
                    BaseErrorController.java // 路由匹配失败时使用的控制器
                Service // 业务代码
                Model // 目录主要用于实体与数据访问层
                    Entity // 数据表实体类
                    Repository // JPA数据仓库
                    Mapper // Mybatis映射文件
                    Provider // Mapper的Sql生成器
                Library // 库类,存放公共类文件/纯定义文件等
                    ApiReturnDefines.java // 接口返回定义
                    ExceptionErrprDefines.java // 异常监听层定义
                Helper // 辅助函数类文件
                OutPut // 接口输出层
                    ApiResult.java // 最终的接口输出格式
                    AiReturn.java // 快速生成ApiResult类,供外部调用
                Exception // 统一的异常处理
                    ControllerHandler // 路由层异常监听
                    SqlHandler // 数据库层异常监听
// 配置项
resources
    appliaction.properties // 项目配置文件

五、控制器中间件

  1. 一般使用Spring过滤器或拦截器实现AOP切面编程

  2. 过滤器和拦截器的对比

    • 作用域不同
      过滤器依赖于servlet容器,只能在 servlet容器,web环境下使用。
      拦截器依赖于spring容器,可以在spring容器中调用,不管此时Spring处于什么环境。
    • 细粒度的不同
      过滤器的控制比较粗,只能在请求进来时进行处理,对请求和响应进行包装。
      拦截器提供更精细的控制,可以分为controller对请求处理之前、渲染视图之后、请求处理之后三个切面。
    • 中断链执行的难易程度不同
      拦截器可以 preHandle方法内返回 false 进行中断。
      过滤器就比较复杂,需要处理请求和响应对象来引发中断,需要额外的动作,比如将用户重定向到错误页面。
  3. 拦截器的使用
    • 编写自定义拦截器(Middleware/ControllerInterceptor.java
    public class ControllerInterceptor implements HandlerInterceptor {
        private Logger logger = LoggerFactory.getLogger(ControllerInterceptor.class);
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            logger.info("preHandle....");
            // token校验等
            String token = request.getHeader("token");
            //     Common.sendJson(response, ApiReturn.fail(1001, "token验证失败"));
            //      return false;
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            logger.info("postHandle...");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            // 接口日志记录等
            logger.info("afterCompletion...");
        }
     ```
    
    **说明**:
    preHandle:对客户端发过来的请求进行前置处理,如果方法返回true,继续执行后续操作,如果返回false,执行中断请求处理,请求不会发送到Controller。可以在这里校验一些权限信息,如token等,校验失败直接以JSON格式返回请求。
    
    postHandler:在请求进行处理后执行,也就是在Controller方法调用之后处理,前提是preHandle方法返回true。具体来说,postHandler方法会在DispatcherServlet进行视图返回渲染前被调用。
    
    afterCompletion: 该方法在整个请求结束之后执行,前提依然是preHandle方法的返回值为true。
    
    • 注册拦截器(Config/InterceptorConfig.java
    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Override
        // 核心方法
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(ControllerInterceptor())
                    //配置拦截规则
                    .addPathPatterns("/**")
                    .order(1);
    
            // 多个拦截器按上述方法持续注册即可,同时也可以设置order值,从小到大执行。
        }
    
        @Bean
        public HandlerInterceptor ControllerInterceptor() {
            return new ControllerInterceptor();
        }
    }
    

六、Controller层

  1. 定义控制器文件
    @RestController
    在类文件头部定义,标明为控制器文件,且输出格式为JSON
    
  2. 路由和参数
    1. 定义路由名称,接收方法
      例:@RequestMapping(value = "/get", method = {RequestMethod.GET, RequestMethod.POST})
      
        可选参数:
      
        value:路由名称
      
        method:指定请求的method类型, GET、POST、PUT、DELETE、PATCH等,可多选
      
        consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html
      
        produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
      
        params: 指定request中必须包含某些参数值
      
        headers: 指定request中必须包含某些指定的header值
      
    2. 参数接收
      例:@RequestParam(value = "fields", required = false, defaultValue = "*") String fields
      
          value:参数名称
      
          defaultValue:默认值
      
          required:是否是必要参数
      
    3. 自定义错误路由
      1. 在Controller层中添加BaseErrorController.java文件,用于监听路由匹配失败的情况
        @Controller
        public class BaseErrorController implements ErrorController {
            @Override
            public String getErrorPath() {
                System.out.print("错误页面");
                return "error/error";
        }
        
            @RequestMapping(value = "/error")
            public void error() throws Exception {
                throw new Exception("路由匹配失败");
            }
        }
        
      2. 在Exception文件夹中添加ControllerHandler.java,用于捕获路由报错并输出。
        @RestControllerAdvice
        public class ControllerHandler {
            // 缺少必选参数
            @ExceptionHandler({MissingServletRequestParameterException.class})
            @ResponseBody
            public ApiResult requestMissingServletRequest(MissingServletRequestParameterException e){
                return ApiReturn.fail(ExceptionErrorDefines.RequestMissingServletRequest, e.getMessage());
            }
        }
        未解决:抛出异常后访问404页面运行环境会报错,但是页面正常  
        

        参考:https://www.jianshu.com/p/393f70b55b1b

  3. Service层调用

    1. Service类成员注入
      • 使用@Autowired修饰符进行依赖注入
        @Autowired
        private final CardService cardService;
        
      • 用构造函数来做注入类成员(推荐使用)
        private StoreBalanceCardsRepository cardsRepository;
        public CardController(StoreBalanceCardsRepository cardsRepository) {
            this.cardsRepository = cardsRepository;
        }
        **注**:
        IntelliJ IDEA使用依赖注入会有IDE报错,但不影响实际编译运行,如需去除报错提示,需要在Dao层(Respository/Mapper)类开头添加注解 `@Repository`
        
    2. 调用
      cardService.get(id, fields);
      

七、Service层

  1. 定义Service文件
    @Service
    在类文件头部定义,标明为Service文件
    
  2. 注入Model层操作文件
    private final StoreBalanceCardsRepository storeBalanceCardsRepository;
    private final StoreBalanceCardsMapper storeBalanceCardsMapper;
    
        CardService(StoreBalanceCardsRepository storeBalanceCardsRepository, StoreBalanceCardsMapper storeBalanceCardsMapper) {
            this.storeBalanceCardsRepository = storeBalanceCardsRepository;
            this.storeBalanceCardsMapper = storeBalanceCardsMapper;
        }
    
  3. 调用Model层文件

八、Model层

  1. Repository

    • Spring中概念,概念类似于数据仓库,是Spring data jpa的实现。居于业务层和数据层之间,将两者隔离开来,在它的内部封装了数据查询和存储的逻辑。

    • Repository和传统意义上的DAO的区别:
      Repository蕴含着真正的OO概念,即一个数据仓库角色,负责所有对象的持久化管理。DAO则没有摆脱数据的影子,仍然停留在数据操作的层面上。Repository是相对对象而言,DAO则是相对数据库而言,虽然可能是同一个东西,但侧重点完全不同。

  2. Mapper

    存放Mybatis数据库关系映射方法

  3. Provider
    为Mapper层提供的SQL生成器,即将SQL的生成与映射解耦。

  4. Entity

    • 根据表结构自动生成实体类
      **注意**:
      i、多次自动生成不会覆盖,如需更新需要把旧的实体类文件删除
      
      ii、有需要的话可以在实体类文件右击generate一键生成构造函数、set get方法、@Autowired等
      
      iii、自动生成的实体类中datetime类型的字段会被转为Java的timestamp类型数据,存储的时候也使用timestamp类型即可
      
      iv、生成出来的实体类catalog = "" 报错,是IDE的报错,不影响使用
      

      参考文档:https://blog.csdn.net/chenju05244554/article/details/1009142081

  5. Hibernate 和 Mybatis 的对比

    • Hibernate优势:
      i、DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
      
      ii、对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
      
      iii、数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
      
      iv、有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。
      
    • Mybatis优势:
      i、MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
      
      ii、MyBatis容易掌握,而Hibernate门槛较高。
      
    • 选用Mybatis的原因
      i、Hibernate无法满足动态获取部分字段的需求,即使是使用Hibernate提供的原始SQL也无法实现
      
      ii、Hibernate的JPA查询只适用于一些简单的情况,如遇到复杂的SQL,Repository中的方法名会很长。这时候又将回到Hibernate的自定义SQL查询,即原生SQL。
      
      iii、Hibernate的维护成本比MyBatis高很多,MyBatis的SQL生成完全取决于开发者,所以SQL修改、维护、优化会比较便利。
      

九、MyBatis使用

  1. 引入MyBatis以及pagehelper分页插件
    dependencies {
        implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:1.1.1'
        implementation group: 'com.github.pagehelper', name: 'pagehelper-spring-boot-starter', version: '1.2.10'
    }
    
  2. application.properties 添加相关配置
    #mybatis设置
    mybatis.type-aliases-package=com.duomai.balance_card.Model.Mapper // 项目中Mapper存放的包
    mybatis.configuration.map-underscore-to-camel-case=true // 自动将sql字段下划线转为驼峰,可以保证取出的数据格式就是数据库中存储的格式
    logging.level.com.duomai.balance_card.Model.Mapper=DEBUG // 开启DEBUG模式,用于开发环境,记录执行的SQL。
    #pagehelper插件设置
    pagehelper.helperDialect=mysql // 指定分页插件使用哪种数据库
    pagehelper.reasonable=false // 分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
    pagehelper.supportMethodsArguments=true // 支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页,设置后无需手动启用分页插件。
    pagehelper.params=pageNum=page;pageSize=limit //  自定义Mapper 接口参数来传递分页参数的参数名
    
  3. 添加Mapper类扫描的两种方式
    • 在启动类中添加对mapper包扫描@MapperScan(推荐使用)
      @SpringBootApplication
      @MapperScan("com.duomai.balance_card.Model.Mapper")
      
    • 在具体的Mapper类上面添加注解 @Mapper

  4. Mapper类SQL用例

    public interface StoreBalanceCardsMapper {
        // select用例
        @SelectProvider(type = StoreBalanceCardsSqlBuilder.class, method = "getById")
        List<Map> getById(@Param("id") int id, @Param("fields") String fields, @Param("page") int page, @Param("limit") int limit);
    
        // 列表
        @SelectProvider(type = StoreBalanceCardsSqlBuilder.class, method = "list")
        Page<Map> list(@Param("fields") String fields, @Param("page") int page, @Param("limit") int limit);
    
        // insert用例
        @InsertProvider(type = StoreBalanceCardsSqlBuilder.class, method = "add")
        @Options(useGeneratedKeys=true, keyProperty="storeBalanceCard.id", keyColumn="id")
        int add(@Param("storeBalanceCard") StoreBalanceCards storeBalanceCard);
    
        // update用例
        @UpdateProvider(type = StoreBalanceCardsSqlBuilder.class, method = "updateName")
        @Options(useGeneratedKeys=true, keyProperty="storeBalanceCard.id", keyColumn="id")
        int updateName(@Param("storeBalanceCard") StoreBalanceCards storeBalanceCard, @Param("limit") int limit);
    
        // delete用例
        @DeleteProvider(type = StoreBalanceCardsSqlBuilder.class, method = "deleteById")
        int delete(@Param("id") int id, @Param("limit") int limit);
    }
    
    • 通过Provider的方式动态获取SQL

    • 需要分页时在具体的方法后多加page、limit参数,自动实现分页

      // Service层调用
      List storeBalanceCard = storeBalanceCardsMapper.list(fields, page, limit);
      // Mapper层实现
      Page<Map> list(@Param("fields") String fields, @Param("page") int page, @Param("limit") int limit);
      
    • 分页组件仅可用于查询,不可用于更新/删除,更新/删除需要另外实现

  5. Providerle类SQL用例

    public class StoreBalanceCardsSqlBuilder {
        private static final String STORE_BALANCE_CARDS = "store_balance_cards";
        public static String getById(int id, String fields) {
            return new SQL(){ { 
                SELECT(fields);
                FROM(STORE_BALANCE_CARDS);
                WHERE("store_balance_cards.id = #{id}");
            } }.toString();
        }
    
        public static String list(String fields) {
            return new SQL(){ {
                SELECT(fields);
                FROM(STORE_BALANCE_CARDS);
            } }.toString();
        }
    
        public static String add(@Param("storeBalanceCard") StoreBalanceCards storeBalanceCard) {
            return new SQL(){ {
                INSERT_INTO(STORE_BALANCE_CARDS);
                VALUES("sys_name", "#{storeBalanceCard.sysName}");
                VALUES("store_id", "#{storeBalanceCard.storeId}");
                VALUES("entity_store_id", "#{storeBalanceCard.entityStoreId}");
                VALUES("name", "#{storeBalanceCard.name}");
                VALUES("type", "#{storeBalanceCard.type}");
                VALUES("item_id", "#{storeBalanceCard.itemId}");
                VALUES("photo", "#{storeBalanceCard.photo}");
                VALUES("state", "#{storeBalanceCard.state}");
                VALUES("upgrade", "#{storeBalanceCard.upgrade}");
                VALUES("recharge_discount", "#{storeBalanceCard.rechargeDiscount}");
                VALUES("updated_at", "#{storeBalanceCard.updatedAt}");
                VALUES("created_at", "#{storeBalanceCard.createdAt}");
            } }.toString();
        }
    
        public static String updateName(@Param("storeBalanceCard") StoreBalanceCards storeBalanceCard, @Param("limit") int limit) {
            return new SQL(){ {
                UPDATE(STORE_BALANCE_CARDS);
                SET("name=#{storeBalanceCard.name}");
                if (storeBalanceCard.getUpdatedAt() != null) {
                    SET("updated_at=#{storeBalanceCard.updatedAt}");
                }
                WHERE("store_id = #{storeBalanceCard.storeId}");
            } }.toString() + " limit " + limit;
        }
    
        public static String deleteById(int id, int limit) {
            return new SQL(){ {
                DELETE_FROM(STORE_BALANCE_CARDS);
                WHERE("store_balance_cards.id = #{id}");
            } }.toString() + " limit " + limit;
        }
    }
    
  6. 分页组件详解

十、接口输出

  1. 接口统一输出格式

    state: // 状态位
    msg: // 接口输出提示信息
    data: { // 总的接口输出数据,可以为空
        data: // 接口数据
        other: // 其他返回数据,如total/page_total等
        ...:
    }
    
  2. 正常接口输出/异常监听输出统一使用OutPut/ApiReturn方法

十一、异常处理

  1. 项目运行异常统一监听
    现有:ControllrHandler 路由异常监听、SqlHandler 数据库操作异常监听
    需要持续添加
  2. 统一使用接口输出类进行返回,杜绝直接返回报错信息。

十二、单元测试

  1. 添加单元测试类

    src\test\java\com\duomai\balance_card\BalanceCardApplicationTests.java

  2. demo

    @RunWith(SpringRunner.class)
    @SpringBootTest
    @AutoConfigureMockMvc
    public class BalanceCardApplicationTests {
    
        @Autowired
        private MockMvc mockMvc;
    
        @Test
        @SuppressWarnings("unchecked")
        public void getCard() throws Exception {
            String card_id = "1";
            String fields = "store_id,item_id,recharge_discount,photo,type,name";
            String res = this.mockMvc.perform(get("/card/get")
                    .param("id", card_id).param("fields", fields)
                    )
                    .andDo(print())
                    .andExpect(status().isOk())
                    .andReturn()
                    .getResponse()
                    .getContentAsString();
           // 接口返回不为空
           assertThat(res).isNotNull();
           // 校验接口返回格式是否完整
           Map<String, Object> api_res = Common.jsonToMap(res);
           assertThat(api_res).isNotNull();
           assertThat(api_res).hasSize(3);
           assertThat(api_res).containsKeys("state", "msg", "data");
           // 校验接口返回state是否正确
           int state = (int) api_res.get("state");
           Map<String, Object> api_data = (HashMap<String, Object>) api_res.get("data");
           assertThat(state).isEqualTo(ApiReturnDefines.SUCCESS);
           // 校验data数据是否正确
           assertThat(api_data).containsOnlyKeys("data");
           ArrayList<Map> data = (ArrayList) api_data.get("data");
           String[] fieldsAll = fields.split(",");
           assertThat(data).hasSize(1);
           assertThat(data.get(0)).containsKeys((Object[]) fieldsAll);
        }
    }
    
  3. 详解
    • 使用 @RunWith(SpringRunner.class)@SpringBootTest 定义测试类
    • 添加注解 @AutoConfigureMockMvc ,使用 Spring MockMvc 模拟Spring的HTTP请求并将其交给控制器,实际上并没有真正地启动服务器,仅仅是Mock,节省了启动服务器的开销。
    • 使用 AssertJ 库类来验证接口返回内容
      在demo中,使用断言验证了接口返回格式、state状态、data数据格式等基本内容。

    参考文档:https://spring.io/guides/gs/testing-web/

十三、项目打包

  1. 不同的构建文件
  • 普通 jar 包 : 会将源码编译后以工具包(即将class打成jar包)的形式对外提供,此时,你的 jar 包不一定要是可执行的,只要能通过编译,可以被别的项目以 import 的方式调用。
  • 可执行 jar 包 : 能通过 java -jar 的命令运行。
  • 普通 war 包 : war 是一个 web 模块,其中包括 WEB-INF,是可以直接运行的 WEB 模块。做好一个 web 应用后,打成包部署到容器中。
    • 可执行 war 包 : 普通 war 包 + 内嵌容器 。
  1. 构建可执行 jar 包

    • IDE打包
      右侧Gradle -> Tasks -> build -> build
    • 命令行打包
      进入项目根目录,执行gradle build
  2. 运行可执行 jar 包
    java -jar build/libs/balance_card-0.0.1.jar
    运行时可带参数,同application.properties中的参数名
    例:

    java -Djava.security.egd=file:/dev/./urandom -jar /opt/ci123/www/html/java/balance_card/build/libs/${jarName}.jar --server.port=80
    

十四、容器化部署

  1. 创建docker镜像
  • Dockfile 编写

    FROM java:8
    MAINTAINER duomai
    # 设置镜像源
    COPY sources.list /etc/apt/sources.list
    # 安装扩展
    RUN apt-get update && apt-get install -y \
    wget \
    curl \
    vim \
    git \
    less
    # 配置系统时间
    RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo 'Asia/Shanghai' > /etc/timezone
    VOLUME /tmp
    # 设置工作目录
    ADD . /opt/ci123/www/html/java
    WORKDIR /opt/ci123/www/html/java
    # 指定输出端口
    EXPOSE 80

    • 快速构建镜像

      sh docker/build.sh [version]

  1. 环境变量配置
  • 配置

    #数据库连接配置 application.properties
    spring.datasource.url=jdbc:mysql://${APP_DB_HOST:192.168.0.235}:${APP_DB_PORT:3355}/${APP_DB_DATABASE:shop_balances}
    spring.datasource.username=${APP_DB_USER:cishop}
    spring.datasource.password=${APP_DB_PASSWORD:fuyuan1906}

    1. 启动容器( docker/run.sh )
  • 启动参数

    -p // 宿主机运行端口
    -d // 宿主机项目地址,缺省为执行run.sh的上一级目录
    -n // 容器名称
    -v // 镜像版本号
    -j // jar包版本,缺省为1.0

  • 环境变量

    # 运行配置
    APP_SERVER_PORT // 容器运行jar包的端口
    # 数据库配置
    APP_DB_HOST
    APP_DB_PORT
    APP_DB_DATABASE
    APP_DB_USER
    APP_DB_PASSWORD
    # 日志配置
    APP_LOG_PATH // 日志输出地址
    APP_LOG_SPRING_WEB_LEVEL // 对应logging.level.org.springframework.web,指org.springframework.web这个包下的日志输出等级,默认为ERROR,开发环境可配置为DEBUG
    APP_LOG_MYBATIS_LEVEL // 对应logging.level.com.duomai.balance_card.Model.Mapper,指balance_card.Model.Mapper包下的日志输出等级,默认为ERROR,开发环境可配置为DEBUG,开启DEBUG之后可在日志中查看DB操作

  • 启动容器

    docker run -d \
    -e APP_SERVER_PORT=8080 \
    -e APP_DB_HOST=192.168.0.235 \
    -e APP_DB_PORT=3355 \
    -e APP_DB_DATABASE=shop_balances \
    -e APP_DB_USER=cishop \
    -e APP_DB_PASSWORD=fuyuan1906 \
    -e APP_LOG_PATH=/opt/ci123/www/html/java/balance_card/log \
    -e APP_LOG_SPRING_WEB_LEVEL=DEBUG \
    -e APP_LOG_MYBATIS_LEVEL=DEBUG \
    -p $port:80 \
    -v $dir:/opt/ci123/www/html/java/balance_card \
    --restart=always \
    --name $name \
    harbor.oneitfarm.com/duomai/java-balance_card:$version \
    sh /opt/ci123/www/html/java/balance_card/docker/start.sh -j $jarName

通过mq消费者ip查找对应启动容器

  1. 首先先确定是否相应的消费者
  2. 3.14上有多个容器,如何确定是哪个容器运行的呢?
    • 由于在linux的docker中,容器里运行的进程会直接通过宿主机执行,所以可以通过查看是否有对应进程
      bash
      ps aux | grep cront/trades/Cashier_trade/payed

    • 获取对应进程的父级pid(也就是运行该脚本的pm2进程),比如这里选择第一个进程的pid为2163,获取到上级pid为29217
      bash
      # ps -ef|awk '$2 ~ /pid/{print $3}'
      ps -ef|awk '$2 ~ /2163/{print $3}'

    • 再获取pm2的上级(即运行容器的pid),获取到为24450
      bash
      ps -ef|awk '$2 ~ /29217/{print $3}'
    • 查看容器pid相对应的进程
      bash
      ps aux | grep 24450

    • 有一串容器编号,复制前一小段,查询对应容器
      bash
      docker ps | grep 0d7eccf5ab