springcloud+nacos+gateway+Feign+RocketMQ学习记录

1、五大组件

注册中心 euraka nacos

负载均衡 ribbon,feign集成了ribbon

熔断降级

路由网关 gateway

配置中心

https://blog.csdn.net/qq904748592/article/details/127572608

2、nacos

nacos默认以集群形式启动

单机模式启动命令

#切换⽬录
cd nacos/bin
#命令启动
./startup.sh -m standalone

2、路由网关

Predict决定了请求由哪一个路由处理,在路由处理之前,需要经过“pre”类型的过滤器处理,处理返回响应之后,可以由“post”类型的过滤器处理

三大组件:路由 断言 过滤器

路由:ID、目标URI 、断言、过滤器 ,如果断言为true,将匹配路由

3、springcloud gateway oauth2

https://blog.csdn.net/const_/article/details/123742701

4、如果服务全都启动之后,注册中心服务器挂掉,不会影响现有服务之间的调用,因为调用使用的是本地的服务列表,但是如果新增或者删除掉了服务,则不能正确访问。服务是定期从注册中心更新服务列表到本地

nacos仅仅是服务注册作用, Rinnon 实现负载均衡

5、Feign工作原理

主要技术就是动态代理和反射

启动类@EnableFeignClients注解扫描所在包及其子包下所有具有@FeignClient注解的接口,

创建代理类实现feign接口中的方法:

  1. 代理类获取当前类所实现的接口也就是Feign接口
  2. 获取接口上的注解@FeignClient(name = “product-service”)
  3. 获取注解上的属性值
  4. 获取方法的引用和注解@RequestMapping(“/product/{pid}”)
  5. 获取传入的参数
  6. 拼接成url,通过ribbon获取本地缓存的服务名,将服务器替换成ip和port
  7. 使用RestTemplate进行网络调用
  8. 内部解析返回的结果字符串,获取方法返回值字节码,
  9. 将返回的字符串解析成方法返回值对象

创建对象并放入到sping容器中。

调用的是代理类的方法

6、容错方案

隔离 :不把所有的线程用于访问其中的一个服务,对线程进行隔离,防止对某一个服务的调用占用所有的线程

超时:超过指定时间后,断开请求,释放线程 实际中超时阈值不要超过2s(调用第三方接口,处理时间未知, 超时时间不可控,要使用异步调用)

限流:从接口处限制请求数

熔断:连续多次访问不成功,自动断开,偶尔发送请求,如果可用则熔断关闭,不可用则为熔断开启

降级:无法调用服务时,调用本地的fallback

常见容错组件:Hystrix Resilience4j sentinel

7、sentinel

​ 项目中添加依赖

<!--sentinel组件-->
<dependency>
 <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

spring:
 cloud:
   sentinel:
     transport:
        port: 9999 #跟控制台交流的端⼝,随意指定⼀个未使⽤的端⼝即可
        dashboard: localhost:8089 # 指定控制台服务的地址

​ 启动sentinel控制台

java -Dserver.port=8089 -Dcsp.sentinel.dashboard.server=localhost:8089 -Dproject.name=sentinel-dashboard -jar  sentinel-dashboard-1.8.6.jar

直接使⽤jar命令启动项⽬(控制台本身是⼀个SpringBoot项⽬)

通过浏览器访问localhost:8089 进⼊控制台 ( 默认⽤户名密码是 sentinel/sentinel )

8 GateWay

先经过断言 后经过过滤器

GatewayHandlerMapping 和 Gateway web Handler 指的就是断言,相当于地址映射和web请求

过滤分为前置拦截和后置拦截。通用规则可以写在过滤器中

过滤器分为局部和全局,GatewayFilter:应⽤到单个路由或者⼀个分组的路由上。 GlobalFilter:应⽤到所有的路由上

编写Filter类时,注意名称是有固定格式xxxGatewayFilterFactory

如果为TimeGatewayFilterFactory

在application.yml中配置

