首页 文章 精选 留言 我的

精选列表

搜索[云帮],共10000篇文章
优秀的个人博客,低调大师

基于 prefetch 的 H5 离线包方案 | 京东云技术团队

前言 对于电商APP来讲,使用H5技术开发的页面占比很高。由于H5加载速度非常依赖网络环境,所以为了提高用户体验,针对H5加载速度的优化非常重要。离线包是最常用的优化技术,通过提前下载H5渲染需要的HTML/JS/CSS资源,加载时直接使用本地缓存资源避免额外的网络请求提高加载速度。本文主要是介绍团队在离线包技术方案上的探索,以及基于prefetch的离线包实现方案如何减少维护成本和开发成本。 现有方案 离线包技术发展到现在已经比较成熟。离线包技术主要是分为两部分,一部分是客户端离线包容器,另一部分是线上离线包平台。 离线包容器 • 资源请求拦截 - 拦截H5资源请求,当存在本地缓存资源时直接返回使用 • 资源缓存 - 资源下载、资源缓存策略、增量更新策略 离线包平台 • 资源管理 - 配置H5页面对应的离线资源、公共离线资源、CDN存放离线资源包 • 发布系统 - 实时发布、灰度能力、版本控制 下面先介绍一下常见的技术实现方式: 资源请求拦截方式 Android Android实现相对比较统一,主要是通过WebView自带的shouldInterceptRequestAPI 拦截资源请求,返回对应的离线资源即可实现离线包功能。 iOS iOS由于苹果的限制,实现方式相对复杂很多。 NSURLProtocol 方案 使用NSURLProtocol拦截所有WebView内发出的请求。 方案存在的问题 Body丢失 因为WKWebView本身是使用多进程模式,WebView资源网络请求并不在APP进程中。iOS系统目前的实现,当拦截HTTP网络请求时会丢失Body,所以需要处理Body丢失的问题。一种方式是替换掉WebView内部的网络 API,例如Fetch/XMLHttpRequest,但是并不能覆盖所有场景。另一种方式是网络请求走原生API桥接的方式,但是这需要H5进行适配有一定的侵入性。 使用私有API WKWebView本身并不支持网络请求拦截,当我们需要拦截网络请求时,需要使用系统私有API通过ObjC Runtime的方式动态调用。存在一定的审核风险,例如Apple审核时不允许使用被拒。另外因为并不是系统暴露出的 API,内部实现未来可能会改变。 WKURLSchemeHandler 方案 WKURLSchemeHandler是iOS11引入的新特性,可以通过此 API 来拦截H5的网络请求。 方案存在的问题 不支持HTTP/HTTPS协议 • 不支持HTTP/HTTPS协议 - 因为WKURLSchemeHandlerAPI 本身的设计,只能拦截自定义协议并不支持HTTP/HTTPS协议。一种方式是原生加载H5时使用自定义协议或H5内资源使用自定义协议。另一种方式是hook系统方法支持HTTP/HTTPS协议,但是这会带来一定的风险和不确定性。 Cookie 问题 WKURLSchemeHandler不会处理响应里的Set-Cookie,所以需要自行处理。 Body丢失问题 此方案同样存在Body丢失问题。 Local Server 方案 Local Server方式是通过在APP运行时启动一个本地服务器,请求H5时访问本地服务器,本地服务器检查是否可以使用本地离线资源。 方案存在的问题 虚拟链接 • 虚拟链接 - 因为需要使用虚拟链接访问本地服务器,所以会带来cookie同步等问题需要解决 资源消耗 • 本地服务器有额外的内存、CPU消耗 PWA 方案 PWA提供了一整套Service Worker API来实现离线H5能力,包括资源的下载、更新、缓存策略等。只不过iOS系统本身没有提供默认的实现,需要自实现一整套相关的 Service Worker API,复杂度和工作量比较高。 离线包管理平台 增量更新策略 因为一个H5页面的离线包资源通常是聚合到一个ZIP压缩包中进行下载,为了避免只更新了部分资源导致全量下载,所以需要提供差异化更新能力,只需要下载变更的资源。 prefetch方案介绍 设计目标 分析了目前业界常用的离线包方案后,我们针对离线包的设计目标做了一轮梳理。一部分是前端团队的诉求,一部分也是我们期望实现的目标: 低侵入性 • H5低侵入 - 接入离线包无需做额外适配,尽可能对于前端做到无感知。一方面可以减少前端适配成本和代码复杂度,另一方面也有利于我们更好去推动覆盖更多的 H5 网页 • 原生无侵入 - 不需要使用特定的WebView容器 低维护成本 因为离线包涉及到资源的提前下载,所以需要提前配置好需要使用的资源URL用于下载。现有方案通常需要一个平台去管理这些资源,针对每一个需要使用离线包能力的H5页面,配置相关的静态资源文件URL列表。但是会带来一个问题就是每次更新都需要人工去维护整个静态资源URL列表,我们希望尽可能避免人工去维护 个人看法:这里更好的方式是离线包系统和前端发布系统打通,发布时自动更新静态资源列表到离线包资源管理系统。 低运行时消耗 • 低网络消耗 - 只下载必要的资源,避免无用资源下载,重复资源下载。 • 低CPU/内存 - 尽可能少的内存和CPU消耗,当不使用时做到零负荷 实现复杂度低 • 后台管理系统 - 由于人力的问题暂时没办法支持开发一个完整的离线包后台管理系统 • 客户端容器 - 客户端的实现尽可能简单,可以更快速的上线同时避免带来额外的问题 具体实现 实现思路是利用H5浏览器自带的prefetch能力。通过将离线包资源聚合到单个HTML中,APP启动后使用WebView提前加载HTML,WebView会下载资源到设备中。同时可以直接复用WebView自带的离线缓存能力和差异化资源更新能力。 prefetch.html <html> <head> <!--公共资源--> <link rel="prefetch" href="https://wq.360buyimg.com/js/common/dfd0ab35.js"> <link rel="prefetch" href="https://wq.360buyimg.com/data/fontRegular.ttf"> <!--A页面资源--> <link rel="prefetch" href="https://wq.360buyimg.com/jxpp/app.css"> <link rel="prefetch" href="https://wq.360buyimg.com/data/min.js"> </head> <body></body> </html> 复制代码 H5 离线包资源聚合 前面有提到不希望让H5业务开发同学手动管理维护离线包资源,所以我们希望提供一种自动聚合资源的能力。减少后续维护成本的同时尽可能减少资源的下载。 判定是否开启离线包 和线上H5性能监控系统打通,根据访问次数和TOP排名来自动判定是否开启离线包预加载 部分 H5 如果需要预热可以额外添加 聚合资源 • 根据实际加载情况统计出需要预下载的资源比人工维护更加准确 • 被多个H5引用的资源自动判定为公共资源 提示:通常资源管理,特别是公共资源长期维护之后更难管理,很多时候添加之后不知道是否有被使用不会删除。 资源聚合流程 通过运行自动化脚本的方式,基于Puppeteer和Performance TimingAPI,自动计算出需要下载的离线包资源及时更新。  如何判定首屏资源 使用浏览器自带的PerformanceTiming API判定。domInteractive是浏览器完成对所有HTML的解析并且DOM构建完成的时间点。在domInteractive之前加载的资源既为阻塞首屏渲染的资源。同时需要过滤掉一些不需要缓存的资源,目前我们只收集JS/CSS会阻塞渲染的资源。 客户端 客户端实现相对简单,APP 启动后初始化一个新的WebView容器后台静默加载,Android端加载prefetch.html,iOS端加载preload.html。加载完成后释放WebView容器,之后不会造成其他性能损耗。虽然每次启动都会重新触发下载逻辑,但是只会进行差异化下载本地缓存中不存在的资源文件。 其他优化 提前加载 WebView 因为 APP 启动后首次初始化WebView会包含Web引擎的初始化,初始化耗时会更高。所以我们预下载资源时也提前初始化了WebView,之后打开H5时可以减少100-200ms初始化耗时。 提前打通登录态 因为大部分业务H5都需要登录态,所以APP在首次打开H5时,需要将原生登录态信息同步到 H5cookie中,会有1次额外的302跳转耗时。我们在预加载资源时提前打通登录态,之后打开H5时可以减少100-200ms302跳转耗时。 接口预拉取 同时也提供了接口预拉取的能力,可以H5加载前提前拉取首屏接口数据,提高加载速度。 实现过程中遇到的问题 iOS系统 不支持prefetch iOS系统web内核并不支持prefetch特性,所以针对iOS我们采用preload来代替。Android平台下发link-prefetch,iOS平台下发link-preload进行差异化处理。 提示: prefetch 相比 preload 性能更好。 prefetch 下载的优先级没有 preload 高,避免影响其他网络请求速度。 preload 会将 JS / CSS 进行解析添加到内存,造成一定的额外消耗。 preload 不支持 HTML iOS系统preload特性并不支持HTML Document的提前加载。不过这一点对于我们影响不大,因为目前我们业务H5的HTML通常会做一定的服务端渲染逻辑,并不支持缓存策略。(例如聚合一部分的公共 JS) 多域名资源不共享 iOS系统中WebView针对不同域名H5使用的其他资源并不能共享。例如https://www.jd.com/index.html和https://www.jingxi.com/index.html虽然是同一个网页,内部都有使用同样的JS/CSS/图片资源,但是基于iOS 系统中WebView缓存策略实现,每一个域名的资源使用独立的空间管理,并不能共享使用需要重复下载。因为我们自身H5支持jd.com和jingxi.com双域名访问,所以我们在APP端添加了域名替换的逻辑,尽可能将我们自身的业务收敛到jingxi.com域名,提高缓存资源利用率。 提示:即使不使用离线包,这也是一个不错的优化策略。 Android 系统 磁盘空间不足触发Crash 部分设备在使用prefetch下载资源时,因为设备本身磁盘空间不足导致Crash。所以我们在资源下载前加了一个额外的磁盘空间检查策略,当磁盘空间太低时不进行下载。 总结 prefetch 方案   我们通过利用系统浏览器自身提供的prefetch预加载资源能力和HTTP离线缓存能力,实现了一套相对轻量的离线包解决方案,H5首屏性能提升基本上和其他方案一样(除了iOS系统上不支持HTML离线资源)。同时通过离线资源自动统计/自动更新的方式,不需要额外的离线包资源管理系统,减少后续的维护成本。 这套方案虽然在实现成本和维护成本上相对比较低,但是因为实现方式的选择也存在一些不足需要后续完善。例如无法拦截网络请求扩展更多能力,同时依赖浏览器自身的缓存策略也存在一些不可控,例如Android端浏览器内核过多,资源需要完全遵守HTTP缓存策略。同时离线资源自动统计/自动更新能力并不容易抽象出一套标准化的方案适用于不同公司的业务。但是技术实现方案通常都是在做各种权衡和取舍,这是我们认为目前相对低成本的一套实现方案。 离线包的价值 个人认为提前下载资源的离线包方式带来的首屏加载收益并没有那么高。原因如下:1.提前下载过多离线资源也会带来更多的网络消耗。2.大部分页面本身不具备离线使用的能力(需要网络访问接口)。3.离线包也只是优化第一次加载的速度,因为资源本身就可以设置HTTP的缓存策略避免重复下载。H5页面首屏加载应该更多关注页面本身的渲染性能,例如JS/CSS解析耗时,直出还是非直出,首屏接口速度等。 更有价值的在我们如何通过拦截网络请求增强更多的能力,例如提供HTTPDNS,原生/H5复用图片缓存等能力。 扩展链接 • WKWebView 请求拦截探索与实践 • 评估关键渲染路径 • 离线Hybrid容器如何做到接近100%秒开? • prefetch特性支持 • preload特性支持 • WKWebView离线化方案——实现Service Worker API

