Bus组件 + RabbitMQ + webHooxs 实现自动刷新配置


前言

config搭建统一配置中心
前一篇文章我们利用config搭建了统一配置中心,来简化我们微服务的配置更改,
但是仍然存在的一个问题就是,如果远端git仓库配置文件更改,虽然config server能够捕捉到更改信息,并自动拉取配置信息到本地,但是config client此时如果要实时更新配置文件,要么是重启,要么对每一个微服务发送**curl -X POST http://localhost:9099/actuator/refresh并且在需要更新的类上添加@RefreshScope注解**,如果是在微服务集群的情况下,我们不可能让每一台服务重启,或者发送一个post请求.

所以这里springcloud为我们提供了Bus(消息总线)组件来简化;
在这里插入图片描述

什么是BUS组件??

主要用来在微服务系统中实现远端配置更新时通过广播形式通知所有客户端刷新配置信息,避免手动重启服务的工作
BUS组件要想生效,必须利用消息中间件,比如Kafaka或者使用AMQP协议的框架(比如RabbitMQ)
流程

1.首先config server得利用BUS组件连接到RabbitMQ,并且所有的config client(即微服务也需要连接到MQ服务器)

2.如果远程仓库配置更新,config server拉取到更新的配置文件后,对Config Server发送curl -X POST http://xxxxxxx/actuator/bus-refresh,其中xxxxx为config server的ip和port,

3 .之后ConfigServer(生产者)就会把curl -X POST http://xxxxxxx/actuator/bus-refresh请求以消息包的方式传送到MQ,然后MQ就会把消息包发送到每一个微服务(消费者),这样每一个微服务收到了curl -X POST http://xxxxxxx/actuator/bus-refreshPOST请求就会实现自动刷新;

在这里插入图片描述
既然要使所有的微服务连接到MQ,因此首先需要安装MQ服务,这里为了简便,使用Docker安装

一、Docker安装RabbitMQ

这里安装的带management,有web管理界面

  	#拉取到本地仓库
 1. docker pull rabbitmq:3.7.7-management

	# 启动服务
 2. docker run -d --name rabbitmq3.7.7 -p 5672:5672 -p 15672:15672 -v `pwd`/data:/var/lib/rabbitmq --hostname myRabbit -e RABBITMQ_DEFAULT_VHOST=my_vhost  -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin rabbitmq:3.7.7-management

  • -d 后台运行容器;

  • –name 指定容器名;

  • -p 指定服务运行的端口(5672:应用访问端口;15672:控制台Web端口号);

  • -v 映射目录或文件;

  • –hostname 主机名(RabbitMQ的一个重要注意事项是它根据所谓的 “节点名称” 存储数据,默认为主机名);

  • -e 指定环境变量;(RABBITMQ_DEFAULT_VHOST:默认虚拟机名;RABBITMQ_DEFAULT_USER:默认的用户名;RABBITMQ_DEFAULT_PASS:默认用户名的密码)

MQ有两个端口,一个是15672,用于访问web界面,另一个是5672用于客户端连接MQ服务
在这里插入图片描述

二、连接到MQ服务器

1.Config Server统一配置中心

1 首先引入Bus组件依赖

<!--引入bus依赖-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

2 编写配置文件

server:
  port: 8900
spring:
  application:
    name: CONFIG-SERVER
  cloud:
    consul:
      host: localhost
      port: 8500
    config:
      server:
        git:
          uri: https://gitee.com/yangjiawen621/configs.git
          default-label: master #默认是master 分支
          username: xxxx
          password: xxxx
  rabbitmq:
    host: 192.168.142.129
    port: 5672
    username: admin
    password: admin
    virtual-host: my_vhost

注意:
但是,这里发现405,原因是curl -X POST http://xxxxxxx/actuator/bus-refresh这个路径是BUS组件内部提供好的,我们要访问需要在Config Server配置文件中开启端点暴露,否则会报405
在这里插入图片描述


server:
  port: 8900
spring:
  application:
    name: CONFIG-SERVER
  cloud:
    consul:
      host: localhost
      port: 8500
    config:
      server:
        git:
          uri: https://gitee.com/yangjiawen621/configs.git
          default-label: master #默认是master 分支
          username: Yangjiawen621
          password: zetyka123789--
  rabbitmq:
    host: 192.168.142.129
    port: 5672
    username: admin
    password: admin
    virtual-host: my_vhost
    
# 开启端点暴露
management:
  endpoints:
    web:
      exposure:
        include: "*"

注意:*在yml中 要加上""双引号,否则报错,在properties后缀文件中不用
启动项目即可

2.Config Client的连接

同样客户端也需要引入Bus组件依赖

<!--引入bus依赖-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

编写bootstrap.yml配置文件