filters:
  - Time = true

9、微服务拆分原则

基于业务逻辑,根据职责范围划分,如果业务之前耦合较大,不应进行拆分 会出现大量的网络调用

基于稳定性,依据修改频率高低的业务进行拆分

基于可靠性,

基于高性能,

10、任务调度

任务调度是为了自动完成特定任务,在约定的特定时刻去执行任务的过程

使用spring的定时任务,在集群的情况下,可能会出现任务重复执行的问题

11、消息中间件RocketMQ

可以解决代码耦合问题,如订单服务调用积分服务,如果为直接远程调用积分微服务,则下单流程紧密依赖积分微服务,如果积分微服务出现故障,则下单流程失败。积分的操作实时性要求没有那么高, 可以使用消息中间件处理

流量削峰,controller层接收到请求后,放入到消息队列,业务层对消息队列按照一定速率进行消费,以减少数据库压力,数据库的每秒并发2500左右

数据分发,广播机制

一般一个业务对应一个队列,也就是topic,实际存储在messagequeue中,一个topic中有四个messagequeue(四个写队列,四个读队列),如果只有一个队列,为了保证线程安全,必须在写操作的时候进行上锁,四个队列提高效率。

通过内部维护一个index来实现:index对队列的size做取模运算,分配到对应队列。

队列size4可以自定义设置,最好与cpu核心数一致

发送的消息封装成message对象存入messagequeue中

RocketMQ安装步骤

1.将压缩包上传服务器,把 rocketmq-all-4.4.0-bin-release.zip 拷⻉到 /usr/local/software

2.使⽤解压命令进⾏解压到 /usr/local ⽬录

unzip /usr/local/software/rocketmq-all-4.4.0-bin-release.zip -d /usr/local

3.软件⽂件名重命名

mv /usr/local/rocketmq-all-4.4.0-bin-release/ /usr/local/rocketmq-4.4/

4.设置环境变量 ,修改/etc/profile

export JAVA_HOME=/usr/local/jdk1.8
export ROCKETMQ_HOME=/usr/local/rocketmq-4.4
export PATH=$JAVA_HOME/bin:$ROCKETMQ_HOME/bin:$PATH

修改后执行 source profile

5.修改脚本中的JVM相关参数,修改⽂件如下

vi /usr/local/rocketmq-4.4/bin/runbroker.sh
vi /usr/local/rocketmq-4.4/bin/runserver.sh

​ 修改启动参数配置 ,因为rocketmq默认需要内存8G,虚拟机中不修改的话启动会报错,实际应用中不需要修改!!!

JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g -Xmn512m"

6.修改配置⽂件

vi /usr/local/rocketmq-4.4/conf/broker.conf

新增配置如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

7.启动NameServer

# 1.启动NameServer
nohup sh mqnamesrv &
# 2.查看启动⽇志
tail -f ~/logs/rocketmqlogs/namesrv.log

8.启动Broker

#1.启动Broker
nohup sh mqbroker -n 部署的IP地址:9876 -c /usr/local/rocketmq-
4.4/conf/broker.conf &
#2.查看启动⽇志
tail -f ~/logs/rocketmqlogs/broker.log

9.使⽤命令查看是否开启成功

jps    #查看java进程命令

需要看到 NamesrvStartup 和 BrokerStartup 这两个进程

消息发送

同步发送

应用程序向中间件发送消息,需要等待消息中间件将信息存储完毕并响应回去后,应用程序才继续向下执行

异步发送

应用程序向中间件发送消息,消息中间件收到消息之后直接返回给程序,应用程序继续向下执行(此时消息并没有完全存储到磁盘),存储完成后会将存储的结果通过回调通知程序

一次性发送

不需要知道消息是否被消息中间件存储,业务程序只管发送消息,使用场景:日志

消息消费

注意:集群模式和广播模式针对的是同一个消费者组下的消费者,对与不同消费者组的消费者来说,所有消息都是可见的,rocketmq的消费是逻辑上的消费,并没有真正的删除数据,所有数据都会保留72小时

