【问题标题】:Gwt performance issue : huge data and celltableGwt 性能问题:庞大的数据和单元格表
【发布时间】:2012-09-25 14:43:25
【问题描述】:

我有一个向我发送大量 DTO 的服务器端服务。我需要将它们放在 CellTable 中。 这就像 10-200 行,我需要同时看到所有内容。

我有一个服务器端日志,用于跟踪我的服务的最后“人造”代码行(就在返回之前)。

最佳情况下,此日志与完全加载的表之间有 2 分钟的间隔。大多数时候,在这个过程中出现了一些问题,这让我陷入了困境。

我有什么选择?

谢谢

编辑: 我尝试了 Andrei 的想法,区分数据下载(rpc 回调)的长度和 celltable 加载的长度。

我想确定一下,这就是我在回调中所做的:

@Override
public void onSuccess(ResponseBean result) {
  Window.alert("success " + new Date().toString());

  resultPanel.updateResults(result);

  Window.alert("celltable " + new Date().toString());
}

第一个警报在约 10 秒后发生。但是第二个永远不会被调用。

这是 updateResults 背后的方法:

public void updateResults(ResponseBean response) {
  dataProvider.getList().clear();
  dataProvider.getList().addAll(response.beans());
  myCellTable.setPageSize(response.beans.size());
}

编辑 2: 我尝试了很多东西。当我尝试用我的数据填充单元格表时会出现问题。 在 Chrome 中,出现了 BSOD 的 chrome 版本。在 Firefox 中,它会静默中断,但不会执行剩余的 js 指令。 如果我尝试在 firebug 中读取控制台,我的 firefox 会使用 ~2.5GB ram 冻结。如果我使用 gwt-runner,它也会冻结。

在我的示例中,只有 4 行(4 个具有多个依赖项的对象)!

我怎样才能找到什么中断和在哪里?

感谢您的帮助:)

最后: 好吧,所以我显然是个白痴。在 getValues() 的某个地方,有一段“业务逻辑”不应该存在,这导致了 OutOfMemory 错误。

尽管如此:

  • 在服务器端跟踪最后一次调用和onSuccess客户端的第一行,看看问题是否与RPC有关
  • 如果您的单元格表中断但只有几行,那么您做了一些您不会引以为豪的事情
  • Scheduler.get().scheduleDeffered 允许您渲染页面,然后执行您的业务逻辑。太酷了。

谢谢大家

【问题讨论】:

  • 那么,这 2 分钟花在哪里了:加载数据还是渲染 CellTable?
  • 好问题!我怎么能看到? (谢谢)
  • GWT.log("我的数据到达" + new Date());然后是渲染 CellTable 的代码,然后是另一个日志条目以标记渲染完成
  • @AndreiVolgin 说了什么,和/或Chrome Speed Tracer
  • 您的代码中不需要 .getList() :) 您是否在表格中使用任何自定义单元格?您在列中的 getValue() 方法中是否有任何逻辑,这取决于其他列中的值?

标签: gwt gwt-celltable


【解决方案1】:

通常我会赌 RPC 序列化是瓶颈,而不是 CellTable 渲染。
但是,您说第一个警报会在 10 秒后出现,这会反对。

使用CellTable 渲染 200 个 DTO 应该足够快。
此外,通过网络发送 200 个 DTO 应该没问题。

我使用CellTable 并通过网络发送了大约 5000 个 DTO,没有任何性能问题(尽管我一次只显示 50 个并使用分页)。

一些想法:

  • 开发模式比生产模式慢得多(尤其是在反序列化方面)。因此也在生产模式下测试性能。
  • 要找出问题的确切原因,请使用 Chrome 开发工具。您可以分析您的代码并查看导致延迟的原因。
  • 您能否在代码中手动创建 200 个虚拟 DTO,并将其传递给您的 updateResults 函数,然后查看性能比较。
  • 您显示多少列?如果您显示许多列,请将其减少到仅一列,看看它如何影响性能。
  • 在 CellTable 列中使用的任何 getter 是否具有昂贵的业务逻辑?

