研究:全球大型瀕危哺乳動物消失 最終原因是人口激增

環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:ENS

西藏野氂牛(Wild Yak,學名:Bos mutus)、巴塔哥尼亞馬駝鹿(Patagonian Huemul,學名:Hippocamelus bisulcus)、不丹的羚牛(Takin,學名:Budorcas taxicolor)、越南的中南大羚(Saola,學名:Pseudoryx nghetinhensis)等世界各地特殊大型哺乳動物正在悄悄消失。過去的幾十年間,連非洲的三種斑馬和牛羚也大幅度減少。

研究人員指出,造成這些哺乳動物消失的原因不僅僅是疾病和棲息地破碎化、森林砍伐或野生動植物貿易。最終原因是人口激增。

而且,除非人類行為大幅度改變,否則科學家認為,這些哺乳動物未來的族群狀況將永遠回不去了。

西藏野氂牛、巴塔哥尼亞馬駝鹿等世界各地特殊大型哺乳動物正悄悄消失。照片來源:birdcapemay(CC BY-NC-ND 2.0)

科學家分析全球大型哺乳動物角色改變的原因 答案正是人口激增

科學家將這些發現發表在《生態與演化前線(Frontiers in Ecology and Evolution)》期刊上,題目是「破碎的食物網和混亂的未來:面對地球人口不斷成長的現代有蹄類(Disassembled food webs and messy projections: modern ungulate communities in the face of unabating human population growth)」。

研究主要作者、科羅拉多州立大學教授伯傑(Joel Berger)說,是付諸行動的時候了,吹捧過去的保育成就對改善人類的未來沒什麼幫助。

在這項研究中,科學家們分析了導致全球生態系統中哺乳動物角色改變的直接和間接原因,同時也指出生態交互作用的本質如何產生根本性的變化,甚至在未來幾十年內更大規模地發生。

他們研究了巴塔哥尼亞馬駝鹿、不丹的羚牛,北美沙漠的野馬、狼和郊狼的變化,以及大型食肉動物就地滅絕後大生態系統改變的必然性。

科學家認為,隨著人口在陸地上的分布越來越廣,這些改變是現在進行式。

「即使是在喜馬拉雅山脈的偏遠地區,人類入侵帶來流浪狗和野狗,嚴重破壞了高經濟價值和文化象徵意義的野生和圈養物種。」不丹基金會的Wangchuk說。

隨著人口激增,大型哺乳動物數量卻是在遞減。圖為巴塔哥尼亞馬駝鹿。照片來源:Vera & Jean-Christophe(CC BY-SA 2.0)

2020年世界人口已接近80億 地球上的哺乳動物有97%是牲畜和人類

人類定居喜馬拉雅山部分地區還是近年暖化融冰後的事。科學家們還指出全球人口的劇烈變化。1830年,海軍中將羅伯特.斐茲洛伊(Robert Fitzroy)乘著小獵犬號穿越南美洲麥哲倫海峽(Magellan Straits)時,地球人口不到12億。到1970年世界地球日,已超過35億。

僅50年後的今天,世界人口已接近80億,牲畜和人類占了地球哺乳動物生物量高達97%。

研究團隊指出,全世界的食物網已被人類大幅改變無法回頭了,重建過去的條件或恢復曾經由原生種創造的生態功能幾乎是不可能的。例如今日除南極洲外,在每個大陸以及美國70%的州中都有野化的家豬。這些動物影響了魚類、爬行動物、鳥類以及其他小型哺乳動物,甚至植物和土壤。

此外,氣候變遷使海洋暖化,導致海洋藻類增生,漁獲量減少。相對魚類的需求減少,陸地上盜獵野生生物的情況隨之增加。

科學家們還記錄下時尚需求如何增加從蒙古、印度和中國輸出至西方的喀什米爾羊毛,導致更多中亞沙漠牧民飼養山羊。這些山羊與原生物種競爭食物,同時也因當地狗越來越多而身處險境。

這些狗不僅是掠食者,還攜帶疾病,傳染雪豹(Snow Leopard,學名:Panthera uncia)、西藏野驢(Kiang,學名:Equus kiang)和普氏原羚(Przewalski’s gazelle,學名:​​​​​​​Procapra przewalskii)等瀕臨滅絕的物種。

棲息在亞洲中部山區的雪豹。照片來源:Antonio Marín Segovia(CC BY-NC-ND 2.0)

但作者們認為,儘管形勢嚴峻,並非沒有機會挽回。像是在極地,適應寒冷氣候的物種仍是人類重要的文化資產,我們仍然有時間保存僅存的生態。

全世界還有許多保護區,如非洲的塞倫蓋蒂和克魯格國家公園、北美黃石公園和朗格-聖伊利亞斯國家公園暨保護區、玻利維亞麥迪迪國家公園、智利和阿根廷的巴塔哥尼亞冰原、中國長塘自然保護區以及世界最大國家公園東北格陵蘭國家公園。

儘管大型哺乳動物食物網將與過去有所不同、有著不同以往的運作方式,但對於未來的走向還有許多選擇。

作者表示:「現在還不算太晚,我們沒有時間為失去哀悼。必須將生態悲痛化為行動,尊重仍然存在的特殊生物多樣性,並透過保護地球的大片原野地來實現。」

Wildife Suffers Humpty Dumpty Effect as Humans Multiply FORT COLLINS, Colorado, June 9, 2020 (ENS)

Some of the world’s largest, most spectacular and unheralded mammals are silently slipping away – species like Tibetan wild yaks and Patagonia’s huemul, Bhutan’s takin and Vietnam’s saola. Even Africa’s three species of zebras and wildebeest have suffered massive reductions over the last several decades.

The reasons for these losses are more than disease and habitat fragmentation, deforestation or wildlife trade, according to researchers. Ultimately, the cause is rampant human population growth.

And unless human behavior changes in unprecedented ways, these scientists warn that future communities of these mammals will never resemble those of the recent past or even today.

The findings are based on a new study, “Disassembled food webs and messy projections: modern ungulate communities in the face of unabating human population growth,” published June 9 in the journal “Frontiers in Ecology and Evolution.”

Joel Berger, lead author of the study and a professor at Colorado State University, said that the time for action is now, and that touting past conservation achievements does little to better humanity’s future.

In this study, the scientists analyzed direct and indirect disruptions that lead to the changing roles of mammals in global ecosystems and noted how the nature of ecological interactions has changed and will do so, on an even larger scale, in the coming decades.

They looked at what has happened with the huemul in Patagonia, takin in Bhutan, wild horses in deserts, wolves and coyotes in North America, and the inevitability of change in big ecosystems as large carnivores are extirpated.

They said this is happening as the human population increases its footprint on land.

“Even in the remote reaches of the Himalayas, stray and feral dogs, a direct result of human intrusions, wreak havoc on wild and domestic species of high economic value and cultural importance,” said Wangchuk of the Bhutan Foundation.

Humans only recently colonized parts of the Himalayas, areas where ice has receded due to warming temperatures. Yet, the authors also point to human population change at a global scale. In 1830 when Vice-Admiral Robert Fitzroy captained his ship, the Beagle, through the Magellan Straits of South America, fewer than 1.2 billion people inhabited Earth. By Earth Day in 1970, there were more than 3.5 billion.

Today, only 50 years later the world’s population approaches eight billion. Livestock and humans now constitute a staggering 97 percent of the planet’s mammal biomass.

The research team explained that worldwide food webs have become irretrievably altered by humans, with little hope to reconstitute even recent past conditions or to put back the ecological functions once created by native species.

Feral pigs, for instance, exist today on every continent except Antarctica, and in 70 percent of the states in the United States. These animals disrupt fish, reptiles, birds and other small mammals, plants and soils.

In addition, climate change warms the oceans, which in turn foments marine algal blooms, reducing fishery catches. With less demand for fish, a consequent uptick in wildlife poaching on land occurs.

The scientists also documented how an appetite for fashion like cashmere increases imports to the west from Mongolia, India and China, resulting in economic incentives for desert pastoralists to produce more domestic goats in central Asia. These goats compete for food with native species and are in danger due to increasing numbers of dogs in these areas.

The dogs are not only predators but also carry diseases, which jeopardizes endangered species like snow leopards, kiang and Przewalksi’s gazelle.

Berger and the study authors suggest that despite the grim findings, all is not yet lost.

Prospects for ecological integrity and the conservation of unheralded species improve greatly toward the world’s edges where the planet’s cold-adapted species remain important to the human spirit and we still have time to save what we have.