集群模式

在消费模式为集群的模式下,同一条消息只能被其中一台机器消费

广播模式

集群中的每台机器都能消费到所有消息

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("helloConsumerGroup");
consumer.setMessageModel(MessageModel.BROADCASTING); //默认为集群模式

顺序消费

将需要顺序消费的消息,在发送时,发送至同一个队列(一个topic下一般有四个队列),消费者使用一个线程消费

生产者端

​ 定义一个MessageQueueSelector,重写select方法,根据业务确定队列index,发送时使用producer.send(msg,selector,10);参数10可以是Object类型的任意参数,用来确定使用哪个队列。

//        发送时选择队列,以实现顺序消费
        MessageQueueSelector selector = new MessageQueueSelector() {
            @Override
            public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
                Long id=(Long) o;
//                根据订单id确定放入哪个队列(条件可变)
                int index=(int)(id%list.size());
                return list.get(index);
            }
        };
        for (int i = 0; i < 10; i++) {
            Message msg = new Message(topic,("RocketMQ顺序消息"+i).getBytes(Charset.defaultCharset()));
            producer.send(msg,selector,10);
        }
消费者端

设置从队列的哪里开始消费,MessageListener设置为MessageListenerOrderly

//        设置从哪开始消费
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//
        consumer.setMessageListener(new MessageListenerOrderly() {

            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
                for (MessageExt msg : list) {
                    System.out.println("线程"+Thread.currentThread()+"队列ID"+msg.getQueueId()+"消息内容"+new String(msg.getBody(), Charset.defaultCharset()));
                }

                return ConsumeOrderlyStatus.SUCCESS;
            }

       });

延迟消费

使用场景:超时未支付 取消订单, 超时未领取红包,取消等,若使用定时任务,系统性能开销较大

发送时标记为延时消息,消息中间件有一个基于内存的定时器,每秒钟扫描,到达指定时间后,消费者才能够消费到此延时消息,rocket不支持任意时长的延时,默认包含18个级别的延时等级,可以再broker.conf中进行修改

发送时生产者对message进行设置

Message msg = new Message(topic,("RocketMQ顺序消息"+i).getBytes(Charset.defaultCharset())); 
msg.setDelayTimeLevel(3);

过滤消费

tag过滤

生产者实例化Message时添加一个tag

Message msg = new Message(topic,"tagA",("RocketMQ消息").getBytes(Charset.defaultCharset())); 

消费者订阅时设置订阅tag

 consumer.subscribe("helloTopic","tagA || tagC");
属性过滤(sql92过滤)

生产者对message添加属性

msg.putUserProperty("age","22");

消费者端根据数据过滤

consumer.subscribe("helloTopic", MessageSelector.bySql("age<30"));

使用属性过滤需要在broker.conf中添加enablePropertyFilter=true

12、springboot集成rocketmq

引入依赖

 <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.0.4</version>
        </dependency>

application.yml

rocketmq:
    name-server: 192.168.164.129:9876
    producer:
      group: my-group

生产者发送

 @Autowired
    private RocketMQTemplate rocketMQTemplate;
 /*发送同步消息*/
    @Test
    public void sendMsg(){
        Message msg= MessageBuilder.withPayload("boot发送同步消息").build();

        rocketMQTemplate.send("hellotopic-boot",msg);
    } 
/*发送异步消息*/
    @Test
    public void sendAsyncMsg() throws InterruptedException {
        Message msg= MessageBuilder.withPayload("boot发送异步消息").build();
        System.out.println("发送前");
        rocketMQTemplate.asyncSend("hellotopic-boot", msg, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("发送状态"+sendResult.getSendStatus());
            }

            @Override
            public void onException(Throwable throwable) {
                System.out.println("消息发送失败");
            }
        });
        System.out.println("发送完毕");
        TimeUnit.SECONDS.sleep(5);
    }
   /*发送一次性消息*/
    @Test
    public void sendOnewayMsg(){
        Message msg= MessageBuilder.withPayload("boot发送一次性消息").build();

        rocketMQTemplate.sendOneWay("hellotopic-boot",msg);
    }