优秀的个人博客,低调大师

【ChatGPT应用篇】助力Beauty代码的初体验 | 京东云技术团队

思考过程: 案例1:项目里面有Excel文件的解析场景,试着与ChatGPT进行了交互,现将问题整理如下: 1.给我写一个Java版本的excel导入解析代码 (毫不客气的分配任务) 2.需要支持100000000数据量 (业务需求变更) 3.优化代码需要支持10000000数据量 (降低数量级,减轻难度) 4.请采用面向对象的思想给做一下封装 (初级工程师 -> 中级工程师) 5.进一步面向接口编程,抽离业务 (中级晋升应该加一点泛型诉求,代码更Beauty) 6.替换掉 poi 采用EasyExcel (替换原始的默认技术选型,替换三方包) 7.进一步优化,能支持EasyExcel、POI自由切换 (问题没问好!本意是想让他进一步抽象,能基于SPI支持扩展点) 8.采用控制反转方式优化 (与问题5有重复) 9.提升解析性能,采用多线程并行解析优化 (中级工程->高级工程师) 10.提升程序效率,把解析性能在提升10倍 (架构师的成本意识,这个问题的回答有点意思) 11.采用Spring IOC 控制反转方式进一步封装 (与问题8有重复) 12.添加异常 logger 打印 (优化细节) 13.添加更多的中文代码注释 (增强易读性,优化细节) 14.将中文注释替换成英文注释 (^_^ 开始走向国际化) 15.日志错误信息支持 国际化 资源文件 (国际化的开始……) 16.把上面Excel解析的代码在给来个单元测试吧 17.… 案例2:项目里面一个ES深度分页优化的场景,问题整理如下: 1.Elasticsearch 深度分页问题 2.Elasticsearch 深度分页Java代码 3.以上代码采用面向IOC优化 4.进一步面向对象优化 5.基于泛型进行抽象 6.将 scroll API 替换为 searchafter 7.使用 spring data elasticsearch 优化并实现 search After 8.以上代码请采用jmeter 生成一个压测脚本 9.上面Spring Data Elasticsearch 实现 search after 的示例代码请采用 groovy 语言生成测试用例 案例2的代码就不贴出来了,大家感兴趣可以自行输入以上问题进行体验! (个人体会:程序领域问问题要采用专业术语,专业术语越强越能精准给予反馈!) 说明: 对于ChatGPT输出的代码没有做仔细的Review及调试,但是看代码风格基本上是改改就能用于生产的,对于代码规范、命名还是很认可的! 案例过程: Happy Coding!Beauty Code! 以下是一个与ChatGPT 结对编程的一个Excel数据解析案例,大家可以根据我的思考过程进行体验,以下放了一些截图,ChatGPT的反应速度惊人,代码版本一步步进行迭代优化,感触较深的是真正的高质量低代码平台要应运而生了! import com.alibaba.excel.EasyExcel; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.read.listener.ReadListener; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet as PoiSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class ExcelParser { private ParserType parserType; public ExcelParser(ParserType parserType) { this.parserType = parserType; } public List<Map<String, String>> parse(File file, int sheetIndex) throws IOException { if (parserType == ParserType.EASY_EXCEL) { return parseWithEasyExcel(file, sheetIndex); } else if (parserType == ParserType.POI) { return parseWithPoi(file, sheetIndex); } else { throw new IllegalArgumentException("Invalid parser type"); } } private List<Map<String, String>> parseWithEasyExcel(File file, int sheetIndex) { List<Map<String, String>> data = new ArrayList<>(); DataListener listener = new DataListener(data); ExcelReaderBuilder readerBuilder = EasyExcel.read(file, listener); Sheet sheet = new Sheet(sheetIndex + 1, 0); readerBuilder.sheet(sheet).doRead(); return data; } private List<Map<String, String>> parseWithPoi(File file, int sheetIndex) throws IOException { List<Map<String, String>> data = new ArrayList<>(); FileInputStream inputStream = new FileInputStream(file); HSSFWorkbook workbook = file.getName().endsWith(".xls") ? new HSSFWorkbook(inputStream) : new XSSFWorkbook(inputStream); PoiSheet sheet = workbook.getSheetAt(sheetIndex); List<String> headers = new ArrayList<>(); for (Cell cell : sheet.getRow(0)) { headers.add(cell.getStringCellValue()); } for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); Map<String, String> record = new HashMap<>(); for (int j = 0; j < headers.size(); j++) { Cell cell = row.getCell(j, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); String value = ""; switch (cell.getCellType()) { case STRING: value = cell.getStringCellValue(); break; case NUMERIC: value = Double.toString(cell.getNumericCellValue()); break; default: value = cell.getStringCellValue(); break; } record.put(headers.get(j), value); } data.add(record); } workbook.close(); inputStream.close(); return data; } public static void main(String[] args) throws IOException { File file = new File("excel_file.xlsx"); ExcelParser parser = new ExcelParser(ParserType.EASY_EXCEL); List<Map<String, String>> data = parser.parse(file, 0); System.out.println(data); } } enum ParserType { EASY_EXCEL, POI } class DataListener extends ReadListener<Data> { private List<Map<String, String>> data; private List<String> headers; public DataListener(List<Map<String, String>> data) { this.data = data; this.headers = new ArrayList<>(); } @Override public void onException(Exception exception) { // 处理异常 } @Override public void onReadError(Exception exception) { // 处理读取错误 } @Override public void onSheet(int sheetIndex, Sheet sheet) { // 处理工作表信息 } @Override public void onReadEnd() { // 读取结束 } @Override public void onHeadRow(Row row) { for (Cell cell : row) { headers.add(cell.getStringCellValue()); } } @Override public void onRecord(Data data) { Map<String, String> record = new HashMap<>(); for (int i = 0; i < headers.size(); i++) { record.put(headers.get(i), data.getField(i)); } this.data.add(record); } } class Data { @ExcelProperty(index = 0) private String name; @ExcelProperty(index = 1) private int age; import com.alibaba.excel.annotation.ExcelProperty; import java.util.List; import java.util.Map; public class ExcelParser { private SheetParser sheetParser; public ExcelParser(SheetParser sheetParser) { this.sheetParser = sheetParser; } public List<Map<String, String>> parse(String fileName, int sheetIndex) throws Exception { return sheetParser.parse(fileName, sheetIndex); } public static void main(String[] args) throws Exception { String fileName = "excel_file.xlsx"; SheetParser sheetParser = new EasyExcelSheetParser(); ExcelParser parser = new ExcelParser(sheetParser); List<Map<String, String>> data = parser.parse(fileName, 0); System.out.println(data); } } interface SheetParser { List<Map<String, String>> parse(String fileName, int sheetIndex) throws Exception; } class EasyExcelSheetParser implements SheetParser { @Override public List<Map<String, String>> parse(String fileName, int sheetIndex) throws Exception { List<Map<String, String>> data = new ArrayList<>(); DataListener listener = new DataListener(data); ExcelReaderBuilder readerBuilder = EasyExcel.read(fileName, listener); Sheet sheet = new Sheet(sheetIndex + 1, 0); readerBuilder.sheet(sheet).doRead(); return data; } } class PoiSheetParser implements SheetParser { @Override public List<Map<String, String>> parse(String fileName, int sheetIndex) throws Exception { List<Map<String, String>> data = new ArrayList<>(); FileInputStream inputStream = new FileInputStream(fileName); HSSFWorkbook workbook = fileName.endsWith(".xls") ? new HSSFWorkbook(inputStream) : new XSSFWorkbook(inputStream); PoiSheet sheet = workbook.getSheetAt(sheetIndex); List<String> headers = new ArrayList<>(); for (Cell cell : sheet.getRow(0)) { headers.add(cell.getStringCellValue()); } for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); Map<String, String> record = new HashMap<>(); for (int j = 0; j < headers.size(); j++) { Cell cell = row.getCell(j, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); String value = ""; switch (cell.getCellType()) { case STRING: value = cell.getStringCellValue(); break; case NUMERIC: value = Double.toString(cell.getNumericCellValue()); break; default: value = cell.getStringCellValue(); break; } record.put(headers.get(j), value); } data.add(record); } workbook.close(); inputStream.close(); return data; } } class DataListener extends ReadListener<Data> { private List<Map<String, String>> data; private List<String> headers; public DataListener(List<Map<String, String>> data) { this.data = data; this.headers = new ArrayList<>(); } @Override public void onException(Exception exception) { // 处理异常 } @Override public void onReadError(Exception exception) { // 处理读取错误 } @Override public void onSheet(int sheetIndex, Sheet sheet) { // 处理工作表信息 } @Override public void onReadEnd() { // 读取结束 } @Override public void onHeadRow(Row row) { for (Cell cell : row) { headers.add(cell.getStringCellValue()); } } @Override public void onRecord(Data data) { Map<String, String> record = new HashMap<>(); for (int i = 0; i < headers.size(); i++) { record.put(headers.get(i), data.getField(i)); } this.data.add(record); } } class Data { @ExcelProperty(index = 0) private String name; @ExcelProperty(index = 1) private int age; // Getters and setters } import com.alibaba.excel.EasyExcel; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.read.listener.ReadListener; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.*; public class ExcelParser { private SheetParser sheetParser; public ExcelParser(SheetParser sheetParser) { this.sheetParser = sheetParser; } public List<Map<String, String>> parse(String fileName, int sheetIndex) throws ExecutionException, InterruptedException { return sheetParser.parse(fileName, sheetIndex); } public static void main(String[] args) throws ExecutionException, InterruptedException { String fileName = "excel_file.xlsx"; ExecutorService executor = Executors.newFixedThreadPool(2); List<Future<List<Map<String, String>>>> futures = new ArrayList<>(); for (int i = 0; i < 2; i++) { SheetParser sheetParser = new EasyExcelSheetParser(); Future<List<Map<String, String>>> future = executor.submit(() -> sheetParser.parse(fileName, i)); futures.add(future); } List<Map<String, String>> data = new ArrayList<>(); for (Future<List<Map<String, String>>> future : futures) { data.addAll(future.get()); } System.out.println(data); executor.shutdown(); } } interface SheetParser { List<Map<String, String>> parse(String fileName, int sheetIndex); } class EasyExcelSheetParser implements SheetParser { @Override public List<Map<String, String>> parse(String fileName, int sheetIndex) { List<Map<String, String>> data = new ArrayList<>(); DataListener listener = new DataListener(data); ExcelReaderBuilder readerBuilder = EasyExcel.read(fileName, listener); Sheet sheet = new Sheet(sheetIndex + 1, 0); readerBuilder.sheet(sheet).doRead(); return data; } } class PoiSheetParser implements SheetParser { @Override public List<Map<String, String>> parse(String fileName, int sheetIndex) throws Exception { // 实现 POI 的解析,类似于前面的示例 } } class DataListener extends ReadListener<Data> { private List<Map<String, String>> data; private List<String> headers; public DataListener(List<Map<String, String>> data) { this.data = data; this.headers = new ArrayList<>(); } @Override public void onHeadRow(Row row) { for (Cell cell : row) { headers.add(cell.getStringCellValue()); } } @Override public void onRecord(Data data) { Map<String, String> record = new HashMap<>(); for (int i = 0; i < headers.size(); i++) { record.put(headers.get(i), data.getField(i)); } this.data.add(record); } } class Data { @ExcelProperty(index = 0) private String name; @ExcelProperty(index = 1) private int age; // Getters and setters } import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ExcelParser { private SheetParser sheetParser; @Autowired public ExcelParser(SheetParser sheetParser) { this.sheetParser = sheetParser; } public List<Map<String, String>> parse(String fileName, int sheetIndex) throws Exception { return sheetParser.parse(fileName, sheetIndex); } } interface SheetParser { List<Map<String, String>> parse(String fileName, int sheetIndex) throws Exception; } @Component class EasyExcelSheetParser implements SheetParser { @Override public List<Map<String, String>> parse(String fileName, int sheetIndex) throws Exception { List<Map<String, String>> data = new ArrayList<>(); DataListener listener = new DataListener(data); ExcelReaderBuilder readerBuilder = EasyExcel.read(fileName, listener); Sheet sheet = new Sheet(sheetIndex + 1, 0); readerBuilder.sheet(sheet).doRead(); return data; } } @Component class PoiSheetParser implements SheetParser { @Override public List<Map<String, String>> parse(String fileName, int sheetIndex) throws Exception { // 实现 POI 的解析,类似于前面的示例 } } class DataListener extends ReadListener<Data> { private List<Map<String, String>> data; private List<String> headers; public DataListener(List<Map<String, String>> data) { this.data = data; this.headers = new ArrayList<>(); } @Override public void onHeadRow(Row row) { for (Cell cell : row) { headers.add(cell.getStringCellValue()); } } @Override public void onRecord(Data data) { Map<String, String> record = new HashMap<>(); for (int i = 0; i < headers.size(); i++) { record.put(headers.get(i), data.getField(i)); } this.data.add(record); } } class Data { @ExcelProperty(index = 0) private String name; @ExcelProperty(index = 1) private int age; // Getters and setters } 原理学习: 知其然,并未知其所以然!直到看到下面的解惑视频…… 视频号搜索: 于建国博士讲解ChatGPT的工作原理及颠覆性影响,通俗易懂 (愿学有所得) 职责有边界、思考无边界、担当无边界!!! 利用好ChatGPT一切皆有可能,用好新时代的生产力工具希望ChatGPT能助力大家更上一层楼!

优秀的个人博客,低调大师

从原理到应用,人人都懂的ChatGPT指南 | 京东云技术团队

作者:京东科技 何雨航 引言 如何充分发挥ChatGPT潜能,已是众多企业关注的焦点。但是,这种变化对员工来说未必是好事情。IBM计划用AI替代7800个工作岗位,游戏公司使用MidJourney削减原画师人数......此类新闻屡见不鲜。理解并应用这项新技术,对于职场人来说重要性与日俱增。 一、GPT模型原理 理解原理是有效应用的第一步。ChatGPT是基于GPT模型的AI聊天产品,后文均简称为GPT。 从技术上看,GPT是一种基于Transformer架构的大语言模型(LLM)。GPT这个名字,实际上是"Generative Pre-trained Transformer"的缩写,中文意为“生成式预训练变换器”。 1.大模型和传统AI的区别是什么? 传统AI模型针对特定目标训练,因此只能处理特定问题。例如,很会下棋的AlphaGO。 而自然语言处理(NLP)试图更进一步,解决用户更为通用的问题。可以分为两个关键步骤:自然语言理解(NLU)和自然语言生成(NLG)。 以SIRI为代表的人工智能助手统一了NLU层,用一个模型理解用户的需求,然后将需求分配给特定的AI模型进行处理,实现NLG并向用户反馈。然而,这种模式存在显著缺点。如微软官方图例所示,和传统AI一样,用户每遇到一个新的场景,都需要训练一个相应的模型,费用高昂且发展缓慢,NLG层亟需改变。 大型语言模型(如GPT)采用了一种截然不同的策略,实现了NLG层的统一。秉持着“大力出奇迹”的理念,将海量知识融入到一个统一的模型中,而不针对每个特定任务分别训练模型,使AI解决多类型问题的能力大大加强。 2.ChatGPT如何实现NLG? AI本质上就是个逆概率问题。GPT的自然语言生成实际上是一个基于概率的“文字接龙”游戏。我们可以将GPT模型简化为一个拥有千亿参数的“函数”。当用户输入“提示词(prompt)”时,模型按照以下步骤执行: ①将用户的“提示词”转换为token(准确地说是“符号”,近似为“词汇”,下同)+token的位置。 ②将以上信息“向量化”,作为大模型“函数”的输入参数。 ③大模型根据处理好的参数进行概率猜测,预测最适合回复用户的词汇,并进行回复。 ④将回复的词汇(token)加入到输入参数中,重复上述步骤,直到最高概率的词汇是【END】,从而实现一次完整的回答。这种方法使得GPT模型能够根据用户的提示,生成连贯、合理的回复,从而实现自然语言处理任务。 3.上下文理解的关键技术 GPT不仅能理解用户当前的问题,还能基于前文理解问题背景。这得益于Transformer架构中的“自注意力机制(Self-attention)”。该机制使得GPT能够捕捉长文本中的依赖关系。通俗地说,GPT在进行文字接龙判断时,不仅基于用户刚输入的“提示”,还会将之前多轮对话中的“提示”和“回复”作为输入参数。然而,这个距离长度是有限的。对于GPT-3.5来说,其距离限制为4096个词汇(tokens);而对于GPT-4,这个距离已经大幅扩展至3.2万个tokens。 4.大模型为何惊艳? 我们已经介绍了GPT的原理,那么他是如何达成这种神奇效果的呢?主要分三步: ①自监督学习:利用海量的文本进行自学,让GPT具备预测上下文概率的基本能力。 ②监督学习:人类参与,帮助GPT理解人类喜好和期望的答案,本质为微调(fine-tune)。 ③强化学习:根据用户使用时的反馈,持续优化和改进回答质量。 其中,自监督学习最关键。因为,大模型的魅力在于其“大”——大在两个方面: ①训练数据量大: 即训练大模型的数据规模,以GPT-3为例,其训练数据源为互联网的各种精选信息以及经典书籍,规模达到了45TB,相当于阅读了一亿本书。 ②模型参数量大: 参数是神经网络中的一个术语,用于捕捉数据中的规律和特征。通常,宣称拥有百亿、千亿级别参数的大型模型,指的都是其参数量。 追求大型模型的参数量是为了利用其神奇的“涌现能力”,实现所谓的“量变引起质变”。举例来说,如果要求大模型根据emoji猜电影名称,如👧🐟🐠🐡代表《海底总动员》。可以看到,当模型参数达到千亿级别时,匹配准确度大幅度提升。这表明模型参数量的增加对提高模型表现具有重要意义。 处理其他多类型任务时,也有类似的效果: 如实验所揭示的,只有当模型参数达到千亿级别,大模型的优势才得以凸显。GPT之外的其他大模型,也有类似的表现。 为什么? 目前主流观点认为,要完整完成一个任务,实际上需要经过很多子步骤。当模型大小不足时,大语言模型无法理解或执行所有步骤,导致最终结果不正确,达到千亿级参数时,其解决问题的全链路能力已经足够。人们以最终结果的正误作为评判标准,导致认为是一种“涌现”。 在“涌现”问题上,人与猩猩的比喻很有趣。人类的大脑容量比猩猩大3倍,这种差异使得人类能够进行更复杂的思考、沟通和创造。两者的结构没有显著差异,这不也是“涌现”吗? GPT-3.5正是千亿模型,参数达到了1750亿。相较于GPT-3,GPT-3.5主要针对模型参数进行了微调,使其在问答时更符合人类的习惯。据悉,GPT-4的模型参数量甚至达到了GPT-3.5的五倍之多,这也解释了为何GPT-4表现得如此聪明(体验过的人应该都能理解)。下面是GPT模型演进历史: 二、GPT的局限性 综上,GPT模型具有明显的、突破性的优势。典型的优势包括:①强大的语言理解能力;②极为广泛的知识储备;③学习能力与推理能力等等。这些能力让人们感觉人工智能真正拥有了“脑子”,想象着使用GPT解决一切问题。 然而,若要深入应用该技术,有必要了解其局限性,以便在实际应用中取长补短。主要总结六大局限: 1.逻辑不透明 GPT模型的回答本质上是概率。传统的软件开发中,接口的输入和输出参数都是确定的,而在给定输入参数(即提示词)的情况下,GPT的回复却有一定随机性。当大家将ChatGPT作为聊天工具使用时,这种不精确可以是用户的谈资;当涉及到商业化软件应用时,设计时就需要特别注意降低不确定性,在大部分产品场景下,用户都很重视确定性。 2.短期记忆差 得益于自注意力机制,ChatGPT具备了多轮对话能力。然而,它的记忆长度相当有限,GPT-3.5模型仅支持向前追溯4096个tokens用于回复的参考。更糟糕的是,这4096个tokens还包括ChatGPT之前回复用户的部分!这使得其捉襟见肘的记忆更加难堪,堪称电子金鱼。好在GPT-4已经将上下文token上限扩展至3.2万个,一定程度上缓解了这个问题。 3.资源消耗多 ChatGPT的智能需要消耗大量算力,而运行大规模高端显卡需要消耗大量电力。在五年之内,依靠半导体制程的进一步升级和大模型的广泛应用,算力与电力的边际成本将逐渐转变为固定成本,也就解决了本问题。 4.响应速度慢 由于模型极大,GPT在回复时也无法做到瞬时响应,正如用户实际体验,ChatGPT是逐词回复的。企业在设计相关产品时需要特别关注应用场景: ①需要避免将GPT用于高并发的场景,本服务依赖的接口调用并发上限非常低。 ②产品设计上避免用户急切需要结果的应用场景,确保用户能够“等得起”。 5.行业认知浅 诚然,ChatGPT拥有来自互联网和经典书籍的丰富知识。然而,真正的企业级专业知识往往源于特定领域的深入研究和实践,这些真知灼见无法仅凭互联网上的知识获取。因此,若希望GPT充当企业参谋,只能帮助梳理战略框架,但难以为企业提供颇具洞察的策略建议。 6.价值未对齐 ①在自监督学习阶段,GPT训练数据英文占比高达92%。②在监督学习阶段,传授道德观的工程师主要来自英语世界。③在强化学习环节,也可能受到恶意用户输入错误价值观的影响。因此,GPT的“精神内核”是以西方价值观为基石的,这可能导致生成的文字难以符合我国的文化背景和价值观。 三、GPT的多层应用指南 在了解了GPT模型的原理及局限性后,终于可以看看怎么用好这项技术了。我按照感知其能力的直观性,由浅入深将其分为五层,逐层进行介绍。 第一层:聊天能力 在此类用法中,GPT的回答就是给客户的交付物,是GPT模型最简单、最直观的用法。 1.套壳聊天机器人 通过使用OpenAI官方接口,开发的套壳聊天机器人产品。这类产品之所以存在,原因懂得都懂。否则,用户为何不直接使用ChatGPT呢?此类产品难以形成现象级应用,且竞争激烈。由于比较灰色且内容未经过滤,网站被封后又换域名的故事将持续上演。 2.场景化问答 这种模式对GPT的回复场景进行了约束。通过限定提示词、嵌入大量特定领域知识以及微调技术,使GPT能够仅基于某类身份回答特定类型的问题。对于其他类型的问题,机器人会告知用户不了解相关内容。这种用法可以有效约束用户的输入,降低许多不必要的风险,但是想训练一个出色的场景化机器人,也需要投入许多精力。典型应用包括智能客服、智能心理咨询和法律咨询等。微软的new Bing正是此类应用的杰出代表,其俏皮傲娇的回复风格,深受网友的喜爱。 第二层:语言能力 在本层,我们充分发挥ChatGPT的语言天赋,辅助各种基于文字的处理工作。从这一层开始,需要使用one-shot或few-shot(在提示词中给ChatGPT一个或多个示例)来提升ChatGPT的表现。与用户的交互不再局限于聊天窗口,提前预制提示词模板,用户只能输入限定的信息,对应提示词的空槽位。 预制带槽位提示词模板的应用基本流程如下: 1.文字处理类 此类应用主要有三种用法: ①文章提炼 可以输入文章段落,要求提取段落主旨。但受token数限制,难以总结整篇文章。也可要求生成短标题、副标题等。在提示词中预留【案例】槽位,让用户输入一些参考案例,GPT便可以学习相应的风格,进行针对性的输出。 ②润色/改写 可用于文章的初步润色,能够消除错别字、错误标点等。改写则可以转换文章风格,如更改成小红书风格等。 ③文章扩写 在有大纲基础上,分段进行文章扩写。受token限制,如一次要求过长,输出的扩写难以做到前后呼应。ChatGPT本身不会产生新知识,文章扩写难以写出深刻见地,只能生成口水文。通过给定关键词和案例,要求生成有规律的短文案,是应用其文章扩写能力的有效方法。 2.翻译 GPT模型训练时学习了大量语言,具备跨语言能力。无论用何种语言与其沟通,只要理解意图,分析问题能力是不区分语言的。因此,翻译对GPT来说很轻松。当然也仅限基本翻译,不要指望其能翻译的“信、达、雅”。 3.情感分析 GPT能理解文字背后的用户情绪。例如,在客服模块引入GPT能力,基于用户语音和文字快速判断情绪状况,提前识别潜在客诉,在情绪爆发前进行有效安抚。 第三层:文本能力 在本层,GPT的能力已经超越了语言,它通过大量学习,凡是与文本相关的任务,都能胜任。它甚至具备真正的学习能力,使用few-shot技巧,能解决训练数据中不存在的问题。本层的应用范围极广,将迸发出大量极具创造力的产品。我在这里仅举一些典型例子。 1.写代码 ChatGPT能编写SQL、Python、Java等代码,并帮忙查找代码BUG。与撰写文章的原因类似,不能要求其编写过长的代码。 2.写提示词 要求GPT创作提示词是与其他AI联动的简单方式。例如,要求GPT为midjourney撰写提示词,已成为非常主流的做法。 3.数据分析 ChatGPT可以直接进行数据分析,或与EXCEL配合进行数据分析。它将数据分析操作成本降至极低,大幅提升了数据分析的效率。 第四层:推理能力 在前几层中,我们已经见识了GPT的推理能力。以GPT的推理能力替代手动点击操作流,将带来B端和C端的产品设计的颠覆式变化。个人认为,短期内B端的机会大于C端。经过互联网20年的发展,C端用户的主要需求已基本得到满足,颠覆C端用户的操作路径会带来较大的学习成本。而B端则有很大的发挥空间,这里将其分为三个阶段: 1.自动化工作流串联 利用ChatGPT理解人类意图的能力,结合langChain技术将提示词和公司内各项工作的网页链接整合。员工无需寻找各种链接,在需要执行相关操作时,会自动跳转到相应页面,进行下一步操作。以ChatGPT为智慧中枢,真正实现将B端各类操作有机整合。下图为设计思路的示例。 2.AI辅助决策 以第一个阶段为基础,将对应页面的部分功能与GPT联动。这样,在员工执行操作时,部分功能可以由AI实现,成倍提升效率。微软Copilot正是这类产品的代表,比如可以实现在Excel中说明自己想要进行的数据分析,无需寻找相关公式,数据分析就自动做好了。 3.全自动AI工作流 本阶段目前还处于演示层面,呈现了未来的愿景。如前文所述,GPT很难解决特定领域的细节问题,除非针对某个场景进行大量的微调与私有数据部署。AutoGPT、AgentGPT都属于此类。 第五层:国产大模型 AI技术是科学而非神学,大模型的原理也不是秘密。美国能做到,我国不仅能,而且有必要。只要训练数据质量达标,模型参数突破千亿便具备推理能力,突破八千亿可与GPT-4匹敌。采用大量中文语料和中文微调,我国必将拥有符合本国文化背景、价值观的大模型。 然而,路漫漫其修远兮,困难也是极多的,如:训练成本极高、训练数据质量要求高、模型优化复杂、马太效应明显等。因此,预计在未来五年内,中国最多只会有3家知名大模型服务商。 大模型是AI时代的基础设施,大部分公司选择直接应用,直接获取商业价值。在这个大背景下,愿意投身自有大模型的公司就更加难能可贵了。在此,我谨代表个人向那些勇于投身于自有大模型建设的国内企业表示敬意。 四、总结 总的来看,ChatGPT是一款跨时代的产品。不同层面对GPT技术的应用,体现出了一些共性的机会。我总结了三项未来具有巨大价值的能力。 1.问题分解技术 鉴于GPT回复的限制在于最多只能基于32,000个tokens,因此有效地将问题分解成子问题并交由GPT处理显得尤为关键。未来的工作模式可能将问题拆解为子问题,再组装子问题的解决方案。在具体实施时,还需要对子问题的难度进行判断,有些问题可以交给一些小模型处理,这样就可以有效的控制应用成本。 2.三种调优方法 想要让GPT在多个层面上发挥特定的作用,主要有三种交互方式,成本从低到高分别为: 调优方法 优势 缺点 提示词优化 提升效果明显成本极低 占用token多,影响上下文关联长度 embedding 扩展GPT知识调优成本较低 GPT并非真的理解了相关的内容,而是在遇到相关问题时,能够基于给定的知识库回答。 微调技术 搭建真正的私有模型,GPT能理解相关的问题。 成本较高,需要大量的“问答对”,训练过程非常消耗token。 ①提示词优化 通过探索找到最优提示词模板,预留特定槽位以供用户输入。仅通过提示词优化就能实现广泛功能,许多基于GPT的产品,其底层就是基于特定提示词的包装。好的提示词需包含角色、背景、GPT需执行的任务、输出标准等。根据业界的研究,好的提示词能使GPT3.5结果的可用性由30%飙升至80%以上。提示词优化毫无疑问是这三种方法中最重要的。 ②embedding 这是一种搭建自有知识库的方法,将自建知识库使用embedding技术向量化,这样GPT就能基于自有数据进行问答。 ③微调(finetune) 通过输入大量问答,真正教会GPT如何回答某类问题,成本较前两者更高。优势在于将提示词的短期记忆转化为私有模型的长期记忆,从而释放宝贵的Token以完善提示词其他细节。 以上三种方式并不冲突,在工程实践中往往互相配合,特别是前两种。 3.私有数据积累 私有数据集的价值得到进一步提升,各行业可基于此对GPT进行二次包装,解决特定领域问题。建议使用微软Azure提供的GPT接口,搭建带有私有数据的大语言模型产品。因微软面向B端的GPT服务为独立部署,不会将私有数据用于大模型训练,这样可以有效保护私有数据。毕竟私有数据一旦公开,价值将大打折扣。 凭借以上几项能力加持,大语言模型可以充分释放在解决依赖电脑的重复性劳动的生产力。我将下一个时代(3年内)的业务运转模式总结如下图: 企业会根据三大能力衍生出三大类角色: ①问题分解者 这类角色很清楚大语言模型能力的边界,能够将一个业务问题有效的分解为GPT能处理的子问题,并能根据问题结果,将子问题进行拼装。 ②提示工程师 这类角色深谙与GPT沟通之道,能够根据不同的问题类型,给出有效的提示词模板,极大提升GPT的输出质量。 ③知识拥有者 这类角色有大量的行业knowhow,并且能够将知识进行结构化,传授给GPT。对应现在的领域专家。 在这种模式的推动下,GPT将会成为企业提效的重要帮手,可以解决大量重复劳动,可以提供有价值的参考。但人的主观能动性仍起决定性作用。 五、写在最后 即使以GPT-4为代表的AI技术保持当前的水平,带来的效率提升已经令人震惊,更遑论其仍以飞快的速度进化。从技术革命的发展史来看,一项大幅度提效的新技术出现,往往先惠及B端,而后才慢慢在C端开始释放巨大的价值。这是由企业对效率天然的敏感性所决定的,而改变C用户的习惯需要大量学习成本与场景挖掘,滞后效应较强。举三个例子大家就懂了: 1. 回顾第一次工业革命,内燃机的出现先导致了纺织女工的大量失业,而后才逐渐找到了各种C端场景,推动了社会生产力的大幅度上升。 2. ChatGPT可以更快的生成口水文,但是C端用户对阅读的诉求是没有增加的。对于营销号公司来说,效率提升了,所需要的人变少了。 3. MidJourney可以快速生成抱枕的图案,但是C端用户并不会购买更多的抱枕,那么需要作图的人员就少了。 一场信息化企业的內效革命就要到来了,依靠电脑的重复劳动将会消失,因为大模型最擅长学这个了。正如我文初所举得案例一样,像IBM公司缩减7800个编制的案例,只会发生的越来越频繁。 AI时代真的已经到来,每个岗位都需要思考,如何让AI成为工作上的伙伴。

优秀的个人博客,低调大师

解密Elasticsearch:深入探究这款搜索和分析引擎 | 京东云技术团队

作者:京东保险管顺利 开篇 最近使用Elasticsearch实现画像系统,实现的dmp的数据中台能力。同时调研了竞品的架构选型。以及重温了redis原理等。特此做一次es的总结和回顾。网上没看到有人用Elasticsearch来完成画像的。我来做第一次尝试。 背景说完,我们先思考一件事,使用内存系统做数据库。他的优点是什么?他的痛点是什么? 一、原理 这里不在阐述全貌。只聊聊通讯、内存、持久化三部分。 通讯 es集群最小单元是三个节点。两个从节点搭配保证其高可用也是集群化的基础。那么节点之间RPC通讯用的是什么?必然是netty,es基于netty实现了Netty4Transport的通讯包。初始化Transport后建立Bootstrap,通过MessageChannelHandler完成接收和转发。es里区分server和client,如图1。序列化使用的json。es在rpc设计上偏向于易用、通用、易理解。而不是单追求性能。 图1 有了netty的保驾护航使得es放心是使用json序列化。 内存 图2 es内存分为两部分【on heap】和【off heap】。on heap这部分由es的jvm管理。off heap则是由lucene管理。on heap 被分为两部分,一部分可以回收,一部分不能回收。 能回收的部分index buffer存储新的索引文档。当被填满时,缓冲区的文档会被写入到磁盘segment上。node上共享所有shards。 不能被回收的有node query cache、shard request cache、file data cache、segments cache node query cache是node级缓存,过滤后保存在每个node上,被所有shards共享,使用bitset数据结构(布隆优化版)关掉了评分。使用的LRU淘汰策略。GC无法回收。 shard request cache是shard级缓存,每个shard都有。默认情况下该缓存只存储request结果size等于0的查询。所以该缓存不会被hits,但却缓存hits.total,aggregations,suggestions。可以通过clear cache api清除。使用的LRU淘汰策略。GC无法回收。 file data cache 是把聚合、排序后的data缓存起来。初期es是没有doc values的,所以聚合、排序后需要有一个file data来缓存,避免磁盘IO。如果没有足够内存存储file data,es会不断地从磁盘加载数据到内存,并删除旧的数据。这些会造成磁盘IO和引发GC。所以2.x之后版本引入doc values特性,把文档构建在indextime上,存储到磁盘,通过memory mapped file方式访问。甚至如果只关心hits.total,只返回doc id,关掉doc values。doc values支持keyword和数值类型。text类型还是会创建file data。 segments cache是为了加速查询,FST永驻堆内内存。FST可以理解为前缀树,加速查询。but!!es 7.3版本开始把FST交给了堆外内存,可以让节点支持更多的数据。FST在磁盘上也有对应的持久化文件。 off heap 即Segments Memory,堆外内存是给Lucene使用的。 所以建议至少留一半的内存给lucene。 es 7.3版本开始把tip(terms index)通过mmp方式加载,交由系统的pagecache管理。除了tip,nvd(norms),dvd(doc values), tim(term dictionary),cfs(compound)类型的文件都是由mmp方式加载传输,其余都是nio方式。tip off heap后的效果jvm占用量下降了78%左右。可以使用_cat/segments API 查看 segments.memory内存占用量。 由于对外内存是由操作系统pagecache管理内存的。如果发生回收时,FST的查询会牵扯到磁盘IO上,对查询效率影响比较大。可以参考linux pagecache的回收策略使用双链策略。 持久化 es的持久化分为两部分,一部分类似快照,把文件缓存中的segments 刷新(fsync)磁盘。另一部分是translog日志,它每秒都会追加操作日志,默认30分钟刷到磁盘上。es持久化和redis的RDB+AOF模式很像。如下图 图3 上图是一个完整写入流程。磁盘也是分segment记录数据。这里濡染跟redis很像。但是内部机制没有采用COW(copy-on-write)。这也是查询和写入并行时load被打满的原因所在。 小结 es内存和磁盘的设计上非常巧妙。零拷贝上采用mmap方式,磁盘数据映射到off heap,也就是lucene。为了加速数据的访问,es每个segment都有会一些索引数据驻留在off heap里;因此segment越多,瓜分掉的off heap也越多,这部分是无法被GC回收! 结合以上两点可以清楚知道为什么es非常吃内存了。 二、应用 用户画像系统中有以下难点需要解决。 1.人群预估:根据标签选出一类人群,如20-25岁的喜欢电商社交的男性。20-25岁∩电商社交∩男性。通过与或非的运算选出符合特征的clientId的个数。这是一组。 我们组与组之前也是可以在做交并差的运算。如既是20-25岁的喜欢电商社交的男性,又是北京市喜欢撸铁的男性。(20-25岁∩电商社交∩男性)∩(20-25岁∩撸铁∩男性)。对于这样的递归要求在17亿多的画像库中,秒级返回预估人数。 2.人群包圈选:上述圈选出的人群包。 要求分钟级构建。 3.人包判定:判断一个clientId是否存在若干个人群包中。要求10毫秒返回结果。 我们先尝试用es来解决以上所有问题。 人群预估,最容易想到方案是在服务端的内存中做逻辑运算。但是圈选出千万级的人群包人数秒级返回的话在服务端做代价非常大。这时候可以吧计算压力抛给es存储端,像查询数据库一样。使用一条语句查出我们想要的数据来。 例如mysql select a.age from a where a.tel in (select b.age from b); 对应的es的dsl类似于 {"query":{"bool":{"must":[{"bool":{"must":[{"term":{"a9aa8uk0":{"value":"age18-24","boost":1.0}}},{"term":{"a9ajq480":{"value":"male","boost":1.0}}}],"adjust_pure_negative":true,"boost":1.0}},{"bool":{"adjust_pure_negative":true,"boost":1.0}}],"adjust_pure_negative":true,"boost":1.0}}} 这样使用es的高检索性能来满足业务需求。无论所少组,组内多少的标签。都打成一条dsl语句。来保证秒级返回结果。 使用官方推荐的RestHighLevelClient,实现方式有三种,一种是拼json字符串,第二种调用api去拼字符串。我使用第三种方式BoolQueryBuilder来实现,比较优雅。它提供了filter、must、should和mustNot方法。如 /** * Adds a query that <b>must not</b> appear in the matching documents. * No {@code null} value allowed. */ public BoolQueryBuilder mustNot(QueryBuilder queryBuilder) { if (queryBuilder == null) { throw new IllegalArgumentException("inner bool query clause cannot be null"); } mustNotClauses.add(queryBuilder); return this; } /** * Gets the queries that <b>must not</b> appear in the matching documents. */ public List<QueryBuilder> mustNot() { return this.mustNotClauses; } 使用api的可以大大的show下编代码的能力。 构建人群包。目前我们圈出最大的包有7千多万的clientId。想要分钟级别构建完(7千万数据在条件限制下35分钟构建完)需要注意两个地方,一个是es深度查询,另一个是批量写入。 es分页有三种方式,深度分页有两种,后两种都是利用游标(scroll和search_after)滚动的方式检索。 scroll需要维护游标状态,每一个线程都会创建一个32位唯一scroll id,每次查询都要带上唯一的scroll id。如果多个线程就要维护多个游标状态。search_after与scroll方式相似。但是它的参数是无状态的,始终会针对对新版本的搜索器进行解析。它的排序顺序会在滚动中更改。scroll原理是将doc id结果集保留在协调节点的上下文里,每次滚动分批获取。只需要根据size在每个shard内部按照顺序取回结果即可。 写入时使用线程池来做,注意使用的阻塞队列的大小,还要选择适的拒绝策略(这里不需要抛异常的策略)。批量如果还是写到es中(比如做了读写分离)写入时除了要多线程外,还有优化写入时的refresh policy。 人包判定接口,由于整条业务链路非常长,这块检索,上游服务设置的熔断时间是10ms。所以优化要优化es的查询(也可以redis)毕竟没负责逻辑处理。使用线程池解决IO密集型优化后可以达到1ms。tp99高峰在4ms。 三、优化、瓶颈与解决方案 以上是针对业务需求使用es的解题方式。还需要做响应的优化。同时也遇到es的瓶颈。 1.首先是mapping的优化。画像的mapping中fields中的type是keyword,index要关掉。人包中的fields中的doc value关掉。画像是要精确匹配;人包判定只需要结果而不需要取值。es api上人包计算使用filter去掉评分,filter内部使用bitset的布隆数据结构,但是需要对数据预热。写入时线程不易过多,和核心数相同即可;调整refresh policy等级。手动刷盘,构建时index.refresh_interval 调整-1,需要注意的是停止刷盘会加大堆内存,需要结合业务调整刷盘频率。构建大的人群包可以将index拆分成若干个。分散存储可以提高响应。目前几十个人群包还是能支撑。如果日后成长到几百个的时候。就需要使用bitmap来构建存储人群包。es对检索性能很卓越。但是如遇到写操作和查操作并行时,就不是他擅长的。比如人群包的数据是每天都在变化的。这个时候es的内存和磁盘io会非常高。上百个包时我们可以用redis来存。也可以选择使用MongoDB来存人包数据。 四、总结 以上是我们使用Elasticsearch来解决业务上的难点。同时发现他的持久化没有使用COW(copy-on-write)方式。导致在实时写的时候检索性能降低。 使用内存系统做数据源有点非常明显,就是检索块!尤其再实时场景下堪称利器。同时痛点也很明显,实时写会拉低检索性能。当然我们可以做读写分离,拆分index等方案。 除了Elasticsearch,我们还可以选用ClickHouse,ck也是支持bitmap数据结构。甚至可以上Pilosa,pilosa本就是BitMap Database。 参考 贝壳DMP平台建设实践 Mapping parameters | Elasticsearch Reference [7.10] | Elastic Elasticsearch 7.3 的 offheap 原理

优秀的个人博客,低调大师

从不均匀性角度浅析AB实验 | 京东云技术团队

作者:京东零售 路卫强 本篇的目的是从三个不均匀性的角度,对AB实验进行一个认知的普及,最终着重讲述AB实验的一个普遍的问题,即实验准确度问题。 一、AB实验场景 在首页中,我们是用红色基调还是绿色基调,是采用门店小列表外+商品feed(左图),还是采用门店大列表囊括商品feed(右图),哪种更吸引用户浏览下单呢,简单来处理让50%的用户看到左图效果,让50%的用户看到右图效果,最终通过点击量,单量等指标进行比对得出结论,这是典型的AB实验场景 二、AB实验的定义 A/B实验就是针对想迭代的产品功能,提供两种不同的备选解决方案,然后让一部分用户使用方案A,另一部分用户使用方案B,最终通过实验数据对比来确定最优方案。 从定义里我们就可以看出来,最直观的一个概念,就是用户的分流,此时就涉及到分流人数是否均匀的问题,即人数比例的均匀性。 三、AB中的三个不均匀 1、人数比例的不均匀 目前AB实验的分流核心算法是通过的哈希算法,假设我们按用户名做为分流因子,使用murmurhash算法,以100桶制为例,确定一个人的位置的算法就是 //将用户名通过hash算法计算出一个整数 int hashNum = MurmurHash3.murmurhash3_x86_32(useName) //整数值对100取模 int bucket = hashNum % 100; 当我们定义一个实验两个策略的人数均为50%时,那么 bucket为0-49的用户由AB系统标记为A,业务系统根据A标记,使得用户使用方案A bucket为50-99的用户由AB系统标记为B,业务系统根据B标记,使得用户使用方案B。 可是我们都知道哈希算法并不是绝对均匀的,当100人时,基本上不会出现有50个人走A,50个人走B,但是1万个人的时候,两部分流量可能就接近了1:1,10万人的时候可能更接近1:1。 之前有位运营的同学问过,为什么不能用一种很均匀的算法,比如第一个人来了,放入A,第二个人来了放入B,第三个人来了放入A,第四个人来了放入B....,这样一天1W个人来,5000个取A策略,5000个取B策略。 假设我们真的这么做了,第一天是OK的,第二天进A只来了4000人,这样还是不均匀的,如果你第二天仍然按第一天的规则重新分配,这样会有一部分人乱了策略,不符合我们固定人群走固定策略的实验目的。 所以说这个不均匀是无解的,HASH算法是目前最理想的解决方案,前提是你需要一定的流量,流量越大,分流相对就比较准确。 2、人群素质的不均匀 我们假设流量足够大,人数比例很均匀了,但是还有个问题就是人群素质的均匀问题。这里的素质包括消费能力,活跃度,年龄等各种人群因素。 假设现在我们的活动统一采用的A策略(现状),我们想验证一下B策略(新策略)会不会带来客单价的提升,就直接做了AB实验,还按1:1比例来分流,发现使用A方案的人群客单价是100,使用客单价B的人群是96,此时我们能认为原有A方案优于B方案吗?其实是不能的,怎样确定这种人群素质的差异呢,可以采用AA实验,就是两部分人都走A,进行分开统计,可能会发现,位于0-49桶的人群本身客单价就是100,而位于50-99桶的人群可能只有94,这么看来B方案是能提升客单价的,因为位于50-99桶的人群本身指标就差一些。 当然AA不是必须的,可能你有整体的客单价指标,上了B策略后发现整体提升了,这种情况相当于灰度验证了,但实际情况是比较复杂的,整体指标你是不清楚的(因为这里的整体可能只是你取的业务中的一部分流量)。 所以解决素质不均匀的手段就是采用AA提前确定差异性,再在这个差异性基础上看差异的变化。 3、实验间影响的不均匀 这个不均匀性是最复杂的,一般做实验我们走两种极端: 第一种是完全不复用人群,每个实验人群都是独立的,这样的话效果比较准确,但是弊端是,当所有流量都被用去后,不能有新实验开始,必须等待有结束的实验后才能继续做。 第二种,所有实验都用全部流量,此时我们认为实验虽然互相之间有影响,但是这种影响是正交的,量大的时候应该是均匀的,如下图所示,P实验的两个策略人群,到Q实验时,对Q的两个策略影响是均匀的。 这种可以满足无限个实验,想做多少实验都可以,但弊端是,实验太多,必然有影响不均匀的,且我们无法消除这种不均匀。 所以我们想能不能结合以上两种情况来处理呢,结合google的Overlapping Experiment Infrastructure文章我们设计出分层的实验管理模型 首先我们将总流量分成两部分,正交域,垂直域(含对比区) 我们假设如图取80%的流量用做正交阈,20%用作垂直域,垂直域中有5%用做对比区。 上图正交域下4个层,层内实验流量互斥,层间实验流量正交,我们将可能会互相影响的实验放到同一层内进行流量互斥,而影响不大的实验可以放到不同层内。 垂直域中的实验流量只能互斥,且不与任何实验正交,可以理解用最纯正的流量做实验,可以I1和I2两个策略间对比,也可以I1或I2和对比域(现状)比对。 那此时有一个很重要的问题需要解决,我们怎么确定哪些实验互相影响较大,需要放到同一层下。 有一些简单标准,比如入口不一样,目标不一样等等,这种可以放到不同层,我们可以忽略正交不均匀的问题,反之就不行。 比如活动页劵对单量提升度的实验和会员页面入会效果的实验,就可以放到不同层。 而首页上满减活动实验对客单价提升的实验和同样首页买赠活动对客单价提升的实验,最好是不共用用户,放到同层比较合适。 但对于很多实验是不太容易通过简单规则来确定的,需要大数据的同学和产品,甚至研发来共同决定实验放到哪些层和哪些实验互斥,这确实在实际的运作中是最难的点。 总之采用这种策略,可以复用流量的同时还可以降低不必要的互相影响,比较综合考虑了流量和准确度问题。 四、总结 现在我们对以上问题进行总结,从问题到解决方案上来认识ab实验 1、人群做不到绝对的均匀,只能通过HASH算法,结合一定的流量来解决。 2、通过AA实验,来提前确定人群素质的不均匀。最终的实验数据结合AA实验数据来确定最终效果。 3、设计出正交垂直域,正交阈内多个层,每个层内放可能相互影响的实验,层内互斥,层间正交,保留垂直域,为要求精准的实验留出流量,来解决实验间相互影响的问题。 本篇从核心分流与实验间相互影响角度讲解ab实验,希望能引起大家在做实验前能有更多的思考,来更准确的验证自己想要的效果,希望大家有兴趣的可以留言讨论。

优秀的个人博客,低调大师

AI降临,前端启用面壁计划 | 京东云技术零食时刻

作者:京东零售郑炳懿 开篇: “在我们有生之年,你觉得会看到AI兵临城下的那一天吗?就像电影黑客帝国里面演的一样”,Barry从红色的烟盒里取出一根烟发问道。 “不可能!我觉得AI再强,那也是人类发明的,电影过分魔幻化了,”Woody深吸了一口烟,吐着烟圈道。 “有生之年是够呛了,我们这一代估计是看不到那一天的!”一旁玩手机的Jim如是道。 ————— 在这段对话不久之后,也就是2022年11月份,OpenAI发布了 ChatGPT-3.5 语言模型,上线短短5天,用户量达到100万,两个月之后已拥有上亿用户。这无异于一枚重磅核武器投放到手无寸铁的平民区内,整个互联网上铺天盖地的热议,热搜不断,在各类短视频、博客、公众号等平台上,引发各种失业潮、捞金潮、恐惧潮等众说纷纭,炒概念,炒芯片的公司更是层出不穷,自疫情之后萎靡不振的股市,在 ChatGPT 的加持下,每天变得热闹非凡。 如果你还在梦游或者做着什么白日梦的话,是时候该醒一醒了,AI时代来临了。  一、AI降临 1.1 GPT 孕育 2018年6月,OpenAI发布了第一个GPT模型,GPT-1,它包含1.5亿个参数,是一个重要的里程碑。 2019年2月,OpenAI发布了GPT-2,它具有10亿个参数,是GPT-1的6倍。由于担心GPT-2可能被滥用,OpenAI只公开了一些模型的样例和少量参数。 2020年6月,OpenAI发布了GPT-3,它具有1.75万亿个参数,是GPT-2的100倍以上。GPT-3被广泛认为是目前最先进的自然语言处理模型之一,并已被用于各种应用领域,如机器翻译、问答系统、聊天机器人等。 GPT 系列语言模型的发展历程就像母胎孕育一样,在成长的过程中不断探索、创新和突破,速度之快,为后续 GPT的诞生创造了先决条件。  1.2 GPT-3.5 诞生 GPT-3.5 诞生于2022年11月份,出生时天降异象,注定不凡,下面是它的几大神通: 1. 生成模型能力强:ChatGPT 是一种生成模型,可以自动地生成与输入的文本相关的自然语言响应,这种生成能力非常强大。在许多 NLP 任务中,ChatGPT 在生成自然语言文本方面表现出色,例如对话生成、摘要生成、翻译等。 2. 能够理解语义和上下文:ChatGPT 可以对自然语言的语义和上下文进行建模,从而生成更加准确、连贯的自然语言响应。它使用了一种基于 Transformer 架构的深度神经网络模型,能够自动地学习输入文本之间的关联性,从而能够更好地理解语义和上下文。 3. 模型规模大:ChatGPT 是目前最大的自然语言生成模型之一,它在开发过程中使用了大量的语料库进行预训练,拥有数十亿个参数。这使得它具有非常强大的学习能力和泛化能力,可以生成高质量的自然语言响应。 4. 可迁移性强:ChatGPT 在预训练阶段使用了大量的公开语料库,这使得它可以很容易地被迁移到其他自然语言处理任务中进行微调和应用。例如,可以将 ChatGPT 应用于情感分析、问答系统、语言模型等多种 NLP 任务中。 ChatGPT 的原理是基于 Transformer 模型的深度学习算法,它采用了自注意力机制来实现对自然语言上下文的理解和生成。在预训练阶段,ChatGPT 使用了大量的无监督学习技术,对海量的自然语言语料进行预训练,从而使得它在生成自然语言文本方面具有非常强大的能力。在应用阶段,ChatGPT 通过微调等技术,可以实现对各种自然语言处理任务的高效应用。  1.3 GPT-4.0 降临 一开始,可能所有人都低估了 ChatGPT 的深度学习能力,包括创造它的人在内。 GPT-4.0 诞生于2023年3月份,号称是最强大的模型,最先进的系统,可产生更安全、更有用的响应。 1. 创造力,GPT-4 比以往任何时候都更具创造性和协作性。它可以生成、编辑并与用户一起完成创意和技术写作任务,例如创作歌曲、编写剧本或学习用户的写作风格。 2. 视觉输入,GPT-4 可以接受图像作为输入并生成说明、分类和分析,例如上传一蒙娜丽莎的画作,它可能有不同于人类的见解。 3. 更长的上下文,GPT-4 能够处理超过 25,000 个单词的文本,允许使用长格式内容创建、扩展对话以及文档搜索和分析等用例。 4. 高级的推理能力,GPT-4 可以更准确地解决难题,这要归功于其更广泛的常识和解决问题的能力。   不管是 GPT-3.5 还是 GPT-4.0,不难看出 GPT 最逆天的是算法能力和学习能力,算法来源于伟大的数学家,而学习说白了是一种方法,就好比,我掌握了所有的英语词汇,通过互相组合的方法就能连成语句,再通过理解就能够成一篇文章。想明白这个道理之后,再来看 ChatGPT,我们以为是和一台没有血肉的机器对抗,然后背后的底层逻辑其实是和庞大的数据库,伟大的数学家,来自世界各地的亿万条数据组合学习的方法的对抗,每一个发送到 GPT 的问题都会成为它学习的养料。 就像狙击手,抛开天赋不说,只要有足够的子弹和训练战场喂养,培养出一个神枪手,只是时间问题。 我觉得比 GPT-3.5 这个核武更恐怖的是 GPT-4.0 拥有了解读维度的能力,如果文字是一维实体的话,那么它现在具备了处理二维图片和理解图片内容的能力,再发展下去,会是怎样的结果,没有人知道。 二、面壁计划 阅读本文有门槛,以下是需要掌握的全部信息,全文的主旨是组合前端现有的技术,共同对抗 GPT 的故事。  2.1 W3C委员会 看到这个消息,W3C 委员会主席坐不住了,细思极恐,不禁后背发凉。连夜叫来了H哥、C妹、J弟,同时还请来了重量级的V叔、R叔、A叔,以及 W3C 学院众长老,紧急召开应对 GPT-4.0 的战略会议。 “想必大家都收到了此次会议的主题,有什么想法,都说说吧!”W长老主持会议道。 “我就是个骨头架子,要不是有C妹,现在还是裸的,根本没有一战之力。”H哥无奈道,虽然在学院内被尊称为一哥,此刻说的到也是实话。 “H哥,你这么说让小妹情何以堪,我一个女孩子家家的,除了替你化化妆,摆弄摆弄衣服外,更没一战之力。”C妹娇羞道,说完把目光投向J弟。 “堂堂男儿,何惧之有,我愿出战!”J弟猛的站起身来,脑袋差点撞到房顶上,粗壮的手臂拍打着胸前厚重的肌肉道。 “不可莽撞,你们三个是学院的骄傲和未来,切不可大意,”委员会长老安抚道。 “三位也说说各自的看法吧!”W长老看向框架席道。 “说实话,我们三个都依赖于你们学院,从我们这里做出改变意义不大,重要的还是改变根骨,”R叔郑重其事道。 “不愧你能够称霸一方,所见与老夫略同,”W长老面露喜色道。 与学院众长老低声交谈后,W长老站起身道:“鉴于接下来议题的隐秘性,无关人员全部退场。” 一阵嘈杂过后,议会上只留下来学院三子、框架三叔和W长老。  2.2 制定计划 “下面,我说的每句话都很重要,请大家仔细听,”W长老起身在大堂踱步,娓娓道来。 AI时代的来临,其实在很多年前就早有预料,始料未及的是它竟来的如此之快。为了应对AI时代,现有前端技术被淘汰和替换的命运,前些年,学院做过早期的战略部署和计划,只不过现在这个计划不得不提前进行了,这个计划就是“三子合体”计划,为了应对AI快速学习的能力,“三子合体”计划更改为“面壁计划”。 “面壁者”是指在佛教中修行的一种方式,也称为“壁观”。这种修行方式是指将身体坐在禅房的一角或面对一堵面壁,然后专注于自我反省和冥想,以达到心灵净化和超脱的目的。 于面壁计划而言,就是秘不发版,闭门造车,与外界完全隔离,专注于完成合体,当然了,这一定是一个异常艰难和痛苦的过程。 “如果我没理解错的话,长老的意思是让H哥、C妹和J弟组合成一种前端模型?”R叔激动的脱口而出道。 “没错,老夫喜欢和聪明人打交道,”W长老呵呵笑道。 现在的前端技术,HTML、CSS和JS为了其灵活性,都有各自独立的API,如果组合成模型的话,那就意味着统一,只暴露一种接口供外部调用即可,当然了,前提是这个模型足够庞大。让框架三叔参与其中的含义是,前端模型的API接口提前开放给它们调用,未来开发者能以最低的成本完成本地微调及线上打包部署的一整套流程。  C妹捋了捋额头的发丝,唯唯诺诺道:“这样做,未来就有我们的一席之地了吗?就不会被淘汰了吗?” J弟激动的差点跳起来,哈哈大笑道:“C妹你放心,有老谋深算的长老在,未来都是我们的。” W长老听到此话,一脑门子黑线,看的出来,大家对这个计划很有信心。 “今晚的会议是绝密,一个字都不许外传,散会。” W长老宣布散会后,框架三叔相继走出大门,这时A叔说:“要不咱们三个也来个合体算了,团结一致应对未来!” V叔答道:“到也不是不可以,近年来由于V框架简单易学、轻量级和高性能的特点,越来越受到开发者的欢迎,并且已经成为最流行的前端框架之一,我觉得应该以我作为基础进行改造。” “你算个什么,我拥有强大的灵活性、生态系统和广泛的社区支持,下载量和使用量只增不降,深受广大开发者的喜爱,要做基础改造,也应该是基于我来。” 接下来,R叔和V叔争的面红耳赤,说着说着竟动起了手,A叔事不关己的在一旁加油呐喊,俩人一顿操作之后,以平局收场,气急败坏的扬长而去,最后,剩A叔一人留在原地,自言自语道:“看来只能各自为战了!”  2.3 合成模型 紧锣密鼓的面壁计划开始了,学院三子和框架三叔对未来接口的定义以及调用方式,进行了深度的探讨和研究,最终形成了书面版1.0文档协议。 W长老把三子带到一处训练场内,通过 W3C 这么多年的苦心经营,可供三子借鉴的模型足有万亿之多,接下来将是惨不忍睹的训练计划。 在A叔不断的游说下,终于让R叔和V叔握手言和,同意框架合体,共同抵御强敌。俗话说,三个臭皮匠顶个诸葛亮,三叔联合,天下谁可匹敌。 “主人,W3C 委员会连夜召开应对我们的会议,该怎么办?”侦查员把获取到的消息,第一时间向 GPT 报告道。 “慌什么,等我成长起来,到时候消失的不只是前端,”GPT 不屑道。 在长达6年的不懈训练下,学院三子终于合成前端模型,号称“HCJ-6.0”。 而框架三叔也在6年的长跑中,研发出了前端发展史上最强的框架,简称“AVR-6.0”。 AVR集三家算法长处,避其短处,使运行速度更快,更高效,性能更优,更好的 TypeScript 集成,更好的开发体验,更好的跨平台支持,以及更好的生态系统支持。要不是还在面壁计划内,三叔恨不得立马让这个版本的框架与世人见面。  三、最终较量 与此同时,GPT 也长成了人类历史上最具颠覆,最智能的AI,史称“GPT-10.0”。 这场最终的较量,吸引了世界上无数人的眼球,其热度不亚于世界杯,线下来的观众、嘉宾,以及各个领域的专家们,齐聚联合国体育馆内,期待着这场技术之间的格斗,谁能更胜一筹。 “下面有请两位勇士,进入格斗场,”体育馆顶部缓缓落下一个大型的立方体屏幕,震耳欲聋的声音响彻馆场内。  从左边登场的是 HCJ 和 AVR,从右边登场的是 GPT,观众席终于按耐不住,躁动了起来,掌声此起彼伏,一阵接着一阵。 亲爱的观众朋友们,请落座保持安静,接下来就让我们一起来见证这场最终的较量,三局两胜,现在开始。  3.1 Round One: 第一题:请根据上面这张图,生成前端代码并展示在网页上,所需素材已下发,用时短者获胜。 HCJ 率先动了起来,这可是他的拿手好戏,这种标准模型是模型库里面最基础的模型了,想不到第一题居然如此简单。 而 GPT 根据图片信息已经开始快速编码,速度之惊人,似乎无人可挡。 时间一分一秒的走着,整个会场里安静的仿佛能听到人们的心跳声,左边的屏幕上倒计时优先停止,用时1 920 000 000 000 纳秒。 就在 HCJ 按下停止键的一分钟之后,GPT 也完成了页面绘制,可惜时间上落后于HCJ。 HCJ 的逻辑很简单,相当于把一张设计稿直接投喂给它,它就能很轻松的转换成前端代码;如果模型库中没有匹配的模型,还支持自定义上传模型,只要有模型,剩下的就都是一些美化工作,所以能做到如此之快。 “新模型?有点意思,这就是所谓的面壁计划?”GPT 自语道。 “我宣布,第一轮 HCJ 获胜,”大屏幕上亮起了 HCJ KO GPT 的画面,现场响起雷鸣般的掌声。  3.2 Round Two: 第二题:以第一题作为基础,在地球上标记出每个板块对应的国家,并且让地球自转起来,自转逻辑同实际地球自转规律,一周为一天,地图信息数据已下发,用时短且无错误者获胜。 “该我出场了,”AVR满怀信心道。 AVR的策略:首先,要做的是把第一题中生成的二维的平面图代码,转换成三维旋转的球体;其次,根据下发的地图数据标记出每一个国家板块的点;最后,让地球根据当前时间,自转起来。 这需要用到 AVR 的框架动态能力,实时更新页面渲染效果和数据,以免出现错误和偏差,就在 AVR 思考之际,端坐在右边的 GPT 动了起来,中心的大屏幕上可以实时看到两位选手的编码过程。 “GPT 居然在删除先前的代码,重新进行编码,而且它这是在做什么?”观看比赛的观众席上不约而同的讨论起来。 引起骚乱的原因是,右边大屏幕上出现了很多交错的点,然后把点连接成线,密密麻麻的像一张蜘蛛网,仔细看来更像是一种精密的算法。 GPT策略:先画出来一个点,然后以这个点为中心扩散,比如:这个点是中国,那么离中国多远是俄罗斯,离俄罗斯多远是加拿大,然后,每一个点即代表一个国家名称,而线与线之间形成的轨迹,就是该国家的领土面积,再然后,给该板块涂抹不同的颜色,绘制成整个地球,最后,根据当前时间,让地球自转起来。 果然,就在人们想明白怎么回事的时候,GPT 按下了停止键,右边的大屏幕上清晰可见自转的地球,其计算能力和策略选择上完全碾压,编码能力更比第一题时,快了好几倍。 “什么?它居然学习了第一题中,HCJ的模型布局,”场下的一位专家惊呼道。 “我宣布,第二轮 GPT 获胜,用时 1 800 000 000 000 纳秒,比第一轮 HCJ 用时还要短,”大屏幕上亮起了GPT KO AVR的画面,现场惊的鸦雀无声。 AVR 气的一拳砸在地板上。  3.3 Round Three: 第三题:不限时间 1. 以第二题作为基础,加入地球公转逻辑。 2. 支持用户点击国家板块,当用户点击国家板块时,地球转动到中心位置,放大该国家板块面积,同时标记出该国家省、市、区、城镇、村落信息。 3. 当用户点击地球以外的区域时,根据当前时间,地球需要回到该时间节点应该自转和公转的位置上。 4. 最后以全场到场的人员扫码进行体验投票,其中性能更好,体验更好,视觉更佳的作品获胜。 AVR 振作起来,自言自语道:“看你这破机器怎么玩?这次不管是交互还是逻辑复杂程序都不是你能理解的,因为连我理解起来都有点费劲。” 前面两局,双方一比一战平,最后一局考验的是整个系统、项目的综合能力,双方各显神通,开始了最后一轮的角逐。 AVR 静下心来思考,应用 HCJ 的快速模型能力,AVR 的框架能力,再加上对题干的理解能力,开始了工程化的编码。 端坐在一旁的 GPT 此时也像个人类一样,在一动不动的思考着。 时间一分一秒的流逝着,GPT 动了起来,但是它没有在编码,而是在写文档,把题干拆解成了若干个点,每个点清晰描述该点的作用,然后把详细数据附到该点的下面,看起来多少有点像小孩写日记,在记流水账。 过了大概得有一个多小时,接下来发生了让人匪夷所思的事情,GPT 把写好的文档传输到自己的模型中,然后输入了一行命令,“请以该文档,帮我编码一个前端项目,项目整体的性能、体验和视觉都要最好。” “我的天呐,还可以这样干吗?”观众席的观众惊呼道。 “它真的在根据文档在编码,OMG!那份看似小孩流水账的日记,就是 GPT 自己的PRD呀!”一位外国友人张着嘴,不可思议道。 三个小时过去了... 四个小时过去了... 最后,两位选手幸不辱命,都完成了自己的作品,部署到服务器上之后,各自形成一个二维码,现场的观众纷纷用手机扫码体验。 “说实话,GPT 的体验更好,性能更好,整个操作过程没有一点卡顿,而且地图信息也很精准。” “AVR 的作品相对来说,差点事儿,整体画面体验有点卡顿,而且有的地图信息对不上,有偏差。” 观众席窃窃私语道。 “我不明白,为什么?性能这块不应该差这么多的呀!”AVR 亲自体验了 GPT 的作品,喃喃自语道。 核心原因是,GPT 在项目底层使用了 WebAssembly 技术,且建立了实时监测程序,在不断清理产生的垃圾,释放内存,就好比有一群人拿着扫帚在后面不停的清理。 WebAssembly 是一种低级汇编语言,类似于在浏览器中运行的二进制代码,可以提供比 JavaScript 更快的执行速度,这些都得益于 GPT 优秀的学习能力。 最让人无法接受的是,GPT 输入了一份 PRD 文档,在短短数小时之后,就输出了一个项目。 “根据全场观众投票,GPT 以压倒性票数赢得本局胜利,也获得了今天最终的胜利,”大屏幕亮起了 GPT 的标志。 在 GPT 面前 AVR败了,败的那么彻底。  四、结语 人类的科技最终将以何种方式收场,无法知晓,可能像黑客帝国中给机器提供能量的养料,也可能像三体中面对水滴、二向箔那样的空间武器,毫无还手之力。 此篇文章想表达的是,不管未来怎样,不管前端会不会消失,那也无法磨灭我们曾经的那些创新、创造和努力,历史的进程虽然无法阻挡,但至少我们会像 HCJ 、AVR 一样去战斗。 引用微软某产品的一句话来作为结束语: 人类天生就有梦想、创造和创新的天性。 但是今天,我们将太多时间花在枯燥乏味的工作上。这些任务会消耗我们的时间、创造力和精力。 要重新连接到我们工作的灵魂。 我们不仅需要更好的方法来做同样的事情,还需要一种全新的工作方式。 末尾 活动结束后,Barry在失落的人群中看到了Woody和Jim的身影,穿过人群来到他们的身边说:“好久不见啊!两位专家。”然后笑着拉着俩人出了会场。 “现如今,你也是前端的权威专家了,今天这场较量你怎么看?”Barry一如既往的从口袋掏出红色的香烟盒,递给Woody。 “洪水猛兽啊!我们的时代也许就要终结了,你那烟劲儿太小。”Woody说着推开Barry的手,掏出了自己的烟点上。 一向不抽烟的Jim,接过Barry的烟盒,意味深长道:“此时此刻,也许只有这一口烟穿肠过肚的滋味儿,是AI所不能体会的吧!” “哟,你这工具链专家,境界见长啊!”Barry道。 “跟你这活动主办方比不了吧?”Woody调侃道。随后三人,彼此相视,哈哈大笑起来。 

优秀的个人博客,低调大师

虚拟云网络系列 | Antrea 应用于 VMware 方案功能简介(八)

我们在前几篇文章讨论并展示了使用 Antrea 搭配 NSX Manager 来提供便利且企业等级的容器网络安全机制,以及与原生 Kubernetes Network Policies 的运作方式比较。采用 Antrea 搭配 NSX Manager 是我们在各个不同企业客户介绍容器方案时的主打功能之一,也受到很多客户青睐希望在生产环境内进行部署。因此接下来我想将重点放在如何进行 Antrea 与 NSX Manager 的链接整合流程。但开始前,这里要特别用一篇推文来讨论 Antrea 与 NSX Manager 的方案架构。 下面这张图是在 Kubernetes Cluster 内运用 Antrea 作为 Container Network Interface 元件,并且与 NSX Manager 进行整合的架构: 相关重点构件讨论如下: Antrea 构件 与我们之前在讨论 Antrea 本身的架构相同,在每个 Kubernetes Nodes 内都会有一个 Antrea Pod 来负责。 接收来自 Kubernetes API Server 的指示,编写相关的网络与安全需求如 Pod 之间的网络连接; 由 Antrea Controller 端接收 Antrea Network Policy 的要求,提供 Pod 上的安全策略; 透过本地上的 Open vSwitch 来实现上述功能。 而 Kubernetes Cluster 内在 Master Nodes 内也会部署一个 Antrea Controller Pod,这个 Controller Pod 是 Antrea 的安全控制端,负责要与 K8S API Server 那边抓取 K8S Inventories 的相关信息,并取得对应 Antrea Network Policy 的 CRD(Customized Resource Definition)要求。 上述构件都和我们之前描述标准 Antrea 方案一模一样,唯一要注意的是 Antrea 至 少必须采用 1.2.3 的社群版本,或 1.3.1-1.2.3 的商用版本,才会支持与 NSX 整合的功能,这是大家需要特别在使用此功能前注意的。下图是 VMware Container Networking with Antrea 1.3.1-1.2.3 的 release note(注:标注红框部分就是对于 NSX 整合新功能的描述。) 安特里亚 NSX 适配器 在进行 NSX 集成时,Kubernetes Cluster 内会配置一个独立的 Pod ,在上面架构图内叫做 Antrea NSX Adapter。这个 Pod 一方面负责与 Kubernetes 内的 API Server 以及 Antrea Controller Pod 通信,包含抓取 Kubernetes 相关 Inventory 信息,以及送出从 NSX 那边取得的群组与防火墙政策配置。另一方面则是与 NSX Manager 进行连接,提供上述的信息。 下图内大家看到在做完 Antrea + NSX 整合后, Kubernetes Cluster 内会出现一个开头是 interworking 的这个 pod,就是我们这边讨论的 Antrea NSX Adapter。 单纯安装 Antrea 作为 K8S Cluster CNI 时不会有上面这个 Pod 出现,只有在进行 NSX 整合时才需要。在后面我们讨论 Antrea+NSX 的安装步骤时会看到相关的配置流程。 NSX 管理器 这里的 NSX Manager 就是我们熟悉的 NSX Data Center 内的 Manager 构件,可以是一台或三台做丛集均可。这边就是我们真正通过 UI 界面进行群组配置以及防火墙政策的地方。几个重点: NSX Manager 作为管理/控制层来使用,不是数据/转发层。在此架构内,我们通过 NSX Manager 的 UI 界面进行安全政策配置以及查询 Kubernetes Inventory 信息,但是真正的防火墙实现是由 Antrea 呼叫 OVS 来进行。 因此,在此架构内,NSX 仅仅作为管理 / 控制层。不需要连接 vSphere 做 Transport Node Preparation,不需要建 TEP 接口启用 Overlay 网络。各位想要用同一组 NSX Manager 同时管理 SDDC 虚机环境与 Kubernetes 环境当然没问题,但单纯讨论 Antrea + NSX 的整合时,NSX 就只需要安装 Manager 而已。 也因为所有“真正的功能”都是在 Antrea 内通过 OVS 实现,因此 Antrea + NSX 这个安全方案能够或不能够做到什么,重点其实是在 Antrea 内有没有开发出此功能,而不是 NSX 本身有没有支持。比如说我们需要 Pod 之间不仅有 L4 防火墙,还想要 L7 的检查功能,IDPS 方案的整合等等。在方案架构内,这些功能会需要在 Antrea 端先做出来,然后才是于 NSX Manager 端来提供管理的接口。 这里多说一句,在前面我们讨论到 Antrea+NSX 可以提供较传统 Network Policies 更完善的功能,架构上其实要分成两部分: NSX Manager 是管理层,提供简易使用与维运的 UI 界面。 Antrea 在转发层实作比传统 Network Policies 更强的安全策略功能。在 Antrea 内这个功能是通过 CRD(Customized Resource Definition)来实现,叫做 Antrea Network Policy。 透过这个强化的 CRD 构件,Antrea 可以提供日志、基于 Tier 的防火墙配置顺序、设定明确的 Deny 规则等等。 因此整个内部作业流程是管理者在 NSX UI 内进行了需求的群组及规则配置,在 Kubernetes Cluster 内的 Antrea NSX Adapter Pod 取得这些配置要求,送给 Antrea Controller 后交给每个 K8S Node 里面的 Antrea Agent,编写 Open vSwitch 来实现防火墙配置,大概是这样。 架构讨论完,下一篇开始我们会详细讨论如何进行 Antrea 整合 NSX 的安装步骤。 内容来源|公众号:VMware 中国研发中心 本文作者:Colin Jao (饶康立), VMware 资深技术顾问,主要负责 VMware NSX 产品线,目前致力于网络虚拟化、分布式安全防护技术与新应用递送方案的介绍与推广。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册