Thrift入門及Java實(shí)例演示【轉(zhuǎn)】

概述

Thrift是一個(gè)軟件框架,用來進(jìn)行可擴(kuò)展且跨語(yǔ)言的服務(wù)的開發(fā)。它結(jié)合了功能強(qiáng)大的軟件堆棧和代碼生成引擎,以構(gòu)建在 C++、Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk、and OCaml 等等編程語(yǔ)言間無(wú)縫結(jié)合的、高效的服務(wù)。

Thrift最初由facebook開發(fā),07年四月開放源碼,08年5月進(jìn)入Apache孵化器。Thrift允許你定義一個(gè)簡(jiǎn)單的定義文件中的數(shù)據(jù)類型和服務(wù)接口。以作為輸入文件,編譯器生成代碼用來方便地生成RPC客戶端和服務(wù)器通信的無(wú)縫跨編程語(yǔ)言。

官網(wǎng)地址:thrift.apache.org

下載配置

到官網(wǎng)下載最新版本,截止今日(2016-04-23)最新版本為0.9.3

  • 如果是Maven構(gòu)建項(xiàng)目的,直接在pom.xml 中添加如下內(nèi)容:
<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.9.3</version>
</dependency>
  • 如果自己編譯lib包,把下載的壓縮包解壓到X:盤,然后在X:\thrift-0.9.3\lib\java 目錄下運(yùn)行ant進(jìn)行自動(dòng)編譯,會(huì)在X:\thrift-0.9.3\lib\java\build\ 目錄下看到編譯好的lib包:libthrift-0.9.3.jar

基本概念

數(shù)據(jù)類型

  • 基本類型:
    bool:布爾值,true 或 false,對(duì)應(yīng) Java 的 boolean
    byte:8 位有符號(hào)整數(shù),對(duì)應(yīng) Java 的 byte
    i16:16 位有符號(hào)整數(shù),對(duì)應(yīng) Java 的 short
    i32:32 位有符號(hào)整數(shù),對(duì)應(yīng) Java 的 int
    i64:64 位有符號(hào)整數(shù),對(duì)應(yīng) Java 的 long
    double:64 位浮點(diǎn)數(shù),對(duì)應(yīng) Java 的 double
    string:utf-8編碼的字符串,對(duì)應(yīng) Java 的 String

  • 結(jié)構(gòu)體類型:
    struct:定義公共的對(duì)象,類似于 C 語(yǔ)言中的結(jié)構(gòu)體定義,在 Java 中是一個(gè) JavaBean

  • 容器類型:
    list:對(duì)應(yīng) Java 的 ArrayList
    set:對(duì)應(yīng) Java 的 HashSet
    map:對(duì)應(yīng) Java 的 HashMap

  • 異常類型:
    exception:對(duì)應(yīng) Java 的 Exception

  • 服務(wù)類型:
    service:對(duì)應(yīng)服務(wù)的類

服務(wù)端編碼基本步驟

  1. 實(shí)現(xiàn)服務(wù)處理接口impl
  2. 創(chuàng)建TProcessor
  3. 創(chuàng)建TServerTransport
  4. 創(chuàng)建TProtocol
  5. 創(chuàng)建TServer
  6. 啟動(dòng)Server

客戶端編碼基本步驟

  1. 創(chuàng)建Transport
  2. 創(chuàng)建TProtocol
  3. 基于TTransport和TProtocol創(chuàng)建Client
  4. 調(diào)用Client的相應(yīng)方法

數(shù)據(jù)傳輸協(xié)議

  1. TBinaryProtocol 二進(jìn)制格式
  2. TCompactProtocol 壓縮格式
  3. TJSONProtocol JSON格式
  4. TSimpleJSONProtocol 提供JSON只寫協(xié)議,生成的文件很容易通過腳本語(yǔ)言解析

提示:客戶端和服務(wù)端的協(xié)議要一致

實(shí)例演示

Thrift生成代碼

創(chuàng)建Thrift文件,比如G:\thrift\test\HelloWorld.thrift ,內(nèi)容如下:

