初探Google Guava_玖富娱乐主管发布


玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。

Guava地点:https://github.com/google/guava

第一次打仗我是在16年春github上,事先在找单机查缓存要领,google guava现在取名是由于JAVA的类库不好用,以是谷歌工程师本身开辟一套,想着google出品必属佳构理念,我们一起来相识一下。

guava在还没做分布式处置惩罚上,单机单整合上大行其道。以是guava在递次机能优化上下了许多的时候,我们称其为单块架构的利器

我以为壮大的有几点:1.鸠合处置惩罚   2.EventBus音讯总线处置惩罚  3.guava cache 单机缓存处置惩罚  4.并发listenableFutrue回调处置惩罚,以下是一切的功用:

1. 基本工具 [Basic utilities]

让运用Java言语变得更温馨

1.1 运用和制止null:null是含糊其词的,会引起令人困惑的毛病,有些时刻它让人很不惬意。许多Guava工具类用疾速失利谢绝null值,而不是盲目地接收

1.2 前置前提: 让要领中的前提搜检更简朴

1.3 罕见Object要领: 简化Object要领完成,如hashCode()和toString()

1.4 排序: Guava壮大的”流通作风对照器”

1.5 Throwables:简化了异常和毛病的流传与搜检

2. 鸠合[Collections]

Guava对JDK鸠合的扩大,这是Guava最成熟和为人所知的局部

2.1 不可变鸠合: 用稳定的鸠合举行防御性编程和机能提拔。

2.2 新鸠合范例: multisets, multimaps, tables, bidirectional maps等

2.3 壮大的鸠合工具类: 供应java.util.Collections中没有的鸠合工具

2.4 扩大工具类:让完成和扩大鸠合类变得更轻易,好比建立Collection的装潢器,或完成迭代器

3. 缓存[Caches]

Guava Cache:当地缓存完成,支撑多种缓存逾期战略

4. 函数式作风[Functional idioms]

Guava的函数式支撑可以或许显着简化代码,但请郑重运用它

5. 并发[Concurrency]

壮大而简朴的笼统,让编写准确的并发代码更简朴

5.1 ListenableFuture:完成后触发还调的Future

5.2 Service框架:笼统可开启和封闭的效劳,资助你保护效劳的状况逻辑

6. 字符串处置惩罚[Strings]

异常有效的字符串工具,包孕支解、衔接、添补等操纵

7. 原生范例[Primitives]

扩大 JDK 未供应的原生范例(如int、char)操纵, 包孕某些范例的无标记情势

8. 区间[Ranges]

可对照范例的区间API,包孕一连和离散范例

9. I/O

简化I/O尤其是I/O流和文件的操纵,针对Java5和6版本

10. 散列[Hash]

供应比Object.hashCode()更庞杂的散列完成,并供应布鲁姆过滤器的完成

11. 事宜总线[EventBus]

宣布-定阅形式的组件通信,但组件不须要显式地注册到其他组件中

12. 数学运算[Math]

优化的、充足测试的数学工具类

13. 反射[Reflection]

Guava 的 Java 反射机制工具类

 

1.Guava EventBus讨论

在设想形式中, 有一种叫做宣布/定阅形式, 即某事宜被宣布, 定阅该事宜的脚色将自动更新。
那末定阅者和宣布者直接耦合, 也就是说在宣布者内要关照定阅者说我这边有器械宣布了, 你收一下。

Observable.just(1).subscribe(new Subsriber(){

    @Override
    public void onCompleted() {
    System.out.println("onCompleted ");
    }

    @Override
    public void onError(Throwable arg0) {
    }

    @Override
    public void onNext(Long arg0) {
        System.out.println("onNext "   arg0);
    }
})

我们可以或许看到, 宣布者宣布一个数字1, 定阅者定阅了这个

而到场eventBus, 宣布者与生产者之间的耦合性就下降了。由于这时刻我们去治理eventbus就可以或许, 宣布者只要向eventbus发送信息就可以或许, 而不须要体贴有若干定阅者定阅了此音讯。模子以下

-玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。-

 

为何说eventBus 是单块架构的利器呢?

起首单块架构就是在一个历程内, 在一个历程内, 我们照样愿望模块与模块之间(功用与功用之间)是松耦合的,而在一个模块中是高度内聚的, 怎样下降肯定的耦合, 使得代码越发有构造, guava eventbus就是支撑历程内通信的桥梁。

设想一下以下营业

