by Daniel Newton
丹尼尔·牛顿
In this post, I’ll be continuing my journey of learning Docker. I am still keeping it simple at this point. This time around, I am going to tackle converting a Spring and Cassandra application to use containers instead of running locally on the host machine. More precisely, using Spring Data Cassandra to sort out the application.
在本文中,我将继续学习Docker。 在这一点上,我仍然保持简单。 这次,我将解决将Spring和Cassandra应用程序转换为使用容器而不是在主机上本地运行的问题。 更准确地说,使用Spring Data Cassandra整理应用程序。
I wish I looked at doing this a while ago. I have written a fair amount of posts on Cassandra. Each time I had to cd
to the correct directory or have a shortcut to start it up. I guess it’s not that big of a deal, but there were a few other things involved — such as dropping and recreating keyspaces so I could test my application from scratch. Now, I delete the container and restart it. To me, anyway, this is helpful!
我希望我前一阵子看着这样做。 我在Cassandra上写了很多文章。 每次我必须cd
到正确的目录或使用快捷方式启动它时。 我想这没什么大不了的,但是还涉及其他一些事情,例如删除和重新创建键空间,以便我可以从头开始测试我的应用程序。 现在,我删除容器并重新启动它。 无论如何,这对我很有帮助!
This post will be different from my previous post, Using Docker to shove an existing application into containers. Instead, I will focus more here on the application side and remove the intermediate steps of using only Docker. I will jump straight into Docker Compose.
这篇文章与我以前的文章《 使用Docker将现有应用程序推送到容器》不同 。 相反,我将更多地集中在应用程序端,并删除仅使用Docker的中间步骤。 我将直接进入Docker Compose。
I think it’s best to start on the container side of the project. The application depends on the configuration of the Cassandra container.
我认为最好从项目的容器方面开始。 该应用程序取决于Cassandra容器的配置。
Let’s go!
我们走吧!
There’s not much going on here. This Dockerfile
builds the Spring application image that will be put into a container in a few moments.
这里没有太多的事情。 这个Dockerfile
构建了Spring应用程序映像,稍后将其放入容器中。
Next up is the docker-compose
file. This will build both the Spring application and Cassandra containers:
接下来是docker-compose
文件。 这将同时构建Spring应用程序和Cassandra容器:
Again, there isn’t too much here. The app
container builds the Spring application using the Dockerfile
defined previously. The cassandra
container instead relies on an existing image, appropriately named cassandra
.
同样,这里没有太多。 app
容器使用Dockerfile
定义的Dockerfile
构建Spring应用程序。 相反, cassandra
容器依赖于现有的映像,适当地命名为cassandra
。
One thing that stands out is that the restart
property is set to always
. This was my lazy attempt to get past how long Cassandra takes to start. Plus all the containers started with docker-compose
start at the same time. This lead to a situation where the application is trying to connect to Cassandra without it being ready. Unfortunately, this leads to the application dying. I hoped that it would have some retry capability for initial connectivity built in… But it does not.
突出的一件事是,将restart
属性设置为always
。 这是我懒惰的尝试,目的是超越卡桑德拉(Cassandra)花费多长时间开始。 加上所有以docker-compose
开头的容器同时启动。 这导致应用程序在未准备就绪的情况下尝试连接到Cassandra的情况。 不幸的是,这导致应用程序崩溃。 我希望它对内置的初始连接将有一些重试功能……但事实并非如此。
When we go through the code, we will see how to deal with the initial Cassandra connection programmatically instead of relying on the application dying and restarting multiple times. You will see my version of handling the connection, anyway. I’m not a huge fan of my solution, but everything else I tried caused me much more pain.
当我们遍历代码时,我们将看到如何以编程方式处理初始Cassandra连接,而不是依赖于应用程序死掉并重新启动多次。 无论如何,您都会看到我处理连接的版本。 我不是我的解决方案的忠实拥护者,但是我尝试的所有其他事情都使我更加痛苦。
I said this post would focus more on the application code, which it will. We are not going to dive into everything I put into this application and how to use Cassandra. For that sort of information, you can have a look at my older posts, which I’ll link at the end. What we will do, though, is examine the configuration code that creates the beans that connect to Cassandra.
我说这篇文章将重点放在应用程序代码上。 我们不会深入研究我在此应用程序中投入的一切以及如何使用Cassandra。 有关此类信息,您可以查看我的较旧文章,这些文章将在最后链接。 但是,我们要做的是检查配置代码,该代码创建连接到Cassandra的bean。
First, let’s go through ClusterConfig
which sets up the Cassandra cluster:
首先,让我们看一下设置Cassandra集群的ClusterConfig
:
There isn’t too much there. There would be even less if Spring would retry the initial connection to Cassandra. Anyway, let’s leave that part for a few minutes and focus on the other points in this class.
那里没有太多。 如果Spring重试与Cassandra的初始连接,将会更少。 无论如何,让我们将这一部分留出几分钟,集中讨论本课程中的其他要点。
The original reason I created ClusterConfig
was to create the keyspace that the application will use. To do this, getKeyspaceCreations
was overridden. When the application connects it will execute the query defined in this method to create the keyspace.
我创建ClusterConfig
的最初原因是创建应用程序将使用的密钥空间。 为此,覆盖了getKeyspaceCreations
。 当应用程序连接时,它将执行此方法中定义的查询以创建键空间。
If this wasn’t needed and the keyspace was created in some other way, for example, a script executed as part of creating the Cassandra container, Spring Boot’s auto-configuration could be relied upon instead. This actually allows the whole application to be configured by the properties defined in application.properties
and nothing else. Alas, it was not meant to be.
如果不需要这样做,并且以其他某种方式创建了键空间,例如,作为创建Cassandra容器的一部分执行的脚本,则可以依靠Spring Boot的自动配置。 实际上,这允许整个应用程序由application.properties
定义的属性进行配置,而仅此而已。 las,这本来不是。
Since we have defined an AbstractClusterConfiguration
, Spring Boot will disable its configuration in this area. Therefore, we need to define the contactPoints
(I named the variable hosts
) manually by overriding the getContactPoints
method. Originally this was only defined in application.properties
. I realised I needed to make this change once I started getting the following error:
由于我们定义了AbstractClusterConfiguration
,Spring Boot将在此区域中禁用其配置。 因此,我们需要通过重写getContactPoints
方法来手动定义contactPoints
(我将其命名为变量hosts
)。 最初,这仅在application.properties
定义。 我意识到一旦开始出现以下错误,我需要进行此更改:
All host(s) tried for query failed (tried: localhost/127.0.0.1:9042 (com.datastax.driver.core.exceptions.TransportException: [localhost/127.0.0.1:9042] Cannot connect))
Before I created ClusterConfig
the address was cassandra
rather than localhost
.
在创建ClusterConfig
之前,该地址为cassandra
而不是localhost
。
No other properties for the cluster need to be configured. Spring’s defaults are good enough for this scenario.
无需为集群配置其他属性。 在这种情况下,Spring的默认值已经足够了。
I have mentioned application.properties
so much at this point, I should probably show you what is in it.
在这一点上,我已经提到太多application.properties
了,我应该向您展示其中的内容。
keyspace-name
and contact-points
have already popped up since they are related to configuring the cluster. schema-action
is needed to create tables based on the entities in the project. We don’t need to do anything else here as auto-configuration is still working in this area.
keyspace-name
和contact-points
已经弹出,因为它们与配置集群有关。 根据项目中的实体创建表需要schema-action
。 我们不需要在此做任何其他事情,因为自动配置仍在该区域中工作。
The fact that the contact-points
value is set to cassandra
is very important. This domain name originates from the name given to the container, in this case, cassandra
. Therefore either cassandra
can be used or the actual IP of the container. The domain name is definitely easier since it will always be static between deployments. To test this theory out, you can change the name of the cassandra
container to whatever you want. It will still connect, as long as you change it in the application.properties
as well.
contact-points
值设置为cassandra
的事实非常重要。 该域名源自提供给容器的名称,在本例中为cassandra
。 因此,既可以使用cassandra
也可以使用容器的实际IP。 域名绝对容易,因为部署之间始终是静态的。 为了验证这一理论,您可以将cassandra
容器的名称更改为所需的名称。 只要您还在application.properties
更改,它仍然会连接。
Back to the ClusterConfig
code. More precisely, the cluster
bean. I have pasted the code below again so it’s easier to look at:
返回到ClusterConfig
代码。 更确切地说,是cluster
bean。 我再次粘贴了下面的代码,以便于查看:
This code is only needed to allow retries on the initial Cassandra connection. It is annoying, but I could not come up with another simple solution. If you have a nicer one, then please let me know!
仅需要此代码才能允许在初始Cassandra连接上重试。 这很烦人,但我无法提出另一个简单的解决方案。 如果您有更好的选择,请告诉我!
What I have done is actually quite simple, but the code itself is not very nice. The cluster
method is a carbon copy of the overridden version from AbstractClusterConfiguration
, with the exception of the RetryingCassandraClusterFactoryBean
(my own class). The original function used a CassandraClusterFactoryBean
(Spring class) instead.
我所做的实际上很简单,但是代码本身并不是很好。 除了RetryingCassandraClusterFactoryBean
(我自己的类)之外, cluster
方法是AbstractClusterConfiguration
重写版本的副本。 原始函数改为使用CassandraClusterFactoryBean
(Spring类)。
Below is the RetryingCassandraClusterFactoryBean
:
以下是RetryingCassandraClusterFactoryBean
:
The afterPropertiesSet
method in the original CassandraClusterFactoryBean
takes its values and creates the representation of a Cassandra cluster by finally delegating to the Datastax Java driver (as I have mentioned throughout the post). If it fails to establish a connection, an exception will be thrown. If the exception is not caught it will cause the application to terminate. That is the whole point of the above code. It wraps the afterPropertiesSet
in a try-catch block specified for the exceptions that can be thrown.
原始CassandraClusterFactoryBean
的afterPropertiesSet
方法采用其值,并通过最终委托给Datastax Java驱动程序来创建Cassandra集群的表示形式(正如我在整个文章中所提到的)。 如果无法建立连接,将引发异常。 如果未捕获到异常,它将导致应用程序终止。 这就是上面代码的重点。 它将afterPropertiesSet
包装在为可能引发的异常指定的try-catch块中。
The sleep
is added to give Cassandra some time to actually start up. There is no point trying to reconnect straight away when the previous attempt failed.
添加了sleep
,使Cassandra有一些时间可以真正启动。 上一次尝试失败时,尝试立即重新连接没有任何意义。
Using this code the application will eventually connect to Cassandra.
使用此代码,应用程序最终将连接到Cassandra。
At this point, I would normally show you some meaningless logs to prove that the application works. But in this situation, it does not bring anything to the table. Trust me when I say, if you run the below command:
在这一点上,我通常会向您显示一些毫无意义的日志,以证明该应用程序可以正常工作。 但是在这种情况下,它并没有带来任何好处。 我说的话请相信我,如果您运行以下命令:
mvn clean install && docker-compose up
then the Spring application image is created and both containers are spun up.
然后创建Spring应用程序映像,并旋转两个容器。
We have had a look at how to put a Spring application that connects to a Cassandra database into containers. One for the application and another for Cassandra.
我们已经看过如何将连接Cassandra数据库的Spring应用程序放入容器中。 一个用于应用程序,另一个用于Cassandra。
The application image is built from the project’s code. The Cassandra image is taken from Docker Hub. The image name is cassandra
just to make sure no one forgets.
应用程序映像是根据项目的代码构建的。 Cassandra映像取自Docker Hub。 图像名称是cassandra
只是为了确保没有人忘记。
In general, connecting the two containers together was relatively simple. The application needed some adjustments to allow retries when connecting to Cassandra running in the other container. This made the code a bit uglier, but it works at least.
通常,将两个容器连接在一起相对简单。 该应用程序需要进行一些调整,以允许在连接到另一个容器中运行的Cassandra时重试。 这使代码有点难看,但至少可以正常工作。
Thanks to the code written in this post, I now have another application that I don’t need to set up on my own machine.
多亏了本文中编写的代码,我现在有了另一个不需要在自己的机器上设置的应用程序。
The code used in this post can be found on my GitHub.
这篇文章中使用的代码可以在我的GitHub上找到 。
If you found this post helpful, you can follow me on Twitter at @LankyDanDev to keep up with my new posts.
如果您发现此帖子有帮助,可以在Twitter上@LankyDanDev关注我,以跟上我的新帖子。
Multiple keyspaces using a single Spring Data CassandraTemplate
Plumbing included with auto-configuration in Spring Data Cassandra
Wow, I didn’t realize I’ve written so many Cassandra posts.
哇,我没意识到我写了那么多Cassandra帖子。
Opinions and views found in my posts are my own and do not represent Accenture’s views on any subject. View all posts by Dan Newton
在我的帖子中发现的观点和观点是我自己的,并不代表埃森哲在任何主题上的观点。 查看丹·牛顿的所有文章
Originally published at lankydanblog.com on September 8, 2018.
最初于2018年9月8日发布在lankydanblog.com上。