国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

手機(jī)h5建站網(wǎng)站優(yōu)化方案模板

手機(jī)h5建站,網(wǎng)站優(yōu)化方案模板,新網(wǎng)企業(yè)郵箱登錄入口,廈門翔安建設(shè)局網(wǎng)站背景 在小小的公司里面,挖呀挖呀挖。最近又挖到坑里去了。一個穩(wěn)定運行多年的應(yīng)用,需要在里面支持多個版本的中間件客戶端;而多個版本的客戶端在一個應(yīng)用里運行時會有同名類沖突的矛盾。在經(jīng)過詢問chatGPT,百度,googl…

背景

在小小的公司里面,挖呀挖呀挖。最近又挖到坑里去了。一個穩(wěn)定運行多年的應(yīng)用,需要在里面支持多個版本的中間件客戶端;而多個版本的客戶端在一個應(yīng)用里運行時會有同名類沖突的矛盾。在經(jīng)過詢問chatGPT,百度,google,github,和各位大佬的文章后,進(jìn)行了總結(jié)。大概有以下幾種解決方案

業(yè)界方案1----更改類路徑

低版本客戶端,更改類路徑;然后重新打包編譯客戶端;這樣不同版本客戶端,使用的類名就不同了

此解決方案
優(yōu)點

1、適合簡單的小項目:無需編寫新代碼

2、可能是最快的一種方式;只需要把代碼下載下來;更改clients下的類路徑,重新編譯即可;屬于不怎么費腦力,但有點費體力的方式。

缺點

1、遇到版本升級,需要把步驟2的過程重新人肉再來一遍。這個比較的那啥。。。。。。

2、不同版本客戶端,可能也會依賴不同版本的三方j(luò)ar包。這個也是蠻棘手的

此種方式適合小項目或者外包等一錘子買賣

解決方案2 -----自定義ClassLoader

使用ClassLoader進(jìn)行類的隔離;不同版本客戶端和依賴三方包,用不同classLoader進(jìn)行加載和隔離;完全杜絕版本問題

此解決方案

1、屬于自研;對ClassLoader的類加載機(jī)制需要有一定的了解

解決方案3------業(yè)界開源方案 sofa-ark

sofa-ark是動態(tài)熱部署和類隔離框架,由螞蟻集團(tuán)開源貢獻(xiàn)。主要提供應(yīng)用模塊的動態(tài)熱部署和類隔離能力

提供功能:

1、包沖突的解決(能解決現(xiàn)有項目遇到的多版本問題)

2、合并部署:多個項目分工程開發(fā),但可以合并部署;還支持靜態(tài)合并部署和動態(tài)合并部署

資料傳送門:

https://github.com/sofastack/sofa-ark

https://www.sofastack.tech/projects/sofa-boot/sofa-ark-readme/

此解決方案:
優(yōu)點

1、功能強(qiáng)大不僅能執(zhí)行類的隔離;還能靜動態(tài)的熱部署;還能進(jìn)行插件的熱插拔;能解決現(xiàn)有工程遇到的問題;文檔也挺齊全的

2、性能,穩(wěn)定性,易用性,應(yīng)該是所有保障的,畢竟螞蟻內(nèi)部也在使用;

缺點

1、雖號稱是輕量級,但那是和OSGI這種重型框架相比,在sofa-ark里,也還有蠻多概念比如:Ark Container,ark包,插件包,biz包;如何打ark包,如何打插件包等等;有一定的學(xué)習(xí)成本,好在文檔齊全能降低一定的入門門檻

2、需要對現(xiàn)有工程進(jìn)行改造,以符合sofa-ark的規(guī)范;打包和部署上還需要遵循其規(guī)范。對于小公司主打的就是一個“自由”這種狀態(tài)來說;有一點點束縛和被迫學(xué)習(xí)了,因為我們都比較的“懶”

此種方式適合大型項目;大型項目開發(fā)人員和開發(fā)應(yīng)用眾多;而sofa-ark制定了相應(yīng)的biz包和插件包的開發(fā)規(guī)范;在代碼復(fù)雜性,模塊化開發(fā),擴(kuò)展性,項目維護(hù),應(yīng)用運行期等都進(jìn)行了綜合考慮。

