淺談Redis在微服務(wù)架構(gòu)中的幾種應(yīng)用場景
本文介紹在SpringCloud中使用Redis作為Pub/Sub異步通信、緩存或主數(shù)據(jù)庫和配置服務(wù)器的三種場景應(yīng)用。
Redis可以廣泛用于微服務(wù)架構(gòu)。它可能是您應(yīng)用程序以多種不同方式利用的少數(shù)流行軟件解決方案之一。根據(jù)要求,它可以充當主數(shù)據(jù)庫,緩存或消息代理。雖然它也是一個鍵/值存儲,但我們可以將它用作微服務(wù)體系結(jié)構(gòu)中的配置服務(wù)器或發(fā)現(xiàn)服務(wù)器。雖然它通常被定義為內(nèi)存中的數(shù)據(jù)結(jié)構(gòu),但我們也可以在持久模式下運行它。
這里我將向您展示一些使用Redis與Spring Boot和Spring Cloud框架之上構(gòu)建的微服務(wù)的示例。這些應(yīng)用程序?qū)⑹褂肦edis Pub / Sub異步通信,使用Redis作為緩存或主數(shù)據(jù)庫,最后使用Redis作為配置服務(wù)器。
Redis作為配置服務(wù)器
如果您已經(jīng)使用Spring Cloud構(gòu)建了微服務(wù),那么您可能對Spring Cloud Config有一些經(jīng)驗。它負責為微服務(wù)提供分布式配置模式。
不幸的是,Spring Cloud Config不支持Redis作為屬性源的后端存儲庫。這就是我決定分叉Spring Cloud Config項目并實現(xiàn)此功能的原因。
我希望我的實現(xiàn)很快將被包含在官方的Spring Cloud版本中,但是,現(xiàn)在,您可以使用我的fork repo來運行它。我們怎么用呢?非常簡單。讓我們來看看。
Spring Boot的當前SNAPSHOT版本2.2.0.BUILD-SNAPSHOT與我們用于Spring Cloud Config的版本相同。在構(gòu)建Spring Cloud Config Server時,我們只需要包含這兩個依賴項,如下所示。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.0.BUILD-SNAPSHOT</version> </parent> <artifactId>config-service</artifactId> <groupId>pl.piomin.services</groupId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> <version>2.2.0.BUILD-SNAPSHOT</version> </dependency> </dependencies>
默認情況下,Spring Cloud Config Server使用Git存儲庫后端。我們需要激活 redis配置文件以強制它使用Redis作為后端。如果您的Redis實例偵聽的地址不是localhost:6379您需要使用spring.redis.*屬性覆蓋自動配置的連接設(shè)置 。這是我們的bootstrap.yml文件。
spring: application: name: config-service profiles: active: redis redis: host: 192.168.99.100
應(yīng)用程序主類應(yīng)注釋@EnableConfigServer:
@SpringBootApplication @EnableConfigServer public class ConfigApplication { public static void main(String[] args) { new SpringApplicationBuilder(ConfigApplication.class).run(args); } }
在運行應(yīng)用程序之前,我們需要啟動Redis實例。這是將其作為Docker容器運行并在端口6379上公開的命令。
$ docker run -d --name redis -p 6379:6379 redis
每個應(yīng)用程序的配置必須在密鑰{spring.application.name}或{spring.application.name}-${spring.profiles.active[n]}。我們必須使用與配置屬性名稱對應(yīng)的鍵創(chuàng)建哈希。
我們的示例應(yīng)用程序driver-management使用三個配置屬性:server.port用于設(shè)置HTTP偵聽端口,spring.redis.host用于更改用作消息代理和數(shù)據(jù)庫的默認Redis地址,以及sample.topic.name用于設(shè)置用于我們的微服務(wù)之間的異步通信的主題的名稱。這是我們?yōu)閐river-management使用RDBTools可視化而創(chuàng)建的Redis哈希的結(jié)構(gòu)。
在Redis CLI 中運行HGETALL ,返回:
>> HGETALL driver-management { "server.port": "8100", "sample.topic.name": "trips", "spring.redis.host": "192.168.99.100" }
在Redis中設(shè)置鍵值并使用redis配置文件運行Spring Cloud Config Server之后,我們需要在客戶端啟用分布式配置功能。要做到這一點,我們只需要包含對每個微服務(wù)的spring-cloud-starter-config依賴pom.xml。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
使用最新穩(wěn)定的Spring Cloud:
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
應(yīng)用程序的名稱是spring.application.name在啟動時從屬性中獲取的,因此我們需要提供以下bootstrap.yml文件。
spring: application: name: driver-management
Redis作為消息代理
現(xiàn)在我們可以在基于微服務(wù)的體系結(jié)構(gòu)中繼續(xù)使用Redis的第二個用例 - 消息代理。我們將實現(xiàn)一個典型的異步系統(tǒng)。
微服務(wù)trip-management在創(chuàng)建新行程后以及完成當前行程后向Redis Pub / Sub發(fā)送通知。通知由訂閱特定頻道的driver-management和接收 。
我們的應(yīng)用非常簡單。我們只需要添加以下依賴項,以便提供REST API并與Redis Pub / Sub集成。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
我們應(yīng)該使用頻道channel名稱和發(fā)布者注冊bean。TripPublisher負責向目標主題發(fā)送消息。
@Configuration public class TripConfiguration { @Autowired RedisTemplate<?, ?> redisTemplate; @Bean TripPublisher redisPublisher() { return new TripPublisher(redisTemplate, topic()); } @Bean ChannelTopic topic() { return new ChannelTopic("trips"); } }
TripPublisher使用RedisTemplate將消息發(fā)送到的話題。在發(fā)送之前,它會使用Jackson2JsonRedisSerializer對來自對象的每條消息轉(zhuǎn)換為JSON字符串。
public class TripPublisher { private static final Logger LOGGER = LoggerFactory.getLogger(TripPublisher.class); RedisTemplate<?, ?> redisTemplate; ChannelTopic topic; public TripPublisher(RedisTemplate<?, ?> redisTemplate, ChannelTopic topic) { this.redisTemplate = redisTemplate; this.redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Trip.class)); this.topic = topic; } public void publish(Trip trip) throws JsonProcessingException { LOGGER.info("Sending: {}", trip); redisTemplate.convertAndSend(topic.getTopic(), trip); } }
我們已經(jīng)在發(fā)布者方面實現(xiàn)了邏輯?,F(xiàn)在,我們可以繼續(xù)在消費接受方面的代碼事先。
我們有兩個微服務(wù)driver-management,passenger-management,它們監(jiān)聽trip-management微服務(wù)發(fā)送的通知 。我們需要定義RedisMessageListenerContainerbean并設(shè)置消息監(jiān)聽器實現(xiàn)類。
@Configuration public class DriverConfiguration { @Autowired RedisConnectionFactory redisConnectionFactory; @Bean RedisMessageListenerContainer container() { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.addMessageListener(messageListener(), topic()); container.setConnectionFactory(redisConnectionFactory); return container; } @Bean MessageListenerAdapter messageListener() { return new MessageListenerAdapter(new DriverSubscriber()); } @Bean ChannelTopic topic() { return new ChannelTopic("trips"); } }
負責處理傳入通知的類需要實現(xiàn)該 MessageListener接口。收到消息后, DriverSubscriber將其從JSON反序列化到對象并更改驅(qū)動程序的狀態(tài)。
@Service public class DriverSubscriber implements MessageListener { private final Logger LOGGER = LoggerFactory.getLogger(DriverSubscriber.class); @Autowired DriverRepository repository; ObjectMapper mapper = new ObjectMapper(); @Override public void onMessage(Message message, byte[] bytes) { try { Trip trip = mapper.readValue(message.getBody(), Trip.class); LOGGER.info("Message received: {}", trip.toString()); Optional<Driver> optDriver = repository.findById(trip.getDriverId()); if (optDriver.isPresent()) { Driver driver = optDriver.get(); if (trip.getStatus() == TripStatus.DONE) driver.setStatus(DriverStatus.WAITING); else driver.setStatus(DriverStatus.BUSY); repository.save(driver); } } catch (IOException e) { LOGGER.error("Error reading message", e); } } }
Redis作為主數(shù)據(jù)庫
雖然使用Redis的主要目的是內(nèi)存中緩存或作為鍵/值存儲,但它也可以充當應(yīng)用程序的主數(shù)據(jù)庫。在這種情況下,以持久模式運行Redis是值得的。
$ docker run -d --name redis -p 6379:6379 redis redis-server --appendonly yes
實體使用哈希散列操作和mmap結(jié)構(gòu)存儲在Redis中。每個實體都需要一個哈希鍵和id。
@RedisHash("driver") public class Driver { @Id private long id; private String name; @GeoIndexed private Point location; private DriverStatus status; // setters and getters ... }
幸運的是,Spring Data Redis為Redis集成提供了一個眾所周知的存儲庫模式。要啟用它,我們應(yīng)該使用@EnableRedisRepositories注釋配置類或主類。使用Spring存儲庫模式時,我們不必自己構(gòu)建對Redis的任何查詢。
@Configuration @EnableRedisRepositories public class DriverConfiguration { // logic ... }
使用Spring Data存儲庫,我們不必構(gòu)建任何Redis查詢,只需遵循Spring Data的約定下的名稱方法。為了我們的示例目的,我們可以使用Spring Data中實現(xiàn)的默認方法。這是存儲庫接口的聲明driver-management。
public interface DriverRepository extends CrudRepository<Driver, Long> {}
不要忘記通過使用@EnableRedisRepositories注釋主應(yīng)用程序類或配置類來啟用Spring Data存儲庫。
@Configuration @EnableRedisRepositories public class DriverConfiguration { ... }
結(jié)論
正如我在前言中提到的,Redis在微服務(wù)架構(gòu)中有各種用例。我剛剛介紹了如何與Spring Cloud和Spring Data一起使用它來提供配置服務(wù)器,消息代理和數(shù)據(jù)庫。Redis通常被認為是緩存存儲,但我希望在閱讀本文之后,您將改變主意。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
IDEA配置Maven并版本統(tǒng)一管理的實現(xiàn)
本文主要介紹了IDEA配置Maven并版本統(tǒng)一管理的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09spring boot中內(nèi)嵌redis的使用方法示例
這篇文章主要給大家介紹了關(guān)于spring boot中內(nèi)嵌redis使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2018-06-06Java中如何給List進行排序(這7種方法輕松實現(xiàn))
在Java項目中可能會遇到給出一些條件,將List元素按照給定條件進行排序的情況,這篇文章主要給大家介紹了關(guān)于Java中如何給List進行排序的相關(guān)資料,通過文中介紹的這7種方法可以輕松實現(xiàn),需要的朋友可以參考下2023-10-10