之前我们在许可证服务和组织服务之间构建了消息集成,以便使用默认的output和input通道,这些通道与Source和Sink接口一起打包在Spring Cloud Stream项目中。然而,如果想要为应用程序定义多个通道,或者想要定制通道的名称,那么开发人员可以定义自己的接口,并根据应用程序需要公开任意数量的输入和输出通道。
要在许可证服务里面创建名为inboundOrgChanges的自定义通道,可以在licensing-service/ src/main/java/com/thoughtmechanix/licenses/events/CustomChannels.java的CustomChannels接口中进行定义,如代码清单8-12所示。
代码清单8-12 为许可证服务定义一个自定义input通道
package com.thoughtmechanix.licenses.events;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.messaging.SubscribableChannel;
public interface CustomChannels {
⇽@Input是方法级别的注解,它定义了通道的名称
@Input("inboundOrgChanges")
⇽-通过@Input注解公开的每个通道必须返回一个SubscribableChannel类
SubscribableChannel orgs();
}
代码清单8-12中的关键信息是,对于要公开的每个自定义input通道,使用@Input注解标记一个返回SubscribableChannel类的方法。如果想要为发
布的消息定义output通道,可以在将要调用的方法上使用@OutputChannel。在output通道的情况下,定义的方法将返回一个MessageChannel类而不是与input通道一起使用的SubscribableChannel类。
@OutputChannel("outboundOrg")
MessageChannel outboundOrg();
定义完自定义input通道之后,接下来就需要在许可证服务中修改两样东西来使用它。首先,需要修改许可证服务,以将自定义input通道名称映射到Kafka主题。代码清单8-13展示了这一点。
代码清单8-13 修改许可证服务以使用自定义input通道
spring:
cloud:
stream:
bindings:
⇽--- 将通道的名称从input更改为inboundOrgChanges
inboundOrgChanges:
destination: orgChangeTopic
content-type: application/json
group: licensingGroup
kafka:
binder:
zkNodes: localhost
brokers: localhost
# input:
# destination: orgChangeTopic
# content-type: application/json
# group: licensingGroup
要使用自定义input通道,需要将定义的CustomChannels接口注入将要使用它来处理消息的类中。对于分布式缓存示例,我已经将处理传入消息的代码移到了licensing-service文件夹下的OrganizationChangeHandler类(在licensing-service/src/main/java/com/-thoughtmechanix/ licenses/events/handlers/OrganizationChangeHandler.java中)。代码清单8-14展示了与定义的inboundOrgChanges通道一起使用的消息处理代码。
代码清单8-14 在OrganizationChangeHandler中使用新的自定义通道
⇽--- 将@EnableBindings从Application类移到OrganizationChangeHandler类。这一次不使用Sink.class,而是使用CustomChannels类作为参数进行传入
@EnableBinding(CustomChannels.class)
public class OrganizationChangeHandler {
⇽--- 使用@StreamListener注解传入通道名称inboundOrgChanges而不是使用Sink.INPUT
@StreamListener("inboundOrgChanges")
public void loggerSink(OrganizationChangeModel orgChange) {
... //为了简洁,省略了其余的代码
}
}
8.4.3 将其全部汇集在一起:在收到消息时清除缓存
到目前为止,我们不需要对组织服务做任何事。该服务被设置为在组织被添加、更新或删除时发布一条消息。我们需要做的就是根据代码清单8-14构建出OrganizationChangeHandler类。代码清单8-15展示了这个类的完整实现。
代码清单8-15 处理许可证服务中的组织更改
@EnableBinding(CustomChannels.class)
public class OrganizationChangeHandler {
⇽--- 用于与Redis进行交互的OrganizationRedisRepository被注入OrganizationChangeHandler
@Autowired
private OrganizationRedisRepository organizationRedisRepository;
private static final Logger logger = LoggerFactory.getLogger(OrganizationChangeHandler.class);
⇽--- 在收到消息时,检查与数据相关的操作,然后做出相应的反应
@StreamListener("inboundOrgChanges")
public void loggerSink(OrganizationChangeModel orgChange) {
logger.debug("Received a message of type " + orgChange.getType());
⇽--- 如果组织数据被更新或者删除,那么就通过organizationRedisRepository类从Redis中移除组织数据
switch(orgChange.getAction()){
case "GET":
logger.debug("Received a GET event from the organization service for organization id {}", orgChange.getOrganizationId());
break;
case "SAVE":
logger.debug("Received a SAVE event from the organization service for organization id {}", orgChange.getOrganizationId());
break;
case "UPDATE":
logger.debug("Received a UPDATE event from the organization service for organization id {}", orgChange.getOrganizationId());
organizationRedisRepository.deleteOrganization(orgChange.getOrganizationId());
break;
case "DELETE":
logger.debug("Received a DELETE event from the organization service for organization id {}", orgChange.getOrganizationId());
organizationRedisRepository.deleteOrganization(orgChange.getOrganizationId());
break;
default:
logger.error("Received an UNKNOWN event from the organization service of type {}", orgChange.getType());
break;
}
}
}
8.5 小结
使用消息传递的异步通信是微服务架构的关键部分。
在应用程序中使用消息传递可以使服务能够伸缩并且变得更具容错性。
Spring Cloud Stream通过使用简单的注解以及抽象出底层消息平台的特定平台细节来简化消息的生产和消费。
Spring Cloud Stream消息发射器是一个带注解的Java方法,用于将消息发布到消息代理的队列中。Spring Cloud Stream消息接收器是一个带注解的Java方法,它接收消息代理队列上的消息。
Redis是一个键值存储,它可以用作数据库和缓存。