The world has remarkable protected areas including Serengeti and Kruger National Park in Africa, Yellowstone and Wrangell-St. Elias National Park & Preserve in North America, Madidi National Park in Bolivia, the Patagonia Ice Fields of Chile and Argentina, the Chang Tang Nature Reserve in China, and Northeast Greenland National Park, the world’s largest national park.

And although food webs with large mammals will be different from those of the past and operate differently today, there are options to shape the future.

“It is not too late and we simply do not have the luxury of time to mourn what we have lost,” said Lambert. “We need to use our ecological grief to implement action and honor the exceptional biodiversity that remains. This can be done by protecting large tracts of the planet’s wild places.”

※ 全文及圖片詳見:ENS

保育類野生動物
哺乳動物
人口
國際新聞
生態保育
生物多樣性

作者

姜唯

如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

林大利

於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

【Spring註解驅動開發】使用@Lazy註解實現懶加載

寫在前面

Spring在啟動時,默認會將單實例bean進行實例化,並加載到Spring容器中。也就是說,單實例bean默認在Spring容器啟動的時候創建對象,並將對象加載到Spring容器中。如果我們需要對某個bean進行延遲加載,我們該如何處理呢?此時,就需要使用到@Lazy註解了。

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

懶加載

懶加載就是Spring容器啟動的時候,先不創建對象,在第一次使用(獲取)bean的時候,創建並使用對象,大家是不是想到了在【設計模式】專題中的單例模式呢?對單例模式不太了解的同學可以猛戳《淺談JAVA設計模式之——單例模式(Singleton)》,也可以查看《設計模式匯總——你需要掌握的23種設計模式都在這兒了!》來系統學習每種設計模式。

非懶加載模式

此時,我們將PersonConfig2類的配置修改成單實例bean,如下所示。

package io.mykit.spring.plugins.register.config;

import io.mykit.spring.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author binghe
 * @version 1.0.0
 * @description 測試@Scope註解設置的作用域
 */
@Configuration
public class PersonConfig2 {
    @Bean("person")
    public Person person(){
        System.out.println("給容器中添加Person....");
        return new Person("binghe002", 18);
    }
}

接下來,在SpringBeanTest類中創建testAnnotationConfig5()方法,如下所示。

@Test
public void testAnnotationConfig5(){
    ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
    System.out.println("IOC容器創建完成");
}

運行SpringBeanTest類中的testAnnotationConfig5()方法,輸出的結果信息如下所示。

給容器中添加Person....
IOC容器創建完成

可以看到,單實例bean在Spring容器啟動的時候就會被創建,並加載到Spring容器中。

懶加載模式

我們在PersonConfig2的person()方法上加上@Lazy註解將Person對象設置為懶加載,如下所示。

package io.mykit.spring.plugins.register.config;

import io.mykit.spring.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

/**
 * @author binghe
 * @version 1.0.0
 * @description 測試@Scope註解設置的作用域
 */
@Configuration
public class PersonConfig2 {

    @Lazy
    @Bean("person")
    public Person person(){
        System.out.println("給容器中添加Person....");
        return new Person("binghe002", 18);
    }
}

此時,我們再次運行SpringBeanTest類中的testAnnotationConfig5()方法,輸出的結果信息如下所示。

IOC容器創建完成

可以看到,此時,只是打印出了“IOC容器創建完成”,說明此時,只創建了IOC容器,並沒有創建bean對象。

那麼,加上@Lazy註解后,bean是何時創建的呢?我們在SpringBeanTest類中的testAnnotationConfig5()方法中獲取下person對象,如下所示。

@Test
public void testAnnotationConfig5(){
    ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
    System.out.println("IOC容器創建完成");
    Person person = (Person) context.getBean("person");
}

此時,我們再次運行SpringBeanTest類中的testAnnotationConfig5()方法,輸出的結果信息如下所示。

IOC容器創建完成
給容器中添加Person....

說明,我們在獲取bean的時候,創建了bean對象並加載到Spring容器中。

那麼,問題又來了,只是第一次獲取bean的時候創建bean對象嗎?多次獲取會不會創建多個bean對象呢?我們再來完善下測試用例,在在SpringBeanTest類中的testAnnotationConfig5()方法中,再次獲取person對象,並比較兩次獲取的person對象是否相等,如下所示。

IOC容器創建完成
給容器中添加Person....
true

從輸出結果中,可以看出使用@Lazy註解標註后,單實例bean對象只是在第一次從Spring容器中獲取對象時創建,以後每次獲取bean對象時,直接返回創建好的對象。

總結

懶加載,也稱延時加載。僅對單例bean生效。單例bean是在Spring容器啟動的時候加載的,添加@Lazy註解后就會延遲加載,在Spring容器啟動的時候並不會加載,而是在第一次使用此bean的時候才會加載,但當你多次獲取bean的時候不會重複加載,只是在第一次獲取的時候會加載,這不是延遲加載的特性,而是單例Bean的特性。

好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

寫在最後

如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回復“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

Zookeeper實現服務註冊/發現

what that?

Zookeeper在分佈式開發中使用頻繁,但許多框架都對其進行了封裝,初學者可能無法較好的理解其工作原理,該文章演示了使用Zookeeper實現服務註冊,服務發現的簡單demo,希望能達到拋磚引玉的效果;

why need RegisterCenter?

之所以需要訪問註冊和服務發現是因為分佈式系統中,服務之間需要相互調用,但若每個服務自己維護一份依賴的服務信息的話,就顯得很麻煩,且自身維護的數據無法保證其實時性,當依賴的服務信息發生變更時,無法及時獲取更新,解決方案就是引入一個註冊中心,服務提供方將自己的信息寫入到註冊中心,服務使用方從註冊中心來獲取服務信息; 如下圖:

client表示服務使用方,server表示服務提供方

實現的效果: 客戶端可自動發現服務信息,當服務狀態發生變化時(上線,下線,更換地址),客戶端可以及時響應變化,效果如下圖:

效果演示

