天天看点

SOFA Ark模块化更新小Demo

创建一个工程,包含3个模块

SOFA Ark模块化更新小Demo

 父工程里面的配置

SOFA Ark模块化更新小Demo

父工程的Pom文件,管理版本

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<packaging>pom</packaging>
    <modules>
        <module>dynamic-stock-mng</module>
		<module>dynamic-provider</module>
		<module>dynamic-facade</module>
	</modules>

    <parent>
		<groupId>com.alipay.sofa</groupId>
		<artifactId>sofaboot-dependencies</artifactId>
		<version>3.2.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>io.sofastack</groupId>
	<artifactId>sofachannel-demo</artifactId>
	<version>1.0.0</version>
	<name>sofachannel-demo</name>
	<description>Demo project for SofaStack Dynamic Module</description>

	<properties>
		<java.version>1.8</java.version>
		<ark.version>1.0.0</ark.version>
		<dashboard.client>1.0.0</dashboard.client>
		<curator.version>2.9.1</curator.version>
		<mybatis.version>1.3.2</mybatis.version>
		<mysql.version>5.1.46</mysql.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>com.alipay.sofa</groupId>
				<artifactId>sofa-dashboard-client</artifactId>
				<version>${dashboard.client}</version>
			</dependency>

			<!-- 引用ark web插件-->
			<dependency>
				<groupId>com.alipay.sofa</groupId>
				<artifactId>web-ark-plugin</artifactId>
				<version>${ark.version}</version>
			</dependency>

			<!-- 引用ark 配置推送扩展插件-->
			<dependency>
				<groupId>com.alipay.sofa</groupId>
				<artifactId>config-ark-plugin</artifactId>
				<version>${ark.version}</version>
			</dependency>

			<dependency>
				<artifactId>curator-client</artifactId>
				<groupId>org.apache.curator</groupId>
				<version>${curator.version}</version>
			</dependency>
			<dependency>
				<artifactId>curator-framework</artifactId>
				<groupId>org.apache.curator</groupId>
				<version>${curator.version}</version>
			</dependency>

			<dependency>
				<groupId>org.mybatis.spring.boot</groupId>
				<artifactId>mybatis-spring-boot-starter</artifactId>
				<version>${mybatis.version}</version>
			</dependency>
			<dependency>
				<groupId>mysql</groupId>
				<artifactId>mysql-connector-java</artifactId>
				<version>${mysql.version}</version>
			</dependency>

			<dependency>
				<groupId>io.sofastack</groupId>
				<artifactId>dynamic-facade</artifactId>
				<version>1.0.0</version>
			</dependency>

		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>2.21.0</version>
			</plugin>
		</plugins>
	</build>

</project>
           

 在facade里面定义方法

SOFA Ark模块化更新小Demo

 在provider里面实现facade里的方法

SOFA Ark模块化更新小Demo

 如果业务经常会涉及controller层的变动,也可以再provider里面写controller层

SOFA Ark模块化更新小Demo

 provider的pom文件,代码如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>sofachannel-demo</artifactId>
        <groupId>io.sofastack</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>dynamic-provider</artifactId>
    <version>1.0.1</version>
    <dependencies>
        <!--facade模块依赖-->
        <dependency>
            <groupId>io.sofastack</groupId>
            <artifactId>dynamic-facade</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alipay.sofa</groupId>
            <artifactId>runtime-sofa-boot-plugin</artifactId>
        </dependency>
        <!--健康检查能力依赖-->
        <dependency>
            <groupId>com.alipay.sofa</groupId>
            <artifactId>healthcheck-sofa-boot-starter</artifactId>
        </dependency>
