Bus组件的使用
上次写到用curl去手动刷新配置,这样不现实,那么就需要一种机制去实现自动刷新配置,这就要用到消息总线Bus组件了,概念:
根据定义和结构图,我们需要先将configserver和configclient挂在一处消息总线上为了以后做消息广播,所以需要中间件RabbitMQ,MQ就是消息队列(消息:数据传输 队列:一种先进先出,后进后出的数据结构,和栈相反),先在windows上安装MQ:
可以直接参照:
搭建好后进入mq默认控制台localhost:15672 , 用户名密码都输入guest进入首页
这样消息中间件MQ就搭起来了,然后就是要把configserver和configclient也挂到MQ上,比较简单,老规矩:引依赖、写配置即可,所有微服务都要写该依赖(只要你想挂到MQ上):
<!--引入bus依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
configserver中写连接MQ的配置,要注意所有的configclient都没必要写到配置里,可以交给远程库去管理:
spring.rabbitmq.host=localhos #连接主机
spring.rabbitmq.port=5672 #连接mq端口
spring.rabbitmq.username=user #连接mq用户名
spring.rabbitmq.password=password #连接mq密码
然后分别启动configserver和configclient,configserver正常启动,但是configclient启动会报错,因为把client连接mq的配置写在了远程库,在启动client时默认会立即去注册到MQ上,但是此时配置文件还没有拉取下来,所以client会直接启动失败,需要加一个配置:
#启动时当远端配置还没有拉取完成时,项目启动过程中所有失败都是允许的
spring.cloud.config.fail-fast=true
这样的话就都可以正常启动了,也都挂在mq上了
执行curl指令可以实现配置的统一刷新(curl的是configserver):
curl -X POST http://localhost:port/actuator/bus-refresh
默认情况下使用curl -X POST http://localhost:port/actuator/bus-refresh这种方式刷新配置是全部广播形式,也就是所有的微服务都能接收到刷新配置通知,但有时我们修改的仅仅是某个服务的配置,这个时候对于其他服务的通知是多余的,因此就需要指定服务进行通知,也是执行curl
指定服务刷新配置实现
指定端口刷新某个具体服务: curl -X POST http://localhost:port/actuator/bus-refresh/configclient:port
指定服务id刷新服务集群节点: curl -X POST http://localhost:port/actuator/bus-refresh/configclient
[注意:][configclient代表刷新服务的唯一标识]
集成webhook实现自动刷新
webhook本质是一种监听机制,用钩子函数实现,简单理解就是点击提交按钮(这是一个事件)然后就去触发某些函数(处理该事件)
实现方式也比较简单,只需要在远程库的设置里配置上面curl 那个bus-refresh接口的路径就行(configserver),但是有一点,我路径里要是配localhost地址,远程库肯定啥也找不到,这时候就需要内网穿透工具暂时使我们内网对外,或者直接把项目挂公网也可以啦,这里就先用内网穿透吧,用natapp来搞,以前做微信公众号时经常用这个工具做测试嗷。
去官网注册,创建免费隧道(有钱建议支持下vip隧道),配置好要穿透的服务的对应端口(我这里要穿的就是configserver),下载客户端,安装很简单,要注意是cmd启动,不是exe,官网:https://natapp.cn/。执行
natapp -authtoken yourtoken
然后去远程库设置的webhooks中添加该域名
可以在查看更多里查看请求历史(查看请求状态)
现在webhook就挂上了,先看看修改前的配置文件,请求一下服务看下返回:
现在修改配置文件内容,看下日志,postman再请求下client接口,可以发现自动刷新配置已经可以实现了:
在远程库添加webhook时会遇到400的错误,需要在configserver中加一个urlfilter:
package com.jin.config;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* @author jinyunlong
* @date 2021/7/21 16:37
* @profession ICBC锅炉房保安
*/
@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((ServletRequest) 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(basePackages = "com.jin.config") //启动类添加扫描包注解
补一点,curl 配置中心时要是报错别忘了在配置文件里加一个开启web端点暴露:
#开启所有web端点暴露 为了访问curl -X POST http://localhost:port/actuator/bus-refresh
management.endpoints.web.exposure.include=*
SpringCloud 微服务工具集总结
小结一下,就直接贴图了:
可以看到有的组件用到了耐非的,有的用到Spring的,还有google的等等,还有不少好组件是alibaba的,就是接下来要看的spring alibaba封装的组件了。