namespace java com.thrift.demo
 
service HelloWorldService {
    string sayHello(1:string username)
}

使用從官網(wǎng)提供下載的thrift-0.9.3.exe,運(yùn)用這個(gè)工具生成相關(guān)代碼:

thrift-0.9.3.exe -r -gen java ./HelloWorld.thrift

將生成的HelloWorldService.java 文件復(fù)制到自己測(cè)試的工程中,我的工程是用Maven構(gòu)建的,故在pom.xml中增加如下內(nèi)容:

<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.9.3</version>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-log4j12</artifactId>
 <version>1.7.5</version>
</dependency>

實(shí)現(xiàn)接口Iface

Java代碼:HelloWorldImpl.java

package com.thrift.demo;

import org.apache.thrift.TException;

public class HelloWorldImpl implements HelloWorldService.Iface {

 public HelloWorldImpl() {
 }

 @Override
 public String sayHello(String username) throws TException {
 return "Hi," + username + " welcome to thrift demo world";
 }

}

TSimpleServer服務(wù)端

簡(jiǎn)單的單線程服務(wù)模型,一般用于測(cè)試。
編寫服務(wù)端server代碼:ThriftServer.java

package com.thrift.demo.server;

import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TBinaryProtocol.Factory;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import org.apache.thrift.transport.TTransportFactory;

import com.thrift.demo.service.HelloWorldService;
import com.thrift.demo.service.impl.HelloWorldServiceImpl;

/**
 ************************************************************
 * @類名 ThriftServer
 * 
 * @AUTHOR Neo
 ************************************************************
 */
public class ThriftServerDemo {

    public void startServer() {
        try {
            System.out.println("Starting Thrift Server......");

            TProcessor processor = new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl());

            TServerSocket serverTransport = new TServerSocket(8191);

            TTransportFactory transportFactory = new TFramedTransport.Factory();

            Factory factory = new TBinaryProtocol.Factory();

            TServer.Args tArgs = new TServer.Args(serverTransport);
            tArgs.protocolFactory(factory);
            tArgs.transportFactory(transportFactory);
            tArgs.processor(processor);

            // 簡(jiǎn)單的單線程服務(wù)模型,一般用于測(cè)試
            TServer server = new TSimpleServer(tArgs);

            server.serve();

        } catch (TTransportException e) {
            System.out.println("Starting Thrift Server......Error!!!");
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        ThriftServerDemo server = new ThriftServerDemo();
        server.startServer();
    }

}

編寫客戶端Client代碼:ThriftClientDemo.java

package com.thrift.demo.client;

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

import com.thrift.demo.service.HelloWorldService;
import com.thrift.demo.service.HelloWorldService.Client;

/**
 ************************************************************
 * @類名 ThriftClient
 * 
 * @AUTHOR Neo
 ************************************************************
 */
public class ThriftClientDemo {

    public static void main(String[] args) {
        try {
            TTransport transport = new TFramedTransport(new TSocket("127.0.0.1", 8191, 5000));
            // 協(xié)議要和服務(wù)端一致
            TProtocol protocol = new TBinaryProtocol(transport);

            Client client = new HelloWorldService.Client(protocol);

            transport.open();

            String string = client.sayHello("Neo");

            System.out.println(string);

            transport.close();

        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        }
    }

}

先運(yùn)行服務(wù)端程序,日志如下:

Starting Thrift Server......

再運(yùn)行客戶端調(diào)用程序,日志如下:

Hello World,Hello Thrift!!! Hi:Neo

測(cè)試成功,和預(yù)期的返回信息一致。

TThreadPoolServer 服務(wù)模型

線程池服務(wù)模型,使用標(biāo)準(zhǔn)的阻塞式IO,預(yù)先創(chuàng)建一組線程處理請(qǐng)求。
編寫服務(wù)端代碼:HelloServerDemo.java


package com.thrift.demo.server;

import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;

import com.thrift.demo.service.HelloWorldService;
import com.thrift.demo.service.impl.HelloWorldServiceImpl;

/**
 ************************************************************
 * @類名 HelloServerDemo
 * 
 * @AUTHOR Neo
 ************************************************************
 */
public class HelloServerDemo {
    public static final int SERVER_PORT = 8191;

    public void startServer() {
        try {
            System.out.println("HelloWorld TThreadPoolServer start ....");

            TProcessor tprocessor = new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl());

            TServerSocket serverTransport = new TServerSocket(SERVER_PORT);
            TThreadPoolServer.Args ttpsArgs = new TThreadPoolServer.Args(serverTransport);
            ttpsArgs.processor(tprocessor);
            ttpsArgs.protocolFactory(new TBinaryProtocol.Factory());

            // 線程池服務(wù)模型,使用標(biāo)準(zhǔn)的阻塞式IO,預(yù)先創(chuàng)建一組線程處理請(qǐng)求。
            TServer server = new TThreadPoolServer(ttpsArgs);
            server.serve();

        } catch (Exception e) {
            System.out.println("Server start error!!!");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        HelloServerDemo server = new HelloServerDemo();
        server.startServer();
    }

}

客戶端Client代碼和之前的一樣,只要數(shù)據(jù)傳輸?shù)膮f(xié)議一致即可,客戶端測(cè)試成功,結(jié)果如下:

Hello World,Hello Thrift!!! Hi:Neo

TNonblockingServer 服務(wù)模型

使用非阻塞式IO,服務(wù)端和客戶端需要指定 TFramedTransport 數(shù)據(jù)傳輸?shù)姆绞健?br> 編寫服務(wù)端代碼:HelloServerDemo.java

package com.thrift.demo.server;

import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;

import com.thrift.demo.service.HelloWorldService;
import com.thrift.demo.service.impl.HelloWorldServiceImpl;

/**
 ************************************************************
 * @類名 HelloServerDemo
 * 
 * @AUTHOR Neo
 ************************************************************
 */
public class HelloServerDemo {
    public static final int SERVER_PORT = 8191;

    public void startServer() {
        try {
            System.out.println("HelloWorld TNonblockingServer start ....");

            TProcessor tprocessor = new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl());

            TNonblockingServerSocket tnbSocketTransport = new TNonblockingServerSocket(SERVER_PORT);
            TNonblockingServer.Args tnbArgs = new TNonblockingServer.Args(tnbSocketTransport);
            tnbArgs.processor(tprocessor);
            tnbArgs.transportFactory(new TFramedTransport.Factory());
            tnbArgs.protocolFactory(new TCompactProtocol.Factory());

            // 使用非阻塞式IO,服務(wù)端和客戶端需要指定TFramedTransport數(shù)據(jù)傳輸?shù)姆绞?            TServer server = new TNonblockingServer(tnbArgs);
            server.serve();

        } catch (Exception e) {
            System.out.println("Server start error!!!");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        HelloServerDemo server = new HelloServerDemo();
        server.startServer();
    }
}

編寫客戶端代碼:HelloClientDemo.java

package com.thrift.demo.client;

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

import com.thrift.demo.service.HelloWorldService;

/**
 ************************************************************
 * @類名 HelloClientDemo
 * 
 * @AUTHOR Neo
 ************************************************************
 */
public class HelloClientDemo {

    public static final String SERVER_IP = "127.0.0.1";

    public static final int SERVER_PORT = 8191;

    public static final int TIMEOUT = 30000;

    public void startClient(String userName) {
        TTransport transport = null;
        try {
            transport = new TFramedTransport(new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT));
            // 協(xié)議要和服務(wù)端一致
            TProtocol protocol = new TCompactProtocol(transport);
            HelloWorldService.Client client = new HelloWorldService.Client(protocol);
            transport.open();
            String result = client.sayHello(userName);
            System.out.println("Thrify client result =: " + result);
        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } finally {
            if (null != transport) {
                transport.close();
            }
        }
    }

    public static void main(String[] args) {
        HelloClientDemo client = new HelloClientDemo();
        client.startClient("Neo");

    }
}

客戶端的測(cè)試成功,結(jié)果如下:

