Skip to content

Blog

How to connect to multiple redis server when using spring boot ?

Purpose

In this article , I will demo how to connect to multiple redis servers using spring boot. Just as following picture shows, if you don’t know how to setup two redis servers in your environment, you can refer to this post.

image-20210305165358100

Environment

  • Spring boot 2+
  • Jdk 8+
  • Redis

Prepare the test data in redis

I deployed two redis docker container bw-redis1 and bw-redis2, now I use command line to set some values in them:

(base) [root@local ~]# docker exec -it bw-redis1 redis-cli
127.0.0.1:6379> set k1 redis1
OK
127.0.0.1:6379> get k1
"redis1"
127.0.0.1:6379> exit
(base) [root@local ~]# docker exec -it bw-redis2 redis-cli
127.0.0.1:6379> set k1 redis2
OK
127.0.0.1:6379> get k1
"redis2"
127.0.0.1:6379> exit
(base) [root@local ~]#

The Project Layout

The layout of this demo project is as follows, all the example code can be found here:

spring-boot-multiredis git:(master) tree .
.
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │   └── bswen
│   │   │   └── sbmr
│   │   │   ├── MultiRedisApplication.java
│   │   │   ├── config
│   │   │   │   ├── Redis1Configuration.java
│   │   │   │   ├── Redis1Property.java
│   │   │   │   ├── Redis2Configuration.java
│   │   │   │   ├── Redis2Property.java
│   │   │   │   └── RedisCommonProperty.java
│   │   │   └── service
│   │   │   ├── MultiRedisTestRunner.java
│   │   └── resources
│   │   └── application.properties

The Pom

We use maven to define the dependencies, I excluded the default lettuce sdk, because it’s not stable. I’d rather use the jedis sdk instead.

pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<artifactId>spring-boot-multiredis</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>

The code

1. Config file

Here in the src/main/resources/application.properties, I defined two redis server configurations,

src/main/resources/application.properties
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database=0
spring.redis2.host=localhost
spring.redis2.port=26379
spring.redis2.database=0

Here we define two redis connection, one is localhost:6379, the other is localhost:26379. You should change them to your real configurations.

2. Property config classes

To read the above properties file into spring boot, we must define configuration classes:

  1. At first, we define a common class as the base class of redis configuration classes
src/main/java/com.bswen.sbmr/config/RedisCommonProperty.java
public class RedisCommonProperty {
private String host;
private int port;
private int database;
//getters setters
}
  1. Then, we define Redis1Property and Redis2Property class to denote the different redis configurations
src/main/java/com.bswen.sbmr/config/Redis1Property.java
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
public class Redis1Property extends RedisCommonProperty {
}

And this:

src/main/java/com.bswen.sbmr/config/Redis2Property.java
@Configuration
@ConfigurationProperties(prefix = "spring.redis2")
public class Redis2Property extends RedisCommonProperty {
}

Pay attention to the ConfigurationProperties annotation, we use it to load different properties from the application.properties file.

  1. Thirdly, we should define how to get the RedisTemplate and RedisConnectionFactory from above properties

For redis server1, we define Redis1Configuration:

src/main/java/com.bswen.sbmr/config/Redis1Configuration.java
@Configuration
public class Redis1Configuration {
@Autowired
private Redis1Property redis1Property;
@Primary
@Bean(name = "redis1ConnectionFactory")
public RedisConnectionFactory userRedisConnectionFactory() {
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
redisConnectionFactory.setHostName(redis1Property.getHost());
redisConnectionFactory.setPort(redis1Property.getPort());
redisConnectionFactory.setDatabase(redis1Property.getDatabase());
return redisConnectionFactory;
}
@Bean(name = "redis1StringRedisTemplate")
public StringRedisTemplate userStringRedisTemplate(@Qualifier("redis1ConnectionFactory") RedisConnectionFactory cf) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(cf);
return stringRedisTemplate;
}
@Bean(name = "redis1RedisTemplate")
public RedisTemplate userRedisTemplate(@Qualifier("redis1ConnectionFactory") RedisConnectionFactory cf) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(cf);
return stringRedisTemplate;
}
}

To avoid the following error:

***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean named 'redisTemplate' that could not be found.

We need to change Redis1Configuration to this:

src/main/java/com.bswen.sbmr/config/Redis1Configuration.java
@Configuration
public class Redis1Configuration {
@Autowired
private Redis1Property redis1Property;
@Primary
@Bean(name = "redis1ConnectionFactory")
public RedisConnectionFactory userRedisConnectionFactory() {
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
redisConnectionFactory.setHostName(redis1Property.getHost());
redisConnectionFactory.setPort(redis1Property.getPort());
redisConnectionFactory.setDatabase(redis1Property.getDatabase());
return redisConnectionFactory;
}
@Bean(name = "redis1StringRedisTemplate")
public StringRedisTemplate userStringRedisTemplate(@Qualifier("redis1ConnectionFactory") RedisConnectionFactory cf) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(cf);
return stringRedisTemplate;
}
@Bean(name = "redis1RedisTemplate")
@Bean(name = {"redis1RedisTemplate","redisTemplate"})
public RedisTemplate userRedisTemplate(@Qualifier("redis1ConnectionFactory") RedisConnectionFactory cf) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(cf);
return stringRedisTemplate;
}
}