實現

  1. 首先保證Zookeeper以安裝啟動,且可以正常訪問

  2. 創建Maven項目並添加Zookeeper的Java客戶端依賴(注意版本號需>3.6)

    				<dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.6.1</version>
            </dependency>
    
  3. 編寫服務提供方

    package com.jerry;
    
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.KeeperException;
    import org.apache.zookeeper.ZooDefs;
    import org.apache.zookeeper.ZooKeeper;
    import org.apache.zookeeper.data.ACL;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.*;
    import java.nio.charset.StandardCharsets;
    import java.util.ArrayList;
    import java.util.Enumeration;
    
    import static java.net.InetAddress.getLocalHost;
    
    public class UserService {
    
        public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
            new UserService().serving();
        }
    
        public void serving() throws IOException, KeeperException, InterruptedException {
            //獲取本機ip地址
            String ip = null;
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface ni = (NetworkInterface) networkInterfaces.nextElement();
                Enumeration<InetAddress> nias = ni.getInetAddresses();
                while (nias.hasMoreElements()) {
                    InetAddress ia = (InetAddress) nias.nextElement();
                    if (!ia.isLinkLocalAddress() && !ia.isLoopbackAddress() && ia instanceof Inet4Address) {
                        ip = ia.getHostAddress();
                    }
                }
            }
            int port = 8988;
    
            //啟動服務
            ServerSocket socket = new ServerSocket(port);
            System.out.println("服務器已啟動...");
            //註冊服務
            serverRegister(ip, port);
            //處理請求
            clientHandler(socket);
        }
    
        private void clientHandler(ServerSocket socket) throws IOException {
            while (true) {
                Socket accept = socket.accept();
                InputStream inputStream = accept.getInputStream();
                byte[] barr = new byte[1024];
                while (true) {
                    int size = inputStream.read(barr);
                    if (size == -1) {
                        //System.out.println("客戶端已關閉..");
                        accept.close();
                        break;
                    }
                    String s = new String(barr, 0, size);
                    //輸出客戶端消息
                    System.out.println(accept.getInetAddress().getHostAddress() + ": " + s);
                }
            }
    
        }
    
        private void serverRegister(String ip, int port) throws IOException, KeeperException, InterruptedException {
            //註冊服務
            ZooKeeper zooKeeper = new ZooKeeper("10.211.55.4: 2181",3000, null);
            try {
                ArrayList<ACL> acl = new ArrayList<>();
                acl.add(new ACL(31, ZooDefs.Ids.ANYONE_ID_UNSAFE));
                zooKeeper.create("/userServer", (ip + ":" + port).getBytes(StandardCharsets.UTF_8), acl, CreateMode.EPHEMERAL);
                System.out.println("服務發布成功!");
            } catch (KeeperException | InterruptedException e) {
                e.printStackTrace();
                throw e;
            }
        }
    }
    
  4. 編寫服務服務使用方

    package com.yyh;
    
    import org.apache.zookeeper.*;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    import java.util.Scanner;
    
    public class UserClient implements Watcher {
        String node = "/userServer"; //服務信息所在的節點 服務提供方和服務消費方一致
        private ZooKeeper zooKeeper;
        String server_ip;
        int server_port;
    
        public static void main(String[] args) throws Exception {
            //開始服務監聽
            UserClient userClient = new UserClient();
            userClient.run();
            //當訪問可用時與服務交互
            Scanner scanner = new Scanner(System.in);
            while (true){
                System.out.println("輸入要發送的信息(e:退出)");
                String text = scanner.next();
                if (text.equals("e"))System.exit(-1);
                if (userClient.server_ip == null){
                    System.err.println("沒有可用的服務...");
                }else {
                    userClient.sendToServer(text);
                }
            }
        }
        
        private void run() throws Exception {
            //連接zookeeper
            zooKeeper = new ZooKeeper("10.211.55.4:2181", 3000, null);
            //嘗試獲取服務信息
            getServerInfo();
            //添加對服務信息的永久監聽
            zooKeeper.addWatch(node,this,AddWatchMode.PERSISTENT);
        }
    
        //獲取服務信息
        private void getServerInfo()  {
            try {
                byte[] data = zooKeeper.getData(node, false, null);
                String[] infos = new String(data).split(":");
                server_ip = infos[0];
                server_port = Integer.parseInt(infos[1]);
                System.out.println("獲取服務信息成功!");
                System.out.println(server_ip+":"+ server_port);
            } catch (KeeperException e) {
                System.err.println("服務信息不存在! 等待服務上線........");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        //當節點狀態發送變化時將執行該方法(通知處理)
        @Override
        public void process(WatchedEvent event) {
            if (event.getPath().equals(node)) {
                //根據具體邏輯處理不同的事件類型,此處只關心節點的創建刪除和更新
                if (event.getType() == Event.EventType.NodeCreated) {
                    System.err.println("服務上線了");
                    getServerInfo();
                } else if (event.getType() == Event.EventType.NodeDataChanged) {
                    System.err.println("服務更新了");
                    getServerInfo();
                }else if (event.getType()== Event.EventType.NodeDeleted){
                    server_ip = null;
                    server_port = 0;
                    System.err.println("服務下線了");
                }
            }
        }
    
        public void sendToServer(String text) {
            InetSocketAddress server_address = new InetSocketAddress(server_ip, server_port);
            Socket socket = new Socket();
            try {
                socket.connect(server_address);
                //System.out.println("連接服務器成功!");
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write(text.getBytes());
                System.out.println("消息發送成功!");
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
  5. 打包服務端代碼,該步驟可忽略,僅為了測試客戶端正確性, 為了在打包時附帶其全部依賴,此處藉助Spring的打包插件,在pom中添加以下內容:

    		<build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>1.5.6.RELEASE</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    

    注意:Spring-boot打包插件會自動獲取項目中的主函數,必須保證主函數只有一個,所以需要暫時註釋客戶端的主函數,最後執行maven的package,得到jar包

  6. 將jar上傳至虛擬機並運行

    java -jar ZookeeperTest-1.0-SNAPSHOT.jar
    

    若沒有其他問題則客戶端依然可以正常連接服務器發送消息;

以上便是使用Zookeeper實現服務註冊和服務發現的具體步驟,在實際開發中,我們可能還會將提供的服務部署為集群,這時可將集群中的各個服務信息作為子節點註冊到指定節點下,客戶端監聽該節點變化,獲取子節點列表從而獲取到服務列表,還可以在此基礎上加上負載均衡算法實現對服務列表的合理訪問; 如圖:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

Dubbo+Zookeeper集群案例

一.開源分佈式服務框架

1.Dubbo是阿里巴巴公司開源的一個高性能優秀的服務框架,使得應用可通過高性能的 RPC 實現服務的輸出和輸入功能,可以Spring框架無縫集成。
   Dubbo是一款高性能、輕量級的開源Java RPC框架,它提供了三大核心能力:①面向接口的遠程方法調用;②智能容錯和負載均衡;③服務自動註冊和發現

2.結構圖

節點角色說明:

Provider: 暴露服務的服務提供方。
Consumer: 調用遠程服務的服務消費方。
Registry: 服務註冊與發現的註冊中心。
Monitor: 統計服務的調用次數和調用時間的監控中心。
Container: 服務運行容器。
 

調用關係說明

0服務容器負責啟動,加載,運行服務提供者provider。
1服務提供者provider在啟動時,(通過連接服務器的client)向註冊中心註冊自己可以提供的服務。(其實就是註冊一些provider自己的ip:port以及對自己提供的服務的描述,比如能幹什麼!)
2服務消費者consumer在啟動時,向註冊中心訂閱自己所需的服務。並註冊自己的ip:port等信息。
3註冊中心返回服務提供者provider地址列表給消費者consumer,如果有變更,註冊中心將基於長連接推送變更數據給消費者consumer。
4服務消費者consumer,從註冊中心返回的提供者provider地址列表中,基於軟負載均衡算法,選一台提供者provider進行調用,如果調用失敗,再選另一台調用。
5服務消費者consumer和提供者provider,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心monitor。
Dubbo 架構具有以下幾個特點,分別是連通性、健壯性、伸縮性、以及向未來架構的升級性。

二.Dubbo作用

  dubbo其實就是一个中間層管理工具,他是一個框架,裏面可以裝你想裝的服務,一般註冊中心大多用zookeeper,當然除了zookeeper,還要Redis等也可以做註冊中心。  
 

三.Dubbo+Zookeeper(註冊中心使用Zookeeper),Zookeeper其實是樹狀結構。

1.可以把register理解成房產中介,provider是賣房的人,張三想賣掉自己在秦淮區的學區房,李四想賣掉自己在棲霞區的學區房,consumer王五是想在棲霞區買學區房給自己孩子上學,王五去中介諮詢后,中介返回給王五的需求 滿足者是李四,王五從中介那得到李四的電話,自己打電話找李四買房。

比如Provider註冊的是  192.168.1.(描述121是吃飯,122睡覺,123打遊戲,124健身四種不同的服務)
2-0、 、dubbo–這是dubbo在ZooKeeper上創建的根節點  /dubbo
2-1 、 Dubbo在Zookeeper上註冊的節點目錄:假設接口名稱是:com.bob.dubbo.service.CityDubboService。
這是服務節點,代表了Dubbo的一個服務  /dubbo/com.bob.dubbo.service.CityDubboService
2-2 、 Dubbo啟動時,Consumer和Provider都會把自身的URL格式化為字符串,然後註冊到zookeeper相應節點下,作為一個臨時節點,當連斷開時,節點被刪除。
這是服務提供者的根節點,其子節點代表了每一個服務真正的提供者/dubbo/com.bob.dubbo.service.CityDubboService/providers
這是服務消費者的根節點,其子節點代表每一個服務真正的消費者;/dubbo/com.bob.dubbo.service.CityDubboService/consumers
2-3、 Consumer在啟動時,不僅僅會註冊自身到 …/consumers/目錄下,同時還會訂閱…/providers目錄下所有子節點,具體的看你訂閱具體是哪一個節點(比如訂閱健身這些服務),實時獲取其上Provider的URL字符串信息。register返回給Consumer這個ip–192.168.1.124,Consumer拿着這個iP直接去找Provider調用這項服務–健身。
2-4 、監控中心啟動時訂閱com.bob.dubbo.service.CityDubboService目錄下的所有提供者和消費者URL。

 

四.Dubbo——Zookeeper補充:

支持以下功能:

 
當提供者出現斷電等異常停機時,註冊中心能自動刪除提供者信息
當註冊中心重啟時,能自動恢復註冊數據,以及訂閱請求
當會話過期時,能自動恢復註冊數據,以及訂閱請求
當設置<dubbo:registry check=”false” />時,記錄失敗註冊和訂閱請求,後台定時重試
可通過設置<dubbo:registry username=”admin” password=”124″ />設置zookeeper 登錄信息
可通過<dubbo:registry group=”dubbo” />設置 zookeeper 的根節點,不設置將使用無 根樹
支持 * 號通配符 <dubbo:redistry group=”*” version=”*” />,可訂閱服務的所有分組 和所有版本的提供者
 
補充:
  消費者從ZK獲取provider地址列表后,會在本地緩存一份。當ZK註冊中心所有節點全部宕掉之後,消費者可以使用本地緩存的服務列表和provider進行通信。
ZK的意義在於為consumer和provider提供服務地址的發布/訂閱服務,讓消費者及時感知最新的服務列表,consumer真正調用provider是通過某種通信協議直接調用,並不依賴ZK。
 
所以當zookeeper宕機之後,不會影響消費者調用服務提供者,影響的是zookeeper宕機之後如果提供者有變動,增加或者減少,zk無法把變更通知推送給consumer,consumer會因為感知不到變更時間,不去拉取最新的服務列表,導致本地緩存的服務列表有可能過時的。

完結,個人理解,如有偏差,請大家指正,謝謝!

2020-06-09 10:58:28

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※推薦台中搬家公司優質服務,可到府估價

Kubernetes內部域名解析的那些事兒

前言

    在kubernets環境中,服務發現大都是基於內部域名的方式。那麼就涉及到內部域名的解析。從1.11版本開始,kubeadm已經使用第三方的CoreDNS替換官方的kubedns作為集群內部域名的解析組件。

kubernets中的4種DNS策略

None

表示空的DNS設置,這種方式一般用於想要自定義 DNS 配置的場景,往往需要和 dnsConfig 配合一起使用達到自定義 DNS 的目的。

Default

此種方式是讓kubelet來決定使用何種DNS策略。而kubelet默認的方式,就是使用宿主機的/etc/resolv.conf文件。

同時,kubelet也可以配置指定的DNS策略文件,使用kubelet參數即可,如:–resolv-conf=/etc/resolv.conf

ClusterFirst

此種方式是使用kubernets集群內部中的kubedns或coredns服務進行域名解析。若解析不成功,才會使用宿主機的DNS配置來進行解析。

ClusterFistWithHostNet

在某些場景下,我們的 POD 是用 HOST 模式啟動的(HOST模式,是共享宿主機網絡的),一旦用 HOST 模式,表示這個 POD 中的所有容器,都要使用宿主機的 /etc/resolv.conf 配置進行DNS查詢,但如果你想使用了 HOST 模式,還繼續使用 Kubernetes 的DNS服務,那就將 dnsPolicy 設置為 ClusterFirstWithHostNet。

策略配置示例

DNS策略,需要在Pod,或者Deployment、RC等資源中,設置 dnsPolicy 即可,以 Pod 為例:

apiVersion: v1
kind: Pod
metadata:
   labels:
    name: cadvisor-nodexxxx
    hostip: 192.168.x.x
  name: cadvisor-nodexxxx
  namespace: monitoring
spec:
  containers:
  - args:
    - --profiling
    - --housekeeping_interval=10s
    - --storage_duration=1m0s
    image: google/cadvisor:latest
    name: cadvisor-nodexxxx
    ports:
    - containerPort: 8080
      name: http
      protocol: TCP
    resources: {}
    securityContext:
      privileged: true
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
  dnsPolicy: ClusterFirst
  nodeName: nodexxxx

kubernets中域名解析流程

# Pod中的resolv.conf的解析配置

[root@l-k8s01 ~]# kubectl exec -it nginx-deploy-5754944d6c-dtzpj cat /etc/resolv.conf

nameserver 10.96.0.2
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

[root@l-k8s01 ~]# kubectl get svc -n kube-system |grep dns

kube-dns   ClusterIP  10.96.0.2   <none>   53/UDP,53/TCP,9153/TCP   158d

a)文件中配置的 nameserver 一般是k8s集群內部的dns服務的ClusterIP,無法ping,但是可以訪問。

b)意味着集群Pod內部的所有域名的解析,都要經過kubedns的虛擬ip 10.96.0.2 進行解析。

c)resolv.conf中search域分別是default.svc.cluster.local svc.cluster.local cluster.local,在kubernets中,域名的全稱必須是 service-name.namespace.svc.cluster.local 。

d)假如集群中有一個svc(Service)名為a,在某個Pod中執行命令 curl a 時,在此Pod中會根據/etc/resolv.conf進行解析流程。選擇nameserver 10.96.0.2進行解析,將字符串’a’帶入到/etc/resolv.conf文件中不同的search域,依次進行查找,如下:

a.default.svc.cluster.local -> a.svc.cluster.local -> a.cluster.local

先查找 a.default.svc.cluster.local ,若找不到,則再查找 a.svc.cluster.local ,依次往下進行,直到找到為止。

curl效率分析

在集群中若存在一個名為a的svc,在Pod中curl a和curl a.default都能實現請求,那麼兩種方式哪個的效率高呢?

那肯定是curl a啦,因為發起此請求時,通過/etc/resolv.conf中第一列的search域就能直接找到 a.default.svc.cluster.local ,直接避免了下一級的查找。

容器中訪問外部域名講述

下文將通過示例說明Pod訪問外部域名時發起的相應的請求信息。

以請求baidu.com為例,因為DNS容器一般不具備bash,所以無法通過docker exec的方式進入容器抓包,所以此處採用 進入到DNS容器的網絡中(不是發起DNS請求的容器)的姿勢去抓包,抓包姿勢準備好后,同時在某容器中訪問baidu.com,即可看到在進行的DNS查找的過程中都產生了什麼樣的數據包。

 

### 實操

# 進入dns容器網絡,準備好抓包姿勢

# 查看Pod所在具體的node節點

[root@master1 ~]# kubectl get pods -n kube-system -o wide|grep dns

coredns-5c48579f88-8wprg  1/1   Running  16    30d   10.244.4.120   node1
coredns-5c48579f88-rsnpr   1/1   Running   0     30d   10.244.5.142   node2

# 這裏以node1上的容器為操作對象,所以到node1節點上進行操作

# 找到容器並打印對應的NS ID

[root@node1 ~]# docker ps |grep dns

a964bbb43534 c0f6e815079e "/coredns -conf /etc…" 2 days ago Up 2 days k8s_coredns_coredns-5c48579f88-8wprg_kube-system_b1e7f3c3-98eb-4843-b156-1d203f98bd74_16
fbd12d2f9c7c k8s.gcr.io/pause:3.1 "/pause" 5 days ago Up 5 days k8s_POD_coredns-5c48579f88-8wprg_kube-system_b1e7f3c3-98eb-4843-b156-1d203f98bd74_3

[root@node1 ~]# docker inspect –format “{{.State.Pid}}”  a964bbb43534

21617

# 進入此容器的網絡Namespace

[root@node1 ~]# nsenter -n -t 21617

# 抓包姿勢就緒

[root@node1 ~]# tcpdump -i eth0 udp dst port 53|grep ‘baidu.com’

 

# 在另外的某容器中,進行域名查找操作

說明:一般pod中沒有nslookup命令,故需要手動安裝,根據不同環境自選以下操作。

### Centos

]# cat /etc/redhat-release

