微服務架構中的Redis


了解如何將Redis與Spring Cloud和Spring Data一起使用以提供配置服務器,消息代理和數據庫。

 

 

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通常被認為是緩存存儲,但是我希望閱讀本文后你會對此有所改變。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM