برنامه نویسی

جلسات ثابت با Apache APISIX – نسخه ی نمایشی

هفته گذشته، مفهوم پشت جلسات چسبنده را توضیح دادیم: شما یک درخواست را به همان بالادست ارسال می‌کنید زیرا داده‌های زمینه مرتبط با جلسه در آن گره وجود دارد. با این حال، در صورت لزوم، باید داده‌ها را در سایر بالادستی‌ها تکرار کنید زیرا ممکن است این داده پایین بیاید. در این پست قصد داریم با یک دمو آن را به تصویر بکشیم.

طراحی کلی

گزینه های طراحی نامحدود هستند. من خودم را در یک پشته آشنا نگه می دارم، JVM. همچنین همانطور که در پست قبل گفته شد، فقط باید جلسات چسبناک را با تکرار جلسه پیاده سازی کرد. طراحی نهایی از دو جزء تشکیل شده است: یک نمونه Apache APISIX با پیکربندی جلسات چسبنده و دو گره JVM که همان برنامه را با تکرار جلسه اجرا می کنند.

برنامه از موارد زیر استفاده می کند:

طراحی به شکل زیر است:

طرح

app1 و app2 دو نمونه از یک برنامه هستند. من نمی خواستم نمودار را با داده های اضافی پر کنم.

قلب برنامه

قلب برنامه یک دانه با محدوده جلسه است که یک شمارنده را می‌پیچد که فقط می‌توان آن را افزایش داد:

@Component
@SessionScope
public class Counter implements Serializable {              //1

    private int value = 0;

    public int getValue() {
        return value;
    }

