Getting Started

如果有homebrew的话,直接执行以下命令即可,brew会处理相关依赖(https://thrift.apache.org/docs/install/)。

  1. brew install thrift

或者可以从源码安装。
下载tar包 https://thrift.apache.org/download
参考 https://thrift.apache.org/docs/BuildingFromSource

先写一个例子,目录结构如下:

  1. ├── pom.xml
  2. ├── src
  3.    ├── main
  4.       ├── java
  5.       └── resources
  6.    └── test
  7.    └── java
  8. └── thrift
  9. ├── Common.thrift
  10. └── ShopService.thrift

pom.xml中添加以下依赖:

  1. <dependency>
  2. <groupId>org.apache.thrift</groupId>
  3. <artifactId>libthrift</artifactId>
  4. <version>0.10.0</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.projectlombok</groupId>
  8. <artifactId>lombok</artifactId>
  9. <version>1.16.18</version>
  10. <scope>provided</scope>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.slf4j</groupId>
  14. <artifactId>slf4j-simple</artifactId>
  15. <version>1.7.25</version>
  16. </dependency>

thrift目录下创建两个thrift文件:

Common.thrift

  1. namespace java me.kavlez.thrift.service
  2. service BaseService {
  3. string echoServiceName()
  4. }

ShopService.thrift

  1. include "Common.thrift"
  2. namespace java me.kavlez.thrift.service
  3. struct Shop {
  4. 1: required i32 id,
  5. 2: required string name
  6. }
  7. struct Item {
  8. 1: required i32 id,
  9. 2: required string name = "unknown",
  10. 3: required string detail,
  11. 4: required Shop shop
  12. }
  13. service ShopService extends Common.BaseService {
  14. Shop queryShopInfo(1: i32 id),
  15. bool isValidShop(1: Shop shop),
  16. set<Item> queryItems(1: i32 shopId),
  17. }

Thrift提供了多个语言的生成器实现,按照thrift文件生成java类,生成代码命令的用法如下:

  1. thrift -r --gen <language> <Thrift filename>

其中-r即recursive,如果在文件中通过include关键字引用了其他文件,-r选项可以一并生成被引用的文件。

例如上面ShopService.thrift中的:

  1. include Common.thrift

默认情况下,代码会在gen-<language>目录下生成,生成目录可以通过--out指定。

生成后再拷贝有点麻烦,直接生成到代码目录下,在工程目录下执行以下命令:

  1. thrift -r --gen java --out src/main/java thrift/ShopService.thrift

执行后src/main/java/目录下生成me/kavlez/thrift/service/目录,以及4个java文件。

在service目录下创建impl,提供接口实现:

  1. package me.kavlez.thrift.service.impl;
  2. import lombok.extern.slf4j.Slf4j;
  3. import me.kavlez.thrift.service.Item;
  4. import me.kavlez.thrift.service.Shop;
  5. import me.kavlez.thrift.service.ShopService;
  6. import org.apache.thrift.TException;
  7. import java.util.Collections;
  8. import java.util.HashSet;
  9. import java.util.Set;
  10. /**
  11. * Created by Kavlez.Kim@gmail.com
  12. */
  13. @Slf4j
  14. public class ShopServiceImpl implements ShopService.Iface {
  15. @Override
  16. public Shop queryShopInfo(int id) throws TException {
  17. return new Shop(id, "DMC_".concat(String.valueOf(id)));
  18. }
  19. @Override
  20. public boolean isValidShop(Shop shop) throws TException {
  21. return shop != null;
  22. }
  23. @Override
  24. public Set<Item> queryItems(int shopId) throws TException {
  25. if (shopId < 1) {
  26. return Collections.emptySet();
  27. }
  28. Set<Item> items = new HashSet<>();
  29. Shop shop = new Shop(1101, "DMC");
  30. for (int i = 0; i < 8; i++) {
  31. Item item = new Item(shopId + i, "sample_".concat(String.valueOf(shopId + i))
  32. , "this is sample_".concat(String.valueOf(i))
  33. , shop);
  34. items.add(item);
  35. }
  36. return items;
  37. }
  38. @Override
  39. public String echoServiceName() throws TException {
  40. return "alo! this is shop service!";
  41. }
  42. }

除了业务实现,我们需要额外做两件事情——构建Server和Client。

构建Server,也就是为Server指定Transparent、Protocol、Processor:

  1. package me.kavlez.thrift.server;
  2. import lombok.extern.slf4j.Slf4j;
  3. import me.kavlez.thrift.service.ShopService;
  4. import me.kavlez.thrift.service.impl.ShopServiceImpl;
  5. import org.apache.thrift.TProcessor;
  6. import org.apache.thrift.protocol.TCompactProtocol;
  7. import org.apache.thrift.server.TServer;
  8. import org.apache.thrift.server.TSimpleServer;
  9. import org.apache.thrift.transport.TServerSocket;
  10. import org.apache.thrift.transport.TTransportException;
  11. /**
  12. * Created by Kavlez.Kim@gmail.com
  13. */
  14. @Slf4j
  15. public class SimpleServerHolder {
  16. public static TServer buildServer() {
  17. TServerSocket serverSocket = null;
  18. try {
  19. serverSocket = new TServerSocket(8081);
  20. } catch (TTransportException e) {
  21. e.printStackTrace();
  22. }
  23. TProcessor tprocessor = new ShopService.Processor<ShopService.Iface>(new ShopServiceImpl());
  24. TServer.Args tArgs = new TServer.Args(serverSocket);
  25. tArgs.protocolFactory(new TCompactProtocol.Factory());
  26. tArgs.processor(tprocessor);
  27. TServer server = new TSimpleServer(tArgs);
  28. return server;
  29. }
  30. public static void main(String[] args) {
  31. TServer server = SimpleServerHolder.buildServer();
  32. log.info("server ready...");
  33. server.serve();
  34. }
  35. }

相应地,构建Client:

  1. package me.kavlez.thrift.client;
  2. import lombok.extern.slf4j.Slf4j;
  3. import me.kavlez.thrift.service.Item;
  4. import me.kavlez.thrift.service.ShopService;
  5. import org.apache.thrift.TException;
  6. import org.apache.thrift.protocol.TCompactProtocol;
  7. import org.apache.thrift.protocol.TProtocol;
  8. import org.apache.thrift.transport.TSocket;
  9. import org.apache.thrift.transport.TTransport;
  10. import java.util.Set;
  11. /**
  12. * Created by Kavlez.Kim@gmail.com
  13. */
  14. @Slf4j
  15. public class SimpleClientHolder {
  16. private TTransport transport;
  17. public ShopService.Client buildClient(String serverAddr, int serverPort, int timeout) throws TException {
  18. this.transport = new TSocket(serverAddr, serverPort, timeout);
  19. TProtocol protocol = new TCompactProtocol(transport);
  20. transport.open();
  21. ShopService.Client client = new ShopService.Client(protocol);
  22. return client;
  23. }
  24. public static void main(String[] args) {
  25. SimpleClientHolder simpleClientHolder = new SimpleClientHolder();
  26. ShopService.Client client = null;
  27. try {
  28. client = simpleClientHolder.buildClient("localhost", 8081, 1000);
  29. Set<Item> items = client.queryItems(666);
  30. log.info("return items = {}", String.valueOf(items));
  31. } catch (TException e) {
  32. e.printStackTrace();
  33. }
  34. if (null != simpleClientHolder.transport) {
  35. simpleClientHolder.transport.close();
  36. }
  37. }
  38. }

依次运行Server和Client,输出正常。

IDL (Interface Description Language)

提供服务的第一步是用IDL编写Thrift文件,IDL几乎可以描述接口所需的所有元素,接口定义中包括以下内容:

namespace

每个thrift文件都在自己的命名空间中,多个thrift文件可以用同一个命名空间作为标识,并指定要使用的语言的generator。

例如:

  1. namespace java me.kavlez.thrift.service
  2. namespace php tutorial

基本类型

类型 说明
bool 布尔类型
i8 (byte) 8-bit 有符号整型,对应java的byte
i16 16-bit 有符号整型,对应java的short
i32 32-bit 有符号整型,对应java的int
i64 64-bit 有符号整型,对应java的long
double 64-bit 浮点类型,对应java的double
string 字符串
binary Blob (byte array)

结构体

用于定义一个对象类型。

字段默认为optional,可以声明required。
字段可以设置默认值。
结构体之间可以互相引用。
0.9.2开始可以引用自身。

  1. struct Shop {
  2. 1: required i32 id,
  3. 2: required string name
  4. }
  5. struct Item {
  6. 1: required i32 id,
  7. 2: required string name = "unknown",
  8. 3: required string detail,
  9. 4: required Shop shop
  10. }

枚举

值是可选项,枚举不能嵌套;基本上就是K、V的形式,不能描述太复杂的枚举类。

  1. enum Numberz {
  2. ONE = 1,
  3. TWO,
  4. THREE,
  5. FIVE = 5,
  6. SIX,
  7. EIGHT = 8
  8. }

常量

可以自定义常量,像Map、List这样的复杂结构可以用json表示。

  1. const i32 INT_CONST = 1234; // a
  2. const map<string,string> MAP_CONST = {"hello": "world", "goodnight": "moon"}
  3. const list<string> LIST_CONST = ["a","b","c"]

容器类型

不支持异构容器,容器的元素类型必须一致。
元素类型可以是service以外的任何类型。

类型 说明
map<t1,t2> Map from one type to another
list<t1> Ordered list of one type
set<t1> Set of unique elements of one type

自定义异常

语法上和struct相似,生成后的代码,不同语言各有各的实现方式。

  1. exception IllegalShopException {
  2. 1: i32 errorCode,
  3. 2: string message,
  4. 3: Shop shop
  5. }

service

一个函数集合,语法和java定义接口的语法类似,下面是一些例子。

  1. service ThriftTest {
  2. /**
  3. * 无返回,空参数列表
  4. */
  5. void testVoid(),
  6. /**
  7. * 声明返回类型、参数
  8. */
  9. string testString(1: string thing),
  10. /**
  11. * 返回结构体
  12. */
  13. Shop queryShopInfo(1: i32 id),
  14. /**
  15. * 结构体作为参数
  16. */
  17. bool isValidShop(1: Shop shop),
  18. /**
  19. * ...
  20. */
  21. set<Item> queryItems(1: i32 shopId),
  22. /**
  23. * 抛出异常
  24. */
  25. bool changeShopStatus(1: i32 shopId) throws(1: IllegalShopException err),
  26. /**
  27. * 多异常
  28. */
  29. bool changeItemStatus(1: i32 itemId) throws(1: IllegalShopException shopErr,2:IllegalItemException itemErr),
  30. /**
  31. * oneway表示该方法在客户端发起请求后不会等待响应,返回类型必须为void
  32. */
  33. oneway void sendMessage(1:i32 shopId,2:string message)
  34. }

thrift working stack

用Thrift构建服务和客户端,架构如下:

  1. +-------------------+ +-------------------+
  2. | Server | | Client |
  3. | | | |
  4. | +---------------+ | | +---------------+ |
  5. | | | | | | | |
  6. | | your code | | | | your code | |
  7. | +---------------+ | | +---------------+ |
  8. | | Service | | | | Service | |
  9. | | processor | | | | Client | |
  10. | +---------------+ | | +---------------+ |
  11. | | | | | | | |
  12. | | Protocol | | | | Protocol | |
  13. | +---------------+ | | +---------------+ |
  14. | | | | | | | |
  15. | | Transport |<--------->| Transport | |
  16. | +---------------+ | | +---------------+ |
  17. +-------------------+ +-------------------+

生成的接口类中大致包括三样,分别是Iface、Client、Processor。
另外还有Server、Transport、Protocol。

Transport

在RPC框架的语境下谈传输层很容易只想到网络通信,但Transport表述的并不只是网络通信。

不如说Transport是多种IO的抽象,其不仅限于网络IO。

比如,基础的TIOStreamTransport,以及其两个子类,TSocket和TZlibTransport。

TSocket在上面的例子中作为TBinaryProtocol依赖的transport类型,与Server的TServerSocket进行通信。

但后者是封装了InflaterInputStream和DeflaterOutputStream,其InputStream并不要求是SocketInputStream。

从开发角度来讲,如果将一个TMemoryBuffer对象传入Protocol,并以此创建某个service对应的Client,再调用相应接口。

整个过程在代码上并没有什么限制,只是运行时抛出org.apache.thrift.TApplicationException。

Protocol

protocol依赖transport,决定双方以什么协议通信,同时也是通信内容的载体。

org.apache.thrift.protocol.TProtocol中的方法声明里,一系列readXX和writeXX,在具体实现中通常都是通过transport来完成。

以TJSONProtocol为例,其实现的TProtocol的所有write方法都是以几个私有的write方法组织起来。

比如,writeI32和writeI64都是通过私有方法writeJSONInteger,而writeJSONInteger则是由实例化时传入的trasnport进行write。

Processor

构建自己的server时需要在tArgs提供一个Processor,比如本文中的ShopService.Processor。
(p.s. 如果需要提供多个Processor,比如再加一个ItemService,则使用TMultiplexedProcessor即可。)

Server通过Processor执行业务逻辑代码,文件中描述的每个函数作为ProcessFunction子类进行实例化,放入Processor的processMap中。

Server收到请求,从输入的protocol中读取方法名,根据方法名从processMap中拿到对应的ProcessFunction;
通过ProcessFunction的process方法执行业务逻辑,过程大体分为3步:

  • 从protocol读入请求参数,构建参数对象;
  • 传入参数,本地执行业务方法。假设方法名为"getItems",调用结果则为getItems_success;
  • 将结果写入protocol,调用protocol.writeXX;

Client

像本文中,指定Transport和Protocol,构建ShopService.Client,客户端通过Client对象像调用本地方法一样调用queryItems;
在ShopService中,Client类同样实现了ShopService.Iface中的方法,以queryItems为例,其实现如下:

  1. public Shop queryShopInfo(int id) throws org.apache.thrift.TException {
  2. send_queryShopInfo(id);
  3. return recv_queryShopInfo();
  4. }

在send_queryShopInfo,构建该函数对应的xx_args对象,将其写入oprot,并通过oprot.tranport进行flush;

相应地,recv_queryShopInfo就是从iport中读取函数的返回值,构建该函数对应的queryShopInfo_result对象。

Server

将Transport、Protocol和Processor集合在一起就是一个完整的Server,父类TServer提供了唯一的抽象方法——serve()。

以TSimpleServer为例,serve中通过java.lang.ServerSocket的accept获取client Socket并转为client Transport,以此获取相应的Processor、创建相应的inputTransport、outputTransport和iProt、oProt。

(p.s. 默认的TProcessorFactory没有子类,其getProcessor(Transport)和并没有通过transport来获取processor。可以用来扩展,比如用一个server提供多版本服务之类的。)

剩下的工作由Processor进行处理,从iPort读入请求信息并构造TMessage,找到相应的ProcessFunction并执行其process方法,这个在上面说过。

Thrift为TServer提供了3种实现:

  • TSimpleServer: 单线程ServerSocket实现,仅用于测试;

  • TThreadPoolServer: 封装了ThreadPoolExecutor,用内部类WorkerProcess表示单个请求,通过每个WorkerProcess对象的transport获取相应的Processor和Protocol,调用业务代码并返回;

  • AbstractNonblockingServer: 非阻塞server抽象类,其serve()方法即整个过程的skeleton,serve()中调用的方法交给其子类提供具体实现。

    1. public void serve() {
    2. // start any IO threads
    3. if (!startThreads()) {
    4. return;
    5. }
    6. // start listening, or exit
    7. if (!startListening()) {
    8. return;
    9. }
    10. setServing(true);
    11. // this will block while we serve
    12. waitForShutdown();
    13. setServing(false);
    14. // do a little cleanup
    15. stopListening();
    16. }

AbstractNonblockingServer的3个子类,分别为:

  • TNonblockingServer: 实现父类的startThreads(),启动selector线程(也就是SelectAcceptThread,父类声明了protected final Selector selector),开始轮询SelectedKeys,检查状态并进行相应处理:

    1. if (key.isAcceptable()) {
    2. handleAccept();
    3. } else if (key.isReadable()) {
    4. handleRead(key);
    5. } else if (key.isWritable()) {
    6. handleWrite(key);
    7. } else {
    8. LOGGER.warn("Unexpected state in select! " + key.interestOps());
    9. }

    另外,使用TNonblockingServer时transport必须为TFramedTransport,以此保证能正确读取单次方法调用。

  • THsHaServer: "HsHa",即"Half-Sync/Half-Async",是TNonblockingServer的子类。

    工作流程和TNonblockingServer相似,主要区别在与handleRead()
    handleRead中完成读取后,另外一项重要的工作就是requestInvoke(buffer),也就是执行processor.process(iProt,oProt)。

    不过,TNonblockingServer是单线程执行,而THsHaServer则是通过线程池。
    将FrameBuffer装进Invocation(其run方法即frameBuffer.invoke()),提交给线程池处理。

    线程池参数的默认值如下:

    1. corePoolSize = 5;
    2. maximumPoolSize = Integer.MAX_VALUE;
    3. keepAliveTime = 60;
    4. workQueue = new LinkedBlockingQueue<Runnable>();
  • TThreadedSelectorServer: 进一步加强HsHaServer,用一个AcceptThread接收所有连接请求,并担任负载均衡的角色。

    负载均衡的工作由构造器参数中的SelectorThreadLoadBalancer进行,该类只提供了一种实现——对已注册的selector线程列表进行round robin。
    AcceptThread处理连接时,通过SelectorThreadLoadBalancer选出selector线程,将接收到的socketChannel放入selector线程的队列中。

    虽然TThreadedSelectorServer的requestInvoke也是使用线程池进行,但线程池的默认配置和THsHaServer不同,默认时为corePoolSize为5的FixedThreadPool。
    如果corePoolSize小为0,则由caller线程执行。

最后,把之前的例子修改一下,看看效果。

AbstractTServerHolder.java

  1. package me.kavlez.thrift.server;
  2. import org.apache.thrift.server.TServer;
  3. import org.apache.thrift.transport.TTransportException;
  4. public abstract class AbstractTServerHolder {
  5. private TServer tServer;
  6. public abstract TServer build() throws TTransportException;
  7. }

ThreadedSelectorServerHolder.java

  1. package me.kavlez.thrift.server;
  2. import me.kavlez.thrift.service.ShopService;
  3. import me.kavlez.thrift.service.impl.ShopServiceImpl;
  4. import org.apache.thrift.protocol.TBinaryProtocol;
  5. import org.apache.thrift.server.TServer;
  6. import org.apache.thrift.server.TThreadedSelectorServer;
  7. import org.apache.thrift.transport.TFramedTransport;
  8. import org.apache.thrift.transport.TNonblockingServerSocket;
  9. import org.apache.thrift.transport.TNonblockingServerTransport;
  10. import org.apache.thrift.transport.TTransportException;
  11. public class ThreadedSelectorServerHolder extends AbstractTServerHolder {
  12. @Override
  13. public TServer build() throws TTransportException {
  14. TNonblockingServerTransport transport = new TNonblockingServerSocket(8090);
  15. TThreadedSelectorServer.Args args = new TThreadedSelectorServer.Args(transport);
  16. ShopService.Processor<ShopService.Iface> shopServiceProcessor
  17. = new ShopService.Processor<ShopService.Iface>(new ShopServiceImpl());
  18. args.processor(shopServiceProcessor)
  19. .protocolFactory(new TBinaryProtocol.Factory())
  20. .transportFactory(new TFramedTransport.Factory());
  21. TServer server = new TThreadedSelectorServer(args);
  22. return server;
  23. }
  24. }

Launcher.java

  1. package me.kavlez.thrift;
  2. import lombok.extern.slf4j.Slf4j;
  3. import me.kavlez.thrift.client.AbstractShopServiceClientHolder;
  4. import me.kavlez.thrift.client.NonBlockingClientHolder;
  5. import me.kavlez.thrift.client.ShopServiceClientAgent;
  6. import me.kavlez.thrift.server.AbstractTServerHolder;
  7. import me.kavlez.thrift.server.ThreadedSelectorServerHolder;
  8. import me.kavlez.thrift.service.Item;
  9. import me.kavlez.thrift.service.ShopService;
  10. import org.apache.thrift.TException;
  11. import org.apache.thrift.server.TServer;
  12. import org.apache.thrift.transport.TTransportException;
  13. import java.io.FileNotFoundException;
  14. import java.util.Set;
  15. import java.util.concurrent.CountDownLatch;
  16. import java.util.concurrent.ExecutorService;
  17. import java.util.concurrent.Executors;
  18. import java.util.concurrent.Future;
  19. @Slf4j
  20. public class Launcher {
  21. static class TServerClientHolderPair {
  22. private AbstractTServerHolder tServerHolder;
  23. private Class<? extends AbstractShopServiceClientHolder> clientHolderClass;
  24. public TServerClientHolderPair(AbstractTServerHolder tServerHolder, Class<? extends AbstractShopServiceClientHolder> clientHolderClass) {
  25. this.tServerHolder = tServerHolder;
  26. this.clientHolderClass = clientHolderClass;
  27. }
  28. }
  29. public static void main(String[] args) throws InterruptedException, TTransportException, FileNotFoundException {
  30. final AbstractTServerHolder serverHolder = new ThreadedSelectorServerHolder();
  31. final TServer tServer = serverHolder.build();
  32. ExecutorService executorService = Executors.newCachedThreadPool();
  33. Future<?> serverFuture = executorService.submit(new Runnable() {
  34. @Override
  35. public void run() {
  36. tServer.serve();
  37. }
  38. });
  39. Thread.sleep(100);
  40. int times = 10;
  41. final CountDownLatch countDownLatch = new CountDownLatch(times);
  42. class ShopServiceClientTask implements Runnable {
  43. @Override
  44. public void run() {
  45. AbstractShopServiceClientHolder clientHolder = null;
  46. clientHolder = new NonBlockingClientHolder();
  47. try {
  48. ShopService.Iface shopService = new ShopServiceClientAgent(clientHolder.build());
  49. for (int i = 0; i < 1000; i++) {
  50. Set<Item> items = shopService.queryItems(666);
  51. log.info("return items = {}", String.valueOf(items));
  52. }
  53. } catch (TException e) {
  54. log.info("thread name={} get TException", Thread.currentThread().getName(), e);
  55. } finally {
  56. clientHolder.close();
  57. countDownLatch.countDown();
  58. }
  59. }
  60. }
  61. long start = System.currentTimeMillis();
  62. for (int i = 0; i < times; i++) {
  63. executorService.submit(new ShopServiceClientTask());
  64. }
  65. countDownLatch.await();
  66. log.info("used {} ms ", System.currentTimeMillis() - start);
  67. tServer.setShouldStop(true);
  68. tServer.stop();
  69. executorService.shutdown();
  70. }
  71. }

Thrift - 快速入门的更多相关文章

  1. Thrift快速入门

    Thrift 简单示例 2017-01-19 16:47:57 首先通过先面两个示例简单感受一下Thrift(RPC)服务端与客户端之间的通信...... RPC学习----Thrift快速入门和Ja ...

  2. RPC学习----Thrift快速入门和Java简单示例

    一.什么是RPC? RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. RPC协议 ...

  3. thrift系列 - 快速入门

    1.简介           Thrift是当前流行的RPC框架之一,它有强大的代码生成引擎,可以跨语言,轻松解决程序间的通信问题. 本文旨在帮助大家快速入门,若想深入原理,请参见thrift官网:h ...

  4. [转]thrift系列 - 快速入门

    原文: http://blog.csdn.net/hrn1216/article/details/51274934 thrift 介绍,入门例子. thrift 是一个RPC框架,实现跨语言 ---- ...

  5. CMake快速入门教程-实战

    http://www.ibm.com/developerworks/cn/linux/l-cn-cmake/ http://blog.csdn.net/dbzhang800/article/detai ...

  6. 转:CMake快速入门教程-实战

    CMake快速入门教程:实战 收藏人:londonKu     2012-05-07 | 阅:10128  转:34    |   来源   |  分享               0. 前言一个多月 ...

  7. Spark2.x学习笔记:Spark SQL快速入门

    Spark SQL快速入门 本地表 (1)准备数据 [root@node1 ~]# mkdir /tmp/data [root@node1 ~]# cat data/ml-1m/users.dat | ...

  8. 大数据技术之_09_Flume学习_Flume概述+Flume快速入门+Flume企业开发案例+Flume监控之Ganglia+Flume高级之自定义MySQLSource+Flume企业真实面试题(重点)

    第1章 Flume概述1.1 Flume定义1.2 Flume组成架构1.2.1 Agent1.2.2 Source1.2.3 Channel1.2.4 Sink1.2.5 Event1.3 Flum ...

  9. [转帖]Hive 快速入门(全面)

    Hive 快速入门(全面) 2018-07-30 16:11:56 琅琊山二当家 阅读数 4343更多 分类专栏: hadoop 大数据   转载: https://www.codercto.com/ ...

随机推荐

  1. 201521123020《java程序设计》第1周学习总结

    1.本周学习总结 1.对JAVA的历史发展的了解 2.了解Java与C/C++的区别,Java语言所写程序较为繁琐,C/C++较为简洁. 3.安装java和jdk 2.书面作业 Q1.为什么java程 ...

  2. 201521123104《JAVA程序设计》第9周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 1. 常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出 ...

  3. ajax中的suceess函数使用this

    今天在写ajax的时候,后台返回数据给前台,可是总是不能把数据正常显示在页面上... 明明已经进入了success函数了,并且在该代码的前后都能够正常执行.. success: function (r ...

  4. Spring - bean的autowire属性(自动装配)

    当我们要往一个bean的某个属性里注入另外一个bean,我们会使用<property> + <ref/>标签的形式.但是对于大型项目,假设有一个bean A被多个bean引用注 ...

  5. iOS根据域名获取ip地址

    引入头文件 #include <netdb.h> #include <sys/socket.h> #include <arpa/inet.h> //根据域名获取ip ...

  6. ThinkPHP中:RBAC权限控制的实习步骤

    使用版本ThinkPHP3.1.3 第一步,建表及数据 第二步,建关联模型 第三步,控制器使用关联模型.配置文件 第四步,模板显示数据 第一步,建表及数据 在数据库中,建立一个companysvn数据 ...

  7. 【重点突破】——two.js模拟绘制太阳月亮地球转动

    一.引言 自学two.js第三方绘图工具库,认识到这是一个非常强大的类似转换器的工具,提供一套固定的接口,可用在各种技术下,包括:Canvas.Svg.WebGL,极大的简化了应用的开发.这里,我使用 ...

  8. handlebar JS模板使用笔记

    直接上代码: (定义模板) (编译注入) ***知识点*** //数据必须为Json数据(强调:jsonp数据不行,和json是两种数据,jsonp多了callback回调函数来包裹json数据) 遍 ...

  9. myeclipse的快捷键

    ------------------------------------MyEclipse 快捷键1(CTRL)-------------------------------------Ctrl+1 ...

  10. java 学习笔记——类之间的关系之封装、继承与多态的详解

    封装 一个封装的简单例子 封装就是把对象的属性(状态)和方法(行为)结合在一起,并尽可能隐蔽对象的内部细节,成为一个不可分割的独立单位(即对象),对外形成一个边界,只保留有限的对外接口使之与外部发生联 ...