spring:
  cloud:
      config:
        # 开启默认快速失败,BUS组件在项目启动时会立即尝试连接
        # MQ服务器,此时配置文件中没有连接MQ的配置,因此需要开启
        # 快速失败,允许启动时连接失败,等拉取到远端配置文件之后,再
        # 尝试连接
        fail-fast: true
        discovery:
          enabled: true
          service-id: CONFIG-SERVER
        label: master #指定从仓库的那个分支拉取配置
        name: testServerConfig  #指定拉取配置文件的名称
        profile: dev #指定拉取配置文件的环境
      consul:
        host: localhost
        port: 8500
#  这段配置放在远端github仓库
#  rabbitmq:
#    host: 192.168.142.129
#    port: 5672
#    username: admin
#    password: admin
#    virtual-host: my_vhost
注意:这里一定要加入fail-fast配置,代表接收快速失败 开启默认快速失败,BUS组件在项目启动时会立即尝试连接MQ服务器,此时配置文件中没有连接MQ的配置,因此需要开启快速失败,允许启动时连接失败,等拉取到远端配置文件之后,再尝试连接; 否则会报错No bean named 'configServerRetryInterceptor' available

三、测试

开启ConfigServer统一配置中心和一个Goods服务
在这里插入图片描述
并且都已成功连接到MQ
在这里插入图片描述
此时远端gitee仓库的配置如下:
在这里插入图片描述
GoodsController代码
注意:一定要加上@RefreshScope注解,代表该类需要刷新配置

@RestController
@RefreshScope
public class GoodsController {
    private static final Logger log = LoggerFactory.getLogger(GoodsController.class);
    
    //获取配置文件中的name
    @Value("${name}") 
    private String name;

    @GetMapping("/goods")
    public String goods() {
        log.info("进入商品服务");
        return "name:  " + name;
    }
}

访问http://localhost:8083/goods
在这里插入图片描述
此时更新远程仓库name为zhangsan,并向Config Server发送curl -X POST http://xxxxxxx/actuator/bus-refresh
这样Goods服务不用重启,就能接受到配置的变化
在这里插入图片描述
发现成功
在这里插入图片描述

在这里插入图片描述
补充一点:
当我们发送curl -X POST http://xxxxxxx/actuator/bus-refresh请求时,该系统中所有服务都会刷新,
这样会引起资源的消耗,我们可以指定服务刷新
curl -X POST http://xxxxxxx/actuator/bus-refresh/服务id
比如,这里我们指定curl -X POST http://xxxxxxx/actuator/bus-refresh/GOODS商品服务才会刷新配置,其他服务比如订单服务等就不会刷新;

四、利用webHooxs实现全自动刷新

通过上面的接收我们发现,当远端仓库配置更新后,我们还需要手动向Config Server发送一个post请求,
这里我们还可以利用gitee/github仓库自带的WebHooxs帮我们实现发送请求
所谓WebHooxs: 是git仓库提供一种特有机制: 这种机制就是一个监听机制 ,当仓库提交事件时会触发对应事件执行,
即发送一个POST请求;

gitee

在这里插入图片描述
在这里插入图片描述

github
在这里插入图片描述

添加完成后,测试时发现400错误,需要我们在config server中添加如下代码

@Component
public class UrlFilter  implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest)request;
        HttpServletResponse httpServletResponse = (HttpServletResponse)response;
 
        String url = new String(httpServletRequest.getRequestURI());
 
        //只过滤/actuator/bus-refresh请求
        if (!url.endsWith("/bus-refresh")) {
            chain.doFilter(request, response);
            return;
        }
 
        //获取原始的body
        String body = readAsChars(httpServletRequest);
 
        System.out.println("original body:   "+ body);
 
        //使用HttpServletRequest包装原始请求达到修改post请求中body内容的目的
        CustometRequestWrapper requestWrapper = new CustometRequestWrapper(httpServletRequest);
 
        chain.doFilter(requestWrapper, response);
 
    }
 
    @Override
    public void destroy() {
 
    }
 
    private class CustometRequestWrapper extends HttpServletRequestWrapper {
        public CustometRequestWrapper(HttpServletRequest request) {
            super(request);
        }
 
        @Override
        public ServletInputStream getInputStream() throws IOException {
            byte[] bytes = new byte[0];
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
 
            return new ServletInputStream() {
                @Override
                public boolean isFinished() {
                    return byteArrayInputStream.read() == -1 ? true:false;
                }
 
                @Override
                public boolean isReady() {
                    return false;
                }
 
                @Override
                public void setReadListener(ReadListener readListener) {
 
                }
 
                @Override
                public int read() throws IOException {
                    return byteArrayInputStream.read();
                }
            };
        }
    }
 
    public static String readAsChars(HttpServletRequest request)
    {
 
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder("");
        try
        {
            br = request.getReader();
            String str;
            while ((str = br.readLine()) != null)
            {
                sb.append(str);
            }
            br.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != br)
            {
                try
                {
                    br.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}

并且在入口类添加注解@ServletComponentScan注解
在这里插入图片描述

总结

之后,我们只要在gitee/github一更新配置,gitee/github就会发送POST请求,然后所有微服务就会自动更新配置
至此,大功告成!!!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值