利用jrebel与arthas在centos7 springboot热部署

jrebel 热部署

jrebel在本地是可以class xml一起热部署,但是远程热部署xml不行,所以用arthas代替去热部署xml
安装包我已经上传自己服务器,供大家下载,百度云麻烦限速https://anan.3xroad.com/download/热部署工具
下载 arthas工具和jrebel反向代理工具里的文件就行,中间那个文章后面再linux直接下载,已开启下载限速,大于13m后都限速,
开放的 arthas工具和jrebel都小于13m,请放心食用
在这里插入图片描述

1.jrebel 反向代理

因为jrebel是收费插件,所以要高一些小动作咱们才能‘正常’使用,当然你也可以拿别人代理好的操作,但别人跑路了你就又要找,索性自己搞吧
在这里插入图片描述
上面那个是linux,下面是win10启动自己选
win下面双击,linux的按下面命令启动

./ReverseProxy_linux_amd64 &

#默认代理8888端口,可以运行一下命令指定端口:ip是内网ip 不是公网
./ReverseProxy_linux_amd64 -l "ip:port" &

服务器安装JRebel并激活—跳过吧 下面有直接linux安装命令
根据的idea下载对应版本 https://www.jrebel.com/products/jrebel/download/prev-releases
我用的是idea2020.3.3,下面没有,只有2020.3.2,要是没你的idea版本,就选个近似的吧
在这里插入图片描述
centos7激活
emmmm,这个ip:port/guid,见下面方法,和idea一样

./activate.sh http://ip:port/guid 你的邮箱地址

在这里插入图片描述

2.idea插件

安装这两个插件,重启idea
在这里插入图片描述
点击 右边 change
在这里插入图片描述
ip:端口,ip就看你怎么反向代理的时候开的了,端口默认8888,后面的验证码可以在下面网站获取,下面那个邮箱随便写
https://www.guidgen.com/
在这里插入图片描述

在这里插入图片描述
有多个选择,就点一下这个,然后界面就剩它一个了,防止Jrebel本地热更新失效,开启Jrebel离线模式
在这里插入图片描述
激活成功
在这里插入图片描述

3.jrebel使用

现在界面就有这两个热启动按钮了
在这里插入图片描述
启动配置选这个
在这里插入图片描述
这个就不用重启部署了,这个按钮刷新项目快捷键Ctrl+shift+F9
在这里插入图片描述

4.jrebel远程热更新

在这里插入图片描述

在这里插入图片描述

远程服务器上安装JRebel

# 下载安装包
curl -O http://dl.zeroturnaround.com/jrebel-stable-nosetup.zip
# 解压下载文件
unzip jrebel-stable-nosetup.zip

# 将自己的激活URL和邮箱地址替换下面的指令参数 url就是ip 不用端口
bash jrebel/bin/activate.sh  url email

# 为了安全起见,防止其他人通过远程连接到JRebel篡改程序,我们为JRebel设置一个密码。
java -jar jrebel/jrebel.jar -set-remote-password 12345678

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

生成启动指令
根据你自己实际情况,下面那个绿的java就是要去centos7执行的命令,紫色参数根据你自己情况修改
这里有两个地方需要修改,一个是-agentpath:就是刚刚在服务器上安装的JRebel目录,另外一个就是自己的jar包名称

java "-agentpath:/root/www/jrebel/lib/libjrebel64.so" -Drebel.remoting_plugin=true -jar demo-0.0.1-SNAPSHOT.jar

在这里插入图片描述
然后在远程服务器上执行命令启动服务,启动成功,控制台会输出JRebel的信息

在这里插入图片描述
每次要远程热更新就点击这个
在这里插入图片描述
右下角出现这个就是成功了,请求接口看看是不是成功没,对xml无效
在这里插入图片描述

5.jrebel 远程调试

在这里插入图片描述
添加远程调试

在这里插入图片描述
ok,在服务器上执行这个命令,按实际自己拼接

java "-agentpath:/root/www/jrebel/lib/libjrebel64.so" -Drebel.remoting_plugin=true -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar demo-0.0.1-SNAPSHOT.jar

成功了,打断点调试吧
在这里插入图片描述

arthas 热部署

centos7 安装java
yum安装的java-1.8.0-openjdk 没有 javac ,需要安装 devel 版本。只能先卸载yum安装的jdk,重新安装devel 版本的jdk了。
没javac 用不了arthas

#查看目前系统中的jdk
rpm -qa | grep jdk

#yum卸载  将上面全部删除,删完再次查询,删到没有为止
yum -y remove xxx

yum install -y java-1.8.0-openjdk-devel.x86_64

1.idea操作

项目中随便地方,两个文件复制进去
在这里插入图片描述

package com.gao.common;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextProvider implements ApplicationContextAware {
    private static ApplicationContext context;

    public ApplicationContext getApplicationContext() {
        return context;
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        context = ctx;
    }
}
package com.gao.common;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * mapper xml 文件重新加载
 *
 * @author 汪小哥
 * @date 01-05-2021
 */
@Slf4j
@Component
public class MybatisMapperXmlFileReloadService {

    private List<SqlSessionFactory> sqlSessionFactoryList;


    public MybatisMapperXmlFileReloadService(List<SqlSessionFactory> sqlSessionFactoryList) {
        this.sqlSessionFactoryList = sqlSessionFactoryList;
    }

