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 中的自动化配置能够很方便的自动化构建接口的具体实现 ...
随机推荐
- js 面向对象之构造器与工厂函数
字面量模式声明一个对象实例 let m = {name: "lisi", age:15} m.constructor // ƒ Object() { [native code] } ...
- 301、404、200、304等HTTP状态
在网站建设的实际应用中,容易出现很多小小的失误,就像mysql当初优化不到位,影响整体网站的浏览效果一样,其实,网站的常规http状态码的表现也是一样,Google无法验证网站几种解决办法,提及到由于 ...
- Mybatis-Generator逆向工程,复杂策略(Criteria拼接条件)
基于上一篇修改 1.Generator配置文件修改,将targetRuntime改为MyBatis3 2.项目结构目录 这个xxxExample就是拼接条件用的 3.测试代码 注释写的很详细 publ ...
- feign.RetryableException: Read timed out executing xxx
feign.RetryableException: Read timed out executing GET http://common-item/service/item/selectTbItemA ...
- Spring Data JPA 提供的各种Repository接口作用
各种Repository接口继承关系: Repository : public interface UserRepository extends Repository<User, Integer ...
- 04_Tutorial 4: Authentication & Permissions 认证和权限
1.认证和权限 0.文档 https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/ https: ...
- 51nod 1488 帕斯卡小三角 斜率优化
思路:斜率优化 提交:\(2\)次 错因:二分写挂 题解: 首先观察可知, 对于点\(f(X,Y)\),一定是由某个点\((1,p)\),先向下走,再向右下走. 并且有个显然的性质,若从\((1,p) ...
- MongoDB 查看chunk块大小
使用mongo shell连到mongos执行命令:AllChunkInfo("dbname.cellname",true) 点击(此处)折叠或打开 AllChunkInfo = ...
- imp
imp 重载模块功能 from imp import reload 当在shell中使用一个模块,原始代码改了,要使用新的还得退出shell重新载入一次,这样的话原来的变量就都不在了 这时候可以使用i ...
- Web前端-JavaScript基础教程下
Web前端-JavaScript基础教程下 <script>有6个属性: async对外部脚本有效,可以用来异步操作,下载东西,不妨碍其他操作. charset为src属性指定字符集. d ...