/*顺序发送*/
    @Test
    public void sendOrderly(){
//        设置队列选择器
        rocketMQTemplate.setMessageQueueSelector(new MessageQueueSelector() {
            @Override
            public MessageQueue select(List<MessageQueue> list, org.apache.rocketmq.common.message.Message message, Object o) {
                String idstr=(String)o;
                Long id= Long.parseLong(idstr);
                int index= (int) (id%list.size());
                return list.get(index);
            }
        });
        Message msg= MessageBuilder.withPayload("boot发送一次性消息").build();

        rocketMQTemplate.sendOneWayOrderly("hellotopic-boot",msg,"10");
    }
//延时消费
@Test
    public  void sendDelay(){
        Message msg=MessageBuilder.withPayload("发送延时消息").build();
        rocketMQTemplate.syncSend("hellotopic-boot",msg,3000,3);
        System.out.println("发送时间"+new Date());
    }
/*过滤发送,主题:标签*/
    @Test
    public  void sendFilterMsg(){
        Message msg=MessageBuilder.withPayload("发送延时消息").build();
        rocketMQTemplate.sendOneWay("hellotopic-boot:TagA",msg);
        System.out.println("发送时间"+new Date());
    }
//sql92表达式过滤
  @Test
    public  void send92SQLMsg(){
        Message msg=MessageBuilder.withPayload("发送延时消息").setHeader("age","22").build();
        rocketMQTemplate.sendOneWay("hellotopic-boot:TagA",msg);
        System.out.println("发送时间"+new Date());
    }

消费者消费

@Component
@RocketMQMessageListener(consumerGroup = "bootconsumer",topic = "hellotopic-boot")
//@RocketMQMessageListener(consumerGroup = "bootconsumer",topic = "hellotopic-boot",messageModel = MessageModel.BROADCASTING)   //广播模式
public class HelloTopicListenser implements RocketMQListener<MessageExt> {
    @Override
    public void onMessage(MessageExt messageExt) {
        System.out.println("收到消息"+new String(messageExt.getBody(), Charset.defaultCharset()));
    }
}
//顺序消费
@Component
@RocketMQMessageListener(consumerGroup = "orderlytopicGroup",topic = "orderlytopic-boot",consumeMode = ConsumeMode.ORDERLY)
public class OrderlyTopicListenser implements RocketMQListener<MessageExt> {
    @Override
    public void onMessage(MessageExt messageExt) {
        System.out.println("收到顺序消息:"+new String(messageExt.getBody(), Charset.defaultCharset())+ "  队列ID:"+ messageExt.getQueueId());
    }
}
//过滤消费,标签过滤  
@RocketMQMessageListener(consumerGroup = "orderlytopicGroup",topic = "orderlytopic-boot",consumeMode = ConsumeMode.ORDERLY,selectorExpression = "TagA")
//sql92过滤,selectorType默认是标签过滤
@RocketMQMessageListener(consumerGroup = "orderlytopicGroup",topic = "orderlytopic-boot",selectorType = SelectorType.SQL92,selectorExpression = "age <25")
{
        System.out.println("收到顺序消息:"+new String(messageExt.getBody(), Charset.defaultCharset())+ "  队列ID:"+ messageExt.getQueueId());
    }
}
//过滤消费,标签过滤  
@RocketMQMessageListener(consumerGroup = "orderlytopicGroup",topic = "orderlytopic-boot",consumeMode = ConsumeMode.ORDERLY,selectorExpression = "TagA")
//sql92过滤,selectorType默认是标签过滤
@RocketMQMessageListener(consumerGroup = "orderlytopicGroup",topic = "orderlytopic-boot",selectorType = SelectorType.SQL92,selectorExpression = "age <25")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值