Thrify client result =: Hello World,Hello Thrift!!! Hi:Neo

THsHaServer服務(wù)模型

半同步半異步的服務(wù)端模型,需要指定為: TFramedTransport 數(shù)據(jù)傳輸?shù)姆绞健?br> 編寫服務(wù)端代碼:HelloServerDemo.java

package com.thrift.demo.server;

import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;

import com.thrift.demo.service.HelloWorldService;
import com.thrift.demo.service.impl.HelloWorldServiceImpl;

/**
 ************************************************************
 * @類名 HelloServerDemo
 * 
 * @AUTHOR Neo
 ************************************************************
 */
public class HelloServerDemo {

    public static final int SERVER_PORT = 8191;

    public void startServer() {
        try {
            System.out.println("HelloWorld THsHaServer start ....");

            TProcessor tprocessor = new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl());

            TNonblockingServerSocket tnbSocketTransport = new TNonblockingServerSocket(SERVER_PORT);
            THsHaServer.Args thhsArgs = new THsHaServer.Args(tnbSocketTransport);
            thhsArgs.processor(tprocessor);
            thhsArgs.transportFactory(new TFramedTransport.Factory());
            thhsArgs.protocolFactory(new TBinaryProtocol.Factory());

            // 半同步半異步的服務(wù)模型
            TServer server = new THsHaServer(thhsArgs);
            server.serve();

        } catch (Exception e) {
            System.out.println("Server start error!!!");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        HelloServerDemo server = new HelloServerDemo();
        server.startServer();
    }
}

客戶端代碼和上一個(gè)服務(wù)模型的Client中的類似,只要注意傳輸協(xié)議一致以及指定傳輸方式為TFramedTransport。

異步客戶端

編寫服務(wù)端代碼:HelloServerDemo.java

package com.thrift.demo.client;

import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;

import com.thrift.demo.service.HelloWorldService;
import com.thrift.demo.service.impl.HelloWorldServiceImpl;

/**
 ************************************************************
 * @類名 HelloServerDemo
 * 
 * @AUTHOR Neo
 ************************************************************
 */
public class HelloServerDemo {

    public static final int SERVER_PORT = 8191;

    public void startServer() {
        try {
            System.out.println("HelloWorld TNonblockingServer start ....");

            TProcessor tprocessor = new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl());

            TNonblockingServerSocket tnbSocketTransport = new TNonblockingServerSocket(SERVER_PORT);
            TNonblockingServer.Args tnbArgs = new TNonblockingServer.Args(tnbSocketTransport);
            tnbArgs.processor(tprocessor);
            tnbArgs.transportFactory(new TFramedTransport.Factory());
            tnbArgs.protocolFactory(new TCompactProtocol.Factory());

            // 使用非阻塞式IO,服務(wù)端和客戶端需要指定TFramedTransport數(shù)據(jù)傳輸?shù)姆绞?            TServer server = new TNonblockingServer(tnbArgs);
            server.serve();

        } catch (Exception e) {
            System.out.println("Server start error!!!");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        HelloServerDemo server = new HelloServerDemo();
        server.startServer();
    }
}

編寫客戶端Client代碼:HelloAsynClientDemo.java

package com.thrift.demo.client;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.apache.thrift.TException;
import org.apache.thrift.async.AsyncMethodCallback;
import org.apache.thrift.async.TAsyncClientManager;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.transport.TNonblockingSocket;
import org.apache.thrift.transport.TNonblockingTransport;

import com.thrift.demo.service.HelloWorldService;
import com.thrift.demo.service.HelloWorldService.AsyncClient.sayHello_call;

/**
 ************************************************************
 * @類名 HelloAsynClientDemo
 * 
 * @AUTHOR Neo
 ************************************************************
 */
public class HelloClientDemo {

    public static final String SERVER_IP = "127.0.0.1";

    public static final int SERVER_PORT = 8191;

    public static final int TIMEOUT = 30000;