<!--        <dependency>-->
<!--            <artifactId>curator-client</artifactId>-->
<!--            <groupId>org.apache.curator</groupId>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <artifactId>curator-framework</artifactId>-->
<!--            <groupId>org.apache.curator</groupId>-->
<!--        </dependency>-->
        <!--增加web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--子模块如果要添加了web依赖,必须要添加这个类隔离的依赖,否则项目启动不了,-->
        <dependency>
            <groupId>com.alipay.sofa</groupId>
            <artifactId>sofa-ark-springboot-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!--这里添加ark 打包插件-->
            <plugin>
                <groupId>com.alipay.sofa</groupId>
                <artifactId>sofa-ark-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <!--goal executed to generate executable-ark-jar -->
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                        <!--ark-biz 包的打包配置  -->
                        <configuration>
                            <!--是否打包、安装和发布 ark biz,详细参考 Ark Biz 文档,默认为false-->
                            <attach>true</attach>
                            <!--ark 包和 ark biz 的打包存放目录,默认为工程 build 目录-->
                            <outputDirectory>target</outputDirectory>
                            <!--default none-->
                            <arkClassifier>executable-ark</arkClassifier>
                            <!-- ark-biz 包的启动优先级,值越小,优先级越高-->
                            <priority>200</priority>
                            <!--设置应用的根目录,用于读取 ${base.dir}/conf/ark/bootstrap.application 配置文件,默认为 ${project.basedir}-->
                            <baseDir>../</baseDir>
                        </configuration>
                    </execution>
                </executions>
            </plugin>


        </plugins>
    </build>
</project>
           

最后是master biz

BizController可以通过接口来install 和 uninstall ark biz

package io.sofastack.stockmng.controller;

import com.alipay.sofa.ark.api.ArkClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;


@RestController
@Slf4j
public class BizController {

    @RequestMapping(value = "/installBiz", method = RequestMethod.POST)
    public String installBiz(@RequestParam(value = "url") String url) throws Throwable {
        log.info("url为:{}",url);
        File file = new File(url);
        ArkClient.installBiz(file);

        return "调用成功";
    }

    @RequestMapping(value = "/uninstallBiz", method = RequestMethod.POST)
    public void uninstallBiz(String bizName, String bizVersion) throws Throwable {
        ArkClient.uninstallBiz(bizName, bizVersion);
    }

    @RequestMapping(value = "/switchBiz", method = RequestMethod.POST)
    public void switchBiz(String bizName, String bizVersion) {
        ArkClient.switchBiz(bizName, bizVersion);
    }

}
           

indexController来测试改变provider里面的实现方法能否改变已经运行的项目的业务逻辑

package io.sofastack.stockmng.controller;

import com.alipay.sofa.runtime.api.annotation.SofaReference;
import io.sofastack.dynamic.facade.StrategyService;
import io.sofastack.dynamic.model.ProductInfo;
import io.sofastack.stockmng.data.DatabaseSeed;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.List;


@Controller
public class IndexController {
    @SofaReference
    private StrategyService strategyService;

    @RequestMapping("/")
    public String index(Model model) {
        model.addAttribute("productList", strategyService.strategy(initProducts()));
        return "index";
    }
    @ResponseBody
    @RequestMapping("/sort")
    public String sort() {
        String test = strategyService.test();
        return test;
    }