解決方案4------OSGI

屬于比較重型解決方案

OSGI 作為業(yè)內(nèi)最出名的類隔離框架,自然是可以被用于解決上述包沖突問題,但是 OSGI 框架太過臃腫,功能繁雜。為了解決包沖突問題,引入 OSGI 框架,有牛刀殺雞之嫌,反而使工程變得更加復(fù)雜,不利于開發(fā)。

我們的選擇

解決方案這么多,我們該選擇哪個方案了?

我們選擇方案2。為什么了?

先說說為什么不選擇其它方案

方案1: 此種方式不一定是最快的方式;因為后續(xù)考慮到每增加一個版本,或者社區(qū)有更新,都要去做一次,更名,打包等。還是挺麻煩,不太自動化,屬于不費腦子費體力。

方案3: 功能強(qiáng)大:在模塊化開發(fā),類隔離,熱部署等功能性上無比優(yōu)異;性能和穩(wěn)定性也有大公司在背書;但我們目前是個小公司,遇到的業(yè)務(wù)場景和技術(shù)場景,沒有那么復(fù)雜,強(qiáng)大的功能給我們,我們不一定能用的上,用的不好可能還會被反噬;由于公司技術(shù)人員對該框架缺少實際使用經(jīng)驗;并且對該框架的實現(xiàn)原理也沒人懂屬于需要現(xiàn)學(xué);使用后萬一后續(xù)出現(xiàn)什么問題,問題定位和維護(hù)也挺麻煩;對小公司來說還是“太重”了;并且還需要對現(xiàn)有工程進(jìn)行改造

方案4: 就不用在詳說了,比方案3還重的解決方案

為什么方案2適合我們?

實現(xiàn)難度上: 對我們小公司來說雖然要自己寫代碼實現(xiàn);但經(jīng)過評估大概幾百行代碼就能搞定,技術(shù)上不是那么的高不可攀;

技術(shù)熟悉度: 團(tuán)隊內(nèi)大家對ClassLoader的機(jī)制還蠻熟悉;

使用經(jīng)驗上: 有多個同事曾經(jīng)用classLoader進(jìn)行過這種隔離機(jī)制的實現(xiàn),但業(yè)務(wù)場景不同;

復(fù)用性上: 寫一次代碼;后續(xù)在遇到多個版本沖突;經(jīng)過簡單的配置即可,不需要修改代碼;更不需要修改三方依賴源碼,比方案1好

在性能和穩(wěn)定性上: 性能上不影響運行期,只會影響到代碼加載期,所以性能這塊還好;而在穩(wěn)定性上,可通過測試環(huán)境長期穩(wěn)定運行和一定的業(yè)務(wù)壓測進(jìn)行驗證;而恰好我們有這樣的測試環(huán)境和線上引流進(jìn)行壓測驗證工具

設(shè)計圖

類的加載機(jī)制.png

ClassLoader的原理: 從上圖可看出,ClassLoader會影響JVM加載類的路徑類的加載順序
類的加載路徑
bootStrap ClassLoader: 加載%JRE_HOME%\lib 下的jar,比如rt.jar等
Extendtion ClassLoader: 加載%JRE_HOME%\lib\ext 目錄下的jar
AppClass ClassLoader: 加載應(yīng)用classpath下的所有類,即工程里依賴三方j(luò)ar和工程的class
加載順序 :雙親委托機(jī)制;加載類時,先讓父ClasserLoader進(jìn)行加載,父Classloader找不到,才讓子ClassLoader進(jìn)行查找和加載。

主要想法: 自定義ClassLoader繼承URLClassLoader(即圖里的AppClassClassLoader);基礎(chǔ)類的加載 還是用雙親委托機(jī)制,由父類去加載,自定義類實現(xiàn)findClass方法,在該方法里加載指定目錄class和jar。

具體工具類-----應(yīng)該可以拿來即用

該實現(xiàn)類主要參考了Cyber365大佬的文章;然后做了一些改動(簡化類,抽取工具類,去除多重if嵌套)
主要由兩個類進(jìn)行實現(xiàn)

  • MiddlewareClassLoader 類 主要做了兩件事
    1:讀取Class文件內(nèi)容;根據(jù)傳入類名,從指定url中查找并讀取到對應(yīng)class文件內(nèi)容
    2:生產(chǎn)Class對象: 傳入class文件內(nèi)容,調(diào)用底層defineClass方法生產(chǎn)

  • UrlUtils 類: 主要做了一件事
    1:根據(jù)指定的url,計算出該url和對應(yīng)子目錄下jar的url。

MiddlewareClassLoader 類

public class MiddlewareClassLoader extends URLClassLoader {private URL[] allUrl;public MiddlewareClassLoader(String[] paths){this(UrlUtils.getURLs(paths));}public MiddlewareClassLoader(URL[] urls) {this(urls, MiddlewareClassLoader.class.getClassLoader());}public MiddlewareClassLoader(URL[] urls, ClassLoader parent) {super(urls, parent);this.allUrl = urls;}protected Class<?> findClass(final String name)throws ClassNotFoundException{return loadExtendClass(name);}//    /**
//     * 不建議重新定義loadClass 方法,打破雙親委派機(jī)制,采用逆向雙親委派
//     *
//     * @param className 加載的類名
//     * @return java.lang.Class<?>
//     * @author Cyber
//     * <p> Created by 2022/11/22
//     */
//    @Override
//    public Class<?> loadClass(String className) throws ClassNotFoundException {
//        Class extClazz = loadExtendClass(className);
//        if(null != extClazz){
//            return extClazz;
//        }
//     return super.loadClass(className);
//    }public Class<?> loadExtendClass(String className) throws ClassNotFoundException {if(null == allUrl){return null;}String classPath = className.replace(".", "/");classPath = classPath.concat(".class");for (URL url : allUrl) {byte[] data = null;ByteArrayOutputStream baos = new ByteArrayOutputStream();InputStream is = null;try {File file = new File(url.toURI());if (!file.exists()) {continue;}JarFile jarFile = new JarFile(file);if (jarFile == null) {continue;}JarEntry jarEntry = jarFile.getJarEntry(classPath);if (jarEntry == null) {continue;}is = jarFile.getInputStream(jarEntry);byte[] buffer = new byte[1024 * 10];int length = -1;while ((length = is.read(buffer)) > 0) {baos.write(buffer, 0, length);}data = baos.toByteArray();System.out.println("********找到classPath=" + classPath + "的jar=" + url.toURI().getPath() + "*******");Class clazz =  this.defineClass(className, data, 0, data.length);return clazz;} catch (Exception e) {e.printStackTrace();} finally {try {if (is != null) {is.close();}baos.close();} catch (IOException e) {e.printStackTrace();}}}return null;}
}

工具類UrlUtils

public class UrlUtils {/*** description 通過文件目錄獲取目錄下所有的jar全路徑信息** @param paths 文件路徑* @return java.net.URL[]* @author Cyber* <p> Created by 2022/11/22*/public static URL[] getURLs(String[] paths) {if (null == paths || 0 == paths.length) {throw new RuntimeException("jar包路徑不能為空.");}List<String> dirs = new ArrayList<String>();for (String path : paths) {dirs.add(path);collectDirs(path, dirs);}List<URL> urls = new ArrayList<URL>();for (String path : dirs) {urls.addAll(doGetURLs(path));}URL[] threadLocalurls = urls.toArray(new URL[0]);return threadLocalurls;}/*** description 遞歸獲取文件目錄下的根目錄** @param path      文件路徑* @param collector 根目錄* @return void* @author Cyber* <p> Created by 2022/11/22*/private static void collectDirs(String path, List<String> collector) {if (null == path || "".equalsIgnoreCase(path)) {return;}File current = new File(path);if (!current.exists() || !current.isDirectory()) {return;}for (File child : current.listFiles()) {if (!child.isDirectory()) {continue;}collector.add(child.getAbsolutePath());collectDirs(child.getAbsolutePath(), collector);}}private static List<URL> doGetURLs(final String path) {if (null == path || "".equalsIgnoreCase(path)) {throw new RuntimeException("jar包路徑不能為空.");}File jarPath = new File(path);if (!jarPath.exists() || !jarPath.isDirectory()) {throw new RuntimeException("jar包路徑必須存在且為目錄.");}FileFilter jarFilter = new FileFilter() {/*** description  判斷是否是jar文件* @param pathname jar 全路徑文件* @return boolean* @author Cyber* <p> Created by 2022/11/22*/@Overridepublic boolean accept(File pathname) {return pathname.getName().endsWith(".jar");}};File[] allJars = new File(path).listFiles(jarFilter);List<URL> jarURLs = new ArrayList<URL>(allJars.length);for (int i = 0; i < allJars.length; i++) {try {jarURLs.add(allJars[i].toURI().toURL());} catch (Exception e) {throw new RuntimeException("系統(tǒng)加載jar包出錯", e);}}return jarURLs;}
}