    public void incrementValue() {
        value++;
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. برای کار سریال سازی Hazelcast لازم است

می توانیم از این bean در کنترلر استفاده کنیم:

@Controller
public class IndexController {

    private final Counter counter;

    public IndexController(Counter counter) {               //1
        this.counter = counter;
    }

    @GetMapping("https://dev.to/")
    public String index(Model model) {                      //2
        counter.incrementValue();
        model.addAttribute("counter", counter.getValue());
        return "index";
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. به لطف جادوی اسپرینگ، لوبیا با محدوده‌ی جلسه را در کنترلر تک تنه تزریق کنید
  2. هنگامی که ما ارسال می کنیم GET درخواست به ریشه، مقدار شمارنده را افزایش داده و به مدل ارسال کنید

در نهایت، مقدار لوبیا را در صفحه Thymeleaf نمایش می دهیم:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<body>
<div th:text="${counter}">3</div>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پیکربندی جلسه بهار با Hazelcast

Spring Session فیلتری را ارائه می دهد که نسخه اصلی را بسته بندی می کند HttpServletRequest نادیده گرفتن getSession() روش. این روش یک مشخصه را برمی گرداند Session پیاده سازی با پشتیبانی پیاده سازی پیکربندی شده با Spring Session، در مورد ما، Hazelcast.

برای پیکربندی Spring Session با Hazelcast فقط به چند ترفند نیاز داریم.

ابتدا، کلاس برنامه Spring Boot را با حاشیه نویسی مربوطه حاشیه نویسی کنید:

@SpringBootApplication
@EnableHazelcastHttpSession
public class SessionApplication {
    ...
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Hazelcast به پیکربندی خاصی نیز نیاز دارد. می توانیم از XML، YAML یا کد استفاده کنیم. از آنجایی که نسخه آزمایشی است، می توانم هر چیزی را که می خواهم انتخاب کنم، پس بیایید آن را کدگذاری کنیم. Spring Boot به یک شی Hazelcast یا یک شی پیکربندی نیاز دارد. مورد دوم کافی است:

@Bean
public Config hazelcastConfig() {
  var config = new Config();
  var networkConfig = config.getNetworkConfig();
  networkConfig.setPort(0);                                                      //1
  networkConfig.getJoin().getAutoDetectionConfig().setEnabled(true);              //2
  var attributeConfig = new AttributeConfig()                                     //3
          .setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
          .setExtractorClassName(PrincipalNameExtractor.class.getName());
  config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME) //3
          .addAttributeConfig(attributeConfig)
          .addIndexConfig(new IndexConfig(
            IndexType.HASH,
            HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE
  ));
  var serializerConfig = new SerializerConfig();
  serializerConfig.setImplementation(new HazelcastSessionSerializer())            //3
                  .setTypeClass(MapSession.class);
  config.getSerializationConfig().addSerializerConfig(serializerConfig);
  return config;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. برای جلوگیری از تضاد پورت، یک پورت تصادفی انتخاب کنید
  2. به Hazelcast اجازه دهید نمونه های دیگر را جستجو کند و به طور خودکار یک خوشه تشکیل دهد. زمانی که طبق طراحی ما به کار گرفته شود، ضروری است
  3. از مستندات جلسه بهار کپی پیست شد

پیکربندی Spring Security

بیشتر نمونه‌های Spring Session به نوعی از Spring Security استفاده می‌کنند، و اگرچه به شدت ضروری نیست، اما طراحی را آسان‌تر می‌کند. اول می خواهم دلیلش را توضیح دهم.

می توان جلسات را به عنوان یک جدول هش غول پیکر در نظر گرفت. در برنامه های معمولی، کلید این است JSESSIONID مقدار کوکی، مقدار، جدول هش دیگری از داده های جلسه. با این حال JSESSIONID مخصوص گره است. برنامه متفاوت خواهد داد JSESSIONID اگر یکی از گره دیگری استفاده کند. از آنجایی که کلید متفاوت است، هیچ راهی برای دسترسی به داده های جلسه وجود ندارد، حتی اگر داده های جلسه در بین گره ها به اشتراک گذاشته شوند. برای جلوگیری از این ضرر، باید کلید متفاوتی پیدا کنیم. Spring Security اجازه می دهد تا از یک اصلی (یا نام ورود) به عنوان کلید داده جلسه استفاده کنید.

در اینجا نحوه تنظیم یک پیکربندی اولیه Spring Security آورده شده است:

@Bean
public SecurityFilterChain securityFilterChain(UserDetailsService service, HttpSecurity http) throws Exception {
  return http.userDetailsService(service)                                    //1
             .authorizeHttpRequests(authorize -> authorize.requestMatchers(
                 PathRequest.toStaticResources().atCommonLocations())        //2
                            .permitAll()                                     //2
                            .anyRequest().authenticated()                    //3
             ).formLogin(form -> form.permitAll()
                                     .defaultSuccessUrl("https://dev.to/")                 //4
             ).build();
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. سرویس پیش‌فرض جزئیات کاربر در حافظه اجازه کلاس‌های جزئیات کاربر سفارشی را نمی‌دهد. مجبور شدم خودم را تهیه کنم.
  2. به همه اجازه دسترسی به منابع استاتیک در مکان‌های “مشترک” را بدهید
  3. تمام درخواست های دیگر باید احراز هویت شوند
  4. به همه اجازه دسترسی به فرم احراز هویت را بدهید
  5. در صورت موفقیت آمیز بودن، به root تغییر مسیر دهید، که کنترلر فوق را ترسیم می کند

طرح خود را در معرض آزمایش قرار دهیم

در کنار پیشخوان، می‌خواهم دو داده اضافی را نمایش دهم: نام میزبان و کاربر وارد شده.

برای نام میزبان، روش زیر را به کنترلر اضافه می کنم:

@ModelAttribute("hostname")
private String hostname() throws UnknownHostException {
    return InetAddress.getLocalHost().getHostName();
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نمایش کاربر وارد شده به یک وابستگی اضافی نیاز دارد:

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در صفحه، این ساده است:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">  <!--1-->
<body>
<td sec:authentication="principal.label">Me</td>                    <!--2-->
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. اضافه کردن sec فضای نام لازم نیست اما ممکن است به IDE کمک کند تا به شما کمک کند
  2. زیربنایی را نیاز دارد UserDetail پیاده سازی برای داشتن یک getLabel() روش

آخرین اما نه کم‌اهمیت، باید Apache APISIX را با جلسات چسبنده پیکربندی کنیم، همانطور که هفته گذشته دیدیم:

routes:
  - uri: /*
    upstream:
      nodes:
        "webapp1:8080": 1
        "webapp2:8080": 1
      type: chash
      hash_on: cookie
      key: cookie_JSESSIONID
#END
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا طراحی اجرا شده در Docker Compose آمده است:

services:
  apisix:
    image: apache/apisix:3.3.0-debian
    volumes:
      - ./apisix/config.yml:/usr/local/apisix/conf/config.yaml:ro
      - ./apisix/apisix.yml:/usr/local/apisix/conf/apisix.yaml:ro    #1
    ports:
      - "9080:9080"                                                  #2
    depends_on:
      - webapp1
      - webapp2
  webapp1:
    build: ./webapp
    hostname: webapp1                                                #3
  webapp2:
    build: ./webapp
    hostname: webapp2                                                #3
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. از فایل تنظیمات قبلی استفاده کنید
  2. فقط API Gateway را در معرض دنیای خارج قرار دهید
  3. نام میزبان را تنظیم کنید تا در صفحه نمایش داده شود

ما می توانیم با استفاده از یکی از دو حساب رمزگذاری شده وارد شوید. من استفاده می کنم john، با رمز عبور john و برچسب زدن John Doe. توجه داشته باشید که Apache APISIX من را روی یک گره تنظیم می کند و در صورت رفرش کردن، از همان استفاده می کند.

اسکرین شات از برنامه ما

می توانیم سعی کنیم با حساب دیگر وارد شوید (jane/jane) از یک پنجره خصوصی و بررسی کنید که شمارنده از 0 شروع می شود.

الان قسمت خوبش شروع میشود. اجازه دهید گره‌ای را که باید میزبان داده‌های جلسه باشد، در اینجا متوقف می‌کنیم webapp2 و صفحه را رفرش کنید:

docker compose stop webapp2
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما می توانیم چیزهای هیجان انگیزی را در سیاهه ها ببینیم. Apache APISIX دیگر نمی تواند آن را پیدا کند webapp2، بنابراین درخواست را به بالادست دیگری که می داند ارسال می کند، webapp1.

  1. درخواست هنوز احراز هویت است. از طریق Spring Security می گذرد
  2. چارچوب، اصل را از درخواست خارج می کند
  3. جلسه بهار را پرس و جو می کند
  4. و مقدار شمارنده صحیحی را که Hazelcast از گره دیگر تکرار کرده است، دریافت می کند

تنها عارضه جانبی افزایش تأخیر به دلیل وقفه زمانی Apache APISIX است. به طور پیش فرض 5 ثانیه است، اما در صورت نیاز می توانید آن را به مقدار کمتری پیکربندی کنید.

وقتی شروع می کنیم webapp2 دوباره، همه چیز دوباره همانطور که انتظار می رود کار می کند.

نتیجه

در این پست، من یک راه اندازی ممکن برای جلسات چسبنده با Apache APISIX و تکرار شامل اکوسیستم Spring و Hazelcast را توضیح دادم. بسیاری از گزینه های دیگر بسته به پشته و چارچوب انتخاب های شما در دسترس هستند.

کد منبع کامل این پست را می توانید در GitHub بیابید:

فراتر رفتن:

در ابتدا در A Java Geek در 2 ژوئیه منتشر شدnd، 2023

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا