Spring BootアプリからRedisを利用するon Heroku

# この記事について

Spring BootアプリーケーションをHeroku上に構築し、そこからRedisを利用する方法について書きます。公式の[Heroku Redis- Connecting In Java](https://devcenter.heroku.com/articles/heroku-redis#connecting-in-java)にも載ってはいるんですが、そのままだと情報不足だと感じたのでちょっと改良しRedisTemplateのBean定義まで含めて載せています。

#### 前提
- HerokuにPush済みのSpring Bootアプリーケーション(2系)があること

## Redisのセットアップ

こちらはHerokuの公式通りです。
RedisのAddOnを追加します。

```bash
$ heroku addons:create heroku-redis:hobby-dev -a <<APP_NAME>>
```

追加できると`heroku addons`で`heroku-redis`が出てくるようになります。
また、Heroku上では`REDIS_URL`という環境変数に接続情報がセットされています。

```bash
$ heroku config | grep redis
REDIS_URL:          redis://h:<<PASSWORD>>@<<HOST>>:<<PORT>>
```

この値は変わることがあるため、直接値をソース上に記載するのではなく`System.getenv("REDIS_URL")`などとして環境変数のまま扱ってあげる必要があります。


## Spring Data Redisの利用

SpringDataRedisを依存関係に追加します。

```xml:pom.xml
<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-redis</artifactId>
</dependency>
```

## Bean定義

Redisを利用するために以下のBean定義を作成します。

- `JedisPoolConfig`: コネクションプールの設定
- `JedisConnectionFactory`:[Jedis](https://github.com/xetorthio/jedis)ベースのコネクションを作成するファクトリ
- `RedisTemplate`: Redisへのアクセスを抽象化してくるクラス


```java:RedistConfigHeroku.java
@Profile("heroku")
@Configuration
public class RedistConfigHeroku {

    @Bean
    JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(10);
        poolConfig.setMaxIdle(5);
        poolConfig.setMinIdle(1);
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(true);
        poolConfig.setTestWhileIdle(true);
        return poolConfig;
    }

    @Bean
    public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig) throws URISyntaxException {
        String envRedisUrl = System.getenv("REDIS_URL");
        URI redisUri = new URI(envRedisUrl);

        RedisStandaloneConfiguration hostConfig = new RedisStandaloneConfiguration();
        hostConfig.setPort(redisUri.getPort());
        hostConfig.setHostName(redisUri.getHost());
        hostConfig.setPassword(redisUri.getUserInfo().split(":", 2)[1]);

        JedisClientConfiguration.JedisClientConfigurationBuilder builder = JedisClientConfiguration.builder();
        JedisClientConfiguration clientConfig = builder
                .usePooling()
                .poolConfig(jedisPoolConfig)
                .build();

        JedisConnectionFactory factory = new JedisConnectionFactory(hostConfig, clientConfig);
        return factory;
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(JedisConnectionFactory connectionFactory) {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setHashKeySerializer(redisTemplate.getKeySerializer());
        redisTemplate.setHashValueSerializer(redisTemplate.getValueSerializer());
        return redisTemplate;
    }
```

利用側の例は以下のようになります。

```java
@Service
public class SomeService {
  private final RedisTemplate<String, String> redisTemplate;

  // コンストラクタインジェクション
  public SomeService(RedisTemplate redisTemplate) {
    this.redisTemplate = redisTemplate;
  }

  public void doSomething() {   
    // write
    redisTemplate.opsForValue().set(key, value);

    // read
    String value = redisTemplate.opsForValue().get(key);
  }
}
```

## Profileの指定

この例ではHeroku用のConfigurationということで`heroku`プロファイルを定義し`@Profile("heroku")`としています。

このBean定義を読んであげるため、Procfileにも`-Dspring.profiles.active=heroku `を追加してあげます。(すでにプロファイルが存在するならそれも含めて)

```Procfile
web: java -jar -Dspring.profiles.active=heroku target/app.jar --server.port=${PORT}
```