[SpringBoot2.4.2] ユーザ登録画面を作る

src/main/resources/templates にinput.htmlファイルを作ります。
formはgetメソッドで、遷移先はinputconfirmとする
thymeleafを使う

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>社員登録</title>
<!-- 	<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css"> -->
	<link th:href="@{/css/style.css}" rel="stylesheet" type="text/css">
</head>
<body>
<h1>社員登録</h1>
<div class="col-md-8">
<form class="form-inline" method="get" action="inputconfirm">
	<div class="form-group">
	    <label class="control-label col-md-2">名前</label>
	    <div class="col-md-4">
	        <input type="text" name="name" class="form-control">
	    </div>
	</div>
	<div class="form-group">
        <label class="control-label col-md-2">所属部署</label>
        <div class="col-md-4">
            <input type="text" name="department" class="form-control">
        </div>
    </div>
    <br>
	<button type="submit" class="btn btn-primary">確認</button>
</form>
</div>

### SpringBootでのCSSファイル
sassで作ったcssファイルは src/main/resources/static 配下に置く

<link th:href="@{/css/style.css}" rel="stylesheet" type="text/css">

### MainController.java
– GetMappingでpathを指定する

package com.example.demo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/test1")
public class MainController {
	
	@GetMapping("input")
	public String input1() {
		return "test1/input";
	}
	
	@GetMapping("inputconfirm")
	public String output1(
			@RequestParam(name = "name") String name,
			@RequestParam(name = "department") String department,
			Model model) {
			model.addAttribute("name", name);
			model.addAttribute("department", department);
			return "test1/input_confirm";
	}
	
}

test1/input_confirm.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>社員登録 確認</title>
<!-- 	<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css"> -->
	<link th:href="@{/css/style.css}" rel="stylesheet" type="text/css">
</head>
<body>
<h1>社員登録 確認</h1>
<div class="col-md-8">
<p>登録内容を確認してください。</p>
<form class="">
<table class="table">
	<tr><td>名前</td><td th:text="${name}">狩野 良平</td></tr>
	<tr><td>所属</td><td th:text="${department}">営業部</td></tr>
</table>
</form>
<button type="button" class="btn btn-primary" onclick="location.href='/input.html'">戻る</button>
<button type="button" class="btn btn-primary" onclick="location.href='/input_complete.html'">登録完了</button>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.1/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.0-alpha1/js/bootstrap.min.js"></script>
</body>
</html>

あれ? もしかして上手くいってる??

[RabbitMQ] Publish/Subscribe

Listing exchanges
$ sudo rabbitmqctl list_exchanges
Listing exchanges …
direct
amq.direct direct
amq.fanout fanout
amq.headers headers
amq.match headers
amq.rabbitmq.log topic
amq.rabbitmq.trace topic
amq.topic topic
…done.

emit_log.php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare('logs', 'fanout', false, false, false);

$data = implode('', array_slice($argv, 1));
if(empty($data)){
	$data = "info:Hello World!";
}
$msg = new AMQPMessage($data);
$channel->basic_publish($msg, 'logs');

echo '[x]Sent', $data, "\n";

$channel->close();
$connection->close();

receive_logs.php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare('logs', 'fanout', false, false, false);

list($queue_name,,) = $channel->queue_declare("", false, false, true, false);

$channel->queue_bind($queue_name, 'logs');

echo " [*] Waiting for messages. To exit press CTRL+C\n";

$callback = function ($msg) {
  echo ' [x]', $msg->body, "\n";
};

$channel->basic_consume($queue_name, '', false, true, false, false, $callback);

while ($channel->is_consuming()) {
    $channel->wait();
}

$channel->close();

$ php receive_logs.php
PHP Fatal error: Uncaught PhpAmqpLib\Exception\AMQPProtocolChannelException: NOT_FOUND – no queue ‘logs’ in vhost ‘/’ in /home/vagrant/dev/mailer/vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AMQPChannel.php:216

なんやこれは。。。よーわからん。。

[RabbitMQ] Work Queues

new_task.php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare('hello', false, false, false, false);

$data = implode('', array_slice($argv, 1));
if(empty($data)){
	$data = "Hello World!";
}
$msg = new AMQPMessage($data);

$channel->basic_publish($msg, '', 'hello');

echo '[x] Sent', $data, "\n";