CentOS Linux release 7.5.1804 (Core)

]# yum -y install bind-utils

### Debian

# cat /etc/issue

Debian GNU/Linux 9

# apt-get install dnsutils -y

root@jenkins-7d66bf7977-cm4x4:~# nslookup baidu.com 10.244.4.120

 

注意:10.244.4.120是node1上的dns pod在kubernets集群中的內部通信ip地址。因為環境中有兩個dns pod,將其指定要單個具體的容器,能夠使抓包數據完整。

 

# 隨後,在前面就緒的抓包姿勢窗口就能看到數據包的出現

[root@node1 ~]# tcpdump -i eth0 udp dst port 53|grep ‘baidu.com’

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
16:57:50.791154 IP 10.244.4.127.51794 > node1.domain: 55406+ A? baidu.com.infra.svc.cluster.local. (51)
16:57:50.792540 IP 10.244.4.127.56306 > node1.domain: 27958+ A? baidu.com.svc.cluster.local. (45)
16:57:50.793439 IP 10.244.4.127.59799 > node1.domain: 27048+ A? baidu.com.cluster.local. (41)
16:57:50.799463 IP 10.244.4.127.39116 > node1.domain: 2303+ A? baidu.com. (27)

說明:

a)數據包中显示的 infra 是執行nslookup的pod的NameSpace;