    /**
     * 初始化默认展示列表,为了实验效果,此处初始化的列表与实际列表是相反的,但是实际排序结果与现场购买订单直接挂钩
     *
     * @return
     */
    private List<ProductInfo> initProducts() {
        List<ProductInfo> products = new ArrayList<>(5);
        for (int i = 4; i >= 0; i--) {
            ProductInfo productInfo = new ProductInfo();
            productInfo.setName(DatabaseSeed.name[i]);
            productInfo.setOrderCount(DatabaseSeed.orderCount[i]);
            productInfo.setSrc(DatabaseSeed.imageUrls[i]);
            productInfo.setAuthor(DatabaseSeed.authors[i]);
            products.add(productInfo);
        }
        return products;
    }
}
           

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>sofachannel-demo</artifactId>
        <groupId>io.sofastack</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>dynamic-stock-mng</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--此组件提供异步初始化 Spring Bean 能力-->
        <dependency>
            <groupId>com.alipay.sofa</groupId>
            <artifactId>runtime-sofa-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>io.sofastack</groupId>
            <artifactId>dynamic-facade</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- 这里添加动态模块相关依赖 -->
        <!--类隔离组件 sofa-ark-springboot-starter-->
        <dependency>
            <groupId>com.alipay.sofa</groupId>
            <artifactId>sofa-ark-springboot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alipay.sofa</groupId>
            <artifactId>web-ark-plugin</artifactId>
        </dependency>
        <!--引入子模块的biz-->
        <dependency>
            <groupId>io.sofastack</groupId>
            <artifactId>dynamic-provider</artifactId>
            <version>1.0.0</version>
            <classifier>ark-biz</classifier>
        </dependency>








    </dependencies>

    <build>
        <plugins>
            <!-- 这里配置动态模块打包插件 -->
            <plugin>
                <groupId>com.alipay.sofa</groupId>
                <artifactId>sofa-ark-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-cli</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <priority>100</priority>
                    <baseDir>../</baseDir>
                    <bizName>stock-mng</bizName>
                </configuration>
            </plugin>


        </plugins>
    </build>

</project>
           

测试,直接package整个项目,拿到master biz的ark-excutable.jar这个jar包,运行

SOFA Ark模块化更新小Demo

 通过postman测试master biz的接口

SOFA Ark模块化更新小Demo

再看看provider biz的接口

SOFA Ark模块化更新小Demo
SOFA Ark模块化更新小Demo

test2是被注释掉的,所以没有办法调用。

接下来修改provider

实现的方法改成654321

SOFA Ark模块化更新小Demo

 把第二个接口放出来

SOFA Ark模块化更新小Demo

修改pom文件里面的version

SOFA Ark模块化更新小Demo

重新package

获取provider 1.0.1版本的ark-biz的jar包

SOFA Ark模块化更新小Demo

 此时我们可以通过telnet命令连接到ark

SOFA Ark模块化更新小Demo
SOFA Ark模块化更新小Demo

可以看到之前provider的版本是1.0.0,如果你的provider没有web依赖,不提供controller层,只是负责实现service的具体方法,可以在直接install上去再切换provider的版本。但如果你的provider要提供controller接口,那么必须先把之前的1.0.0版本的provider uninstall之后才能install上去。因为只要install上去之后,controller层就会加载好。而你1.0.1版本的server.servlet.context-path 跟1.0.0版本是相同的,会导致失败,所以需要先uninstall。如果你把1.0.1版本的server.servlet.context-path修改一下,install上去之后,就算不处于激活状态,1.0.1版本的接口也是可以访问的。

在这里我们先uninstall

SOFA Ark模块化更新小Demo

系统提示正在uninstall ,我们再看看版本

SOFA Ark模块化更新小Demo

此时,provider已经卸载掉了,再去调用provider提供的接口会报404,去调用sort接口会报500

我们把1.0.1版本给他install上,我在网上看到的教程都是可以通过biz -i 指令进行install,但是我一直install不上去。

SOFA Ark模块化更新小Demo

 如上画所示,虽然提示了正在install,但是等了很久,我再去查看,依然只有孤零零的一个master biz

SOFA Ark模块化更新小Demo

 好在我们提供了接口,通过bizController接口调用ark内置的命令来install,也就是我们之前写的BizController里面的方法。

SOFA Ark模块化更新小Demo

 我们来调用一下

SOFA Ark模块化更新小Demo

 提示成功了,去看一下呢

SOFA Ark模块化更新小Demo

已经成功install了,我们去看一下接口

SOFA Ark模块化更新小Demo

sort的业务逻辑已经改变了,返回的数字变成了654321

SOFA Ark模块化更新小Demo
SOFA Ark模块化更新小Demo

1.0.1版本provider提供的两个controller接口也调用成功。

这里我们的demo测试基本完成了,但是我还是遇到了一个小问题,当我不需要某个provider biz的jar包的时候,删不掉,提示我正在被占用,有没有哪位大佬能告诉我怎么解决啊。