kafka發(fā)送基礎(chǔ)類

@Slf4j
public abstract class AbstractKafkaProducer {private String kafkaClassName = "org.apache.kafka.clients.producer.KafkaProducer";private String stringSerializerClassName = "org.apache.kafka.common.serialization.StringSerializer";private String serializerClassName = "org.apache.kafka.common.serialization.Serializer";private String producerRecordClassName = "org.apache.kafka.clients.producer.ProducerRecord";private MiddlewareClassLoader middlewareClassLoader;private Object producerObject = null;private Method sendMethod = null;private Constructor producerRecordConstructor = null;//類的加載和初始化public void init(String jarPath){middlewareClassLoader = new MiddlewareClassLoader(new String[]{jarPath});try {ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader();Thread.currentThread().setContextClassLoader(middlewareClassLoader);Class kafkaProduceClazz = middlewareClassLoader.loadClass(kafkaClassName);Class kafkaStringSerializerClazz = middlewareClassLoader.loadClass(stringSerializerClassName);Class kafkaSerializerClazz = middlewareClassLoader.loadClass(serializerClassName);//加載KafkaProducer類Class ProducerRecordClazz = middlewareClassLoader.loadClass(producerRecordClassName);Constructor producerConstructor = kafkaProduceClazz.getConstructor(Map.class, kafkaSerializerClazz, kafkaSerializerClazz);Map<String,Object> produceConfigMap = new HashMap<String,Object>();produceConfigMap.put("retries",3);produceConfigMap.put("retry.backoff.ms",10000);produceConfigMap.put("acks","all");//回調(diào)方法,讓自來可更改生產(chǎn)端配置addExtendConfig(produceConfigMap);producerObject = producerConstructor.newInstance(produceConfigMap,kafkaStringSerializerClazz.newInstance(),kafkaStringSerializerClazz.newInstance());sendMethod = kafkaProduceClazz.getMethod("send",new Class[]{ProducerRecordClazz});producerRecordConstructor =  ProducerRecordClazz.getConstructor(String.class,Object.class);Thread.currentThread().setContextClassLoader(threadClassLoader);System.out.println("========end======");} catch (Exception e) {e.printStackTrace();}}protected abstract void addExtendConfig(Map<String, Object> produceConfigMap);//發(fā)送消息public void send(String topic,String msg){try {sendMethod.invoke(producerObject,producerRecordConstructor.newInstance(topic,msg));} catch (Exception e) {throw new RuntimeException(e);}}
}

Kafka09Producer

@Data
public class Kafka09Producer extends AbstractKafkaProducer {private String bootstrapServers;@Overrideprotected void addExtendConfig(Map<String, Object> produceConfigMap) {produceConfigMap.put("bootstrap.servers",bootstrapServers);}}

Kafka32Producer

@Data
public class Kafka32Producer extends AbstractKafkaProducer {private String bootstrapServers;@Overrideprotected void addExtendConfig(Map<String, Object> produceConfigMap) {produceConfigMap.put("bootstrap.servers",bootstrapServers);}}

核心測試代碼

