Spring Cloud(7.3):配置Consumer Server
接下来我们创建一个消费者服务。消费者服务从生产者服务拿取商品-价格信息,并保存在Redis中。同时,接收消息队列中生产者服务的更新提示,如果某个商品-价格被修改,则删除Redis中的缓存数据,并重新从生产者服务中取。
配置pom.xml
首先,在pom.xml中添加spring-cloud-stream,spring-cloud-starter-stream-kafka,spring-data-redis及Redis的客户端jedis依赖。
<!-- Spring cloud: stream -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
<!-- Spring cloud starter: kafka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<!--Spring Data: Redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<!--Redis Client -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
配置通道(channel),绑定器(binder),接收器(sink)
public interface ProductPriceSource {
@Input("productPriceInput")
SubscribableChannel productPriceInput();
}
[注] 这里创建了一个叫“productPriceInput”的自定义接收通道,如果不使用自定义,可以直接使用org.springframework.cloud.stream.messaging.Sink接口及叫input的接收通道(下面的yml文件会讲如何配置)。
@Component
public class ProductPriceMessageReceiver { private static final Logger logger = LoggerFactory.getLogger(ProductPriceMessageReceiver.class); @Autowired
private ProductPriceRedisRepository productPriceRedisRepository; @StreamListener("productPriceInput")
public void receiveMessage(Long productId) { logger.info(String.format(">>>>> Received Kafka message productId=%s.", productId.toString()));
if (productPriceRedisRepository.contains(productId)) {
productPriceRedisRepository.delete(productId);
logger.info(
String.format(">>>>> Delete ProductPrice data productId=%s from cache.", productId.toString()));
} else {
logger.info(
String.format(">>>>> No ProductPrice data productId=%s in cache. Skip.", productId.toString()));
}
}
}
[注] 这里配置了一个接收器bean,当消息队列有消息时,接受该消息并清除相应product的缓存。
@SpringBootApplication
@EnableBinding({ ProductPriceSource.class })
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
[注] Application中加入@EnableBinding注解,并把定义好的发射通道(output)或接收通道(input)绑定到该服务中。可以绑定多个。
配置RedisRepository
这里我们额外用到了Redis,当我们引入jar包后,系统会自动给我们创建一个RedisTemplate<Object, Object>。我们可以用这个RedisTemplate来对Redis进行增删改查操作。
public interface RedisRepository<HK, HV> {
boolean contains(HK id);
HV find(HK id);
void save(HV value);
void delete(HK id);
}
@Repository
public class ProductPriceRedisRepository implements RedisRepository<Long, ProductPriceEntity> { private static final String HASH_NAME = "product_price"; /**
* 自动注入的Bean有:<br>
* RedisTemplate<Object, Object>(in RedisAutoConfiguration)<br>
* StringRedisTemplate(in RedisAutoConfiguration)<br>
* RedisConnectionFactory(JedisConnectionFactory)<br>
*
* 所以只能定义成RedisTemplate<Object, Object>的形式<br>
*/
@Autowired
private RedisTemplate<Object, Object> redisTemplate; private HashOperations<Object, Long, ProductPriceEntity> hashOperations; @PostConstruct
private void init() {
hashOperations = redisTemplate.opsForHash();
} /* (non-Javadoc)
* @see com.mytools.repository.RedisRepository#contains(java.lang.Object)
*/
@Override
public boolean contains(Long id) {
return hashOperations.keys(HASH_NAME).contains(id);
} /* (non-Javadoc)
* @see com.mytools.repository.RedisRepository#find(java.lang.Object)
*/
@Override
public ProductPriceEntity find(Long productId) {
return hashOperations.get(HASH_NAME, productId);
} /* (non-Javadoc)
* @see com.mytools.repository.RedisRepository#save(java.lang.Object)
*/
@Override
public void save(ProductPriceEntity entity) {
hashOperations.put(HASH_NAME, entity.getProductId(), entity); } /* (non-Javadoc)
* @see com.mytools.repository.RedisRepository#delete(java.lang.Object)
*/
@Override
public void delete(Long productId) {
hashOperations.delete(HASH_NAME, productId);
}
}
配置application.yml
spring:
# Stream/Kafka info
cloud:
stream:
bindings:
# input -> productPriceInput (自定义通道)
productPriceInput:
# 要读到消息的消息队列的名称
destination: productPriceTopic
# 发送和接收消息类型
content-type: application/json
# 消费者组:保证消息只会被一组服务实例处理一次
group: productPriceGroup
# 使用kafka作为消息总线
kafka:
binder:
# 运行着kafka服务器的网络地址
brokers: www.mtools.com
# Redis/pool info (RedisProperties)
redis:
database:
host: www.mytools.com
port:
password:
timeout:
jedis:
pool:
# 最大连接数,0为没有限制
max-active:
# 最大空闲连接,0为没有限制
max-idle:
#最大建立连接等待时间,如果超过此时间将接到异常,设为-1表示无限制
max-wait: -1
#最小空闲连接,0为没有限制
min-idle:
API及其他业务逻辑
@Controller
@RequestMapping("pp")
public class ProductPriceController { @Autowired
private ProductPriceServiceImpl productPriceService; @GetMapping(value = "find/productId/{productId}")
@ResponseBody
public ProductPriceEntity find(@PathVariable String productId) {
return productPriceService.find(Long.valueOf(productId));
}
}
[注] 这里创建了一个API,用于查询商品价格。
@Service
@Transactional
public class ProductPriceServiceImpl { private static final Logger logger = LoggerFactory.getLogger(ProductPriceServiceImpl.class); @Autowired
private ProductPriceRedisRepository productPriceRedisRepository; @Autowired
private LoadBalancerClient loadBalancer; public ProductPriceEntity find(Long productId) { ProductPriceEntity result = null; if (productPriceRedisRepository.contains(productId)) {
result = productPriceRedisRepository.find(productId);
logger.info(">>>>> Get data from cache. {}", result.toString());
return result;
} ServiceInstance instance = loadBalancer.choose("app-db");
String path = String.format("http://%s:%s/app-db/pp/find/productId/%s", instance.getHost(), instance.getPort(),
productId.toString());
logger.info(path); RestTemplate restTemplate = new RestTemplate();
ResponseEntity<ProductPriceEntity> response = restTemplate.exchange(path, HttpMethod.GET, null,
ProductPriceEntity.class);
result = response.getBody();
logger.info(">>>>> Get data from downstream. {}", result == null ? "null" : result.toString()); if (result != null) {
productPriceRedisRepository.save(result);
} return result;
}
}
[注] 具体逻辑如下:
(1)如果缓存中有该数据,则从缓存中获取数据。
(2)如果缓存中没有该数据,则调用生产者服务获取数据。拿到数据后再存入缓存中。
Spring Cloud(7.3):配置Consumer Server的更多相关文章
- Spring Cloud Config(配置中心)
每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 一.简介 Spring Cloud Config为分布式系统中的外部配置提供服务器和客 ...
- spring cloud config将配置存储在数据库中
Spring Cloud Config Server最常见是将配置文件放在本地或者远程Git仓库,放在本地是将将所有的配置文件统一写在Config Server工程目录下,如果需要修改配置,需要重启c ...
- 跟我学SpringCloud | 第六篇:Spring Cloud Config Github配置中心
SpringCloud系列教程 | 第六篇:Spring Cloud Config Github配置中心 Springboot: 2.1.6.RELEASE SpringCloud: Greenwic ...
- Spring Cloud Config 实现配置中心,看这一篇就够了
Spring Cloud Config 是 Spring Cloud 家族中最早的配置中心,虽然后来又发布了 Consul 可以代替配置中心功能,但是 Config 依然适用于 Spring Clou ...
- spring cloud 2.x版本 Eureka Server服务注册中心教程
本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 1.创建服务注册中心 1.1 新建Spring boot工程:eureka-server 1 ...
- Spring Cloud Config的配置中心获取不到最新配置信息的问题
Spring Cloud Config的配置中心获取不到最新配置信息的问题 http://blog.didispace.com/spring-cloud-tips-config-tmp-clear/
- Spring Cloud Feign 自定义配置(重试、拦截与错误码处理) 实践
Spring Cloud Feign 自定义配置(重试.拦截与错误码处理) 实践 目录 Spring Cloud Feign 自定义配置(重试.拦截与错误码处理) 实践 引子 FeignClient的 ...
- spring cloud Eureka client配置(consumer通过Eureka发起对provider的调用)
参考:http://www.ityouknow.com/springcloud/2017/05/12/eureka-provider-constomer.html springboot版本:2.0.3 ...
- 笔记:Spring Cloud Ribbon 客户端配置详解
自动化配置 由于 Ribbon 中定义的每一个接口都有多种不同的策略实现,同时这些接口之间又有一定的依赖关系,Spring Cloud Ribbon 中的自动化配置能够很方便的自动化构建接口的具体实现 ...
随机推荐
- Latex快速注释掉多行
最简单的方法就是选中你想要注释掉的内容,同时按住Ctrl+Alt+Shift+→
- AtCoder Grand Contest 032 B - Balanced Neighbors——构造
题意 B - Balanced Neighbors 给定一个整数 $N$($3\leq N \leq 100$),构造一个顶点编号为 $1...N$ 的无向图,需满足如下两个条件: 简单图且连通 存在 ...
- SQL死锁处理
查询死锁 select request_session_id spid, OBJECT_NAME(resource_associated_entity_id) tableName from sys.d ...
- 第三章 基本的bash shell命令
1.硬链接:等同于引用了原文件,并未产生新的文件,不同的硬链接共用一个inode 2.符号链接:创建的是一个新文件,新文件指向原文件,因为是不同的文件,所以有不同的inode
- Fidller抓包分析post请求
目的:抓包是为了最近做接口测试做准备,以前没有用过这个工具,最近来学下,但是网上很多文章了,所以不一一记录,有一部分参考即可 1.如何抓取想要的web端或者手机端包,已经有很多文章谢了,推荐的参考文章 ...
- 有没有一个工具可以帮助查找python的bug和进行静态的代码分析?
答:PyChecker是一个python代码的静态分析工具,它可以帮助查找python代码的bug, 会对代码的复杂度和格式提出警告 Pylint是另外一个工具可以进行codingstandard检查
- wx.navigateTo的url不生效的问题
比如我要要从index页面跳转到logs. 在跳转的时候应该用switchTab,而不是wx.navigateTo 看api这句话 https://developers.weixin.qq.com/m ...
- avalon.js中使用owl-carousel轮播图
<?php if($banners){?> <div class="ms-controller" ms-controller="bannerShow ...
- IO流——常用IO流详解
1:字节流 字节流:用于处理以字节为单位的二进制文件(如音乐,图片等) InputStream 是抽象类 它的对应子类FileInputStream可以被实例化 构造方法: FileInputStre ...
- 一起来学习React-Native之react-navigation基本解析
前言 不久前自己也完整开发了一个React-Native项目,对其中的一些知识存在疑惑,再加上项目时间比较紧张,来不及做系统的学习.现在来回顾自己开发当中存在的疑惑点,和大家分享.第一篇是关于路由 ...