ããã¯ããªã«ãããããŠæžãããã®ïŒ
以åãAmazon SQS Java Temporary Queue ClientãšElasticMQã䜿ã£ãŠãäžæãã¥ãŒãšRPCã䜿ã£ããšã³ããªãŒãæžããããšããããŸãã
Amazon SQS互換のElasticMQを使って、Temoporary Queue+RPCを試してみる - CLOVER🍀
ãã®Amazon SQS Java Temporary Queue Clientã§ãããAWS SDK for Javaã®ããŒãžã§ã³1ã䜿ã£ãŠããã®ã§ããã2022幎ã®5æã«
AWS SDK for Java v2ã䜿ãããã«ãªã£ã2.0.0ããªãªãŒã¹ãããŠããã®ã§ãã¡ãã£ãšè©ŠããŠã¿ããããªãšã
Amazon SQSã®äžæãã¥ãŒãšRPC
ãããããAmazon SQSã®äžæãã¥ãŒãšRPCã«ã€ããŠæãåºããšããããã
äžæãã¥ãŒã«ã€ããŠã¯ãã¡ãã«èšèŒããããAmazon SQS Java Temporary Queue Clientã䜿ãããšã§äžæãã¥ãŒãäœãããšãã§ãããš
æžãããŠããŸãã
Amazon SQS一時キュー - Amazon Simple Queue Service
Amazon SQS Java Temporary Queue Clientã®GitHubãªããžããªã¯ããã¡ãã
ãããŠãäžæãã¥ãŒã®æãäžè¬çãªäœ¿çšäŸããªã¯ãšã¹ã - ã¬ã¹ãã³ã¹ååŒã®ã¡ãã»ãŒãžã³ã°ãã¿ãŒã³ã ãšãããŠããŸãã
äŸãšããŠããã°ã€ã³åŠçãAmazon SQSãä»ããŠå¥ã®ã¢ããªã±ãŒã·ã§ã³ïŒãµãŒããŒåŽïŒã«åŠçãããçµæãåãåãïŒã¯ã©ã€ã¢ã³ãåŽïŒãããª
ãœãŒã¹ã³ãŒããæžãããŠããŸãã
åæ§ã®ããšããAWSã®ããã°ãšã³ããªãŒã«ãæžãããŠããŸãã
Simple Two-way Messaging using the Amazon SQS Temporary Queue Client | AWS Compute Blog
ããã§ã以åè©Šããæã«ã¯Amazon SQS Java Temporary Queue Clientã¯AWS SDK for Java v1ã䜿ã£ãŠãããv2ã«å¯ŸããŠã¯issueãããã ã
ã ã£ãã®ã§ãããAmazon SQS Java Temporary Queue Client 2.0.0ããªãªãŒã¹ãããããšã§ç¶æ³ãå€ãã£ãããã§ãã
Two Way Messaging - Virtual Queues · Issue #1647 · aws/aws-sdk-java-v2 · GitHub
Amazon SQS Java Temporary Queue Client 2.0.0ããã¯ãAWS SDK for Java v2ã䜿ããŸãã
ãã£ãããªã®ã§ãä»åã¯Amazon SQS Java Temporary Queue Client 2.0.0ãšLocalStackã䜿ã£ãŠãåã®ãšã³ããªãŒã§æžããå
容ã
æžãçŽããŠã¿ãããšæããŸãã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã
LocalStackã
$ python3 -V Python 3.10.6 $ localstack --version 1.3.1
èµ·åã
$ localstack start
AWS CLIãLocalStackã®æäŸãããã®ãéããŠããŸãã
$ awslocal --version aws-cli/2.9.21 Python/3.9.11 Linux/5.15.0-58-generic exe/x86_64.ubuntu.22 prompt/off
Javaã
$ java --version openjdk 17.0.5 2022-10-18 OpenJDK Runtime Environment (build 17.0.5+8-Ubuntu-2ubuntu122.04) OpenJDK 64-Bit Server VM (build 17.0.5+8-Ubuntu-2ubuntu122.04, mixed mode, sharing) $ mvn --version Apache Maven 3.8.7 (b89d5959fcde851dcb1c8946a785a163f14e1e29) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 17.0.5, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-58-generic", arch: "amd64", family: "unix"
æºå
ãŸãã¯ããã¥ãŒãäœæããå¿
èŠããããŸãããä»åã¯my-queue
ãšããååã§äœæããŸããã
$ awslocal sqs create-queue --queue-name my-queue { "QueueUrl": "http://localhost:4566/000000000000/my-queue" }
次ã«ãAmazon SQS Java Temporary Queue Clientã䜿ãããã«MavenäŸåé¢ä¿ãªã©ã®èšå®ãè¡ããŸãã確èªã¯ãã¹ãã³ãŒãã§è¡ãããšã«
ããã®ã§ãJUnitçãåãããŠè¿œå ã
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <aws-java-sdk.version>2.19.31</aws-java-sdk.version> </properties> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>amazon-sqs-java-temporary-queues-client</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>sqs</artifactId> <version>2.19.31</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.24.2</version> <scope>test</scope> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>test-utils</artifactId> <version>2.19.31</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M7</version> </plugin> </plugins> </build>
Amazon SQS Java Temporary Queue Clientã¯ä»¥äžã§ãæšç§»çäŸåé¢ä¿ã®äžã«AWS SDK for Java v2ãå«ãŸããŠã¯ããã®ã§ããã
<dependency> <groupId>com.amazonaws</groupId> <artifactId>amazon-sqs-java-temporary-queues-client</artifactId> <version>2.0.0</version> </dependency>
ã¡ãã£ãšå€ãã£ãã®ã§ãæ瀺çã«æ°ããããŒãžã§ã³ãæå®ããŠãããŸããã
<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>sqs</artifactId> <version>2.19.31</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>test-utils</artifactId> <version>2.19.31</version> <scope>test</scope> </dependency>
ç°¡åãªEchoã¯ã©ã€ã¢ã³ãïŒãµãŒããŒãæžã
ã§ã¯ãAmazon SQSã®äžæãã¥ãŒã䜿ã£ãããã°ã©ã ãæžãããã§ãããååã®ãé¡ãšåæ§ã«ç°¡åãªEchoã¯ã©ã€ã¢ã³ãïŒãµãŒããŒã
æžããŠããããšæããŸãã
ã¯ã©ã€ã¢ã³ãããµãŒããŒã«ã¡ãã»ãŒãžãéãããµãŒããŒã¯åãåã£ãã¡ãã»ãŒãžã«ç°¡åã«è£ 食ããŠè¿ããšãããã®ã«ããŸãã
ãŸãã¯ãLocalStackäžã®Amazon SQSã«ã¢ã¯ã»ã¹ããããã®SqsClient
ãäœæããéšåã
src/test/java/org/littlewings/sqs/LocalSqsBuilder.java
package org.littlewings.sqs; import java.net.URI; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.sqs.SqsClient; public class LocalSqsBuilder { public static SqsClient create() { return SqsClient .builder() .credentialsProvider( StaticCredentialsProvider.create( AwsBasicCredentials.create("mock", "mock") ) ) .region(Region.US_EAST_1) .endpointOverride(URI.create("http://localhost:4566")) .build(); } }
ã¯ã¬ãã³ã·ã£ã«ã¯åºå®ããšã³ããã€ã³ãã¯LocalStackã®ãã®ãæãããã«ããŠããŸãã
次ã«ããµãŒããŒãäœæã
src/test/java/org/littlewings/sqs/TemporaryQueueRpcServer.java
package org.littlewings.sqs; import java.util.Random; import java.util.concurrent.TimeUnit; import com.amazonaws.services.sqs.AmazonSQSResponder; import com.amazonaws.services.sqs.AmazonSQSResponderClientBuilder; import com.amazonaws.services.sqs.MessageContent; import com.amazonaws.services.sqs.util.SQSMessageConsumer; import com.amazonaws.services.sqs.util.SQSMessageConsumerBuilder; import software.amazon.awssdk.services.sqs.SqsClient; public class TemporaryQueueRpcServer { SqsClient sqsClient; AmazonSQSResponder responder; SQSMessageConsumer consumer; TemporaryQueueRpcServer() { } public static TemporaryQueueRpcServer create(String queueUrl) { TemporaryQueueRpcServer server = new TemporaryQueueRpcServer(); SqsClient sqsClient = LocalSqsBuilder.create(); AmazonSQSResponder responder = AmazonSQSResponderClientBuilder .standard() .withAmazonSQS(sqsClient) .build(); SQSMessageConsumer consumer = SQSMessageConsumerBuilder .standard() .withAmazonSQS(sqsClient) .withQueueUrl(queueUrl) .withConsumer(requestMessage -> { MessageContent requestContent = MessageContent.fromMessage(requestMessage); String requestBody = requestContent.getMessageBody(); MessageContent responseContent = new MessageContent("â â â " + requestBody + "â â â "); responder.sendResponseMessage(requestContent, responseContent); }) .build(); server.sqsClient = sqsClient; server.responder = responder; server.consumer = consumer; return server; } public void start() { consumer.start(); } public void stop() { consumer.close(); responder.shutdown(); sqsClient.close(); } }
ãã¡ããåãåã£ãã¡ãã»ãŒãžãéãè¿ãããã®AmazonSQSResponder
ã
AmazonSQSResponder responder = AmazonSQSResponderClientBuilder .standard() .withAmazonSQS(sqsClient) .build();
ãã¡ããã¡ãã»ãŒãžãåä¿¡ããŠåŠçãè¡ãããã®SQSMessageConsumer
ã§ãã
SQSMessageConsumer consumer = SQSMessageConsumerBuilder .standard() .withAmazonSQS(sqsClient) .withQueueUrl(queueUrl) .withConsumer(requestMessage -> { MessageContent requestContent = MessageContent.fromMessage(requestMessage); String requestBody = requestContent.getMessageBody(); MessageContent responseContent = new MessageContent("â â â " + requestBody + "â â â "); responder.sendResponseMessage(requestContent, responseContent); }) .build();
Amazon SQSã§ã¯ãã¥ãŒãããŒãªã³ã°ããŠã¡ãã»ãŒãžã®ç£èŠãè¡ãã®ã§ãSQSMessageConsumer
ã«ã¯ãã¥ãŒã®URLãèšå®ããå¿
èŠã
ãããŸãã
SQSMessageConsumer#start
ã§ãã¥ãŒã®ç£èŠãå§ãŸããŸãã
public void start() { consumer.start(); }
åæ¢ã¯ãã¡ãã
public void stop() { consumer.close(); responder.shutdown(); sqsClient.close(); }
ã¯ã©ã€ã¢ã³ãåŽã¯ããã¹ãã³ãŒããšããŠäœæããŸãããŸãã¯é圢ããã
src/test/java/org/littlewings/sqs/TemporaryQueueRpcTest.java
package org.littlewings.sqs; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import com.amazonaws.services.sqs.AmazonSQSRequester; import com.amazonaws.services.sqs.AmazonSQSRequesterClientBuilder; import com.amazonaws.services.sqs.MessageContent; import org.junit.jupiter.api.Test; import software.amazon.awssdk.services.sqs.SqsClient; import software.amazon.awssdk.services.sqs.model.Message; import software.amazon.awssdk.services.sqs.model.SendMessageRequest; import static org.assertj.core.api.Assertions.assertThat; public class TemporaryQueueRpcTest { interface ThrowableRunnable { void run() throws Exception; } void withServer(String queueUrl, ThrowableRunnable runnable) throws Exception { TemporaryQueueRpcServer server = TemporaryQueueRpcServer.create(queueUrl); server.start(); try { runnable.run(); } finally { server.stop(); } } // ããã«ããã¹ããæžãïŒïŒ }
å ã»ã©æžãããµãŒããŒã®ãèµ·åãšåæ¢ãè¡ãã¡ãœãããä»ããŠããŸãã
ã§ã¯ãã¯ã©ã€ã¢ã³ãåŽãæžããŠã¿ãŸãã
@Test void simpleRpc() throws Exception { String queueUrl = "http://localhost:4566/000000000000/my-queue"; withServer(queueUrl, () -> { SqsClient sqsClient = LocalSqsBuilder.create(); AmazonSQSRequester requester = AmazonSQSRequesterClientBuilder .standard() .withAmazonSQS(sqsClient) .build(); try { SendMessageRequest request = SendMessageRequest .builder() .queueUrl(queueUrl) .messageBody("Hello World!!") .build(); Message responseMessage = requester.sendMessageAndGetResponse(request, 10, TimeUnit.SECONDS); MessageContent responseContent = MessageContent.fromMessage(responseMessage); System.out.println(responseMessage); System.out.println(responseContent.getMessageAttributes()); assertThat(responseContent.getMessageBody()).isEqualTo("â â â Hello World!!â â â "); } finally { requester.shutdown(); sqsClient.close(); } }); }
ã¡ãã»ãŒãžéä¿¡ãè¡ãã®ã¯ãAmazonSQSRequester
ãšãªããŸãã
AmazonSQSRequester requester = AmazonSQSRequesterClientBuilder .standard() .withAmazonSQS(sqsClient) .build();
AmazonSQSRequester#sendMessageAndGetResponse
ã§ãã¡ãã»ãŒãžéä¿¡ãããã§ã¯ãåæåŒã³åºããè¡ã£ãŠããŸãã
Message responseMessage = requester.sendMessageAndGetResponse(request, 10, TimeUnit.SECONDS);
çµæã¯ããã§1床åºåããŠããŸããã
System.out.println(responseMessage); System.out.println(responseContent.getMessageAttributes());
ããããããã¡ããš
Message(MessageId=2f0e38ea-8440-4247-ab9b-260306b29273, ReceiptHandle=NDA5OWM0ZWUtOTEzMi00YTUzLThlMDEtOTkyZGFiNGNiYzkxIGFybjphd3M6c3FzOnVzLWVhc3QtMTowMDAwMDAwMDAwMDA6X19SZXF1ZXN0ZXJDbGllbnRRdWV1ZXNfX2ViOTNlNDkyLWFhOGYtNGNlNC04NGI1LWZiZDQwMzllMDRkNC0wIDJmMGUzOGVhLTg0NDAtNDI0Ny1hYjliLTI2MDMwNmIyOTI3MyAxNjc1NjA5Njk2LjE5MDY1OQ==, MD5OfBody=b14f6a41b31fd409988bec57f9a1a5cb, Body=â â â Hello World!!â â â , Attributes={ApproximateReceiveCount=1, SentTimestamp=1675609696169, SenderId=000000000000, ApproximateFirstReceiveTimestamp=1675609696190}, MD5OfMessageAttributes=d3d6adc048d9998de883abfd0e923eca, MessageAttributes={__AmazonSQSVirtualQueuesClient.QueueName=MessageAttributeValue(StringValue=__RequesterClientQueues__2a96b18c-ab99-4e99-a48f-d6c4b92043f1, DataType=String)})
ãã¡ãã§ããã
{__AmazonSQSVirtualQueuesClient.QueueName=MessageAttributeValue(StringValue=__RequesterClientQueues__2a96b18c-ab99-4e99-a48f-d6c4b92043f1, DataType=String)}
åŒã³åºããçµãã£ãããããããåæ¢ã
requester.shutdown(); sqsClient.close();
éä¿¡ããã¡ãã»ãŒãžãå¢ãããŠã¿ã
次ã«ãéä¿¡ããã¡ãã»ãŒãžãå¢ãããŠãã¡ãããšRPCãšããŠåäœã§ããŠããã確èªããŠã¿ãããšæããŸãã
ãŸãã¯ãµãŒããŒåŽãã¡ãã»ãŒãžããšã«ãã©ã³ãã ã«ã¹ãªãŒãããããã«ããŠã¿ãŸãã
src/test/java/org/littlewings/sqs/TemporaryQueueRpcSCerver.java
// çç¥ public class TemporaryQueueRpcServer { // çç¥ public static TemporaryQueueRpcServer createRandomSleep(String queueUrl) { Random random = new Random(); random.nextInt(10); TemporaryQueueRpcServer server = new TemporaryQueueRpcServer(); SqsClient sqsClient = LocalSqsBuilder.create(); AmazonSQSResponder responder = AmazonSQSResponderClientBuilder.standard() .withAmazonSQS(sqsClient) .build(); SQSMessageConsumer consumer = SQSMessageConsumerBuilder .standard() .withAmazonSQS(sqsClient) .withQueueUrl(queueUrl) .withPollingThreadCount(5) .withConsumer(requestMessage -> { MessageContent requestContent = MessageContent.fromMessage(requestMessage); String requestBody = requestContent.getMessageBody(); MessageContent responseContent = new MessageContent("â â â " + requestBody + "â â â "); try { int sleepTime = random.nextInt(10); System.out.printf("Server[%s]: %d sec sleep%n", Thread.currentThread().getName(), sleepTime); TimeUnit.SECONDS.sleep(sleepTime); } catch (InterruptedException e) { // ignore } responder.sendResponseMessage(requestContent, responseContent); }) .build(); server.sqsClient = sqsClient; server.responder = responder; server.consumer = consumer; return server; } // çç¥ }
ååElasticMQã§è©Šããæã¯ããŒãªã³ã°ããã¹ã¬ããæ°ãå¢ãããšæåãäžå®å®ã«ãªããŸããããä»åã¯ããã¯ãªããŸããã§ããã
SQSMessageConsumer consumer =
SQSMessageConsumerBuilder
.standard()
.withAmazonSQS(sqsClient)
.withQueueUrl(queueUrl)
.withPollingThreadCount(5)
ã¡ãã»ãŒãžãåä¿¡ããåŸã¯ãã©ã³ãã ã«ã¹ãªãŒããããŠããŸãã
.withConsumer(requestMessage -> { MessageContent requestContent = MessageContent.fromMessage(requestMessage); String requestBody = requestContent.getMessageBody(); MessageContent responseContent = new MessageContent("â â â " + requestBody + "â â â "); try { int sleepTime = random.nextInt(10); System.out.printf("Server[%s]: %d sec sleep%n", Thread.currentThread().getName(), sleepTime); TimeUnit.SECONDS.sleep(sleepTime); } catch (InterruptedException e) { // ignore }
ç¶ããŠãã¯ã©ã€ã¢ã³ãåŽã50åã®ã¡ãã»ãŒãžãéãããã«ããŠã¿ãŸãã
src/test/java/org/littlewings/sqs/TemporaryQueueRpcTest.java
// çç¥ public class TemporaryQueueRpcTest { // çç¥ void withRandomSleepServer(String queueUrl, ThrowableRunnable runnable) throws Exception { TemporaryQueueRpcServer server = TemporaryQueueRpcServer.createRandomSleep(queueUrl); server.start(); try { runnable.run(); } finally { server.stop(); } } @Test public void concurrent() throws Exception { String queueUrl = "http://localhost:4566/000000000000/my-queue"; withRandomSleepServer(queueUrl, () -> { SqsClient sqsClient = LocalSqsBuilder.create(); AmazonSQSRequester requester = AmazonSQSRequesterClientBuilder .standard() .withAmazonSQS(sqsClient) .build(); try { Map<String, CompletableFuture<Message>> futures = new LinkedHashMap<>(); for (int i = 0; i < 50; i++) { String uuid = UUID.randomUUID().toString(); SendMessageRequest request = SendMessageRequest .builder() .queueUrl(queueUrl) .messageBody(uuid) .build(); CompletableFuture<Message> responseMessage = requester.sendMessageAndGetResponseAsync(request, 60, TimeUnit.SECONDS); futures.put(uuid, responseMessage); } assertThat(futures).hasSize(50); futures.forEach((uuid, message) -> { long start = System.currentTimeMillis(); MessageContent responseContent = MessageContent.fromMessage(message.join()); System.out.println("elapsed: " + (System.currentTimeMillis() - start) / 1000.0 + " sec"); assertThat(responseContent.getMessageBody()).isEqualTo("â â â " + uuid + "â â â "); }); } finally { requester.shutdown(); sqsClient.close(); } }); } }
éä¿¡ããã¡ãã»ãŒãžã®å 容ã¯ããªã¯ãšã¹ãããšã«å¥ã ã«ãªãããã«UUIDã«ããŠã¿ãŸããã
String uuid = UUID.randomUUID().toString(); SendMessageRequest request = SendMessageRequest .builder() .queueUrl(queueUrl) .messageBody(uuid) .build();
ã¡ãã»ãŒãžã®éä¿¡ã¯ãAmazonSQSRequester#sendMessageAndGetResponseAsync
ã䜿ã£ãŠéåæã«å€æŽã
CompletableFuture<Message> responseMessage = requester.sendMessageAndGetResponseAsync(request, 60, TimeUnit.SECONDS);
ããã§ãæåŸ ã®ã¡ãã»ãŒãžãåŠçã§ããŠããã確èªã
assertThat(futures).hasSize(50); futures.forEach((uuid, message) -> { long start = System.currentTimeMillis(); MessageContent responseContent = MessageContent.fromMessage(message.join()); System.out.println("elapsed: " + (System.currentTimeMillis() - start) / 1000.0 + " sec"); assertThat(responseContent.getMessageBody()).isEqualTo("â â â " + uuid + "â â â "); });
ãªã¯ãšã¹ããšã¬ã¹ãã³ã¹ã®å 容ã察ã«ãªã£ãŠããã®ã§ãä»åãRPCãšããŠåäœã§ããŠããããšã¯ç¢ºèªã§ããŸããã
ãã ãä»åã®ç°å¢ã§ã¯ã¡ãã»ãŒãžãããããéããšãµãŒããŒåŽã§ããçšåºŠã¡ãã»ãŒãžãåŠçããããªããšã¯ã©ã€ã¢ã³ãã«ã¡ãã»ãŒãžã
è¿ããªãããã§ãä»åã®æžãæ¹ã ãšæåã«éã£ãã¡ãã»ãŒãžãåãåããŸã§ã®æéãããªãé·ããªã£ããããŸãã
ãããå®å®ããŠããªãæããããã®ã§ããã®ãããã¯æ¬åœã«äœ¿ããªãã¡ãããšããAmazon SQSã§æåã確èªããæ¹ãè¯ãããã§ããã
ãšãããããAmazon SQS Java Temporary Queue Clientã®2.0.0ãšäžæãã¥ãŒã䜿ã£ãŠRPCãè¡ããšããç®çã¯éæã§ããã®ã§ã
è¯ããšããŸããã
ãŸãšã
2.0.0ã«ãªã£ãAmazon SQS Java Temporary Queue ClientãšLocalStackã䜿ã£ãŠãäžæãã¥ãŒãšRPCãè©ŠããŠã¿ãŸããã
ååã¯ãã£ããããã£ãã®ã§ãããä»åã¯ããã§ããªãã£ãã§ãããElasticMQã§ãªããŠããLocalStackã§ãåããŸãããã
ãã ãããããã¡ãã»ãŒãžãéã£ãæã®åããäžå®å®ãªã®ã¯ä»åãå€ãããªãã£ãã®ã§ãå®éã®æåã¯æ¬ç©ã䜿ã£ãŠç¢ºèªããšããã®ã
æ£è§£ã ãããšããæèŠãå€ãããŸããããããããããªã£ãã®ã¯è¯ãã£ãããªãšæããŸãã