Sentinel 的主要功能就是容错,主要体现为下面这三个:
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
编写一个 Controller 测试使用
@RestController
@RequestMapping(path = "/message")
public class MessageController {
@GetMapping(path = "/test1")
public String test1(){
return "测试高并发 1";
}
@GetMapping(path = "/test2")
public String test2(){
return "测试高并发 2";
}
}
org.springframework.cloud
spring-cloud-starter-gateway
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: order_route # 当前路由的标识, 要求唯一
uri: http://localhost:8081 # 请求要转发到的地址
order: 1 # 路由的优先级,数字越小级别越高
predicates: # 断言(就是路由转发要满足的条件)
- Path=/order-serv/** # 当请求路径满足Path指定的规则时, 才进行路由转发
filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 # 转发之前去掉 1 层路径
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
3.修改配置文件
server:
port: 9001
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos 服务地址
gateway:
discovery:
locator:
enabled: true
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: product_route # 当前路由的标识, 要求唯一
uri: lb://service-order # lb 指的是从 nacos 中按照名称获取微服务, 并遵循负载均衡策略
order: 1 # 路由的优先级,数字越小级别越高
predicates: # 断言(就是路由转发要满足的条件)
- Path=/order-serv/** # 当请求路径满足 Path 指定的规则时,才进行路由转发
filters: #过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 # 转发之前去掉 1 层路径
@Component
public class TokenFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//获取请求中的参数部分
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (!"123456".equals(token)) {//模拟验证 token
System.out.println("鉴权失败");
exchange.getResponse().setStatusCode(201);
return exchange.getResponse().setComplete();//响应状态码
}
//调用 chain.filter 继续向下游执行
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
1 导入依赖
com.alibaba.csp
sentinel-spring-cloud-gateway-adapter
@Configuration
public class GatewayConfiguration {
private final List viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider>
viewResolversProvider,ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers =
viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
// 初始化一个限流的过滤器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
// 配置初始化的限流参数
@PostConstruct
public void initGatewayRules() {
Set rules = new HashSet<>();
rules.add(new GatewayFlowRule("order_route") //资源名称,对应路由 id
.setCount(1) // 限流阈值
.setIntervalSec(1) // 统计时间窗口,单位是秒,默认是 1 秒
);
GatewayRuleManager.loadRules(rules);
}
// 配置限流的异常处理器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler
sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(viewResolvers,
serverCodecConfigurer);
}
// 自定义限流异常页面
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
public Mono handleRequest(ServerWebExchange
serverWebExchange, Throwable throwable) {
Map map = new HashMap<>();
map.put("code", 0);
map.put("message", "接口被限流了");
return ServerResponse.status(HttpStatus.OK).
contentType(MediaType.APPLICATION_JSON_UTF8).
body(BodyInserters.fromObject(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
解压
在解压目录 rocketmq-console 的 pom.xml 中添加如下 JAXB 依赖。
javax.xml.bind
jaxb-api
2.3.0
com.sun.xml.bind
jaxb-impl
2.3.0
com.sun.xml.bind
jaxb-core
2.3.0
javax.activation
activation
1.1.1
org.apache.rocketmq
rocketmq-spring-boot-starter
2.0.2
public class MQProducerTest {
public static void main(String[] args) throws Exception {
//1. 创建消息生产者, 指定生产者所属的组名
DefaultMQProducer producer = new DefaultMQProducer("myproducer-group");
//2. 指定 Nameserver 地址
producer.setNamesrvAddr("192.168.109.131:9876");
//3. 启动生产者
producer.start();
//4. 创建消息对象,指定主题、标签和消息体
Message msg = new Message("myTopic", "myTag",("RocketMQ Message").getBytes());
//5. 发送消息
SendResult sendResult = producer.send(msg, 10000);
System.out.println(sendResult);
//6. 关闭生产者
producer.shutdown();
}
}
public class MQConsumerTest {
public static void main(String[] args) throws Exception {
//1. 创建消息消费者, 指定消费者所属的组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("myconsumergroup");
//2. 指定 Nameserver 地址
consumer.setNamesrvAddr("192.168.109.131:9876");
//3. 指定消费者订阅的主题和标签
consumer.subscribe("myTopic", "*");
//4. 设置回调函数,编写处理消息的方法
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(Listmsgs,ConsumeConcurrentlyContext context) {
System.out.println("Receive New Messages: " + msgs);//返回消费状态
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5. 启动消息消费者
consumer.start();
System.out.println("Consumer Started.");
}
}
org.apache.rocketmq
rocketmq-spring-boot-starter
2.0.2
org.apache.rocketmq
rocketmq-client
4.4.0
rocketmq:
name-server: 127.0.0.1:9876 #rocketMQ 服务的地址
producer:
group: shop-order # 生产者组
@Autowired
private RocketMQTemplate rocketMQTemplate;
rocketMQTemplate.convertAndSend("order-topic", order);
org.apache.rocketmq
rocketmq-spring-boot-starter
2.0.2
org.apache.rocketmq
rocketmq-client
4.4.0
rocketmq:
name-server: 127.0.0.1:9876
@Service
@RocketMQMessageListener(consumerGroup = "shop-user", topic = "order-topic")
public class SmsService implements RocketMQListener {
@Override
public void onMessage(Order order) {
System.out.println("收到一个订单信息:"+ JSON.toJSONString(order)+",接下来发送短信");
}
}
//同步消息
//参数一: topic
//参数二: 消息内容
SendResult sendResult = rocketMQTemplate.syncSend("test-topic-1", "这是一条同步消息");
System.out.println(sendResult);
发送异步消息
//参数一: topic
//参数二: 消息内容
//参数三: 回调函数, 处理返回结果
rocketMQTemplate.asyncSend("test-topic-1", "这是一条异步消息", new
SendCallback(){
@Override
public void onSuccess(SendResult sendResult) {
System.out.println(sendResult);
}
@Override
public void onException(Throwable throwable) {
System.out.println(throwable);
}
});
//让线程不要终止
Thread.sleep(30000000);
rocketMQTemplate.sendOneWay("test-topic-1", "这是一条单向消息");
思考: 添加 synchronized 锁,能够解决问题
为每个线程的添加一个版本号,删除时,判断版本号.
导入依赖
org.redisson
redisson
3.6.5
//创建 Redisson 对象
@Bean
public Redisson getRedisson(){
Config config = new Config();
config.useSingleServer().setAddress("redis://120.48.37.232:6379").setDatabase(0);
return (Redisson)Redisson.create(config);
}