$channel->close();
$connection->close();

worker.php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare('hello', false, false, false, false);

echo " [*] Waiting for messages. To exit press CTRL+C\n";

$callback = function ($msg) {
  echo ' [x] Received ', $msg->body, "\n";
  sleep(substr_count($msg->body,'.'));
  echo "[x] Done\n";
};

$channel->basic_consume('hello', '', false, true, false, false, $callback);

while ($channel->is_consuming()) {
    $channel->wait();
}

$ php new_task.php “A very hard task which takes two seconds..”
[x] SentA very hard task which takes two seconds..

$ php worker.php
[*] Waiting for messages. To exit press CTRL+C
[x] Received Hello World!
[x] Done
[x] Received Hello World!
[x] Done
[x] Received A very hard task which takes two seconds..
[x] Done

worker.phpのshellを複数立てると、分散して処理される
[vagrant@localhost mailer]$ php new_task.php First message.
[x] SentFirstmessage.
[vagrant@localhost mailer]$ php new_task.php Second message..
[x] SentSecondmessage..
[vagrant@localhost mailer]$ php new_task.php Third message…
[x] SentThirdmessage…
[vagrant@localhost mailer]$ php new_task.php Fourth message….
[x] SentFourthmessage….

$ php worker.php
[*] Waiting for messages. To exit press CTRL+C
[x] Received Secondmessage..
[x] Done
[x] Received Fourthmessage….
[x] Done
すげえ

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare('task_queue', false, true, false, false);

$data = implode('', array_slice($argv, 1));
if(empty($data)){
	$data = "Hello World!";
}
$msg = new AMQPMessage(
	$data,
	array('delivery_mode'=> AMQPMessage::DELVERY_MODE_PERSISTENT)
);

$channel->basic_publish($msg, '', 'task_queue');

echo '[x] Sent', $data, "\n";


$channel->close();
$connection->close();
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare('task_queue', false, true, false, false);

echo " [*] Waiting for messages. To exit press CTRL+C\n";

$callback = function ($msg) {
  echo ' [x] Received ', $msg->body, "\n";
  sleep(substr_count($msg->body,'.'));
  echo "[x] Done\n";
  $msg->ack();
};

$channel->basic_qos(null, 1, null);
$channel->basic_consume('task_queue', '', false, false, false, false, $callback);

while ($channel->is_consuming()) {
    $channel->wait();
}

$channel->close();
$connection->close();

queueの振る舞いだが、わかったようでわからんな。。

RabbitMQとは

RabbitMQとは
-> pivotalによって開発されているオープンソースメッセージブローカー
-> AMQP, STOMP, MQTT, HTTPに対応

AMAPとは?
– メッセージ転送に使われる通信プロトコル

Exchange
– Exchange は、生成されたMessageを受け取る役割
Binding
– ExchangeとMessageQueueの対応付け
Message Queue
– Messageを蓄積し、Consumer Applicationnに引き渡す

Publisher Applicationによって生成されたMessageは、Exchangeに渡される
ExchangeはBindingに基づいて、Messageを適切なMessage Queueに引き渡す
Consumer Applicationによって、Message QueueからMessageが取り出される

Direct
– Direct な Exchange は、Messageに付与されているrouting keyと、bindingに設定されているbinding key を見て、routing key = binding key となるような、Message Queue に Message を配送
Fanout
– Fanout なExchangeは、bindされているMessage Queueすべてに受け取ったメッセージの配送
Topic
– メッセージにrouting keyを指定して、そのkeyを元に配送するMessage Queueを選択

RabbitMQは、オープンソースでErlangで記述

公式サイト: https://www.rabbitmq.com/

ちょっと理解するのに時間がかかりそう

RabbitMQでMailerQを実行する

$ sudo yum install php-pecl-amqp

setting.php

$address = 'amqp://guest:guest@192.168.33.10';
$outbox = 'outbox_test';
$resultbox = 'results_test';

$recipientDomain = 'gmail.com';
$recipientEmail = 'hogehoge@gmail.com';
$fromAddress = 'info@hpscript.com';

send.php

require_once('settings.php');

try {
	$connection = new AMQPConnection(array(
		'host' = $hostname,
		'login' => $username,
		'password' => $password
	));

	$connection->connect();
}
catch(AMQPException $exception){
	echo "Could not establish a connection to the RabbitMQ server.\n";
}