b)根據數據显示,在真正解析到 baidu.com 之前,經歷了baidu.com.infra.svc.cluster.local. > baidu.com.svc.cluster.local. > baidu.com.cluster.local. 三次DNS請求。

請求浪費的原因

上文在正確請求到baidu.com之前,有過三次無效請求,即意味着請求浪費,那為什麼會出現那種情況呢,請繼續往下看。

 

# Pod中的resolv.conf的解析配置

root@jenkins-7d66bf7977-cm4x4:/# cat /etc/resolv.conf
nameserver 10.96.0.2
search infra.svc.cluster.local svc.cluster.local cluster.local host.com
options ndots:5

 

# options ndots:5 解釋

如果查詢的域名包含的點”.”,不到5個,那麼進行DNS查找,將使用非完全限定名稱(或者叫絕對域名),如果你查詢的域名包含點數大於等於5,那麼DNS查詢,默認會使用絕對域名進行查詢。

如果我們請求的域名是,a.b.c.d.e,這個域名中有4個點,那麼容器中進行DNS請求時,會使用非絕對域名進行查找,使用非絕對域名,會按照 /etc/resolv.conf 中的 search 域,走一遍追加匹配:

a.b.c.d.e.cicd.svc.cluster.local. ->

a.b.c.d.e.svc.cluster.local. ->

a.b.c.d.e.cluster.local.

直到找到為止。如果走完了search域還找不到,則使用 a.b.c.d.e. ,作為絕對域名進行DNS查找。

 

說明:

a)請求域名中點數少於5個時,先走search域,最後將其視為絕對域名進行查詢;

b)請求域名中點數大於等於5個時,直接視為絕對域名進行查找,只有當查詢不到的時候,才繼續走 search 域。

優化請求浪費

使用全限定域名

當訪問某域名時,以 ‘.’ 為後綴,即使用 完全限定域名(絕對域名),這樣發起的域名請求時將不會走search域進行匹配,而是直接使用整個原始域名字符串為個體進行解析。

 

如:

nslookup baidu.com.

配置特定ndots

在kubernets中,ndots值默認是5。是因為,Kubernetes 認為,內部域名,最長為5,要保證內部域名的請求,優先走集群內部的DNS,而不是將內部域名的DNS解析請求,有打到外網的機會,Kubernetes 設置 ndots 為5是一個比較合理的行為。

如果有特定業務需求,也可配置ndots,如下:

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsConfig:
    options:
      - name: ndots
        value: "1"

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

網頁設計最專業,超強功能平台可客製化

RocketMQ系列(三)消息的生產與消費

前面的章節,我們已經把RocketMQ的環境搭建起來了,是一個兩主兩從的異步集群。接下來,我們就看看怎麼去使用RocketMQ,在使用之前,先要在NameServer中創建Topic,我們知道RocketMQ是基於Topic的消息隊列,在生產者發送消息的時候,要指定消息的Topic,這個Topic的路由規則是怎樣的,這些都要在NameServer中去創建。

Topic的創建

我們先看看Topic的命令是如何使用的,如下:

./bin/mqadmin updateTopic -h

usage: mqadmin updateTopic -b <arg> | -c <arg>  [-h] [-n <arg>] [-o <arg>] [-p <arg>] [-r <arg>] [-s <arg>] -t
       <arg> [-u <arg>] [-w <arg>]
 -b,--brokerAddr <arg>       create topic to which broker
 -c,--clusterName <arg>      create topic to which cluster
 -h,--help                   Print help
 -n,--namesrvAddr <arg>      Name server address list, eg: 192.168.0.1:9876;192.168.0.2:9876
 -o,--order <arg>            set topic's order(true|false)
 -p,--perm <arg>             set topic's permission(2|4|6), intro[2:W 4:R; 6:RW]
 -r,--readQueueNums <arg>    set read queue nums
 -s,--hasUnitSub <arg>       has unit sub (true|false)
 -t,--topic <arg>            topic name
 -u,--unit <arg>             is unit topic (true|false)
 -w,--writeQueueNums <arg>   set write queue nums

其中有一段,-b <arg> | -c <arg>,說明這個Topic可以指定集群,也可以指定隊列,我們先創建一個Topic指定集群,因為集群中有兩個隊列broker-abroker-b,看看我們的消息是否在兩個隊列中負載;然後再創建一個Topic指向broker-a,再看看這個Topic的消息是不是只在broker-a中。

創建兩個Topic,

./bin/mqadmin updateTopic -c 'RocketMQ-Cluster' -t cluster-topic -n '192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876'

./bin/mqadmin updateTopic -b 192.168.73.130:10911 -t broker-a-topic

第一個命令創建了一個集群的Topic,叫做cluster-topic;第二個命令創建了一個只在broker-a中才有的Topic,我們指定了-b 192.168.73.130:10911,這個是broker-a的地址和端口。

生產者發送消息

我們新建SpringBoot項目,然後引入RocketMQ的jar包,

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.3.0</version>
</dependency>

然後配置一下生產者的客戶端,在這裏使用@Configuration這個註解,具體如下:

@Configuration
public class RocketMQConfig {

    @Bean(initMethod = "start",destroyMethod = "shutdown")
    public DefaultMQProducer producer() {
        DefaultMQProducer producer = new
                DefaultMQProducer("DefaultMQProducer");
											producer.setNamesrvAddr("192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876;");
        return producer;
    }
}
  • 首先創建一個生產者組,名字叫做DefaultMQProducer;
  • 然後指定NameServer,192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876;
  • 最後在@Bean註解中指定初始化的方法,和銷毀的方法;

這樣,生產者的客戶端就配置好了,然後再寫個Test類,在Test類中向MQ中發送消息,如下,

@SpringBootTest
class RocketmqDemoApplicationTests {

    @Autowired
    public DefaultMQProducer defaultMQProducer;

    @Test
    public void producerTest() throws Exception {

        for (int i = 0;i<5;i++) {
            Message message = new Message();
            message.setTopic("cluster-topic");
            message.setKeys("key-"+i);
            message.setBody(("this is simpleMQ,my NO is "+i).getBytes());

            SendResult sendResult = defaultMQProducer.send(message);
            System.out.println("SendStatus:" + sendResult.getSendStatus());
            System.out.println("BrokerName:" + sendResult.getMessageQueue().getBrokerName());
        }
    }
}
  • 我們先自動注入前面配置DefaultMQProducer;
  • 然後在Test方法中,循環5次,發送5個消息,消息的Topic指定為cluster-topic,是集群的消息,然後再設置消息的key和內容,最後調用send方法發送消息,這個send方法是同步方法,程序運行到這裡會阻塞,等待返回的結果;
  • 最後,我們打印出返回的結果和broker的名字;

運行一下,看看結果:

SendStatus:SEND_OK
BrokerName:broker-b
SendStatus:SEND_OK
BrokerName:broker-b
SendStatus:SEND_OK
BrokerName:broker-b
SendStatus:SEND_OK
BrokerName:broker-b
SendStatus:SEND_OK
BrokerName:broker-a

5個消息發送都是成功的,而發送的隊列有4個是broker-b,1個broker-a,說明兩個broker之間還是有負載的,負載的規則我們猜測是隨機。

我們再寫個測試方法,看看broker-a-topic這個Topic的發送結果是什麼樣子的,如下:

@Test
public void brokerTopicTest() throws Exception {

    for (int i = 0;i<5;i++) {
        Message message = new Message();
        message.setTopic("broker-a-topic");
        message.setKeys("key-"+i);
        message.setBody(("this is broker-a-topic's MQ,my NO is "+i).getBytes());

        defaultMQProducer.send(message, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("SendStatus:" + sendResult.getSendStatus());
                System.out.println("BrokerName:" + sendResult.getMessageQueue().getBrokerName());
            }

            @Override
            public void onException(Throwable e) {
                e.printStackTrace();
            }
        });

        System.out.println("異步發送 i="+i);

    }
}
  • 消息的Topic指定的是broker-a-topic,這個Topic我們只指定了broker-a這個隊列;
  • 發送的時候我們使用的是異步發送,程序到這裏不會阻塞,而是繼續向下執行,發送的結果正常或者異常,會調用對應的onSuccess和onException方法;
  • 我們在onSuccess方法中,打印出發送的結果和隊列的名稱;

運行一下,看看結果:

異步發送 i=0
異步發送 i=1
異步發送 i=2
異步發送 i=3
異步發送 i=4
SendStatus:SEND_OK
SendStatus:SEND_OK
SendStatus:SEND_OK
SendStatus:SEND_OK
BrokerName:broker-a
SendStatus:SEND_OK
BrokerName:broker-a
BrokerName:broker-a
BrokerName:broker-a
BrokerName:broker-a

由於我們是異步發送,所以最後的日誌先打印了出來,然後打印出返回的結果,都是發送成功的,並且隊列都是broker-a,完全符合我們的預期。

消費者

生產的消息已經發送到了隊列當中,再來看看消費者端如何消費這個消息,我們在這個配置類中配置消費者,如下:

@Bean(initMethod = "start",destroyMethod = "shutdown")
public DefaultMQPushConsumer pushConsumer() throws MQClientException {
    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("DefaultMQPushConsumer");
    consumer.setNamesrvAddr("192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876;");
    consumer.subscribe("cluster-topic","*");
    consumer.registerMessageListener(new MessageListenerConcurrently() {
        @Override
        public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
            if (msgs!=null&&msgs.size()>0) {
                for (MessageExt msg : msgs) {
                    System.out.println(new String(msg.getBody()));
                    System.out.println(context.getMessageQueue().getBrokerName());
                }
            }

            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        }
    } );
    return consumer;
}
  • 我們創建了一個消費者組,名字叫做DefaultMQPushConsumer;
  • 然後指定NameServer集群,192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876;
  • 消費者訂閱的Topic,這裏我們訂閱的是cluster-topic,後面的*號是對應的tag,代表我們訂閱所有的tag;
  • 最後註冊一個併發執行的消息監聽器,實現裡邊的consumeMessage方法,在方法中,我們打印出消息體的內容,和消息所在的隊列;
  • 如果消息消費成功,返回CONSUME_SUCCESS,如果出現異常等情況,我們要返回RECONSUME_LATER,說明這個消息還要再次消費;

好了,這個訂閱了cluster-topic的消費者,配置完了,我們啟動一下項目,看看消費的結果如何,

this is simpleMQ,my NO is 2
broker-b
this is simpleMQ,my NO is 3
broker-b
this is simpleMQ,my NO is 1
broker-b
this is simpleMQ,my NO is 0
broker-a
this is simpleMQ,my NO is 4
broker-b

結果符合預期,cluster-topic中的5個消息全部消費成功,而且隊列是4個broker-b,1個broker-a,和發送時的結果是一致的。

大家有問題歡迎評論區討論~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

「MoreThanJava」Java發展史及起航新世界

  • 「MoreThanJava」 宣揚的是 「學習,不止 CODE」,本系列 Java 基礎教程是自己在結合各方面的知識之後,對 Java 基礎的一個總回顧,旨在 「幫助新朋友快速高質量的學習」
  • 當然 不論新老朋友 我相信您都可以 從中獲益。如果覺得 「不錯」 的朋友,歡迎 「關注 + 留言 + 分享」,文末有完整的獲取鏈接,您的支持是我前進的最大的動力!

Part 1. Java 發展簡史

  • 圖片來源:https://www.geeksforgeeks.org/the-complete-history-of-java-programming-language/

起源:”Green” 項目

20 世紀 90 年代,單片式計算機系統誕生,單片式計算機系統不僅廉價,而且功能強大,使用它可以大幅度提升消費性电子產品的智能化程度。

SUN 公司為了搶佔市場先機,在 1991 年成立了一個由詹姆斯·高斯林(James Gosling)領導,名為 “Green” 項目小組,目的是開發一種 能夠在各種消費性电子產品上運行的程序架構(主要是像 有線電視轉換盒 這一類 處理能力和內存都很有限,並且 CPU 廠商又各不相同 的消費設備)。

由於這些消費設備的處理能力和內存都有限,所以語言必須 非常小且能夠生成非常緊湊的代碼。另外,由於不同廠商會選擇不同的 CPU,因此很重要的一點是這種語言 不應該與任何特定的體繫結構綁定。代碼短小、緊湊且與平台無關,這些要求促使開發團隊設計出一個 可移植的語言,可以為虛擬機生成中間代碼。

不過,Sun 公司的人都有 UNIX 的應用背景。因此,所開發的語言以 C++ 為基礎,而不是 Lisp/ Smalltalk 或 Pascal。不過,就像 Gosling 在專訪中談道:“畢竟,語言只是實現目標的工具,而不是目標本身。”

Gosling 把這種語言稱為 “Oak”(直譯為橡樹,大概是因為它非常喜歡自己辦公室窗外的一顆橡樹…)。後來 Sun 公司發現,Oak 是一種已有的計算機語言名字,於是 Gosling 和他的團隊進行了一次頭腦風暴,多次討論后,從 Java/ DNA/ SILK/ RUBY 中決定使用 Java 來命名。事實證明這是一個很有靈感的選擇。

埋沒:沒人為 “Green” 項目買單

1992 年,Green 項目發布了它的第一個產品,稱之為 「* 7」。這個產品可以提供非常智能的遠程控制。遺憾的是,Sun 公司對生產這個產品並不感興趣。Green 項目組的人員必須找出其他的方法來講他們的技術推向市場。

然而,仍然沒有任何一家標準消費品电子公司對此感興趣。於是,Green 項目組投標了一個設計有線電視盒的項目,它能提供視頻點播等新型有線服務,但他們沒能拿到這個合同 (有趣的是,得到這個項目的公司的領導恰恰是開闢 Netscape 公司的 Jim Clark。Netscape 公司後來對 Java 的成功給予了很大的幫助。)

Green 項目 (這時已經換了一個新名字 ———— “First Person 公司”)1993 年一整年以及 1994 年上半年,一直在苦苦尋找買家購買他們的技術。然而,一個也沒有找到 (Partick Naughton ———— 項目組的創始人之一,也是完成了大多數營銷工作的人,聲稱為了銷售這項技術,已累計飛行了 300,000 英里)

1994 年 First Person 公司解散了。

轉機:Internet 的壯大

當這一切在 Sun 公司發生的時候,Internet 的萬維網也在日漸發展壯大。萬維網的關鍵是把超文本頁面轉換到屏幕上的瀏覽器。

1994 年大多數人都在使用 Mosaic,這是一個 1993 年出自伊利諾斯大學超級計算中心的非商業化的 Web 瀏覽器( Mosaic 的一部分是由 Marc Andreessen 編寫的。當時,他作為一名參加半工半讀項目的本科生, 編寫了這個軟件,每小時的薪水只有 6.85 美元。他後來成了 Netscape 公司的創始人之一和技術總監, 可謂名利雙收。)

在接受 SunWorld 採訪的時候,Gosling 說在 1994 年中期,Java 語言的開發者意識到: “我們能夠建立一個相當酷的瀏覽器。在客戶機/ 服務器主流模型中,瀏覽器恰好需要我們已經完成的一些工作:體繫結構中立、實時、可靠、安全 ———— 這些在工作站環境並不太重要,所以,我們決定開發瀏覽器”

實際的瀏覽器是由 Patrick Naughton 和 Jonathan Payne 開發的,並演變為 HotJava 瀏覽器。為了炫耀 Java 語言超強的能力,HotJava 瀏覽器採用 Java 編寫。設計者讓 HotJava 瀏覽器具有在網頁中執行內嵌代碼的能力。 這一 “技術證明” 在 1995523 日的 SunWorld’95 上得到展示,同時引發了人們延續至今的對 Java 的狂熱追逐。

至此,這一場持續長達 20 多年的「Java 熱」開始了。

  • 觀察近 20 年的數據,Java 的排名從未跌出過前三,而且有將近一半的年份搶佔了透明,不得不令人感嘆:「流水的程序員,鐵打的 Java 啊!」

Part 2. Java 與 Internet

  • 圖片來源:https://www.morethanshipping.com/internet-things-iot-will-help-logistics/

如果 Java 僅僅只是眾多的程序設計語言中的一種,你可能就會問:為什麼它如此重要呢?為什麼它促使計算機編程語言向前邁進了革命性的一步?

如果從傳統的程序設計的角度看,問題的答案似乎不太明顯。儘管 Java 對於解決傳統的單機程序設計問題非常有用,但同樣重要的事,它解決了在萬維網(WWW)上的程序設計問題

Web 1.0 時代的程序設計問題

在剛創造 Java 的年代(20 世紀 90 年代),整個互聯網還處於 Web 1.0 的網絡萌芽階段。

在 之前的一篇文章 其實有對 Web 做了一些概念性的描述(概念、發展、體繫結構)。

