جلسات ثابت با 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++;
}
}
- برای کار سریال سازی 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";
}
}
- به لطف جادوی اسپرینگ، لوبیا با محدودهی جلسه را در کنترلر تک تنه تزریق کنید
- هنگامی که ما ارسال می کنیم
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;
- برای جلوگیری از تضاد پورت، یک پورت تصادفی انتخاب کنید
- به Hazelcast اجازه دهید نمونه های دیگر را جستجو کند و به طور خودکار یک خوشه تشکیل دهد. زمانی که طبق طراحی ما به کار گرفته شود، ضروری است
- از مستندات جلسه بهار کپی پیست شد
پیکربندی 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();
}
- سرویس پیشفرض جزئیات کاربر در حافظه اجازه کلاسهای جزئیات کاربر سفارشی را نمیدهد. مجبور شدم خودم را تهیه کنم.
- به همه اجازه دسترسی به منابع استاتیک در مکانهای “مشترک” را بدهید
- تمام درخواست های دیگر باید احراز هویت شوند
- به همه اجازه دسترسی به فرم احراز هویت را بدهید
- در صورت موفقیت آمیز بودن، به 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-->
- اضافه کردن
sec
فضای نام لازم نیست اما ممکن است به IDE کمک کند تا به شما کمک کند - زیربنایی را نیاز دارد
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
- از فایل تنظیمات قبلی استفاده کنید
- فقط API Gateway را در معرض دنیای خارج قرار دهید
- نام میزبان را تنظیم کنید تا در صفحه نمایش داده شود
ما می توانیم با استفاده از یکی از دو حساب رمزگذاری شده وارد شوید. من استفاده می کنم john
، با رمز عبور john
و برچسب زدن John Doe
. توجه داشته باشید که Apache APISIX من را روی یک گره تنظیم می کند و در صورت رفرش کردن، از همان استفاده می کند.
می توانیم سعی کنیم با حساب دیگر وارد شوید (jane
/jane
) از یک پنجره خصوصی و بررسی کنید که شمارنده از 0 شروع می شود.
الان قسمت خوبش شروع میشود. اجازه دهید گرهای را که باید میزبان دادههای جلسه باشد، در اینجا متوقف میکنیم webapp2
و صفحه را رفرش کنید:
docker compose stop webapp2
ما می توانیم چیزهای هیجان انگیزی را در سیاهه ها ببینیم. Apache APISIX دیگر نمی تواند آن را پیدا کند webapp2
، بنابراین درخواست را به بالادست دیگری که می داند ارسال می کند، webapp1
.
- درخواست هنوز احراز هویت است. از طریق Spring Security می گذرد
- چارچوب، اصل را از درخواست خارج می کند
- جلسه بهار را پرس و جو می کند
- و مقدار شمارنده صحیحی را که Hazelcast از گره دیگر تکرار کرده است، دریافت می کند
تنها عارضه جانبی افزایش تأخیر به دلیل وقفه زمانی Apache APISIX است. به طور پیش فرض 5 ثانیه است، اما در صورت نیاز می توانید آن را به مقدار کمتری پیکربندی کنید.
وقتی شروع می کنیم webapp2
دوباره، همه چیز دوباره همانطور که انتظار می رود کار می کند.
نتیجه
در این پست، من یک راه اندازی ممکن برای جلسات چسبنده با Apache APISIX و تکرار شامل اکوسیستم Spring و Hazelcast را توضیح دادم. بسیاری از گزینه های دیگر بسته به پشته و چارچوب انتخاب های شما در دسترس هستند.
کد منبع کامل این پست را می توانید در GitHub بیابید:
فراتر رفتن:
در ابتدا در A Java Geek در 2 ژوئیه منتشر شدnd، 2023