try {
	$channel = new AMQPChannel($connection);
} catch (AMQPConnectionException $exception){
	echo "Connection to the broker was lost(creating channel).\n";
}

try {
	$exchange = new AMQPExchange($channel);
	$exchange->setName('exchange1');
	$exchange->setType('fanout');
	$exchange->declareExchange();
} catch (AMQPExchangeException $exception){
	echo "CHannel is not connected to broker(declaring exchange).\n";
} catch (AMQPConnectionException $exception){
	echo "Connection to the broker was lost(declaring exchange).\n";
}

try {
	$queue = new AMQPQueue($channel);
	$queue->setName($outbox);
	$queue->setFlags(AMQP_DURABLE);
	$queue->declareQueue();
	$queue->bind('exchange1','key2');
} catch (AMQPQueueException $exception){
	echo "Channel is not connected to a broker(creating queue).\n";
} catch (AMQPConnectionException $exception){
	echo "Connection to the broker was lost.(creating queue)/\n";
}

$jsonMessage = json_encode(array(
	'envelope'=>$fromAddress,
	'recipient' => $recipientEmail,
	'mime' => "From:".$fromAddress."\r\n"
		. "To:". $recipientEmail . "\r\n"
		. "Subject: Example subject\r\n\r\n"
		. "This is the example message text"
));

try {
	$message = $exchange->publish($jsonMessage,'key2');
} catch (AMQPExchangeException $exception){
	echo "Channel is not connected to a broker(publishing message on queue).\n";
} catch (AMQPConnectionException $exception){
	echo "Connection to the broker was lost(publishing message on queue).\n";
} catch (AMQPChannelException $exception){
	echo "The channel is not open(publishing message on queue).\n";
}

$connection->disconnect();
require_once('settings.php');

try {
	$connection = new AMQPConnection(array(
		'host' => $hostname,
		'login' => $username,
		'password' => $password,
		'vhost' => $vhost

	));

	$connection->connect();
} catch (AMQPException $exception){
	echo "Could not establish a connection to the RabbitMQ servier.\n";
}

try {
	$channel = new AMQPChannel($connection);
} catch (AMQPConnectionException $exception){
	echo "Connection to the broker was lost(creating channel).\n";
}

try {
	$queue = new AMQPQueue($channel);
	$queue->setName($resultbox);
	$queue->setFlags(AMQP_DURABLE);
	$queue->bind('exchange1','key2');
	$queue->declareQueue();
} catch(AMQPQueueException $exception){
	echo "Channel is not connected to a broker(creating queue).\n";
} catch (AMQPConnectionException $exception){
	echo "Connection to the broker was lost.(creating queue)/\n";
}

while ($envelope = $queue->get()){
	echo "Received:\n";
	echo $envelope->getBody(). "\n";
	echo "----------------------------------------------\n\n";
}

$connection->disconnect();

$ php result.php
PHP Fatal error: Uncaught Error: Class ‘AMQPConnection’ not found in /home/vagrant/dev/mailer/result.php:6
Stack trace:
#0 {main}
thrown in /home/vagrant/dev/mailer/result.php on line 6 

ん? どういうこと?
RabbitMQの使い方がよくわかってないな。。

[AmazonLinux2] RabbitMQをインストールして実行する

$ sudo yum -y update
// epelをインストール
$ sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
$ sudo yum-config-manager –enable epel
// erlangをインストール
$ sudo yum install erlang –enablerepo=epel
$ sudo yum install yum-plugin-versionlock
$ sudo yum versionlock gcc-*
$ sudo yum install -y socat
$ sudo yum install logrotate
$ sudo rpm –import https://dl.bintray.com/rabbitmq/Keys/rabbitmq-release-signing-key.asc
$ sudo yum install rabbitmq-server

$ sudo rabbitmq-plugins enable rabbitmq_management
$ chkconfig rabbitmq-server on
$ sudo systemctl start rabbitmq-server
$ systemctl status rabbitmq-server
● rabbitmq-server.service – RabbitMQ broker
Loaded: loaded (/usr/lib/systemd/system/rabbitmq-server.service; disabled; vendor preset: disabled)
Active: active (running) since 火 2021-02-16 09:31:04 JST; 3s ago
Main PID: 28564 (beam)
CGroup: /system.slice/rabbitmq-server.service
├─28564 /usr/lib64/erlang/erts-5.10.4/bin/beam -W w -K true -A30 -P 1048576 — -root /u…
├─28636 inet_gethost 4
└─28637 inet_gethost 4
$ sudo rabbitmqctl list_queues
Listing queues …
…done.