問題一:網頁沒有交互

Web 1.0 的網站是靜態的。最初的互聯網只有一種很簡單的 單向過程:你 對某一個服務器發起一個請求,然後它 返回 給你一個 文件,你的機器(俗稱客戶端)上的 瀏覽器軟件 根據本地機器的格式來 解讀並展示 這個文件的內容。這期間沒有任何的交互發生,因為最初的瀏覽器只是一個 “展示器”,它甚至不能執行最簡單的計算任務。(另一方面,它確是安全的,因為它在你的本地機器上不會執行任何程序,而這些程序可能包含 bug 和病毒)

用戶 只能訪問 這些站點而不會對它們做出任何貢獻。這就像你捧起書架中的一本書一樣,它是一種 「只讀」 模式的存在,如果你想與創造這本書的出版社也好,作者也好建立鏈接,只能通過其他的一些方式。(當時的網站也是主要是向消費者展示產品,從感興趣的消費者那裡收錢)

很快人們就不滿足於只是從服務器傳遞迴頁面,人們希望實現完整的客戶/ 服務器能力,使得客戶可以將信息反饋給服務器,來完成例如:在服務器上進行數據查找,並將用戶提供的新信息加到服務器中,服務器管理人員接受到新信息之後就下發訂單的操作。

問題二:響應緩慢

早期的瀏覽器不僅沒有交互,而且它還趨向於讓服務器和 Internet 阻塞。因為在任何時候,只要 你需要完成 通過編程 才能實現的 任務,就必須將信息發揮到 服務器處理。然而在互聯網中,在任意時刻都有可能會有 成百上千 的客戶向服務器發出請求,所以任何小的延遲都會產生重大的影響。

為了解決這個問題,人們採用了各種不同的方法。首先,圖形標準得到了加強,這使得在瀏覽器中可以播放質量更好的動畫和視頻。剩下的問題通過引入 在客戶端瀏覽器中運行程序 的能力就可以解決,這被稱為 「客戶端編程」

問題三:客戶端編程平台各異

大多數運行 Web 瀏覽器的機器都是能夠執行大型任務的強有力的引擎。在使用原始的靜態 HTML 方式的情況下,它們通常只是閑在那裡,等着服務器送來下一個頁面。

客戶端編程意味着 Web 瀏覽器能夠用來執行任何它可以完成的工作,使得返回給用戶的結果 更迅速 (不用全部的結果都等着服務器來運算),而且使得你的網站 更加具有交互性 (那些不需要使用服務器數據的操作可以完全在本地完成)

但客戶端編程的問題是:它與通常意義上的編程十分不同,參數幾乎相同,而平台卻不同。在 Web 瀏覽器中編程就像是使用一台功能受限的操作系統,而每一台功能還略微的有差別。最終,你不僅需要編寫程序,還需要處理因為平台不同帶來的兼容問題。

小結

因為「沒有交互」和「訪問緩慢」的問題,所以引入「客戶端編程」,因為引入客戶端編程,遇到「各種各樣不僅僅是平台差異帶來的問題」。

Java 的解決方案

插件和腳本語言 “差點意思”

在當時,客戶端編程所邁出的最重要的一步就是 插件(plug-in) 的開發。通過這種方式,用戶可以下載一段代碼,並將其插入到瀏覽器中適當的位置,以此來為瀏覽器添加新的功能。

  • 圖片來源:https://zhuanlan.zhihu.com/p/28889449

插件又引發了瀏覽器 腳本語言(scripting language) 的開發。通過使用某種腳本語言,你可以將客戶端程序的源代碼直接嵌入到 HTML 頁面中,解釋這種語言的插件在 HTML 頁面被显示時自動激活。(腳本語言可以解決客戶端編程中遇到的百分之八十的問題) 腳本語言先天就相當易於理解,因為它們只是作為 HTML 頁面一部分的簡單文本,當服務器收到要獲取該頁面的請求時,它們可以被快速加載。此方法的缺點是代碼會直接暴露給任何瀏覽(或竊取)的人,但是,通常不會使用腳本語言去做相當複雜的事情,所以這個缺點不會太嚴重。

如果腳本語言可以解決客戶端編程百分之八十的問題的話,那麼剩下那百分之二十 (那才是真正難啃的骨頭) 又該怎麼辦呢?

Java 帶來了 Applet

  • 圖片來源:https://www.ibm.com/developerworks/cn/java/

Java 帶着 Applet 及時出現。

1995 年, Java 之父 James Gosling 和 Sun 公司科學辦公室主任 John Gage 一起前往蒙特利,去參加一個 TED 會議,兩人要在那裡展示一個划時代的技術, 號稱向能把枯燥的靜態網頁變得栩栩如生,美輪美奐。

演示開始了,James Gosling 把鼠標指向了瀏覽器中的一個 3D 分子模型,來回地旋轉它,台下的觀眾發出陣陣驚嘆聲,他們被鎮住了,從沒有人想到在瀏覽器中也能實現這麼 “美輪美奐” 的效果 !

  • 圖片來源:https://zhuanlan.51cto.com/art/201911/606791.htm

Java 火了!

這個演示所使用的技術就是 Applet。

Applet 是只在 Web 瀏覽器中運行的小程序,它是作為網頁的一部分而自動下載的 (就像是網站圖片被自動下載一樣)。當 Applet 被激活時,它變開始執行一個程序,這正是它優雅的地方:它提供了一種分發軟件的方式,一旦用戶需要客戶端軟件時,就自動從服務器把客戶端軟件分發給用戶。

用戶獲取最新版本的客戶端軟件時不會產生錯誤,而且也不需要很麻煩的重新安裝過程 (有點像現在的小程序)。Java 的這種設計方式,使得程序員只需要創建單一的程序,而只要一台計算機有瀏覽器,且瀏覽器具有內置的 Java 解釋器 (大部分機器都有),那麼這個程序就可以自動在這台計算客戶端盡可能地多做事情。例如,不必跨網絡地發送一張請求表單來檢查自己是否填寫了錯誤的日期或者其他參數,客戶端計算機就可以快速地標出錯誤數據。

這不僅立即就獲得了快速的響應能力,而且也降低了網絡流量和服務器負載,從而不會使整個網絡的速度慢下來。

Java 對服務端編程的加持

當提出對服務器的請求之後,會發生什麼呢?大部分時間,請求只是要求「給我發送一個文件」,之後瀏覽器會以某種適當的形式解釋這個文件,例如將其作為 HTML 頁面、圖片、Java applet 或腳本程序等來解釋。

更複雜的對服務器的請求通常涉及數據庫,這可能會需要服務器端對請求到的數據進行一定的編排 (例如把數據嵌到一個表格之內) 來最終使其成為一個 HTML 文件發送給客戶端 (當然,如果客戶端具備更多的只能,你完全可以把原始數據發送給客戶端讓它自己進行編排工作..)。另一種常見的情形是:你註冊賬號或者提交訂單,這對數據庫數據造成了更改,而這些必須通過服務器端的某些代碼進行處理,這就是所謂的 服務端編程

Java 後來編寫的被稱為 Servlet 的程序 (及其衍生物 JSP),是許多開發網站的公司遷移到 Java 上的主要原因。尤其是因為憑藉 Java 跨平台的特性 消除了處理具有不同能力的瀏覽器時所遇到的問題

小結

Java 憑藉自身強大、安全、跨平台、國際化的特性,加上解決了當時客戶端、服務端開發的諸多 “痛點”,成功搭上 Internet 這列 “國際快車”,一躍成為了時下 20 實際 90 年代中) 最熱門的語言之一,並持續火熱至今 (這跟 Java 自身不斷地成熟有脫不開的關係)

現如今 Applet 和 Servlet 兩個技術已經逐步淡出人們的視野,但在 Java 的歷史上,是舉足輕重的兩個突破點。

Part 3. Hello Wrold!

  • 圖片來源:https://medium.com/@thiagonascimento/time-to-first-hello-world-11a4735602f2

當我們集中注意力 學習一種新的編程語言 時,教程上的 第一個案例 就是如何 在計算機屏幕上显示短語 Hello,world! 也許這條短語最知名的來源是貝爾實驗室的備忘錄《C 語言編程——一份教程》。這份材料編寫於 1974 年。不過在編寫於 1972 年的 B 語言教程中,我們同樣看到了這條短語的身影。