我们愿望在数据到来以后, 举行入库, 同时可以或许对数据举行报警展望, 当发作报警了, 可以或许有以下几个行动, 向手机端发送推送, 向web端发送推送, 向手机端发送短信。

在一样平常情况下我们可以或许如许完成: (伪代码以下)

processData(data){
    insertintoDB(data); //实行入库操纵
    predictWarning(data);   // 实行报警展望
}
在predictWarning(data)中{
    if(data reaches warning line){
        sendNotification2App(data); //向手机端发送推送
        sendNotification2Web(data); // 向web端发送推送
        sendSMS2APP(data);      //手机端发送短信
    }
}
在这里我不去讲详细是怎样向web端发送推送, 怎样发送短信。重要用到第三方平台

剖析

入库和报警展望是没有直接联络,或许是不分先后递次的, 同样在报警模块中, 向3个客户端发送信息也应该是没有联络的, 以是以上虽然可以或许完胜利用, 但不相符代码的合理性。

应该是怎样的逻辑呢? 以下图

 

 

当数据事宜触发, 宣布到data EventBus 上, 入库和预警离别定阅这个eventBus, 就会触发这两个事宜, 而在预警事宜中, 将事宜发送到warning EventBus 中, 由以下3个定阅的客户端举行发送音讯。

怎样完成

先来说同步, 即定阅者收到事宜后顺次实行, 下面都是伪代码, 详细的入库细节等我在这里不供应。

@Component
public class DataHandler{
    
    @Subscribe
    public void handleDataPersisist(Data data){
        daoImpl.insertData2Mysql(data);
    }
    
    @Subscribe
    public void predictWarning(Data data){
        if(data is warning){ // pseudo code  若是预警
            Warning warning = createWarningEvent(data);  // 依据data建立一个Warning事宜
            postWarningEvent(warning)
        }
    }
    
    protected postWarningEvent(Warning warning){
        EventBusManager.warningEventBus.post(warning);// 宣布到warning event 上
    }
    
    @PostConstruct   // 由spring 在初始化bean后实行
    public void init(){
        register2DataEventBus();
    }
    
    // 将本身注册到eventBus中
    protected void register2DataEventBus(){
        EventBusManager.dataEventBus.register(this);
    }
    
}

@Component
public class WarningHandler{
    @Subscribe
    public void sendNotification2AppClient(Warning warning){
        JpushUtils.sendNotification(warning);
    }
    @Subscribe
    public void sendSMS(Warning warning){
        SMSUtils.sendSMS(warning);
    }
    @Subscribe
    public void send2WebUsingWebSocket(Warning warning){
        WebsocketUtils.sendWarning(warning);
    }
    
    @PostConstruct   // 由spring 在初始化bean后实行
    public void init(){
        register2WarningEventBus();
    }
    
    // 将本身注册到eventBus中
    protected void register2DataEventBus(){
        EventBusManager.warningEventBus.register(this);
    }
}


/**
 * 治理一切的eventBus
 **/
public class EventBusManager{
    public final static EventBus dataEventBus = new EventBus();
    public final static EventBus warningEventBus = new EventBus();
    
}



简化
// 我们发明每个Handler都要举行注册,
public abstract class BaseEventBusHandler{
    
    @PostConstruct
    public void init(){
        register2EventBus();
    }
    private void register2EventBus(){
        getEventBus().register(this);
    }
    protected abstract EventBus getEventBus();
}
如许在写本身的eventBus只须要

@Component
public class MyEventBus extends BaseEventBusHandler{
    @Override
    protected abstract EventBus getEventBus(){
        retrun EventBusManager.myEventBus;
    }
}

在现在的运用场景下, 同步是我们不愿望的, 异步场景也很轻易完成。
只须要将EventBus 改成
 AsyncEventBus warningEvent = new AsyncEventBus(Executors.newFixedThreadPool(1))
 AsyncEventBus dataEventBus = new AsyncEventBus(Executors.newFixedThreadPool(3))

 

2.鸠合处置惩罚

1.optional空值对照

2.鸠合排序guava

    Integer[] inumber={55,22,33};
        System.out.println(new Ordering<Integer>(){
            @Override
            public int compare(Integer left, Integer right) {
                return left-right;
            }
        }.sortedCopy(Arrays.asList(inumber)));

//java 须要自定义compare

 

3.guava cache 缓存触发机制

营业场景,当某一个文件保存的有效期30分钟后删除;某一个文件轻易凌驾肯定限制。

基于容量的收受接管:

划定缓存项的数量不凌驾流动值,只需运用CacheBuilder.maximumSize(long)。缓存将实验收受接管近来没有运用或总体上很少运用的缓存项。—— 正告:在缓存项的数量到达限制值之前,即缓存项的数量切近亲近限制值时缓存就能够举行收受接管操纵。这个size指的是cache中的条目数,不是内存巨细或是其他.
public class GuavaCacheTest {
    public static void main(String[] args) {
        Cache<Integer, String> cache = CacheBuilder.newBuilder().maximumSize(2).build();
        cache.put(1, "a");
        cache.put(2, "b");
        cache.put(3, "c");
        System.out.println(cache.asMap());
        System.out.println(cache.getIfPresent(2));
        cache.put(4, "d");
        System.out.println(cache.asMap());
    }
}

基于时候的收受接管

guava 供应两种准时收受接管的要领

expireAfterAccess(long, TimeUnit):缓存项在给准时候内没有被读/写访问,则收受接管。请注意这类缓存的收受接管递次和基于巨细收受接管一样。

expireAfterWrite(long, TimeUnit):缓存项在给准时候内没有被写访问(建立或掩盖),则收受接管。若是以为缓存数据总是在固准时刻后变得陈腐不可用,这类收受接管体式格局是可取的。

public class GuavaCacheTest {
    public static void main(String[] args) {
        LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(3, TimeUnit.SECONDS).removalListener(new RemovalListener<Object, Object>() {
            @Override
            public void onRemoval(RemovalNotification<Object, Object> notification) {
                System.out.println("remove key["   notification.getKey()   "],value["   notification.getValue()   "],remove reason["   notification.getCause()   "]");
            }
        }).recordStats().build(
                new CacheLoader<Integer, Integer>() {
                    @Override
                    public Integer load(Integer key) throws Exception {
                        return 2;
                    }
                }
        );
        cache.put(1, 1);
        cache.put(2, 2);
        System.out.println(cache.asMap());
        cache.invalidateAll();
        System.out.println(cache.asMap());
        cache.put(3, 3);
        try {
            System.out.println(cache.getUnchecked(3));
            Thread.sleep(4000);
            System.out.println(cache.getUnchecked(3));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Cache<Integer, Integer> cache = CacheBuilder.newBuilder().maximumSize(100).expireAfterAccess(3, TimeUnit.SECONDS).build();

 4.ListenableFuture 异步回调关照

传统JDK中的Future经由过程异步的体式格局盘算返回效果:在多线程运算中能够或许能够在没有结束返回效果,Future是运转中的多线程的一个援用句柄,确保在效劳实行返回一个Result。

ListenableFuture可以或许许可你注册回调要领(callbacks),在运算(多线程实行)完成的时刻举行挪用,  或许在运算(多线程实行)完成后马上实行。如许简朴的革新,使得可以或许显着的支撑更多的操纵,如许的功用在JDK concurrent中的Future是不支撑的。

ListenableFuture 中的基本要领是addListener(Runnable, Executor), 该要领会在多线程运算完的时刻,指定的Runnable参数传入的工具会被指定的Executor实行。

添加回调(Callbacks)

多半用户喜好运用 Futures.addCallback(ListenableFuture<V>, FutureCallback<V>, Executor)的体式格局, 或许 别的一个版本version(译者注:addCallback(ListenableFuture<V> future,FutureCallback<? super V> callback)),默许是接纳 MoreExecutors.sameThreadExecutor()线程池, 为了简化运用,Callback接纳轻量级的设想.  FutureCallback<V> 中完成了两个要领:

  • onSuccess(V),在Future胜利的时刻实行,依据Future效果来推断。
  • onFailure(Throwable), 在Future失利的时刻实行,依据Future效果来推断。

ListenableFuture的建立

对应JDK中的 ExecutorService.submit(Callable) 提交多线程异步运算的体式格局,Guava 供应了ListeningExecutorService 接口, 该接口返回 ListenableFuture 而响应的 ExecutorService 返回一般的 Future。将 ExecutorService 转为 ListeningExecutorService,可以或许运用MoreExecutors.listeningDecorator(ExecutorService)举行装潢。

ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
ListenableFuture explosion = service.submit(new Callable() {
  public Explosion call() {
    return pushBigRedButton();
  }
});
Futures.addCallback(explosion, new FutureCallback() {
  // we want this handler to run immediately after we push the big red button!
  public void onSuccess(Explosion explosion) {
    walkAwayFrom(explosion);
  }
  public void onFailure(Throwable thrown) {
    battleArchNemesis(); // escaped the explosion!
  }
});

 

 

-玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。