$ php send.php
[x] sent ‘Hello World!’
$ php recieve.php
[*] Waiting for messages. To exit press CTR+C
[x] Received Whats up

ぎゃあああああああああああああああああああああああ
助けてくれええええええええええええええええええええええええええええ

RabbitMQの送受信

送信側
send.php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

// コネクションの確立
$connection = new AMQPStreamConnection('192.168.33.10', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare('hello', false, false, false, false);

$msg = new AMQPMessage('Whats up');

$channel->basic_publish($msg, '', 'hello');

echo "[x] sent 'Hello World!'\n";

$channel->close();

受信側

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('192.168.33.10', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare('hello', false, false, false, false);

echo "[*] Waiting for messages. To exit press CTR+C\n";

$callback = function($msg){
	echo '[x] Received ', $msg->body, "\n";
};

$channel->basic_consume('hello', '', false, true, false, false, $callback);

while(count($channel->callbacks)){
	$channel->wait();
}

$ php recieve.php
PHP Fatal error: Uncaught PhpAmqpLib\Exception\AMQPIOException: stream_socket_client(): unable to connect to tcp://192.168.33.10:5672 (Connection refused) in /home/vagrant/dev/mailer/vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/StreamIO.php:111

サーバにRabbitMQをインストールしないとダメなのか。。

MailerQを使いたい

MailerQを使ってメール配信したい。
公式サイトを見ると、AMQPが必要らしい。
で、AMQPにはbcmatchが必要とのことで、bcmatchをインストールする

$ php -v
PHP 7.4.11 (cli) (built: Oct 21 2020 19:12:26) ( NTS )
$ sudo yum list | grep php
// 省略
php.x86_64 7.4.14-1.amzn2 amzn2extra-php7.4
php-bcmath.x86_64 7.4.14-1.amzn2 amzn2extra-php7.4
$ sudo yum install php-bcmath

AMQPとは?
-> Advanced Message Queuing Protocol(AMQP)の略
-> メッセージ指向ミドルウェアのオープンスタンダードなアプリケーション層プロトコル
-> AMQPはメッセージングプロバイダとクライアントに、HTTPなどの手法と同じように異なるベンダ間で正しく相互運用できるような振る舞いを要求

Github: https://github.com/php-amqplib/php-amqplib
This library is a pure PHP implementation of the AMQP 0-9-1 protocol. It’s been tested against RabbitMQ.
The library was used for the PHP examples of RabbitMQ in Action and the official RabbitMQ tutorials.
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

$ composer require php-amqplib/php-amqplib
$ ls
composer.json composer.lock vendor

send_msg.php

require_once __DIR__ . '/vendor/autoload.php';

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

$connection = new AMQPStreamConnection('192.168.33.10', 8000, 'password', '/my_vhost');

$channel = $connection->channel();
$channel->queue_declar('Hello_World', false, false, false, false);

$msg = new AMQPMessage('Hello RabbitMQ World!');
$channel->basic_publish($msg, '', 'Hello_World');
echo " [x] Sent 'Hello_World'\n";

$channel->close();
$connection->close();

$ php send_msg.php
PHP Fatal error: Uncaught PhpAmqpLib\Exception\AMQPIOException: stream_socket_client(): unable to connect to tcp://192.168.33.10:8000 (Connection refused) in /home/vagrant/dev/mailer/vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/StreamIO.php:111

ん? なんやこれ、どういうことだ。。。

[Laravel8.x] web.phpとapi.phpの違い

Routeにはweb.phpとapi.phpがある。

web.php
-> ブラウザからHTTPリクエストを受けて、画面に表示するようなルーティングを設定する場合に使用する

api.php
-> 外部からのHTTPリクエストを受けて値を返却するようなルーティング
-> ミニマムなミドルウェアが適用される

kernel.php
-> apiは、VerifyCsrfTokenが入っていない

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

キャッシュとして失敗回数を保存しているのかな。
apiで認証しているのはわかるが、イマイチ理解できんな。。