@Slf4j
public class MiddlewareClassLoaderTest {public static void main(String[] args) throws InterruptedException {//kafka 0.9的測試Kafka09Producer kafka09Producer = new Kafka09Producer();kafka09Producer.setBootstrapServers(KafkaConfig.getInstance().getProperty("bootstrap.servers"));kafka09Producer.init("/data/app/product-kafka/ext-lib/kafka090");kafka09Producer.send("topic090","msg090:" + System.currentTimeMillis());//kafka 3.2的測試Kafka32Producer kafka32Producer = new Kafka32Producer();kafka32Producer.setBootstrapServers(KafkaConfig.getInstance().getProperty("bootstrap.servers"));kafka32Producer.init("/data/app/product-kafka/ext-lib/kafka32");kafka32Producer.send("topic320","msg320:" + System.currentTimeMillis());Thread.sleep(60 * 1000);}
}

測試結(jié)果

kafka09類的加載
image.png

kafka32類的加載
image.png

總結(jié)

ClassLoader除了能加載指定版本jar包外;還可以做熱部署和熱更新;如果要再次加載同一個類達(dá)到熱更新;可 new一個classLoader然后loadClass,再用該Class去實例化對象即可。
還有一個困擾新手較久的注意點:Class的加載和Object實例化需要分開去看待,ClassLoader只影響類的加載;類的實例化是另外一個問題。

原創(chuàng)不易,請點贊,留言,關(guān)注,收藏 4暴擊 ^^

參考資料:

https://blog.csdn.net/briblue/article/details/54973413 ClassLoader類加載機(jī)制,類加載順序

https://juejin.cn/post/7168678691839410213 Cyber365大佬的文章

http://m.aloenet.com.cn/news/45519.html

相關(guān)文章:

  • 網(wǎng)站默認(rèn)樣式表上海知名網(wǎng)站制作公司
  • 做網(wǎng)站需要編程推廣點擊器
  • 做影視免費網(wǎng)站違法嗎百度官網(wǎng)網(wǎng)站登錄
  • 深圳學(xué)網(wǎng)站開發(fā)關(guān)鍵詞挖掘站網(wǎng)
  • wordpress數(shù)據(jù)庫出錯山東進(jìn)一步優(yōu)化
  • 佛山網(wǎng)站如何制作云南seo網(wǎng)絡(luò)優(yōu)化師
  • 中國建設(shè)委員會的官方網(wǎng)站seo免費視頻教程
  • 醫(yī)院網(wǎng)站建設(shè)情況電商推廣平臺
  • 響應(yīng)式網(wǎng)站無法做百度聯(lián)盟最新seo操作
  • 科技網(wǎng)站實例上海seo推廣整站
  • 黃埔區(qū)做網(wǎng)站什么是互聯(lián)網(wǎng)銷售
  • 方案策劃網(wǎng)站網(wǎng)上商城網(wǎng)站開發(fā)
  • 個人網(wǎng)站建設(shè)教程網(wǎng)站排名查詢站長之家
  • 濟(jì)南網(wǎng)站建站公司東莞網(wǎng)絡(luò)營銷推廣軟件
  • 全國網(wǎng)站制作公司排名我是seo關(guān)鍵詞
  • 網(wǎng)站優(yōu)化價格新河seo怎么做整站排名
  • 深圳做微信網(wǎng)站設(shè)計軟文推廣多少錢
  • 百度中搜到網(wǎng)站名字電商培訓(xùn)內(nèi)容
  • 如何快速做企業(yè)網(wǎng)站包括商城常見的營銷方式有哪些
  • 什么是權(quán)重高的網(wǎng)站搜狗站長
  • 國外獨立站建站站長工具seo綜合查詢推廣
  • 廣州網(wǎng)匠營銷型網(wǎng)站建設(shè)公司濟(jì)南網(wǎng)站seo
  • Wordpress網(wǎng)站調(diào)用代碼2024年新冠疫情最新消息今天
  • 陽江市企業(yè)網(wǎng)站優(yōu)化企業(yè)如何進(jìn)行宣傳和推廣
  • 網(wǎng)站建設(shè)優(yōu)化服務(wù)特色高端網(wǎng)站設(shè)計
  • 重慶唐卡裝飾公司深圳市企業(yè)網(wǎng)站seo
  • 建網(wǎng)站開源代碼全國新冠疫情最新消息
  • wordpress用戶名忘記密碼廣州seo站內(nèi)優(yōu)化
  • 網(wǎng)站內(nèi)容建設(shè)的原則是什么意思整合營銷策略有哪些
  • 用老域名做網(wǎng)站還是新域名武漢seo首頁優(yōu)化技巧