了解如何将Redis與Spring Cloud和Spring Data一起使用以提供配置伺服器,消息代理和資料庫。

JAVA 進階集中營zhuanlan.zhihu.com
Redis可以廣泛用于微服務架構中。它可能是少數流行的軟體解決方案之一,你的應用程式可以通過許多不同的方式來利用這些解決方案。根據要求,它可以充當主資料庫,緩存或消息代理。雖然它也是鍵/值存儲,但我們可以将其用作微服務體系結構中的配置伺服器或發現伺服器。盡管通常将其定義為記憶體中的資料結構,但我們也可以在持久模式下運作它。
通過這篇文章,我将結合我自己所掌握的和近期在優銳課學習到的知識,向你展示一些将Redis與基于Spring Boot和Spring Cloud架構建構的微服務一起使用的示例。這些應用程式将使用Redis釋出/訂閱,使用Redis作為緩存或主資料庫,最後使用Redis作為配置伺服器,彼此之間進行異步通信。這是說明所描述體系結構的圖檔。
Redis作為配置伺服器
如果你已經使用Spring Cloud建構了微服務,則可能對Spring Cloud Config有所了解。它負責為微服務提供分布式配置模式。 不幸的是,Spring Cloud Config不支援将Redis作為屬性源的後端存儲庫。這就是為什麼我決定派生一個Spring Cloud Config項目并實作此功能的原因。我希望我的實作将很快包含在Spring Cloud的官方發行版中,我們如何使用它?很簡單的。讓我們來看看。
Spring Boot的目前SNAPSHOT版本是2.2.0.BUILD-SNAPSHOT,與用于Spring Cloud Config的版本相同。在建構Spring Cloud Config Server時,我們僅需要包括這兩個依賴項,如下所示。
1 <parent>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-parent</artifactId>
4 <version>2.2.0.BUILD-SNAPSHOT</version>
5 </parent>
6 <artifactId>config-service</artifactId>
7 <groupId>pl.piomin.services</groupId>
8 <version>1.0-SNAPSHOT</version>
9 <dependencies>
10 <dependency>
11 <groupId>org.springframework.cloud</groupId>
12 <artifactId>spring-cloud-config-server</artifactId>
13 <version>2.2.0.BUILD-SNAPSHOT</version>
14 </dependency>
15 </dependencies>
預設情況下,Spring Cloud Config Server使用一個Git存儲庫後端。 我們需要激活一個redisprofile來強制它使用Redis作為後端。 如果你的Redis執行個體偵聽的位址不是localhost:6379,則需要使用spring.redis.*屬性覆寫自動配置的連接配接設定。 這是我們的bootstrap.yml檔案。
1 spring:
2 application:
3 name: config-service
4 profiles:
5 active: redis
6 redis:
7 host: 192.168.99.100
應用程式主類應使用@EnableConfigServer進行注釋。
1 @SpringBootApplication
2 @EnableConfigServer
3 public class ConfigApplication {
4 public static void main(String[] args) {
5 new SpringApplicationBuilder(ConfigApplication.class).run(args);
6 }
7 }
在運作應用程式之前,我們需要啟動Redis執行個體。這是将其作為Docker容器運作并在端口6379上公開的指令。
1 $ docker run -d --name redis -p 6379:6379 redis
每個應用程式的配置都必須在鍵${spring.application.name}或${spring.application.name}-${spring.profiles.active[n]}可用。
我們必須使用與配置屬性名稱相對應的鍵來建立hash。我們的示例應用程式驅動程式管理使用三個配置屬性:server.port用于設定HTTP偵聽端口,spring.redis.host用于更改用作消息代理和資料庫的預設Redis位址,以及sample.topic.name用于設定名稱。微服務之間用于異步通信的主題。這是我們為使用RDBTools可視化的驅動程式管理建立的Redis hash的結構。
該可視化等效于運作Redis CLI指令HGETALL,該指令傳回哈希中的所有字段和值。
1 >> HGETALL driver-management
2 {
3 "server.port": "8100",
4 "sample.topic.name": "trips",
5 "spring.redis.host": "192.168.99.100"
6 }
在Redis中設定鍵和值并使用有效的redisprofile運作Spring Cloud Config Server之後,我們需要在用戶端啟用分布式配置功能。為此,我們隻需要将spring-cloud-starter-config依賴項包含到每個微服務的thepom.xml中即可。
1 <dependency>
2 <groupId>org.springframework.cloud</groupId>
3 <artifactId>spring-cloud-starter-config</artifactId>
4 </dependency>
我們使用Spring Cloud的最新穩定版本。
1 <dependencyManagement>
2 <dependencies>
3 <dependency>
4 <groupId>org.springframework.cloud</groupId>
5 <artifactId>spring-cloud-dependencies</artifactId>
6 <version>Greenwich.SR1</version>
7 <type>pom</type>
8 <scope>import</scope>
9 </dependency>
10 </dependencies>
11 </dependencyManagement>
應用程式的名稱是在啟動時從屬性spring.application.name擷取的,是以我們需要提供以下bootstrap.yml檔案。
1 spring:
2 application:
3 name: driver-management
Redis作為消息代理
現在,我們可以繼續研究基于微服務的體系結構中Redis的第二個用例——消息代理。我們将實作一個典型的異步系統,如下圖所示。在建立新行程并完成目前行程後,微服務行程管理會将通知發送到Redis Pub / Sub。該通知由預訂特定頻道的駕駛員管理和乘客管理兩者接收。
我們的申請非常簡單。我們隻需要添加以下依賴項即可提供REST API并與Redis Pub / Sub內建。
1 <dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-web</artifactId>
4 </dependency>
5 <dependency>
6 <groupId>org.springframework.boot</groupId>
7 <artifactId>spring-boot-starter-data-redis</artifactId>
8 </dependency>
我們應該使用通道名稱和釋出者來注冊bean。TripPublisher負責将消息發送到目标主題。
1 @Configuration
2 public class TripConfiguration {
3 @Autowired
4 RedisTemplate<?, ?> redisTemplate;
5 @Bean
6 TripPublisher redisPublisher() {
7 return new TripPublisher(redisTemplate, topic());
8 }
9 @Bean
10 ChannelTopic topic() {
11 return new ChannelTopic("trips");
12 }
13 }
TripPublisher 使用RedisTemplate将消息發送到主題。 發送之前,它将使用Jackson2JsonRedisSerializer将對象中的所有消息轉換為JSON字元串。
1 public class TripPublisher {
2 private static final Logger LOGGER = LoggerFactory.getLogger(TripPublisher.class);
3 RedisTemplate<?, ?> redisTemplate;
4 ChannelTopic topic;
5 public TripPublisher(RedisTemplate<?, ?> redisTemplate, ChannelTopic topic) {
6 this.redisTemplate = redisTemplate;
7 this.redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Trip.class));
8 this.topic = topic;
9 }
10 public void publish(Trip trip) throws JsonProcessingException {
11 LOGGER.info("Sending: {}", trip);
12 redisTemplate.convertAndSend(topic.getTopic(), trip);
13 }
14 }
我們已經在釋出方實作了邏輯。現在,我們可以在訂戶端進行實施。我們有兩個微服務驅動程式管理和乘客管理,它們偵聽旅行管理微服務發送的通知。我們需要定義RedisMessageListenerContainer bean并設定消息偵聽器實作類。
1 @Configuration
2 public class DriverConfiguration {
3 @Autowired
4 RedisConnectionFactory redisConnectionFactory;
5 @Bean
6 RedisMessageListenerContainer container() {
7 RedisMessageListenerContainer container = new RedisMessageListenerContainer();
8 container.addMessageListener(messageListener(), topic());
9 container.setConnectionFactory(redisConnectionFactory);
10 return container;
11 }
12 @Bean
13 MessageListenerAdapter messageListener() {
14 return new MessageListenerAdapter(new DriverSubscriber());
15 }
16 @Bean
17 ChannelTopic topic() {
18 return new ChannelTopic("trips");
19 }
20 }
負責處理傳入通知的類需要實作MessageListenerinterface。 收到消息後,DriverSubscriber會将其從JSON反序列化為對象,并更改驅動程式的狀态。
1 @Service
2 public class DriverSubscriber implements MessageListener {
3 private final Logger LOGGER = LoggerFactory.getLogger(DriverSubscriber.class);
4 @Autowired
5 DriverRepository repository;
6 ObjectMapper mapper = new ObjectMapper();
7 @Override
8 public void onMessage(Message message, byte[] bytes) {
9 try {
10 Trip trip = mapper.readValue(message.getBody(), Trip.class);
11 LOGGER.info("Message received: {}", trip.toString());
12 Optional<Driver> optDriver = repository.findById(trip.getDriverId());
13 if (optDriver.isPresent()) {
14 Driver driver = optDriver.get();
15 if (trip.getStatus() == TripStatus.DONE)
16 driver.setStatus(DriverStatus.WAITING);
17 else
18 driver.setStatus(DriverStatus.BUSY);
19 repository.save(driver);
20 }
21 } catch (IOException e) {
22 LOGGER.error("Error reading message", e);
23 }
24 }
25 }
Redis作為主資料庫
盡管使用Redis的主要目的是記憶體中緩存或作為鍵/值存儲,但它也可以充當應用程式的主資料庫。在這種情況下,值得在持久模式下運作Redis。
1 $ docker run -d --name redis -p 6379:6379 redis redis-server --appendonly yes
使用hash操作和mmap結構将實體存儲在Redis中。每個實體都需要具有hash鍵和ID。
1 @RedisHash("driver")
2 public class Driver {
3 @Id
4 private Long id;
5 private String name;
6 @GeoIndexed
7 private Point location;
8 private DriverStatus status;
9 // setters and getters ...
10 }
幸運的是,Spring Data Redis為Redis內建提供了衆所周知的存儲庫模式。要啟用它,我們應該使用@EnableRedisRepositories注釋配置或主類。當使用Spring倉庫模式時,我們不必自己建構對Redis的任何查詢。
1 @Configuration
2 @EnableRedisRepositories
3 public class DriverConfiguration {
4 // logic ...
5 }
使用Spring Data存儲庫,我們不必建構任何Redis查詢,隻需遵循Spring Data約定的名稱方法即可。有關更多詳細資訊,請參閱我以前的文章Spring Data Redis簡介。出于示例目的,我們可以使用Spring Data内部實作的預設方法。這是驅動程式管理中存儲庫接口的聲明。
1 public interface DriverRepository extends CrudRepository<Driver, Long> {}
不要忘記通過使用@EnableRedisRepositories注釋主應用程式類或配置類來啟用Spring Data存儲庫。
1 @Configuration
2 @EnableRedisRepositories
3 public class DriverConfiguration {
4 ...
5 }
結論
微服務架構中Redis的使用案例多種多樣。我剛剛介紹了如何輕松地将其與Spring Cloud和Spring Data一起使用以提供配置伺服器,消息代理和資料庫。Redis通常被認為是緩存存儲,但是我希望閱讀本文後你會對此有所改變。
最後,分享一份進階寶典《Java核心知識點整理.pdf》,覆寫了JVM、鎖、高并發、反射、Spring原理、微服務、Zookeeper、資料庫、資料結構等等,擷取請
點選下方文檔即可
書籍、Java進階路線、面試題,PDF文檔資料。shimo.im