    /**
     * 重新 加载mapper xml  【这里可以使用arthas 进行调用远程增强】
     * eg  ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getBean("mybatisMapperXmlFileReloadService").reloadAllSqlSessionFactoryMapper("/root/xxx.xml")' -c xxx
     *
     * @param mapperFilePath
     * @return
     */
    public boolean reloadAllSqlSessionFactoryMapper(String mapperFilePath) {
        if (CollectionUtils.isEmpty(sqlSessionFactoryList)) {
            log.warn("not find SqlSessionFactory bean");
            return false;
        }

        Path path = Paths.get(mapperFilePath);
        if (!Files.exists(path)) {
            log.warn("mybatis reload mapper xml not exist ={}", mapperFilePath);
            return false;
        }

        AtomicBoolean result = new AtomicBoolean(true);

        // 删除mapper 缓存 重新加载
        sqlSessionFactoryList.parallelStream().forEach(sqlSessionFactory -> {
            Configuration configuration = sqlSessionFactory.getConfiguration();
            if (!this.removeMapperCacheAndReloadNewMapperFile(path, configuration)) {
                log.warn("reload new mapper file fail {}", path.toString());
                result.set(false);
            }
        });
        return result.get();
    }


    private Object readField(Object target, String name) {
        Field field = ReflectionUtils.findField(target.getClass(), name);
        ReflectionUtils.makeAccessible(field);
        return ReflectionUtils.getField(field, target);
    }

    /**
     * 删除老的mapper 缓存 加载新的mapper 文件
     *
     * @param watchPath
     * @param configuration
     * @return
     */
    private boolean removeMapperCacheAndReloadNewMapperFile(Path watchPath, Configuration configuration) {
        try (InputStream fileInputStream = Files.newInputStream(watchPath)) {
            XPathParser context = new XPathParser(fileInputStream, true, configuration.getVariables(), new XMLMapperEntityResolver());
            XNode contextNode = context.evalNode("/mapper");
            if (null == contextNode) {
                return false;
            }
            String namespace = contextNode.getStringAttribute("namespace");
            if (namespace == null || namespace.isEmpty()) {
                throw new BuilderException("Mapper's namespace cannot be empty");
            }

            this.removeOldMapperFileConfigCache(configuration, contextNode, namespace);
            this.addNewMapperFile(configuration, watchPath, namespace);
            return true;
        } catch (Exception e) {
            log.warn("load fail {}", watchPath.toString(), e);
        }
        return false;
    }

    /**
     * 删除老的mapper 相关的配置文件
     *
     * @param configuration
     * @param mapper
     * @param namespace
     * @see XMLMapperBuilder#configurationElement
     */
    private void removeOldMapperFileConfigCache(Configuration configuration, XNode mapper, String namespace) {
        String xmlResource = namespace.replace('.', '/') + ".xml";
        ((Set<?>) this.readField(configuration, "loadedResources")).remove(xmlResource);
        for (XNode node : mapper.evalNodes("parameterMap")) {
            String parameterMapId = this.resolveId(namespace, node.getStringAttribute("id"));
            ((Map<?, ?>) this.readField(configuration, "parameterMaps")).remove(parameterMapId);
        }
        for (XNode node : mapper.evalNodes("resultMap")) {
            String resultMapId = this.resolveId(namespace, node.getStringAttribute("id"));
            ((Map<?, ?>) this.readField(configuration, "resultMaps")).remove(resultMapId);
        }
        for (XNode node : mapper.evalNodes("sql")) {
            String sqlId = this.resolveId(namespace, node.getStringAttribute("id"));
            ((Map<?, ?>) this.readField(configuration, "sqlFragments")).remove(sqlId);
        }
        for (XNode node : mapper.evalNodes("select|insert|update|delete")) {
            String statementId = this.resolveId(namespace, node.getStringAttribute("id"));
            ((Map<?, ?>) this.readField(configuration, "mappedStatements")).remove(statementId);
        }
    }

    /**
     * 加载新的mapper 文件
     *
     * @param configuration
     * @param watchPath
     * @param namespace
     */
    private void addNewMapperFile(Configuration configuration, Path watchPath, String namespace) throws IOException {
        try (InputStream fileInputStream = Files.newInputStream(watchPath)) {
            String xmlResource = namespace.replace('.', '/') + ".xml";
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(fileInputStream, configuration,
                    xmlResource,
                    configuration.getSqlFragments());
            xmlMapperBuilder.parse();
        }

    }


    private String resolveId(String namespace, String id) {
        return namespace + "." + id;
    }
}

安装插件
在这里插入图片描述
修改你自己的包
在这里插入图片描述
我选的阿里云oss的,会自动上传拉取,热更新,自己去oss申请配置吧
在这里插入图片描述
要热部署的xml选择
在这里插入图片描述
出现这个,插件自动帮你复制好了,直接去服务器上执行这个命令就可以热部署了
在这里插入图片描述

2.服务器上操作

服务器上执行以下命令,不然会失败

yum install telnet
yum install --assumeyes unzip
#查看当前用户是什么
who am i
#我的是root,ah.sh见下图
sudo su root && ./as.sh
#等下从阿里oss拉取的xml,会自动放在/root/opt/arthas下面,所以给/root 读写权限
sudo chmod -R 777 /root

#然后执行上面idea 复制的命令

在这里插入图片描述
oss拉取的xml,下面全部都是
在这里插入图片描述
执行idea复制的ml,中途要 -l回车 再执行下面
在这里插入图片描述
成功替换(ps 注意 重启项目热更新的全部失效!!!!!!!!包括jerbel的)
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值