【讨论】:

    【解决方案2】:

    对于这么多数据,您应该使用寻呼机和 AsyncDataProvider。这是我们如何做到这一点的模板。它可能无法满足您的需求,但它会给您一个起点。 DatastoreObjectProxy 从 EntityProxy 扩展而来,并使用请求工厂来获取项目。您可以将您的表添加到此数据提供程序中,然后分页器将处理获取您的数据块。

    public abstract class DaoBaseDataProvider<T extends DatastoreObjectProxy> extends AsyncDataProvider<T> implements SearchProvider<T> {
    
        public static final ProvidesKey<DatastoreObjectProxy> KeyProvider = new ProvidesKey<DatastoreObjectProxy>() {
    
            @Override
            public Long getKey(DatastoreObjectProxy item) {
                return item.getId();
            }
        };
    
        public interface HasBaseDataProvider<T extends DatastoreObjectProxy> {
            public void setDataProvider(DaoBaseDataProvider<T> dataProvider);
    
            public void setSelectionModel(SelectionModel<? super T> selectionModel);
        }
    
        public DaoBaseDataProvider() {
            super(new ProvidesKey<T>() {
                public Long getKey(T item) {
                    // Always do a null check.
                    return (item == null) ? null : item.getId();
                }
            });
        }
    
        @Override
        public void search(String searchText) {
    
            getSearchQuery(searchText).fire(new Receiver<List<T>>() {
    
                @Override
                public void onSuccess(List<T> values) {
                    updateRowData(0, values);
                    updateRowCount(values.size(), true);
                }
            });
        }
    
        public void showAll() {
            updateRowCount(10, false);
        }
    
        @Override
        protected void onRangeChanged(HasData<T> display) {
            final Range range = display.getVisibleRange();
    
            Request<List<T>> request = getRangeQuery(range);
            if (getWithProperties() != null) {
                request.with(getWithProperties());
            }
    
            request.fire(new Receiver<List<T>>() {
    
                @Override
                public void onSuccess(List<T> values) {
                    updateRowData(range.getStart(), values);
                }
            });
        }
    
        public abstract String[] getWithProperties();
    
        public abstract DaoRequest<T> getRequestProvider();
    
        public void remove(final Set<Long> idsToDelete) {
            getDeleteQuery(idsToDelete).fire(new Receiver<Integer>() {
    
                @Override
                public void onSuccess(Integer response) {
                    for (HasData<T> display : getDataDisplays()) {
                        // Force the display to relaod it's view.
                        display.setVisibleRangeAndClearData(display.getVisibleRange(), true);
                    }
                }
            });
        }
    
        /**
         * This can be overridden so that you can change the query.
         * 
         * @param range
         * @return
         */
        protected Request<List<T>> getRangeQuery(Range range) {
            Request<List<T>> request = getRequestProvider().findRange(range.getStart(), range.getLength());
            if (getWithProperties() != null) {
                request.with(getWithProperties());
            }
            return request;
        }
    
        protected Request<List<T>> getSearchQuery(String searchText) {
            Request<List<T>> request = getRequestProvider().search(searchText).with(getWithProperties());
            if (getWithProperties() != null) {
                request.with(getWithProperties());
            }
            return request;
    
        }
    
        protected Request<Integer> getDeleteQuery(Set<Long> idsToDelete) {
            return getRequestProvider().deleteList(idsToDelete);
        }
    }
    

    这是用于获取对象的客户端类。您可以使用自己的 RequestContext 扩展它。

    public interface DaoRequestFactory extends RequestFactory {
    
        @Service(value = DaoBase.class, locator = DaoServiceLocator.class)
        public interface DaoRequest<T extends DatastoreObjectProxy> extends RequestContext {
    
            Request<T> find(Long id);
    
            Request<List<T>> findRange(int start, int end);
    
            Request<Integer> deleteList(Set<Long> idsToDelete);
    
            Request<List<T>> search(String searchText);
    
            Request<Long> save(T obj);
    
            Request<Void> delete(Long objId);
        }
    }
    

    【讨论】:

    • 我不知道它是否真的有帮助。用户需要一次查看所有行,我发现问题是在我收到所有数据后发生的。破坏的是 celltable 的加载。
    • 我认为最好的办法是单步执行代码。我想有一些递归正在进行。您是否尝试过在 eclipse 中调试它并设置一些断点。这应该告诉你你在哪里度过你的时间。此外,由于您只有 4 个具有多个依赖项的对象,您还可以尝试使用数据透视表/单元格网格(在 gwt 中不是本机)来加载更多依赖数据。例如,这适用于数据立方体。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-29
    • 2017-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-09
    相关资源
    最近更新 更多