Because springboot2 needs a default bean named redisTemplate, so we reuse the redis1RedisTemplate bean, add a name redisTemplate to it.

For redis server2, we define Redis2Configuration:

src/main/java/com.bswen.sbmr/config/Redis2Configuration.java
@Configuration
public class Redis2Configuration {
@Autowired
private Redis2Property redis2Property;
@Bean(name = "redis2ConnectionFactory")
public RedisConnectionFactory roleRedisConnectionFactory() {
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
redisConnectionFactory.setHostName(redis2Property.getHost());
redisConnectionFactory.setPort(redis2Property.getPort());
redisConnectionFactory.setDatabase(redis2Property.getDatabase());
return redisConnectionFactory;
}
@Bean(name = "redis2StringRedisTemplate")
public StringRedisTemplate roleStringRedisTemplate(@Qualifier("redis2ConnectionFactory") RedisConnectionFactory cf) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(cf);
return stringRedisTemplate;
}
@Bean(name = "redis2RedisTemplate")
public RedisTemplate roleRedisTemplate(@Qualifier("redis2ConnectionFactory") RedisConnectionFactory cf) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(cf);
//setSerializer(stringRedisTemplate);
return stringRedisTemplate;
}
}

Now we have setup the properties and the configurations , we have beans of RedisTemplate , we can test it as follows:

src/main/java/com.bswen.sbmr/service/MultiRedisTestRunner.java
@Component
public class MultiRedisTestRunner implements CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(MultiRedisApplication.class);
@Autowired
@Qualifier("redis1StringRedisTemplate")
private StringRedisTemplate stringRedisTemplate;
@Autowired
@Qualifier("redis2StringRedisTemplate")
private StringRedisTemplate stringRedis2Template;
//@Override
public void run(String... strings) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
try {
testRedis1();
testRedis2();
}finally {
latch.await();
}
}
private void testRedis2() {
try {
for (int i = 1; i < 2; i++) {
logger.info("=====================================================================");
logger.info("start loop redis 2:" + i);
String key = "k" + i;
String primaryKeyValue = stringRedis2Template.opsForValue().get(key);
logger.info("=====================================================================");
logger.info(String.format("read from the redis2, key %s value is %s", key, primaryKeyValue));
}
}finally {
}
}
private void testRedis1() {
try {
for (int i = 1; i < 2; i++) {
logger.info("=====================================================================");
logger.info("start loop redis 1:" + i);
String key = "k" + i;
String primaryKeyValue = stringRedisTemplate.opsForValue().get(key);
logger.info("=====================================================================");
logger.info(String.format("read from the redis1, key %s value is %s", key, primaryKeyValue));
}
}finally {
}
}
}

Run it, we get this result:

Terminal window
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.2)
2024-12-30 18:05:56.099 WARN 49528 --- [ main] o.s.boot.StartupInfoLogger : InetAddress.getLocalHost().getHostName() took 5002 milliseconds to respond. Please verify your network configuration (macOS machines may need to add entries to /etc/hosts).
2024-12-30 18:06:01.116 INFO 49528 --- [ main] com.bswen.sbr2.MultiRedisApplication2 : Starting MultiRedisApplication2 using Java 1.8.0_341 on MacBook-Pro-zzz.local with PID 49528 (/Users/zzz/....../bswen-project/spring-boot-multiredis2/target/classes started by zzz in /Users/zzz/private/bw/bswen-github/bswen-project)
2024-12-30 18:06:01.117 INFO 49528 --- [ main] com.bswen.sbr2.MultiRedisApplication2 : No active profile set, falling back to default profiles: default
2024-12-30 18:06:01.509 INFO 49528 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2024-12-30 18:06:01.512 INFO 49528 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2024-12-30 18:06:01.533 INFO 49528 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 6 ms. Found 0 Redis repository interfaces.
2024-12-30 18:06:02.058 INFO 49528 --- [ main] com.bswen.sbr2.MultiRedisApplication2 : Started MultiRedisApplication2 in 21.403 seconds (JVM running for 21.935)
2024-12-30 18:06:02.060 INFO 49528 --- [ main] c.b.sbr2.service.MultiRedisTestRunner : =====================================================================
2024-12-30 18:06:02.060 INFO 49528 --- [ main] c.b.sbr2.service.MultiRedisTestRunner : start loop redis 1:1
2024-12-30 18:06:02.187 INFO 49528 --- [ main] c.b.sbr2.service.MultiRedisTestRunner : =====================================================================
2024-12-30 18:06:02.188 INFO 49528 --- [ main] c.b.sbr2.service.MultiRedisTestRunner : read from the redis1, key k1 value is redis1
2024-12-30 18:06:02.188 INFO 49528 --- [ main] c.b.sbr2.service.MultiRedisTestRunner : =====================================================================
2024-12-30 18:06:02.188 INFO 49528 --- [ main] c.b.sbr2.service.MultiRedisTestRunner : start loop redis 2:1
2024-12-30 18:06:03.286 INFO 49528 --- [ main] c.b.sbr2.service.MultiRedisTestRunner : =====================================================================
2024-12-30 18:06:03.287 INFO 49528 --- [ main] c.b.sbr2.service.MultiRedisTestRunner : read from the redis2, key k1 value is redis2