Hello, World! 是一種偉大的教學方法。這是一項能夠輕鬆完成的小任務,同時也代表着一種標準,體現出不同編程語言之間的重要差異。此外,這也是高級程序員在安裝新環境測試一切是否正常的快速簡便方法。(有時候,程序員們也會使用「hello world」運行時間來比較不同語言與環境的速度水平。) 也許更重要的是,Hello, world! 具有一種溫暖而柔和的力量,對編程新人有着一種莫名的親和力。

「代碼擁有無窮威力,而新的世界已經向你張開懷抱。」 ———— Chris Noessel,IBM 公司 AI 設計負責人

public class Main {

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

以上就是Java 語言版本 Hello World 程序。(現在看不懂也沒關係,可以進 https://c.runoob.com/compile/10 這個網站在線運行測試一下看看效果…)

至此,歡迎你進入 Java 的世界。

參考資料

  1. 《Thinking in Java》 第四版;
  2. 《Java 核心技術 卷 I》 第 11 版;
  3. The complete History of Java Programming Language – https://www.geeksforgeeks.org/the-complete-history-of-java-programming-language/
  4. Java 發展簡史:初生遇低谷,崛起於互聯網 – https://www.chainnews.com/articles/628715645859.htm
  5. 永別了,Java的“小蘋果”! – https://zhuanlan.51cto.com/art/201911/606791.htm
  6. 改變世界的代碼行 – https://www.infoq.cn/article/5CaYH8NbS6BmptWKRgkX

往期精彩

  1. 「MoreThanJava」當大學選擇了計算機之後應該知道的
  2. 「MoreThanJava」計算機發展史—從織布機到IBM
  3. 「MoreThanJava」計算機系統概述
  4. 「MoreThanJava」一文了解二進制和CPU工作原理
  5. 「MoreThanJava」機器指令到彙編再到高級編程語言
  • 本文已收錄至我的 Github 程序員成長系列 【More Than Java】,學習,不止 Code,歡迎 star:https://github.com/wmyskxz/MoreThanJava
  • 個人公眾號 :wmyskxz,個人獨立域名博客:wmyskxz.com,堅持原創輸出,下方掃碼關注,2020,與您共同成長!

非常感謝各位人才能 看到這裏,如果覺得本篇文章寫得不錯,覺得 「我沒有三顆心臟」有點東西 的話,求點贊,求關注,求分享,求留言!

創作不易,各位的支持和認可,就是我創作的最大動力,我們下篇文章見!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

泰國禁農藥「巴拉刈」和「毒死蜱」 美國、巴西抗議

摘錄自2020年6月22日自由時報報導

泰國本(6)月初通過新的農藥禁令,將劇毒農藥巴拉刈(Paraquat)和殺蟲劑「毒死蜱」(Chlorpyrifos)列入危險物質清單,進而影響另條法規:禁止含有殘留違禁化學物質的農產品進口。美國與巴西對此提出抗議,認為泰國過度限制、強硬的手段,會損害他們農產品的出口。

泰國農業部副部長Mananya Thaiset表示,巴拉刈已在各種研究中被證明其與帕金森氏症有關。而針對「毒死蜱」(Chlorpyrifos),許多科學研究也提出它會影響孩童的大腦發育,已在歐盟和美國加州被禁止使用。

泰國目前約有1000萬個農戶開始受到禁令影響,該國農業普遍認為相關禁令會產生連鎖破壞反應,因為泰國的動物飼料幾乎完全仰賴進口的大豆、小麥。工商業暨銀行聯合委員會(Joint Standing Committee on Commerce, Industry and Banking)警告,該禁令將造成1.7兆泰銖(約新台幣1.6兆元)的損失和1200萬個工作機會消失,呼籲總理放寬禁令緩衝期限至2021年年底。

毒物
農林漁牧業
公害污染
永續發展
環境經濟
污染治理
土地利用
循環經濟
國際新聞
泰國
美國
巴西
禁用農藥
糧食

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

6萬不到的大空間7座車 是否能超越神車宏光?

動力方面,北汽幻速H3採用的是1。5L動力發動機,最大功率113馬力,最大扭矩為150牛米。匹配一款5速手動變速箱。主要競爭對手分析:銷量神車 五菱宏光指導價:4。18-6。98萬銷量大得驚人的宏光是幻速H3的主要競爭對手,而我們可以拿幻速H3新上市的車型簡單對比一下同價位的宏光。

近日,北汽幻速官方宣布,北汽幻速H3手動舒適天窗版車型已經正式上市!它的售價僅為5.98萬元,這款車型是在H3舒適型的基礎上加入了許多配置,但是售價沒有增加!

先來看看這款新車型帶有的亮點配置

電動天窗

8英寸液晶屏

GpS導航、藍牙

ABS+EBD

倒車雷達

行車自動落鎖/解鎖

前排駕駛席安全氣囊

對於一輛售價不足6萬的MpV來說,這樣的配置還是相當給力的。

我們再來看看幻速H3的車身尺寸,首先它的車身長寬高為4660*1770*1780mm,帶行李架車型高度為1800mm。軸距達到了2800mm,並且提供7或8座的座椅布局,實用性表現不錯。

而且其後備廂的儲物空間可以擴展至3573L。

外觀設計在這個價位中有着較好的表現,中網設計簡約,但是頭燈造型犀利。車身的輪廓線條比較分明、幹練。

動力方面,北汽幻速H3採用的是1.5L動力發動機,最大功率113馬力,最大扭矩為150牛米。匹配一款5速手動變速箱。

主要競爭對手分析:

銷量神車 五菱宏光

指導價:4.18-6.98萬

銷量大得驚人的宏光是幻速H3的主要競爭對手,而我們可以拿幻速H3新上市的車型簡單對比一下同價位的宏光。

可以說,幻速H3手動舒適天窗版車型還是要多出許多舒適性配置的,性價比表現不錯。而宏光的起售價則更多,有更多的車型可選擇。

編者語:

低價的MpV市場發展到現在,在配置、性價比方面的競爭也日益激烈了!如果你覺得幻速H3手動舒適天窗版的配置足夠給力的話,就去多了解這款車吧!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

中國駕校都教會了我們什麼?一出門就狗帶了

5萬元左右。為毛人家德國高速路上如果出現什麼事故,救護車都能第一時間暢通無阻的到達現場,車子都會讓出一條路來,看看人家要上的課程吧。德國的考駕照流程,也是分為理論和路考的,3測試、理論課、駕駛課、除此之外還要參加急救培訓課程。

從駕校畢業出來的孩子

你們是否覺得教練最厲害的地方

其實不是他們的技術有多叼

而是他們是比費玉清還要厲害的黃段子

從他們身上你能學到最多的就是說段子

話說國內的駕校都是要經過交通部門管理的,負責監考的也是交管部門,照理來說,強強聯手,門下出來個個都是優秀畢業生。

可是偏偏上路后就出現一些令人驚訝的各種奇葩事情。

收費那麼貴,教出來的是小學生?首先個人角度看,中國駕校的收費算是比較合理的,畢竟一分錢一分貨。

什麼鬼,考完科目一了還他么分不清路牌標識,死記硬背拿了90分,最後還是都還給教練了。

尼瑪,為什麼這個車位我會倒不進去,明明這倒車方法在駕校學得挺6的啊!

長途開車上路訓練為毛都是一些比較偏僻的地方,教練說是比較少車,沒那麼危險,然後吃飯的時候轉臉就去飯店老闆那裡嘿嘿嘿了,回扣杠杠的,我就問一句以後上路我專挑車少的地方走嘛?

什麼,還掛科,叫你沒準備好禮物吧,你看人家誰誰誰買了煙就一次性通過了。

套路,都是套路啊,實際學到的東西並不多,靠得還是後天的積累。

都說德國駕照是世界上難考的幾本駕照之一,和中國到底有什麼差距呢,一起來探討一下吧!

首先看一下人家的教練車和我們的教練車有什麼不一樣,不必羡慕,畢竟人家的費用總共下來要1.5萬元左右。

為毛人家德國高速路上如果出現什麼事故,救護車都能第一時間暢通無阻的到達現場,車子都會讓出一條路來,看看人家要上的課程吧。

德國的考駕照流程,也是分為理論和路考的,3測試、理論課、駕駛課、除此之外還要參加急救培訓課程。

安全意識

在參加理論考試之前,參考人員必須去參加一場約10小時的急救課程,學會各種各樣的急救措施,而且在德國在遇到交通事故的時候,你是有義務要下車對傷者進行急救措施的。

汽車構本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!