    public void startClient(String userName) {
        try {
            TAsyncClientManager clientManager = new TAsyncClientManager();
            TNonblockingTransport transport = new TNonblockingSocket(SERVER_IP, SERVER_PORT, TIMEOUT);

            TProtocolFactory tprotocol = new TCompactProtocol.Factory();
            HelloWorldService.AsyncClient asyncClient = new HelloWorldService.AsyncClient(tprotocol, clientManager, transport);
            System.out.println("Client start .....");

            CountDownLatch latch = new CountDownLatch(1);
            AsynCallback callBack = new AsynCallback(latch);
            System.out.println("call method sayHello start ...");
            asyncClient.sayHello(userName, callBack);
            System.out.println("call method sayHello .... end");
            boolean wait = latch.await(30, TimeUnit.SECONDS);
            System.out.println("latch.await =:" + wait);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("startClient end.");
    }

    public class AsynCallback implements AsyncMethodCallback<sayHello_call> {

        private CountDownLatch latch;

        public AsynCallback(CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        public void onComplete(sayHello_call response) {
            System.out.println("onComplete");
            try {
                // Thread.sleep(1000L * 1);
                System.out.println("AsynCall result =:" + response.getResult().toString());
            } catch (TException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                latch.countDown();
            }
        }

        @Override
        public void onError(Exception exception) {
            System.out.println("onError :" + exception.getMessage());
            latch.countDown();
        }
    }

    public static void main(String[] args) {
        HelloAsynClientDemo client = new HelloAsynClientDemo();
        client.startClient("Neo");
    }
    
}

先運(yùn)行服務(wù)程序,再運(yùn)行客戶端程序,測(cè)試結(jié)果如下:

Client start .....
call method sayHello start ...
call method sayHello .... end
onComplete
AsynCall result =:Hello World,Hello Thrift!!! Hi:Neo
latch.await =:true
startClient end.

設(shè)計(jì)思路

  • Thrift的Server類型有TSimpleServer、TNonblockingServer、THsHaServer、TThreadedSelectorServer、TThreadPoolServer
  • TSimpleServer是單線程阻塞IO的方式,僅用于demo
  • TNonblockingServer是單線程非阻塞IO的方式,通過java.nio.channels.Selector的select()接收連接請(qǐng)求,但是處理消息仍然是單線程,吞吐量有限不可用于生產(chǎn)
  • THsHaServer使用一個(gè)單獨(dú)的線程處理IO,一個(gè)獨(dú)立的worker線程池處理消息, 可以并行處理所有請(qǐng)求
  • TThreadPoolServer使用一個(gè)專用連接接收connection,一旦接收到請(qǐng)求就會(huì)放入ThreadPoolExecutor中的一個(gè)worker里處理,當(dāng)請(qǐng)求處理完畢該worker釋放并回到線程池中,可以配置線程最大值,當(dāng)達(dá)到線程最大值時(shí)請(qǐng)求會(huì)被阻塞。TThreadPoolServer性能表現(xiàn)優(yōu)異,代價(jià)是并發(fā)高時(shí)會(huì)創(chuàng)建大量線程
  • TThreadedSelectorServer是thrift 0.8引入的實(shí)現(xiàn),處理IO也使用了線程池,比THsHaServer有更高的吞吐量和更低的時(shí)延,與TThreadPoolServer比性能相近且能應(yīng)對(duì)網(wǎng)絡(luò)IO較多的情況
  • 對(duì)于客戶端較少的情況,TThreadPoolServer也有優(yōu)異的性能表現(xiàn),但是考慮到未來SOA可能的高并發(fā),決定采用TThreadedSelectorServer
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自:http://blog.csdn.net/kesonyk/article/details/50924489 ...
    晴天哥_王志閱讀 25,329評(píng)論 2 38
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 34,628評(píng)論 18 399
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,762評(píng)論 11 349
  • 在面對(duì)這個(gè)問題的時(shí)候,其實(shí)我們應(yīng)該首先解決幾個(gè)問題:1.什么是真正的藝術(shù)?2.什么是藝術(shù)家?3.什么是真正的藝術(shù)家...
    六戈島夫閱讀 3,617評(píng)論 0 0

友情鏈接更多精彩內(nèi)容