It works!

Summary

In this post, I demonstrated how to connect to multiple redis server instances with spring boot 2, the key point is that you should setup specific configuration for each redis server connection. Thanks for your reading. All code of the project is uploaded to github.com, you can download at this link.

Final Words + More Resources

My intention with this article was to help others who might be considering using multiple redis in one spring boot application. So I hope that’s been the case here. If you still have any questions, don’t hesitate to ask me by email: [email protected].

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

⭐️ Thanks For Your Support 🙏

How to deploy or setup multiple redis servers in one machine using docker ?

Purpose

This post will show you how to deploy and setup multiple redis servers in one machine. Just as the following picture shows, I will install two redis servers on my server.

diagram shows how to deploy multiple redis servers in one machine

Environment

  • Linux or MacOS

Solution

Step 1: Install docker

You can follow this guide to install docker on your server.

Step 2: Start redis server1

Now let’s start redis server 1

Terminal window
docker run --name my-redis -p 6379:6379 --restart always --detach redis

Let’s check the command’s options:

Option 1: use -p to map docker port to host port

-P : Publish all exposed ports to the host interfaces
-p=[] : Publish a container's port or a range of ports to the host
format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
Both hostPort and containerPort can be specified as a
range of ports. When specifying ranges for both, the
number of container ports in the range must match the
number of host ports in the range, for example:
-p 1234-1236:1234-1236/tcp

So for redis1 server, I am listening port 6379 on my host and it is mapped to docker port 6379.

Option 2: use --restart to make it always running

Using the --restart flag on Docker run you can specify a restart policy for how a container should or should not be restarted on exit.

alwaysAlways restart the container regardless of the exit status. When you specify always, the Docker daemon will try to restart the container indefinitely. The container will also always start on daemon startup, regardless of the current state of the container.

Option 3: use --detach or -d to run in background

When starting a Docker container, you must first decide if you want to run the container in the background in a “detached” mode or in the default foreground mode:

-d=false: Detached mode: Run container in the background, print new container id

To start a container in detached mode, you use -d=true or just -d option. By design, containers started in detached mode exit when the root process used to run the container exits,

Now let’s check the running redis:

Terminal window
main git:(master) docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
61c10b3a6470 redis "docker-entrypoint.s…" 13 seconds ago Up 11 seconds 0.0.0.0:6379->6379/tcp my-redis
main git:(master) lsof -i:6379
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
com.docke 26624 bswen 22u IPv4 0x444a1d7b8d308949 0t0 TCP *:6379 (LISTEN)
com.docke 26624 bswen 23u IPv6 0x444a1d7b79382209 0t0 TCP localhost:6379 (LISTEN)
main git:(master)

We can see that the redis1 server in docker is running, it’s listenning on port 6379.

Step 3: start redis server 2

Now let’s deploy redis server 2 named my-redis2, I will change the listenning port to 26379, but the docker port is still 6379.

Terminal window
docker run --name my-redis2 -p 26379:6379 --restart always --detach redis

You can see that we only changed the hostport to 26379 and the name of the docker instance to my-redis2.

Let’s verify it:

➜ main git:(master) docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cb1e517e0a0f redis "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:26379->6379/tcp my-redis2
61c10b3a6470 redis "docker-entrypoint.s…" 43 seconds ago Up 41 seconds 0.0.0.0:6379->6379/tcp my-redis

It works!

Final Words + More Resources

My intention with this article was to help others who might be considering deploying their own redis. So I hope that’s been the case here. If you still have any questions, don’t hesitate to ask me by email: [email protected].

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

⭐️ Thanks For Your Support 🙏

How to check website or url health periodically and automatically by using python script?

1. Purpose

In this post, I will demo how to create a python script to check a url(website/webservice)‘s health periodically and automatically .

2. Environment

  • Python 2.x or 3.x
  • Linux system

3. The solution

3.1 The environment

Suppose we hava a web service which provides some sort of services to users, now we want it to run always and if it’s not healthy, we should restart the service automatically.

The service URL prefix is:

http://localhost:8080/myservice

And we also have a restart script for this service located at:

/opt/myservice/restart.sh

3.2 Add a health check endpoint for our service

For health check purpose, we should add a dedicated endpoint to be accessed by the health check script, it should be fast , here is our implementation in java language:

@RequestMapping(method = RequestMethod.GET,value = "hello",headers = "Accept=application/json")
public @ResponseBody String hello() {
return "world";
}

It means that if we caccess the below url:

http://localhost:8080/myservice/hello

We should get this result(http response status code equals 200):

world

Otherwise , the status code should not be 200 , just as the picture shows:

image-20210616152439563

It health check fails, the restart script should be run immediately.

3.3 Setup the logger and imports for python script

Before coding the business, we should setup the environment for our script, just as follows:

import time
import os
import logging
from logging.handlers import TimedRotatingFileHandler
#setup the loggers for daily logging file rolling
logger = logging.getLogger()
logger.setLevel(logging.INFO)
file_handler = TimedRotatingFileHandler("healthcheck_myservice.log", when="midnight", interval=1)
file_handler.suffix = "%Y%m%d"
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(file_handler)
# global variables.
THE_URL = "http://localhost:8080/myservice/hello"
RESTART_SH = '/opt/myservice/restart.sh'
SLEEP_INTERVAL = 60

In the above code, we use the logging module in python to setup a daily rolling file handler for our program, it would create a log file everyday with the suffix year-month-day. You can view more information about the TimedRotatingFileHandler from this website.

The parameter when="midnight" in TimedRotatingFileHandler means that it would Roll over at midnight.

3.4 Implement the health check function with python

First, we need a function to check the status of the service, just as follows:

def health_check(url):
try:
logger.info("start access url %s" % (url))
import urllib
a=urllib.urlopen(url) #this is python2 version, the python3 compatible version is listed below
if a:
if a.getcode()==200:
lines = a.readlines()
if lines is not None and len(lines)==1:
if lines[0]=='world':
logger.info("health ok")
return True
else:
logger.info("warning code:"+str(a.getcode()))
except Exception as e:
logger.error('health check failed %s ' % (str(e)))
logger.info("health not ok")
return False

You can see that , the above function would return True only when the service response code is 200 and the responsecontent is world, or else, it would return False to the caller.

For python 2.x , we can use:

import urllib
urllib.urlopen(url)

For python 3.x , you can replace the urllib.urlopen with follows:

import urllib.request
with urllib.request.urlopen(url) as response:
html = response.read()

3.5 Implement the periodically checking logic

Now we should create a function to run the health check periodically and restart the service automatically if the check fails.

def check_periodically(url,failed_action_func):
while True:
try:
time.sleep(SLEEP_INTERVAL)
logger.info("check awake, start check")
if not health_check(url):
failed_action_func()
except Exception as e:
logger.error('check periodically failed %s ' % (str(e)))
pass
def restart_server():
do_script(RESTART_SH)
pass
def do_script(the_scripts):
logger.info("=========start script %s======"%the_scripts)
result = os.popen(the_scripts).read()
logger.info(result)
logger.info("=========end script %s======" % the_scripts)
pass

3.6 The main function

At last, we should provide the main function to execute the whole script:

if __name__ == '__main__':
check_periodically(THE_URL,restart_server)
pass

3.7 Save the script and execute it in background

Then we save the above script as healthcheck_myservice.py, and execute in background as follows:

Terminal window
$ nohup python healthcheck_myservice.py > healthcheck_myservice.out &

Then we can check the log as follows:

Terminal window
$ tail -f healthcheck_myservice.log

We can get these logs:

Terminal window
2021-06-16 15:54:23,775 - root - INFO - health ok
2021-06-16 15:55:23,834 - root - INFO - check awake, start check
2021-06-16 15:55:23,834 - root - INFO - start access url http://localhost:8080/myservice/hello
2021-06-16 15:55:23,839 - root - INFO - health ok
2021-06-16 15:56:23,899 - root - INFO - check awake, start check
2021-06-16 15:56:23,899 - root - INFO - start access url http://localhost:8080/myservice/hello
2021-06-16 15:56:23,906 - root - INFO - health ok

It works!

4. The whole script

#check if my service is running fine, or else, it would restart it
import urllib
import time
import os
import logging
from logging.handlers import TimedRotatingFileHandler
logger = logging.getLogger()
logger.setLevel(logging.INFO)
file_handler = TimedRotatingFileHandler("healthcheck_myservice.log", when="midnight", interval=1)
file_handler.suffix = "%Y%m%d"
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(file_handler)
#logger.addHandler(ch)
THE_URL = "http://localhost:8080/myservice/hello"
RESTART_SH = '/opt/myservice/restart.sh'
SLEEP_INTERVAL = 60
#perform the health check, return True if the url is accessible.
def health_check(url):
try:
logger.info("start access url %s" % (url))
a=urllib.urlopen(url)
if a:
if a.getcode()==200:
lines = a.readlines()
if lines is not None and len(lines)==1:
if lines[0]=='world':
logger.info("health ok")
return True
else:
logger.info("warning code:"+str(a.getcode()))
except Exception as e:
logger.error('health check failed %s ' % (str(e)))
logger.info("health not ok")
return False
#check the health for specified period
def check_periodically(url,failed_action_func):
while True:
try:
time.sleep(SLEEP_INTERVAL)
logger.info("check awake, start check")
if not health_check(url):
failed_action_func()
except Exception as e:
logger.error('check periodically failed %s ' % (str(e)))
pass
def restart_server():
do_script(RESTART_SH)
pass
def do_script(the_scripts):
logger.info("=========start script %s======"%the_scripts)
result = os.popen(the_scripts).read()
logger.info(result)
logger.info("=========end script %s======" % the_scripts)
pass
if __name__ == '__main__':
check_periodically(THE_URL,restart_server)
pass

5. Summary

In this post, I demonstrated how to use python to do health check job in linux.

How to solve HUGGINGFACEHUB_API_TOKEN error when trying to access huggingface models?

1. Purpose

In this post, I will show you how to solve HUGGINGFACEHUB_API_TOKEN error when trying to access huggingface models, here is the detailed error messages:

error:

Terminal window
(langchain_helloworld_env) helloworld git:(master) python test2.py
/Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages/langchain/__init__.py:34: UserWarning: Importing HuggingFaceHub from langchain root module is no longer supported. Please use langchain.llms.HuggingFaceHub instead.
warnings.warn(
Traceback (most recent call last):
File "/Users/bswen/work/python/myutils/learn_langchain/helloworld/test2.py", line 7, in <module>
llm = HuggingFaceHub(repo_id="bigscience/bloom-1b7")
File "/Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages/langchain/load/serializable.py", line 97, in __init__
super().__init__(**kwargs)
File "/Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages/pydantic/v1/main.py", line 341, in __init__
raise validation_error
pydantic.v1.error_wrappers.ValidationError: 1 validation error for HuggingFaceHub
__root__
Did not find huggingfacehub_api_token, please add an environment variable `HUGGINGFACEHUB_API_TOKEN` which contains it, or pass `huggingfacehub_api_token` as a named parameter. (type=value_error)
(langchain_helloworld_env) helloworld git:(master)

core error message:

Terminal window
Did not find huggingfacehub_api_token, please add an environment variable `HUGGINGFACEHUB_API_TOKEN` which contains it, or pass `huggingfacehub_api_token` as a named parameter. (type=value_error)

Here is the code:

test2.py
import os
from langchain import HuggingFaceHub
llm = HuggingFaceHub(repo_id="bigscience/bloom-1b7")
resp = llm.predict("Please name my flower shop")
print(resp)

You can see that I am trying to load the huggingface model bigscience/bloom-1b7, but the error occurred.



2. Solution

The line is important:

os.environ["HUGGINGFACEHUB_API_TOKEN"] = "hf_nJnjdwfvR......"

Now let’s execute the script again, here is the result:

Terminal window
(langchain_helloworld_env) helloworld git:(master) python test2.py
/Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages/langchain/__init__.py:34: UserWarning: Importing HuggingFaceHub from langchain root module is no longer supported. Please use langchain.llms.HuggingFaceHub instead.
warnings.warn(
Traceback (most recent call last):
File "/Users/bswen/work/python/myutils/learn_langchain/helloworld/test2.py", line 7, in <module>
llm = HuggingFaceHub(repo_id="bigscience/bloom-1b7")
File "/Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages/langchain/load/serializable.py", line 97, in __init__
super().__init__(**kwargs)
File "/Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages/pydantic/v1/main.py", line 341, in __init__
raise validation_error
pydantic.v1.error_wrappers.ValidationError: 1 validation error for HuggingFaceHub
__root__
Could not import huggingface_hub python package. Please install it with `pip install huggingface_hub`. (type=value_error)
(langchain_helloworld_env) helloworld git:(master) pip install huggingface_hub
Looking in indexes: http://mirrors.aliyun.com/pypi/simple/
Collecting huggingface_hub
Downloading http://mirrors.aliyun.com/pypi/packages/ef/b5/b6107bd65fa4c96fdf00e4733e2fe5729bb9e5e09997f63074bb43d3ab28/huggingface_hub-0.18.0-py3-none-any.whl (301 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 302.0/302.0 kB 595.8 kB/s eta 0:00:00
Collecting filelock (from huggingface_hub)
Downloading http://mirrors.aliyun.com/pypi/packages/81/54/84d42a0bee35edba99dee7b59a8d4970eccdd44b99fe728ed912106fc781/filelock-3.13.1-py3-none-any.whl (11 kB)
Collecting fsspec>=2023.5.0 (from huggingface_hub)
Downloading http://mirrors.aliyun.com/pypi/packages/e8/f6/3eccfb530aac90ad1301c582da228e4763f19e719ac8200752a4841b0b2d/fsspec-2023.10.0-py3-none-any.whl (166 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 166.4/166.4 kB 417.7 kB/s eta 0:00:00
Requirement already satisfied: requests in /Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages (from huggingface_hub) (2.31.0)
Requirement already satisfied: tqdm>=4.42.1 in /Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages (from huggingface_hub) (4.66.1)
Requirement already satisfied: pyyaml>=5.1 in /Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages (from huggingface_hub) (6.0.1)
Requirement already satisfied: typing-extensions>=3.7.4.3 in /Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages (from huggingface_hub) (4.8.0)
Requirement already satisfied: packaging>=20.9 in /Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages (from huggingface_hub) (23.2)
Requirement already satisfied: charset-normalizer<4,>=2 in /Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages (from requests->huggingface_hub) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages (from requests->huggingface_hub) (3.4)
Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages (from requests->huggingface_hub) (2.0.7)
Requirement already satisfied: certifi>=2017.4.17 in /Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages (from requests->huggingface_hub) (2023.7.22)
Installing collected packages: fsspec, filelock, huggingface_hub
Successfully installed filelock-3.13.1 fsspec-2023.10.0 huggingface_hub-0.18.0
(langchain_helloworld_env) helloworld git:(master) python test2.py
/Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages/langchain/__init__.py:34: UserWarning: Importing HuggingFaceHub from langchain root module is no longer supported. Please use langchain.llms.HuggingFaceHub instead.
warnings.warn(
/Users/bswen/miniconda3/envs/langchain_helloworld_env/lib/python3.10/site-packages/huggingface_hub/utils/_deprecation.py:127: FutureWarning: '__init__' (from 'huggingface_hub.inference_api') is deprecated and will be removed from version '0.19.0'. `InferenceApi` client is deprecated in favor of the more feature-complete `InferenceClient`. Check out this guide to learn how to convert your script to use it: https://huggingface.co/docs/huggingface_hub/guides/inference#legacy-inferenceapi-client.
warnings.warn(warning_message, FutureWarning)
, Just call it "love of flowers".
(langchain_helloworld_env) helloworld git:(master)

Success!

3. Summary

In this post, I demonstrated how to solve HUGGINGFACEHUB_API_TOKEN error when trying to access huggingface models, the key point is to create a token for your script on huggingface website. That’s it, thanks for your reading.

How to solve IllegalArgumentException: Host must not be empty when using springboot data redis and lettuce?

1. Purpose

In this post, I will demo how to solve the below exception or error when using spring boot data redis and lettuce:

Terminal window
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.2)
2021-03-21 15:27:30.705 INFO 61218 --- [ Test worker] c.bswen.app10.service.TestRedisService : Starting TestRedisService using Java 1.8.0_121 on MBP-bswen with PID 61218 (started by bswen in /Users/bswen/private/bw/bswen-github/bswen-springboot23/app10)
2021-03-21 15:27:30.706 INFO 61218 --- [ Test worker] c.bswen.app10.service.TestRedisService : The following profiles are active: test
2021-03-21 15:27:31.572 INFO 61218 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2021-03-21 15:27:31.576 INFO 61218 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2021-03-21 15:27:31.599 INFO 61218 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 7 ms. Found 0 Redis repository interfaces.
2021-03-21 15:27:32.011 WARN 61218 --- [ Test worker] o.s.w.c.s.GenericWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'redisService': Unsatisfied dependency expressed through field 'redisClient'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisClient' defined in class path resource [com/bswen/app10/config/RedisConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.lettuce.core.RedisClient]: Factory method 'redisClient' threw exception; nested exception is java.lang.IllegalArgumentException: Host must not be empty
2021-03-21 15:27:32.035 INFO 61218 --- [ Test worker] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-03-21 15:27:32.088 ERROR 61218 --- [ Test worker] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'redisService': Unsatisfied dependency expressed through field 'redisClient'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisClient' defined in class path resource [com/bswen/app10/config/RedisConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.lettuce.core.RedisClient]: Factory method 'redisClient' threw exception; nested exception is java.lang.IllegalArgumentException: Host must not be empty
...
at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_121]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisClient' defined in class path resource [com/bswen/app10/config/RedisConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.lettuce.core.RedisClient]: Factory method 'redisClient' threw exception; nested exception is java.lang.IllegalArgumentException: Host must not be empty
.... org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) ~[na:na]
at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_121]
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'redisService': Unsatisfied dependency expressed through field 'redisClient'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisClient' defined in class path resource [com/bswen/app10/config/RedisConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.lettuce.core.RedisClient]: Factory method 'redisClient' threw exception; nested exception is java.lang.IllegalArgumentException: Host must not be empty
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643) ~[spring-beans-5.3.3.jar:5.3.3]
org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.3.jar:5.3.3]
... 122 common frames omitted
Caused by: java.lang.IllegalArgumentException: Host must not be empty
at io.lettuce.core.internal.LettuceAssert.notEmpty(LettuceAssert.java:44) ~[lettuce-core-6.0.2.RELEASE.jar:6.0.2.RELEASE]
at io.lettuce.core.RedisURI$Builder.redis(RedisURI.java:1151) ~[lettuce-core-6.0.2.RELEASE.jar:6.0.2.RELEASE]
at com.bswen.app10.config.RedisConfiguration.redisClient(RedisConfiguration.java:17) ~[main/:na]
at com.bswen.app10.config.RedisConfiguration$$EnhancerBySpringCGLIB$$fd54fa1a.CGLIB$redisClient$0(<generated>) ~[main/:na]
at com.bswen.app10.config.RedisConfiguration$$EnhancerBySpringCGLIB$$fd54fa1a$$FastClassBySpringCGLIB$$5b3d5194.invoke(<generated>) ~[main/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.3.3.jar:5.3.3]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.3.3.jar:5.3.3]
at com.bswen.app10.config.RedisConfiguration$$EnhancerBySpringCGLIB$$fd54fa1a.redisClient(<generated>) ~[main/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.3.jar:5.3.3]
... 123 common frames omitted
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:355)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$7(ClassBasedTestDescriptor.java:350)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:742)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:349)
org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
... org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
... 90 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisClient' defined in class path resource [com/bswen/app10/config/RedisConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.lettuce.core.RedisClient]: Factory method 'redisClient' threw exception; nested exception is java.lang.IllegalArgumentException: Host must not be empty
... 122 more
Caused by: java.lang.IllegalArgumentException: Host must not be empty
at io.lettuce.core.internal.LettuceAssert.notEmpty(LettuceAssert.java:44)
at io.lettuce.core.RedisURI$Builder.redis(RedisURI.java:1151)
at com.bswen.app10.config.RedisConfiguration.redisClient(RedisConfiguration.java:17)
at com.bswen.app10.config.RedisConfiguration$$EnhancerBySpringCGLIB$$fd54fa1a.CGLIB$redisClient$0(<generated>)
at com.bswen.app10.config.RedisConfiguration$$EnhancerBySpringCGLIB$$fd54fa1a$$FastClassBySpringCGLIB$$5b3d5194.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
at com.bswen.app10.config.RedisConfiguration$$EnhancerBySpringCGLIB$$fd54fa1a.redisClient(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 123 more
TestRedisService > testGetAndSet() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at AutowiredAnnotationBeanPostProcessor.java:643
Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:658
Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:185
Caused by: java.lang.IllegalArgumentException at LettuceAssert.java:44
1 test completed, 1 failed
> Task :app10:test FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app10:test'.
> There were failing tests. See the results at: file:///Users/bswen/private/bw/bswen-github/bswen-springboot23/app10/build/test-results/test/
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 8s
6 actionable tasks: 5 executed, 1 up-to-date

The core exception is :

Terminal window
[com/bswen/app10/config/RedisConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.lettuce.core.RedisClient]: Factory method 'redisClient' threw exception; nested exception is java.lang.IllegalArgumentException: Host must not be empty
... 122 more
Caused by: java.lang.IllegalArgumentException: Host must not be empty
at io.lettuce.core.internal.LettuceAssert.notEmpty(LettuceAssert.java:44)
at io.lettuce.core.RedisURI$Builder.redis(RedisURI.java:1151)
at com.bswen.app10.config.RedisConfiguration.redisClient(RedisConfiguration.java:17)
at

2. The Environment

  • Jdk 8+
  • Spring boot 2+
  • Gradle 6+

3. The code and solution

3.1 The project structure

The project’s main package is com.bswen.app10, its main purpose is to connect to a redis server and do some unit tests.

└── src
├── main
│   ├── java
│   │   └── com
│   │   └── bswen
│   │   └── app10
│   │   ├── RedisApplication.java
│   │   ├── config
│   │   │   └── RedisConfiguration.java
│   │   ├── domain
│   │   │   └── SyncCommandCallback.java
│   │   └── service
│   │   └── RedisService.java
│   └── resources
│   ├── application-dev.properties
│   └── application.properties
└── test
├── java
│   └── com
│   └── bswen
│   └── app10
│   ├── package-info.java
│   └── service
│   └── TestRedisService.java
└── resources
└── application-test.properties

3.2 The project’s dependency

Let’s see the project’s gradle dependencies:

plugins {
id 'org.springframework.boot' version '2.4.2'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation('org.springframework.boot:spring-boot-starter-data-redis')
implementation 'io.lettuce:lettuce-core'
implementation 'org.apache.commons:commons-pool2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
// exclude vintage, because we only use junit5
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
// specify the test profile , spring boot would try to load application-test.properties when testing
systemProperty 'spring.profiles.active', 'test'
useJUnitPlatform()
reports {
junitXml.enabled = true
html.enabled = false
}
}

You can see that we are using springboot v2.4.2, and we are using spring-boot-starter-redis with lettuce, which is the default connector. we added lettuce-core as redis connection manager.

3.3 The configuration class

We need a configuration class to configure the redis connection.

package com.bswen.app10.config;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties("spring.redis")
public class RedisConfiguration {
private String host;
private int port;
@Bean
RedisClient redisClient() {
RedisURI uri = RedisURI.Builder.redis(this.host, this.port)
.build();
return RedisClient.create(uri);
}
}

In the above code, we define two fields(host and post) to construct the redisURI , which will be used by Lettuce RedisClient to create the redis connection.

3.4 The configuration file

I have specified the test profile when doing unit testing. So, the src/test/resources/application-test.properties file’s content is as follows:

src/test/resources/application-test.properties
spring.redis.host=2.3.4.7
spring.redis.port=6379

3.5 The test class

This is the test code:

package com.bswen.app10.service;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class TestRedisService {
@Autowired
private RedisService redisService;
@Test
public void testGetAndSet() {
redisService.set("hello","world");
Assertions.assertEquals(redisService.get("hello"),"world");
}
}

I just put a key/value to redis and then get the value with the same key, the assertion should be true.

3.6 Run the code

When we run the above test with gradle, we get this exception:

Terminal window
[com/bswen/app10/config/RedisConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.lettuce.core.RedisClient]: Factory method 'redisClient' threw exception; nested exception is java.lang.IllegalArgumentException: Host must not be empty
... 122 more
Caused by: java.lang.IllegalArgumentException: Host must not be empty
at io.lettuce.core.internal.LettuceAssert.notEmpty(LettuceAssert.java:44)
at io.lettuce.core.RedisURI$Builder.redis(RedisURI.java:1151)

why?

3.7 The reason

The reason for this error is as follows:

  • The lettuce RedisURI$Builder can not find the host of the redis
  • The host is configured in our RedisConfiguration, but the host field does not have getter and setter to access it

3.8 The solution

So the solution is easy, here is it:

3.9 Re-run the test

Terminal window
Testing started at 15:31 ...
> Task :app10:cleanTest
> Task :app10:compileJava
> Task :app10:processResources UP-TO-DATE
> Task :app10:classes
> Task :app10:compileTestJava UP-TO-DATE
> Task :app10:processTestResources UP-TO-DATE
> Task :app10:testClasses UP-TO-DATE
> Task :app10:test
15:31:08.196 [Test worker] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
15:31:08.216 [Test worker] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
15:31:08.264 [Test worker] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.bswen.app10.service.TestRedisService] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
15:31:08.280 [Test worker] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.bswen.app10.service.TestRedisService], using SpringBootContextLoader
15:31:08.285 [Test worker] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.bswen.app10.service.TestRedisService]: class path resource [com/bswen/app10/service/TestRedisService-context.xml] does not exist
15:31:08.285 [Test worker] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.bswen.app10.service.TestRedisService]: class path resource [com/bswen/app10/service/TestRedisServiceContext.groovy] does not exist
15:31:08.286 [Test worker] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.bswen.app10.service.TestRedisService]: no resource found for suffixes {-context.xml, Context.groovy}.
15:31:08.287 [Test worker] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.bswen.app10.service.TestRedisService]: TestRedisService does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
15:31:08.349 [Test worker] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.bswen.app10.service.TestRedisService]
15:31:08.438 [Test worker] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [/Users/bswen/private/bw/bswen-github/bswen-springboot23/app10/build/classes/java/main/com/bswen/app10/RedisApplication.class]
15:31:08.439 [Test worker] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.bswen.app10.RedisApplication for test class com.bswen.app10.service.TestRedisService
15:31:08.577 [Test worker] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [com.bswen.app10.service.TestRedisService]: using defaults.
15:31:08.578 [Test worker] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.event.ApplicationEventsTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
15:31:08.603 [Test worker] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@55b822bf, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@78d3e486, org.springframework.test.context.event.ApplicationEventsTestExecutionListener@13d65ec5, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@3f6f6222, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@2b5602aa, org.springframework.test.context.support.DirtiesContextTestExecutionListener@4c462492, org.springframework.test.context.transaction.TransactionalTestExecutionListener@6d476ba9, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@29fd7a51, org.springframework.test.context.event.EventPublishingTestExecutionListener@32256e96, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@4fbaaef4, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@19769e89, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@5b9b5004, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@4253d43f, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener@58040f8f, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@75ab50d0]
15:31:08.607 [Test worker] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@1e6a10f3 testClass = TestRedisService, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@1802b0a0 testClass = TestRedisService, locations = '{}', classes = '{class com.bswen.app10.RedisApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@1355ba10, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@2e7d7325, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@52bfcb96, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@ce7e970, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@25135e5, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@32d0dfaf], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true]], class annotated with @DirtiesContext [false] with mode [null].
15:31:08.641 [Test worker] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.2)
2021-03-21 15:31:08.996 INFO 61274 --- [ Test worker] c.bswen.app10.service.TestRedisService : Starting TestRedisService using Java 1.8.0_121 on MBP-bswen with PID 61274 (started by bswen in /Users/bswen/private/bw/bswen-github/bswen-springboot23/app10)
2021-03-21 15:31:08.997 INFO 61274 --- [ Test worker] c.bswen.app10.service.TestRedisService : The following profiles are active: test
2021-03-21 15:31:09.953 INFO 61274 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2021-03-21 15:31:09.957 INFO 61274 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2021-03-21 15:31:09.985 INFO 61274 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 9 ms. Found 0 Redis repository interfaces.
2021-03-21 15:31:12.034 INFO 61274 --- [ Test worker] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-03-21 15:31:13.388 INFO 61274 --- [ Test worker] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator'
2021-03-21 15:31:13.451 INFO 61274 --- [ Test worker] c.bswen.app10.service.TestRedisService : Started TestRedisService in 4.8 seconds (JVM running for 6.238)
2021-03-21 15:31:13.823 INFO 61274 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
BUILD SUCCESSFUL in 8s
6 actionable tasks: 3 executed, 3 up-to-date
15:31:13: Tasks execution finished ':app10:cleanTest :app10:test --tests "com.bswen.app10.service.TestRedisService"'.

It works!

4. Summary

In this post, we demonstrated how to solve the IllegalArgumentException: Host must not be empty problem when using lettuce with springboot data redis, you can see that we should provide appropriate configurations to lettuce to construct the connection from lettuce to redis server. Thanks for your reading. Regards.