首页> 搜索结果页
"百度地图api调用限制" 检索
共 70 条结果
免费API接口调用大全
前言夏柔给大家收集了大量的免费API调用接口,无ApiKye限制,没有调用请求限制(只要不是疯狂CC),给大家免费调用!不是广告!每日一言每日一言,欢迎对接API接口https://api.aa1.cn/doc/yiyan.html全国行政规划数据库全国省市区镇村【五级版】,数据来源于国家统计局,私有化部署,托管至字节跳动API接口https://api.aa1.cn/doc/api-xz.html伪原创api -免费接口同行伪原创接口,可对接API接口https://api.aa1.cn/doc/other-wyc.html安慰文案乖乖,不要不高兴啦~API接口https://api.aa1.cn/doc/api-wenan-anwei.html扒站接口爬虫版爬取目标站静态资源,接口由浑欲不胜簪维护API接口https://api.aa1.cn/doc/api-bz-scrapy.html全球天气城市指数查询全球城市天气指数,例如北京:101010100API接口https://api.aa1.cn/doc/tianqi-zs.html车牌号归属地查询全国车牌号查询,支持政府、公安、部队、民用等车牌API接口https://api.aa1.cn/doc/car-number-fl.html百度转语音普通版托管至字节跳动AMD架构服务器,生成语音并转存输出,可持续性服务调用API接口https://api.aa1.cn/doc/api-baidu-01.html身份证校验信息接口校验机制完善,严格遵守国家身份证规律正则匹配;接口托管于字节跳动服务器,欢迎对接!API接口https://api.aa1.cn/doc/sfz.html高质量小姐姐秒播线路【秒播线路】高质量现爬抖音各种小姐姐视频,可以说是精品中的精品汇聚:陈佩奇,程女士,万小七,爆胎草莓酱,井川里予,巨型萝莉,蔡萝莉, 一栗小莎子,菜菜…等知名女网红(无忧集团旗下艺人…太多不展示了)大量抖音现产变装API接口https://api.aa1.cn/doc/girl-11-08-ml.html聚合域名检测微信、QQ拦截检测,备案查询API接口https://api.aa1.cn//doc/api-lj-gf.html高质量小姐姐介绍版输出的视频直链带有:免费API:api.aa1.cn、乱码等字眼,这个接口是正则匹配并重新输出播放视频的介绍。API接口https://api.aa1.cn/doc/api-girl-11-02.html时间段模糊一言分为上午、中午、下午、凌晨、早晨等,每个时间段的一言和暖心一句API接口https://api.aa1.cn/doc/time-tx.html获取网站tdk获取网站tdk及描述API接口https://api.aa1.cn/doc/api-web-head.htmlQQ邮箱新通道新邮箱通道,此通道通信正常API接口https://api.aa1.cn/doc/api-mail.html获取网页所有图片爬取目标站当前页面所有图片源API接口https://api.aa1.cn/doc/api-web-img.html小姐姐短视频「随机」遍历目录下文件,视频数据来源于自媒体平台API接口https://api.aa1.cn/doc/girl.htmlQQ获取头像获取你的QQ头像API接口https://api.aa1.cn/doc/qqimg.htmlQQ获取昵称获取你的QQ昵称API接口https://api.aa1.cn/doc/qqnicheng.htmlQQ获取邮箱获取你的QQ邮箱API接口https://api.aa1.cn/doc/qqemail.htmlQQ获取JSON获取你的QQ数据并返回JSONAPI接口https://api.aa1.cn/doc/qqjson.htmlPC端风景视频「随机」内嵌至你的网站背景,绝绝子!API接口https://api.aa1.cn/doc/api-fj.html朋友圈一言朋友圈每日经典一句API接口https://api.aa1.cn/doc/pyq.html舔狗日记如何舔女神?来看看这里!API接口https://api.aa1.cn/doc/tiangou.htmlPC端美女图片「随机」显示图片,有新老照片,嫌旧的勿接入API接口https://api.aa1.cn/doc/pc-girl_bz.html小姐姐短视频第二版大概10G爬虫数据,欢迎对接API接口https://api.aa1.cn/doc/api-dy-girl.html网红御姐萝莉头像「随机」显示图片,可用于您的站点头像API接口https://api.aa1.cn/doc/api-tx.html搞笑短视频「随机」播放抖音搞笑短视频API接口https://api.aa1.cn/doc/api-video-gaoxiao.html情感一言情感伤感语录,欢迎对接API接口https://api.aa1.cn/doc/api-wenan-qg.html毒鸡汤每日一份毒鸡汤,清醒你的人生API接口https://api.aa1.cn/doc/api-wenan-dujitang.html神回复搞笑神回复,搞笑每一天API接口https://api.aa1.cn/doc/api-wenan-shenhuifu.html搞笑语录每日搞笑一句话,开心一整天API接口https://api.aa1.cn/doc/api-wenan-gaoxiao.html名人名言毒鸡汤不够?上名人说的名API接口https://api.aa1.cn/doc/api-wenan-mingrenmingyan.html网易云热评网易云热评数据接口,欢迎对接API接口https://api.aa1.cn/doc/api-wenan-wangyiyunreping.html情侣对话短视频「随机」播放上千个情侣对话视频,欢迎对接API接口https://api.aa1.cn/doc/api-video-qinglvduihua.html朋友圈抒情视频「随机」播放朋友圈文案短视频,欢迎对接API接口https://api.aa1.cn/doc/api-video-pyqshuqing.htmlICP备案对接备案管理通信局api,实时数据API接口https://api.aa1.cn/doc/icp.html每日壁纸抓取360壁纸,每日4K/动漫/爱情/清新/炫酷等10大图库API接口https://api.aa1.cn/doc/api-meiribizhi.htmlwhois查询域名whois查询,支持300+后缀API接口https://api.aa1.cn/doc/whois.html苏州电视台-维护苏州电视直播源,欢迎对接API接口https://api.aa1.cn/doc/live.html手机号归属地查询手机号归属地,欢迎对接API接口https://api.aa1.cn/doc/guishu.htmlQQ邮箱-维护代发邮件API,欢迎对接API接口https://api.aa1.cn/doc/mail.html网易云音乐获取网易云ID的音乐直链,欢迎对接API接口https://api.aa1.cn/doc/wymusic.html喜马拉雅主播信息采集喜马拉雅主播信息,欢迎对接API接口https://api.aa1.cn/doc/ximalaya.html知乎每日新闻知乎每日最新新闻,欢迎对接API接口https://api.aa1.cn/doc/zhihu-news.html本地IP显示当前设备IP,欢迎对接API接口https://api.aa1.cn/doc/myip.html域名拦截检测腾讯官方接口,实时检测域名是否拦截API接口https://api.aa1.cn/doc/domain-ok.html全国天气-维护全国天气API,欢迎对接API接口https://api.aa1.cn/doc/api-weather.htmlIP签名档生成图片并返回你的网站展示API接口https://api.aa1.cn/doc/ip-img.html爱奇艺热搜词爱奇艺视频热搜关键词,欢迎对接API接口https://api.aa1.cn/doc/iqy-hot.html爱奇艺视频搜索爱奇艺电影搜素,欢迎对接API接口https://api.aa1.cn/doc/iqy-name.html搜狗百度热搜排行榜搜狗/百度热搜排行榜,欢迎对接API接口https://api.aa1.cn/doc/sougou-baidu.html抖音即时热搜榜抖音热搜榜,欢迎对接API接口https://api.aa1.cn/doc/douyin-hot.html每日高清手绘壁纸高清手绘地址,上千张图片压缩版本,无损压缩API接口https://api.aa1.cn/doc/api-gqsh.html随机生成头像框随机生成QQ头像框,更换QQ号直接起飞~API接口https://api.aa1.cn/doc/api-tksc.html端口扫描扫描域名/服务器IP开放端口列表API接口https://api.aa1.cn/doc/api-port.htmlMD5在线加解密加解密MD5,仅简单的md5解密API接口https://api.aa1.cn/doc/api-md5.htmlQQ空间访客查询查询QQ空间访客数量API接口https://api.aa1.cn/doc/api-qqfangke.html字数检测检查多少个文本API接口https://api.aa1.cn/doc/api-zishu.html二维码生成自定义二维码API接口https://api.aa1.cn/doc/api-qrcode.html自定义导航页面自定义导航引导页,无任何广告植入API接口https://api.aa1.cn/doc/api-dh.html市区级IP定位IP市区级定位,对接腾讯地图APIAPI接口https://api.aa1.cn/doc/api-sqdw.html文本相似度对比对比两串文本相似度API接口https://api.aa1.cn/doc/api-xsd.html自定义链接跳转自定义链接跳转API接口https://api.aa1.cn/doc/api-tz.htmlPing测试一键ping网站延迟API接口https://api.aa1.cn/doc/api-ping.html随机生成密码随机生成密码API接口https://api.aa1.cn/doc/api-mima.html小爱AI聊天小爱人工智能聊天接口API接口https://api.aa1.cn/doc/api-xiaoai.html随机动漫横屏壁纸横屏动漫壁纸,适配「PC端」「手机banner」「横屏版」API接口https://api.aa1.cn/doc/api-pctu1.html随机4K风景壁纸输出无压缩、高质量壁纸,若卡顿请使用压缩版API接口https://api.aa1.cn/doc/api-fj-1.html随机4k风景壁纸(压缩)输出4k压缩壁纸,若需要原图输出请使用原版API接口https://api.aa1.cn/doc/api-fj-2.html准点报时准点报时,1:00~24:00API接口https://api.aa1.cn/doc/api-baoshi.html微视随机展示视频随机抓取微视短视频API接口https://api.aa1.cn/doc/api-vs.html虎牙直播根据检索主播名返回主播及直播间的相关信息API接口https://api.aa1.cn/doc/api-huya.html斗鱼直播根据检索主播名返回主播及直播间的相关信息API接口https://api.aa1.cn/doc/api-douyu.htmlQQ天气 - 维护城市级天气API接口https://api.aa1.cn/doc/api-qqtq.html小鸡词典他们在说什么?这梗我没听过,我错过了什么瓜?小鸡词典,查梗搜瓜神器。API接口https://api.aa1.cn/doc/api-jiki.html快递地址解析快递地址快速解析API接口https://api.aa1.cn/doc/api-kdjx.htmlCosplay相册Cosplay相册图片直链及介绍API接口https://api.aa1.cn/doc/api-list-cos.html本地新闻全国本地新闻API接口https://api.aa1.cn/doc/api-list-news.html有道翻译在线中英互译API接口https://api.aa1.cn/doc/api-fanyi-yd.html敏感词检测敏感词检测,违禁词反政府词库,持续更新API接口https://api.aa1.cn/doc/api-mgc.html微信无限公众号微信无限公众号回调,管理赞助服务号API接口https://api.aa1.cn/doc/api-wxapi.html收录工具可查询百度、搜狗、360、谷歌收录量API接口https://api.aa1.cn/doc/api-sl.html手机号估价手机号估价API接口https://api.aa1.cn/doc/api-phone-gj.htmlQQ号码估价qq号码估价API接口https://api.aa1.cn/doc/api-qq-gj.html小人举牌小人举牌,自定义文字API接口https://api.aa1.cn/doc/api-jupai.html获取网站TDK大全返回JSON格式,获取网站大全API接口https://api.aa1.cn/doc/api-web-head-json.html全国疫情查询城市级疫情地区查询API接口https://api.aa1.cn/doc/api-yq.html我在人间凑数的日子我在人间凑数的日子API接口https://api.aa1.cn/doc/api-renbjian.htmlQQ访客查询获取QQ空间的访客数量API接口https://api.aa1.cn/doc/api-qq-fangke.html骚话文案污话骚话接口API接口https://api.aa1.cn/doc/api-saohua.html随机谜语随机谜语,猜谜语接口API接口https://api.aa1.cn/doc/api-miyu.html成语查询沪江成语接口查询API接口https://api.aa1.cn/doc/api-chengyu.html查字接口新华字典数据,欢迎对接API接口https://api.aa1.cn/doc/api-zi.html全国天气日期级较稳天气接口,可对接API接口https://api.aa1.cn/doc/api-tianqi-3.html汉英文案中文or英文正能量文案API接口https://api.aa1.cn/doc/api-wenan-yingwen.html爱情文案爱情语录文案API接口https://api.aa1.cn/doc/api-wenan-aiqing.html早安语录每日早安语录,开心一整天API接口https://api.aa1.cn/doc/zaoanyulu.html全球疫情数据全球疫情实时数据API接口https://api.aa1.cn/doc/api-yq-all.html口吐芬芳接口口吐芬芳,五种类型API接口https://api.aa1.cn/doc/api-wenan-ktff.html高质量小姐姐短视频高质量现爬抖音各种小姐姐视频,可以说是精品中的精品汇聚:陈佩奇,程女士,万小七,爆胎草莓酱,井川里予,巨型萝莉,蔡萝莉, 一栗小莎子,菜菜…等知名女网红(无忧集团旗下艺人…太多不展示了)大量抖音现产变装API接口https://api.aa1.cn/doc/api-girl-11-02.html随机姓名接口随机输出姓名API接口https://api.aa1.cn/doc/api-xingming.html简易版扒站接口可扒html,简易接口,速度较快API接口https://api.aa1.cn/doc/api-bz.html网页外链获取获取当前网页所有超链接API接口https://api.aa1.cn/doc/api-web-a.html今日热点全网热点,采集今日的所有热点新闻API接口https://api.aa1.cn/doc/topbaidu.html油价查询同行赞助的油价接口,可调用,对接省份API接口https://api.aa1.cn/doc/api-yj.html根据标题或关键字生成内容-免费api接口同行超牛逼的Ai生成文章工具API接口https://api.aa1.cn/doc/other-freewyc-2.html本地Ping本地Ping返回数据API接口https://api.aa1.cn/doc/api-ping-yk.html随机生成密码随机生成密码,复杂度较高API接口https://api.aa1.cn/doc/api-mm.html获取网站状态获取目标站状态API接口https://api.aa1.cn/doc/api-web-code.htmlQQ高清无损音乐接口对接腾讯音乐会员高清无损音乐接口,较稳定API接口https://api.aa1.cn/doc/music-api.html顺口溜大全随机顺口溜大全,词库不是很多,看情况对接API接口https://api.aa1.cn/doc/api-wenan-shunkouliu.htmlBase64转imgbase图片转img,生成png/jpegAPI接口https://api.aa1.cn/doc/base64-img.htmlUniCode转码中文和uniCode互转API接口https://api.aa1.cn/doc/unicode.html————————————————
文章
数据采集  ·  JSON  ·  人工智能  ·  编解码  ·  自然语言处理  ·  API  ·  定位技术  ·  数据安全/隐私保护  ·  数据格式  ·  计算机视觉
2022-11-21
高密度 ARM 服务器如何引领“数智时代”发展,打通“智变质变”正循环
并行计算 | 多样性计算 | ARM架构深度学习 | 高性能计算 | ARM服务器如今随着算力、高性能计算的快速发展,数字经济已经成为全球经济增长的主引擎。数字经济的快速发展,使得深度学习、数据分析、数据挖掘等技术迅猛发展起来。伴随国家政策东数西算的出台,传统的风冷散热方式已经不足以满足散热需要,这就需要新兴的液冷散热技术以此满足节能减排、静音高效的需求。作为国内品牌服务器厂商,蓝海大脑液冷GPU服务器是基于ARM 架构的基础软硬件设施、行业应用及服务,涵盖从底层硬件、基础软件到上层行业应用的全产业链条。硬件方面,围绕ARM处理器,涵盖包括智能网卡芯片、底板管理控制器(BMC)芯片、固态硬盘(SSD)、磁盘阵列卡(RAID卡)、主板等部件以及个人计算机、服务器、存储等整机产品。基础软件方面,涵盖操作系统、虚拟化软件、数据库、中间件、存储软件、大数据平台、数据保护和云服务等基础软件及平台软件。行业应用方面,蓝海大脑产业生态覆盖政府、金融、电信、能源、大企业等各大行业应用,提供全面、完整、一体化的信息化解决方案。随着通用算力的普及,各行各业的数字化得到快速发展的同属又产生更多的数据,这就需要更多的算力。预计到2030年,全球通用计算算力相比2020年将增长10倍,AI算力将增长500倍。计算从通用计算进入通用计算+AI计算的多样性计算时代。通用计算构建了数字经济发展的基础,AI计算将成为数字经济发展的加速器。从数字化到智能化,人工智能作为新的GPT,将使数字经济迈向新高度。为此华为联合合作伙伴发出《迈向智能世界计算白皮书》,以下将对该白皮书进行解读,一起了解算力发展六大趋势。注:由于篇幅有限需要更多详细资料,请在公众号末尾留下您的邮箱,小编会将PDF文件发您邮箱,共同进步。ARM 成为多样性 计算的重要选择一、产业趋势1、应用的多样化驱动算力多样性发展1)随着自动驾驶、云游戏VR/AR等应用的兴起,以及物联网、移动应用、短视频、个人娱乐、人工智能的爆炸式增长,应用越来越多样化,用户对应用体验的追求不断提高。数据中心侧传统单一架构难以满足要求。万物互联的智能时代非结构化数据占比越来越大。相对应原来可以用数据库二维表结构来实现的结构化数据,海量、多种多样非结构化数据,如文本、图片、语音、视频等数据的加工、处理、传输,自然需要多样性的计算来匹配。举例来说,CPU处理大数据、Web等场景是非常匹配的,但对于图形、图像的处理,需要GPU来匹配;而日常生活中的图形 /图像识别、智能搜索推荐等,需要基于AI计算的NPU处理。因此业务应用场景的多样性、数据的多样化,使得计算进入多样性计算的新时代。2)未来超过70%的数据和应用将在边缘产生和处理。边缘和移动端设备受场景约束,处理能力和性能的提升受到限制,需要与云协同。随着5G的规模部署,网络传输时延、带宽、连接密度均得到数量级的提升,给端-边-云协同提供了基础保障。目前云、边、端的计算架构、开发模式存在较大差异,应用须多次开发和部署。2、多样性计算需求,加速算力格局转换,ARM算力从嵌入式场景快速延深至服务器场景 ARM算力是从最初的端侧起步,在智能手机、平板、智能电视等领域占据绝对领先的份额,但随着云、边、端协同的驱动、多样性计算的发展,已经开始进入到算力更高的服务器领域,同时也表现出显著的优势:1)在分布式数据库、大数据、Web前端等高并发应用场景中,单芯片核数更多的ARM架构处理器相比传统处理器拥有更好的并发处理效率。2)绝大多数移动终端采用ARM架构处理器,端云同构为开发人员在整个生态系统的编写与优化上提供便利,而且能够降低异构环境开发所造成的性能损失和潜在漏洞风险。随着云化进程的推进,大量基于ARM架构的终端业务与数据中心的云端业务维持同构,可以实现应用开发、部署和运行的无缝协同,大幅度降低开发者开发难度。3)ARM生态优势不断推动技术进步,近年来不断涌现出创新的服务器产品和解决方案,如蓝海大脑液冷服务器就是基于ARM架构,华为基于ARM架构鲲鹏处理器打造了TaiShan系列服务器等。在高性能计算领域,以ARM、RISC-V 为代表的多样性计算平台也逐渐发挥重要作用,例如欧盟EPI项目致力于打造本土基于ARM架构核心处理器和RISC-V架构加速器芯片的百亿亿级超级计算机;日本“富岳”超算系统采用自主开发的ARM架构处理器,成为全球首台基于ARM芯片的TOP500冠军超级计算机等。4)ARM架构授权模式让合作伙伴既自主发展又共享生态平台,加速产业链多样化。ARM的商业模式不以出售芯片为主,而是架构授权。 合作伙伴可以根据自身需求,灵活选择不同的授权模式:架构授权模式,基于ARM 架构,可以自主扩充指令集并升级产品CPU核授权模式(软核和硬核),基于ARM CPU IP可实现设计生产,升级则需完成新CPU 核授权的获取5)2000年x86占据市场第一份额,总算力输出达到70%。到2020年,算力架构发生逆转,世界上最大算力架构变成了ARM平台,基于ARM指令的处理器总算力输出占比超过 80%。3、中国市场,服务器侧ARM生态已逐步成熟,并全面应用于国计民生行业全球范围内,以ARM为核心架构的CPU已经开始显现出增长趋势。在中国,众多芯片厂商和云巨头也纷纷布局基于ARM架构的系列产品,鲲鹏、飞腾已耕耘多年,ARM服务器市场份额持续增加。以鲲鹏为代表的ARM服务器,已经广泛应用于包括政府、金融、电信、电力、交通、制造、教育、医疗等行业核心场景;各行业生态已经建立,超过12000个行业应用完成适配认证,产业生态瓶颈已经消除。二、行动建议1、基于业务需求,识别适合ARM架构的业务场景,主动规划部署ARM架构服务器数字化转型、人工智能和5G在垂直行业的广泛应用带来了海量数据处理、高能效边缘计算等问题,尤其在电信、金融、政府、能源等重点行业,ARM架构能够更好的满足数字化应用对IT基础设施算力的严苛要求,在升级发展中发挥关键作用。以电信行业为例,5G时代数据量爆发式增长、电信云面临从架构到底层硬件基础设施的全面升级,在容器化部署、分布式存储和边缘计算等关键场景都非常适合引入ARM架构,充分利用其多核高并发、大内存和高内存带宽等架构优势。1)IT云方面IT支撑系统业务逻辑更趋复杂,实时数据处理、高并发数据处理、大数据分析等技术需求不断扩大,容器化部署、分布式处理等场景加速向CRM、BOSS、MSS等核心系统渗透,需要底层IT基础设施在并行计算、内存容量和带宽等方面提供更好能力匹配2)网络云方面5G核心网采用原生云化设计思路和微服务架构,将网元功能拆分为细颗粒度的网络服务,为差异化的业务场景提供敏捷的系统架构支持,核心网容器化、硬件资源池化成为发展方向,对底层计算架构的多样性、负载能力和计算效率提出新要求3)边缘节点方面为应对大视频、物联网等各类高带宽和低时延的边缘计算类业务,电信云计算能力将向移动边缘节点下沉,边缘数据中心 IT 基础设施将面临计算、存储等网络能力的全面提升以实现大流量、高并发、低时延的本地数据处理能力。蓝海大脑围绕重点行业的计算诉求,主动推进ARM架构服务器的应用,依托ARM处理器多核高并发、高效可靠的硬件平台,以及在基础软件方面的领先优势和安全特性,在大数据、分布式存储、数据库和云平台等计算场景中构建安全可靠的算力底座。电信行业主要计算场景2、有节奏的开展现有应用适配、迁移,并基于ARM架构,持续开发原生应用以电信行业为例,根据电信行业的业界专家评估绘制的《电信行业ARM架构迁移路径图》显示,ARM架构平均优势高,平均迁移难度较小,其中云核心网、大数据经营分析系统、大数据网络优化平台、CRM前台和中台、网关资源管理系统、网管性能管理系统、BOSS话单存储、Cloud VR等系统的ARM架构优势明显并且迁移难度偏低,均可优先考虑适配迁移。在迁移过程中,针对行业应用跨架构迁移周期长、工作量大的问题,通过ARM架构配套的应用迁移工具,将代码修改、汇编语言翻译、兼容文件替换、编译调试、调优诊断等迁移关键步骤在工具辅助下自动完成,降低开发人员技术门槛、提升应用迁移效率,引导行业加快应用迁移进展。迁移完成之后,在后续版本迭代及新功能开发过程中,通过ARM架构配套的开发工具,帮助开发人员便捷获取和使用ARM架构优势特性,开发出高性能软件,同时自动完成典型场景下的应用包构建和执行,提升开发效率和体验,引导开发人员持续基于ARM架构原生开发行业应用,深入构建行业软件生态。电信行业ARM架构迁移路径图3、通过全栈软硬件优化,充分释放多样算力,发挥极致性能为适应行业应用快速创新及多样性计算的需求,进一步提升软件运行性能,面向ARM架构的全栈优化能力必不可少,通过使用包括一系列的硬件加速库、软件加速包、开源加速组件、典型场景的性能优化解决方案等,围绕硬件、基础软件,到场景化应用开展全栈优化,充分发挥应用极致性能。1)硬件加速提供CPU、内存、磁盘、网络子系统等硬件基础性能优化参考,包括系统硬件配置优化方法及硬件加速库,消除性能瓶颈,提升硬件资源利用率2)软件加速围绕系统指令、媒体转码、数学算法、存储网络等方向,提供一系列软件加速包,优化大数据加解密、分布式存储压缩、视频转码等常用软件性能3)基础软件优化开源软件作为最重要的软件开发模式之一是软件生态的核心,让开源软件与ARM平台进行充分的适配和优化尤为重要,持续在开源社区贡献关键性能优化成果,提供典型场景下的开源加速组件,让主流开源软件能够在ARM架构上发挥最佳性能4)典型场景优化面向大数据、分布式存储和数据库等行业应用的典型计算场景,提供加速数据处理、优化存储访问和提升算力部署密度的场景优化方案,有针对性的提升行业应用性能三、解决方案蓝海大脑提供基于ARM架构的鲲鹏全栈基础软件平台解决方案 鲲鹏计算产业是基于鲲鹏处理器(基于ARM 架构)的基础软硬件设施、行业应用及服务,涵盖从底层硬件、基础软件到上层行业应用的全产业链条。纵观鲲鹏计算产业生态全景,硬件方面,围绕鲲鹏处理器,涵盖包括智能网卡芯片、底板管理控制器(BMC)芯片、固态硬盘(SSD)、磁盘阵列卡(RAID卡)、主板等部件以及个人计算机、服务器、存储等整机产品。基础软件方面,涵盖操作系统、虚拟化软件、数据库、中间件、存储软件、大数据平台、数据保护和云服务等基础软件及平台软件。行业应用方面,鲲鹏计算产业生态覆盖政府、金融、电信、能源、大企业等各大行业应用,提供全面、完整、一体化的信息化解决方案。鲲鹏计算产业从2019年正式起航,在全球鲲鹏计算产业伙伴的共同努力下,已经构筑了完整的基础软硬件生态和人才发展体系,并在各大国计民生行业实现了规模商用落地,为行业数字化变革和应用创新提供了强大稳定的算力支持。作为鲲鹏计算产业的发起者和重要成员,华为秉持“硬件开放、软件开源、使能伙伴,发展人才”的策略,通过战略性、长周期的研发投入,吸纳全球计算产业的优秀人才和先进技术和产业伙伴一起,持续推进全栈计算技术的创新发展,构筑面向多样性计算的全球开源体系与产业标准,推动鲲鹏生态全面发展。鲲鹏全栈开放,使能全产业伙伴创新1、鲲鹏主板开放,伙伴优先,使能商业成功2019年华为面向伙伴开放基于鲲鹏处理器的主板、网卡、硬盘等标准部件,帮助整机合作伙伴快速推出自有品牌的服务器产品。2020年华为发布了主板开放2.0,通过基础板+扩展板的开放模式,基础板沉淀共性,减少伙伴重复开发;扩展板实现创新,使能伙伴差异化竞争力;同时结合BIOS/BMC软件开放,支持伙伴自行开发差异化部件,打造创新整机产品。当前,鲲鹏主板走向更加开放,华为仅聚焦“CPU+内存”最小计算单元,通过全量组件化方式,实现从使能伙伴创新走向伙伴主导创新。此外,在鲲鹏主板开放的同时,华为从研发、制造、采购供应、服务、商业模式、解决方案、市场、人力资源、财务、文化十大方面,全方面对伙伴进行赋能,帮助伙伴快速成长,使能合作伙伴打造更有竞争力的鲲鹏计算产品。市场上,华为践行伙伴优先,将自有品牌TaiShan服务器逐步退出市场,和伙伴不竞争,把市场空间让出来,支持伙伴商业成功,2022年1到10月,伙伴出货占比已达95%以上。硬件开放,伙伴优先,使能商业成功2、基础软件开源,持续创新,实现最佳支持鲲鹏基础软件方面,华为坚定开源,把自身多年来构建的操作系统能力和数据库能力开源出来,让合作伙伴能够在此基础上做增量开发,由此来提升中国的基础软件产业水平,和伙伴共建生态。并创建了openEuler开源社区和openGauss开源社区,以社区 运作的方式,同产业伙伴和广大开发者共同构建基础软件生态。当前无论是openEuler,或是openGauss,均在鲲鹏服务器上做了大量的性能优化工作,最终实现最佳支持鲲鹏,为鲲鹏生态的体系构建,奠定了基础。 以openGauss为例,通过NUMA-Aware优化,Inplace-Update融合引擎,多存储引擎架构,软硬协同优化等技术为用户带来多样化业务场景下极致、稳定的数据业务处理能力,在鲲鹏2路服务器上实现性能达150W TpmC,鲲鹏4路服务器上达230W TpmC,单节点处理能力业界领先,同时保持內核在高负载情况下性能抖动小于5%,业界稳定性最优。在2022年4月openGauss3.0版本,发布分布式解决方案,在性能方面持续精进,16节点性能达到1000万 tpmC,领先目前竞品性能2倍。3、使能极简开发,极致性能,繁荣应用生态鲲鹏开发套件(鲲鹏DevKit)使能应用极简开发鲲鹏生态发展的关键挑战是应用软件迁移,为了帮助开发者加速应用迁移和算力升级,华为提供鲲鹏开发套件DevKit,包括代码迁移、开发调试、编译、测试、调优和诊断等一系列工具套件。 鲲鹏DevKit主要面向不同计算平台间的应用迁移以及鲲鹏平台原生开发,当前实现1-2人天应用无忧迁移。2022年鲲鹏DevKit2.0聚焦原生开发能力增强,面向全研发作业流程,提供鲲鹏开发框架和场景化SDK、鲲鹏编译工具、鲲鹏调试器、云测服务、以及面向全场景性能分析和调优,让开发者更便捷高效的基于鲲鹏原生开发,效率提升50%+。鲲鹏DevKit:从“应用迁移”走向“原生开发”, 开发效率提升50%+鲲鹏BoostKit,从硬件、基础软件,到场景化应用开展全栈优化,主要面向伙伴和客户的开发者,提供高性能开源组件、基础加速软件包、应用加速软件包,使能应用极致性能。其中,高性能开源组件由伙伴从开源社区、鲲鹏社区获取,直接编译/部署,目前90%主流开源软件已支持鲲鹏,实现开源软件在鲲鹏上开箱即用。基础加速软件包,面向伙伴开源、开放丰富的基础性能优化方法、加速库、加速算法,释放鲲鹏算力。应用加速软件包,联合伙伴开展解决方案创新,提供业界领先的加速组件、算法,实现应用性能倍增。鲲鹏BoostKit 1.0面向鲲鹏聚焦的八大主力场景,把鲲鹏算力性能发挥到极致。在很多传统计算负载中,CPU的实际利用率并不高,大量有效计算能力浪费在等待数据的加载上。2021 年全新推出的BoostKit 2.0,提供五大类“数据亲和”加速组件,包括数据就近计算,数据加速传输,数据并行化处理,数据安全等,对数据全处理流程进行负载优化,从而大幅提升应用性能。鲲鹏BoostKit :“数据亲和”五大加速组件,使能应用性能倍增通过使能极简开发、极致性能,鲲鹏在国计民生行业的技术生态满足度从19年的仅9%,逐年稳步提升,22年底预计达70%以上,生态兼容性的瓶颈已基本消除,初步构建起繁荣的鲲鹏应用生态。极简开发,极致性能,繁荣应用生态4、全栈协同,加速行业规模应用鲲鹏与伙伴、开发者一路前行,全面进入国计民生行业核心应用场景。在政府,鲲鹏与北明、超图、太极和神州软件等伙伴服务于各省市政务云;在金融,鲲鹏携手长亮、麒麟软件、科蓝、华锐等伙伴服务于大行和金融机构的核心交易系统。在电信运营商,鲲鹏和亚信、浩瀚深度、东方国信等伙伴服务于三大运营商的网络云、IT云与公有云。在电力,鲲鹏携手南瑞集团、许继、麒麟信安和岳能科技等伙伴服务于国网、南网电力调度系统。邮储银行作为拥有百年历史的金融机构,在中国有6亿用户,4万个营销网点,是国家普惠金融的主力,为国民经济发展做了突出贡献。现有的核心系统采用经典的大型机+商业软件搭建而成,支撑了邮储银行初期信息化,电子金融。但随着金融服务在线化,小额交易频次越来越高等这些服务场景的变化,对传统的核心系统带来了巨烈的冲击,尤其在交易热点时段,现有系统弹性不足,造成交易缓慢。商业软件架构与技术封闭,迭代慢,在应对金融创新乏力。无法继续支撑邮储银行向前发展。因此,邮储银行从19年初开始启动下一代金融核心的预研,为了保持持续创力的能力和可能,邮储银行决定基于通用计算平台加开源软件技术构建分布基础IT能力。整个不仅保持灵活的资源扩缩容能力,还具有丰富的开源软件生态,使未来的技术获取等方面成本更低。同时邮储采用企业级业务建模,对邮储上千种业务进行抽像建模,使用业务逻辑关系更清晰。 同时基于鲲鹏服务器和openGauss的原型验证,结果超越客户预期。并引入了微服务,容器等业界先进成熟的技术。经过一年多的建设,并于21年4月18日技术平台上线,开始接入生产系统进行镜像验证于6月上线分布式运维系统,利用AI技术解决海量节点带来的运维复杂度。系统于22年3月份全量投产,支持邮储日均20亿笔的交易和未来10年的业务发展。 邮储银行是国內首个建成新一代个人业务新核心的国有大行,证明了鲲鹏openGauss在金融这种的对可靠性和性能要求极高的场景,不但可以胜任, 而且可以很好,鲲鹏的多核、高并发,结合openGauss高性能、高可用及智能运维等內核能力,助力邮储个人新核心业务处理能力5倍提升,支取和查询等核心业务场景的性能提升25%以上,这些数据都可以提升客户的使用体验与感知,提升满意度,加强邮储银行服务竞争力。 邮储银行通过分布式金融新核心建设,在金融服务技术上已走到同行前列,相信凭借邮储银行人的勇于开拓的创新精神,未来会持续领先,为同业树立新的标杆和为用户带来更好的服务。多样性算力全场景的协同操作系统作为计算产业中最基础的软件,承担着抽象底层硬件,向上层应用提供统一接口的核心功能,是计算产业的关键环节。面向多样性计算和海量应用场景,操作系统应支持多样算力和多种应用的协同,成为数字产业的可靠软件底座。一、产业趋势在IT产业的全栈系统中,处理器是硬件的基础,操作系统是所有软件的根基。操作系统是软件的根操作系统作为连接底层基础硬件(处理器,整机/部件)和上层应用的最基础软件,被称为IT产业的魂:硬件提供算力的供给,应用软件是算力价值的实现,而操作系统则完成算力释放。一方面,操作系统面向硬件系统,提供更好更高效的硬件资源管理能力;另一方面,操作系统面向应用和用户,沉淀应用领域共性,提供更为便利易用的人机交互。1、多样性计算时代,呼唤面向数字基础设施的操作系统 计算产业从通用计算已经进入到通用计算+AI计算的多样性计算时代。多种算力协同发展,对操作系统提出了新的要求。首先,操作系统对上层应用,要屏蔽不同硬件的差异,提供统一的接口,要完成不同计算架构、不同硬件的兼容适配,提供良好的兼容 性,为应软件用的部署提供尽可能的便利。其次,针对不同的硬件的特征,操作系统需要针对性的优化,确保能充分发挥硬件的能力,提升性能。比如,基于ARM架构的处理器,其典型特征是核数更多,这使得ARM处理器在高并发应用场景,更具竞争力。因此,操作系统需要针对多核的处理器进行优化,确保多核任务并发时的任务调度更加合理,避免任务冲突,提高系统整体性能。 此外,除了针对不同架构的CPU优化,CPU和GPU、NPU等其他特定用途之间处理器之间的协同,也是影响系统效率的关键因素。操作系统层面,在处理CPU任务和GPU、NPU任务时,协调好这些任务的调度,成为必要的能力。2、数字化走向深入,操作系统面向云管边端全场景应用协同发展随着云计算的快速发展,云计算和云服务已经成为各企业进行数字化转型的优先选择。无论是高科技行业还是传统行业,无论是大企业还是小企业,都可以通过云服务随时随地获取数字化转型所必需的计算、存储等硬件资源,大数据、AI、IoT 等技术资源,以及凝结了领先企业大量投入的经验知识资源,极大提升了企业运行效率。云上的应用与其他场景的应用协同场景越来越丰富,比如AI应用在云和边缘的协同。通过云端充足强大的算力进行AI训练,而且云端能很好的支持多种不同的服务和AI框架,此外云端可以简化训练的开发,无需软件下载、无需配置、无需安装。边缘端则利用靠近数据产生和采集的优势,在边缘端可以迅速把采集的数据拿去做推理,快速得到推理结果的同时,避免了向云端传输大量数据带来的高成本。 因此,操作系统通过在软件底层实现应用在云、管、边、端、数据的高效、可靠、安全交换,是可以大幅提升系统整体效率和安全性的。3、开源成为主流软件开发模式, 操作系统开源共建成为产业共识 开源已经成为主流软件开发模式。从全球范围来看,过去一年,开源整体呈现高速发展的趋势。据最新官方报告,2021年全球最大开源代码平台GitHub活跃用户数和活跃代码仓库数量均有明显增长,其中新增活跃用户数超过 1600 万、新增活跃代码仓库数量超过 6100 万。中国开源贡献者占比明显提升,从2015年的7%的占比,快速提升至2021年的11%。开源模式越来越成为全球软件技术和产业创新的主导模式。 同时,开放开源是软件技术创新,特别是发展操作系统这类基础软件的重要途径,充分利用开源,参与开源,支持开源,发展操作系统,联合做大做强是当前最为可行之路。构建根植于中国的开源社区,培养良好的土壤和与环境,可以为产业打造可持续发展的创新之地。二、行动建议1、规划部署支持数字基础设施多样算力的操作系统,使能全场景应用协同创新 通过规划部署支持不同应用场景、支持多样性算力的统一数字基础设施操作系统,打通不同硬件架构和多种场景应用,实现更优的性能,业务更好的协同。在企业的各类数字应用场景中,通常部署了各种不同类型的计算设备,典型的包括服务器、边缘设备,嵌入式等等。不同设备安装各类不同的操作系统,给整体系统运行运维带来挑战;设备间的互联互通复杂度也因此显著提升;不同应用之间的可靠、安全的交互,协同相对繁琐。统一的数字基础设施操作系统,可以实现从操作系统底层完成设备间的连接、数据交互,从而大幅提升运行运维效率。 2、分析应用迁移策略,制定应用迁移计划,完成应用高效迁移 部署新的操作系统,应选择具备可持续演进性、基础兼容性和支持应用快速迁移的能力的技术路线。可持续演进是指除了可靠、稳定、安全等基础能力外,所选择的技术路线有具备独立维护、长期演进的机制和能力;基础兼容性是指在操作系统南向各类处理器、整机、板卡的兼容性支持,以及北向的各类应用的适配性;应用迁移能力是指需要提供包括兼容性识别、应用迁移与调优,系统测试等全流程的自动化工具和技术支持文档。 操作系统迁移是一个系统工程,包括从技术路线选型、系统分析、方案设计、移植适 配、迁移实施和测试上线等全流程,因此需要组建合理的团队、详细的计划,有节奏分阶段实施,在确保业务持续稳定运行的情况下有序开展。3、加入开源操作系统社区,积极拥抱开源、回馈开源 通过主动加入开源社区,与社区核心组织和成员的运作与沟通,保持与业界各类领先技术的同步,可获取最新的技术趋势、业务方向以及关键支撑。企业、高校、操作系统厂商等组织单位加入操作系统开源社区,加强交流,合作共建共赢,共同发展。更为重要的是操作系统开源社区提供各类开发工具,硬件资源,技术指导以及各类在线服务,企业鼓励有能力的开发人员加入社区,获取最新的开发手册、技术补丁以及开发平台,可以大幅度提升应用开发效率。成熟的开源社区,除了提供日常代码审核、提交的工具之外,还包括大量满足开发全流程所需要的各类资源,同时社区是技术人员积累能力、了解技术趋势、解决技术难题,互相交流的平台。三、解决方案 华为是全球领先的ICT(信息与通信)基础设施和智能终端提供商,在ICT领域提供包括服务器、存储、云服务、边缘计算、基站、路由器、工业控制等各类产品和解决方案。在多年的全系列产品研发过程中,不断累积软件根技术,全面布局操作系统等基础软件,满足自身业务发展需要。 2019年12月,华为创立欧拉开源项目,通过开源的方式,把积累的操作系统能力开放出来,携手产业伙伴共同发展操作系统产业,得到了产业积极响应。目前,欧拉开源操作系统发展迅速,生态快速构建,已累计实现超过245万套装机,国内新增市场份额超过22%,跨越生态拐点,成为企业数字化转型、应用创新、构筑安全可靠操作系统的首选技术路线。1、欧拉,面向数字基础设施的开源操作系统欧拉是面向数字基础设施的开源操作系统,支持服务器、云计算、边缘计算、嵌入式等应用场景,支持多样性计算,致力于提供安全、稳定、易用的操作系统。通过为应用提供确定性保障能力,支持OT领域应用及OT与ICT的融合。欧拉持续丰富南向多样性设备支持,北向使能 IT、CT和OT全场景应用。当前欧拉已经实现主流计算架构100%覆盖,支持包括ARM、x86、RISC-V等全部主流CPU指令集,同时支持NPU、GPU和DPU等多种异构算力,适配超过100款整机、300款板卡,成为最佳支持多样性算力的开源操作系统。在北向应用生态上,与伙伴协作,适配了一万多款应用,主流应用场景100%支持,满足各行业不同应用需求。欧拉,面向数字基础设施的开源操作系统1)统一操作系统,支持多设备通过一套操作系统架构,支持多样性设备。欧拉采用全量组件原子化,支持内核灵活组合,全栈服务化按需构建,可以根据设备不同的资源能力和业务需求灵活裁剪,按需构建不同的操作系统版本,满足不同设备对于操作系统的要求。同时,欧拉支持构建服务自助化,支持“菜单式”配置内核和系统服务,可以针对软件包、文件级、函数级的不同层级分级灵活组合,自动化、简化版本构建。 进一步,欧拉还提供多设备协同套件,来实现不同设备间的能力互助和资源共享。 2)应用一次开发,覆盖全场景欧拉通过一套标准API,ICT+OT全场景提供统一API,这样就实现了操作系统与应用之间交互语言的统一; 同时,通过欧拉SDK,把各种应用所需数据能力、音视频能力、安全等能力,进行统一封装,使能极简开发;欧拉Devkit开发套件,还可以方便的集成到各种主流应用开发平台。 3)欧拉与鸿蒙能力共享,生态互通欧拉是数字基础设施开源操作系统,鸿蒙是面向万物互联的智能终端操作系统,欧拉和鸿蒙,进一步打通,就可以更好地服务数字全场景。欧拉和鸿蒙已经实现了内核技术和分布式能力共享。通过共享分布式套件,实现了欧拉和鸿蒙的互通,两大开源操作系统打通,欧拉覆盖云管边,鸿蒙覆盖端,欧拉+鸿蒙共同服务全场景数字应用。未来进一步在安全OS、设备驱动框架、以及新编程语言等方面实现共享,通过能力共享,实现生态互通。欧拉与鸿蒙能力共享 生态互通2、欧拉开源共建,已构建成熟的产业生态1)欧拉开源项目2019年12月,华为创建欧拉开源项目,成立欧拉开源社区,开源代码上线。 2021年11月,在操作系统产业峰会2021上,在“政产学研用”各方代表的共同见证下,华为携手社区全体伙伴,将欧拉开源操作系统全量代码、品牌商标、社区基础设施等相关资产贡献给中国开放原子开源基金会,具体包括:华为自己开发的数百万的自研代码,版权和知识产权许可,超过8000多个经过华为和社区验证的软件包,openEuler以及相关项目的中英文的商标品牌共30多个,域名4个,以及构建服务与测试体系代码托管,社区运营平台等社区基础设施。这实现了欧拉开源操作系统从企业 主导到产业主导的重要转变,有利于促进欧拉开源项目从开放治理走向自治繁荣。2)欧拉生态繁荣,欧拉社区已成为国内最具活力开源社区 截止目前,国内外10+家主流操作系统厂商 (麒麟、统信、麒麟信安、SUSE等)均已发布了欧拉路线的操作系统商业发行版;社区当前已有超过400家企业加入,汇聚了从处理 器、整机、到基础软件、应用软件、行业客户等全产业链核心伙伴;社区已经有超过1万名开源贡献者,创立近100个SIG组(特别兴趣小组),社区维护的核心软件包达到8000多个。 欧拉创新领先的技术,良好的硬件兼容性,丰富的应用软件生态和覆盖全场景的部署能力,为欧拉的规模部署提供了充分的条件。 截至目前,欧拉技术路线的操作系统,已经在数字政府、电信、金融、电力等多个行业实现大规模部署应用在核心系统中,为各行业提供稳定、可靠的数字根基,累计部署量超过245万套,国内新增市场份额占比达到22%,在数字政府、金融行业增速第一。 部署欧拉路线操作系统的用户包括三大运营商(中国移动、中国电信、中国联通)、两大电网(国家电网、南方电网)以及多个大型国有和商业银行(建设银行、工商银行、中信银行、中国银联等)等。典型应用案例包括:中移在线大数据平台、中国电信云平台、国家电网核心调度系统、中国建设银行信用卡核心系统等等。3、欧拉,以发展根技术引领操作系统创新欧拉以内核级创新,打造最佳多样性算力支持、全场景数字基础设施操作系统,成为企业数字化转型、应用创新的首选可靠操作系统技术路线。欧拉引领操作系统内核创新。作为社区主要成员,华为自 2012 年以来向 Linux Kernel 社区贡献,在 Linux Kernel 5.10版本中,华为内核代码贡献排名第一。欧拉最佳支持多样性算力。支持鲲鹏、x86、飞腾、龙芯、申威、RISC-V等多种处理器架构,并且性能相比主流操作系统更佳。欧拉打破不同场景操作系统生态壁垒,成为首个全场景数字基础设施操作系统。欧拉统一支持服务器、云计算、边缘计算、嵌入式等等应用场景。截止目前,欧拉已经发布2个LTS(长生命周期支持)版本和4个创新版本。华为不做欧拉商业发行版,通过社区使能伙伴商业发行版、企业自用版、社区发行版等多种形式,促进操作系统产业健康、高速发展。华为持续在欧拉开源项目贡献,包括技术创新、社区运营、生态建设等。华为联接、计算和云等各领域继续全面使用欧拉技术路线,以社区版本为基线,构筑华为自用操作系统版本。欧拉技术路线的操作系统,主要包括以下集中形式:1)社区发行版由欧拉社区成员和社区开发者共同构建发布的开源操作系统版本,以免费的形式通过社区提供。社区每2年发布一个长周期(LTS:Long Term Support)版本, 比如:openEuler 20.03 LTS版,openEuler 22.03 LTS版。2)商业发行版操作系统产业伙伴(即OSV), 结合各自的优势,基于欧拉的社区版,开发自己的商业发行版操作系统,面向最终用户提供和销售有竞争力的产品。比如麒麟软件有限公司的银河麒麟高级服务器操作系统V10、统信服务器操作系统V20(1020e), 麒麟信安操作系统V3(欧拉版),SUSE数硕Linux等。3)企业自用版具备自研能力的企业,基于欧拉的社区发行版,开发自用的操作系统版本 (非独立销售或不销售)。比如华为公司通信设备搭载的自研操作系统、中国移动BCLinux for Euler、中国电信CTyunOS,中国联通CULinux、百度 Linux 智能云操作系统等。欧拉已发布2个LTS版本和4个创新版本人工智能算力增长是主要增量一、宏观趋势1、数字经济飞速发展将催生强劲算力需求,人工智能算力是主要增量当前,数字经济正在成为全球经济的主要增长点,算力作为数字经济时代新的生产力,是支撑数字经济发展的坚实基础。算力已成为全球战略竞争新焦点,是国民经济发展的重要引擎,全球各国的算力水平与经济发展水平也呈现出显著的正相关。 与此同时,人工智能技术的不断发展带来了远远超越摩尔定律的算力需求。从2011年深度学习技术兴起到今天,对人工智能算力的需求一直是指数级增长的,每隔3-4个月算力需求翻一倍。2020年,自然语言处理模型GPT-3参数量达到1750亿,算力需求是3640PD(PD代表 以千万亿次每秒的算力计算一天所用的浮点计算量);2021年,鹏程盘古——业界首个全开源 2000亿参数中文预训练语言模型,使用E级AI算力的鹏城云脑II算了50天,算力需求达到了 25000PD;到2023年,这种大模型的算力需求能到百万PD,这就对现有计算处理能力提出了严峻的考验。 在蓬勃的需求带动下,全球算力发展水平正在持续扩大,而在这其中,人工智能算力成为主要增量。华为预测,到2030年,人类将进入YB 数据时代,全球通用算力将增长10倍达到3.3 ZFLOPS(FP32),人工智能算力将增长500倍, 超过105 ZFLOPS(FP16)。2、人工智能正日益快速渗透行业 应用的核心场景人工智能技术的落地为行业带来更多价值,不仅提高了企业的运作效率、生产效率,还推动了企业创新的能力。调研发现,采用人工智能三年以上的企业,已经在多方面获得显著的收益,实现收入增加和生产效率提升。在2021的行业调研中,TOP3的行业人工智能渗透度均超过了50%,最高的渗透度甚至超过了80%。人工智能将在城市、交通、制造、能源、医药、教育、农业等行业持续渗透,为衣食住行带来更智能的体验:在城市领域,城市的智慧化治理成为实现城市可持续发展的必然选择。未来,每一个物理实体都将有一个数字孪生,如城市楼宇、水资源、基础设施等将组成城市数字孪生,实现更加智能的城市管理。城市智慧治理将带来100 倍的社会数据聚集,人工智能技术将实现高效城市治理。 在交通领域,预计2030年,全球道路上的电动汽车、面包车、重型卡车和公共汽车数量将达到1.45亿辆。每辆汽车行驶中产生的数据需要在汽车与城市之间频繁进行数据交换,借助智慧交通基础设施的AI分析能力,城市通勤时间将得到大幅提升(日均通勤缩短15-30分钟),交通事故和汽车对城市碳排放量也随之大幅降低。智能带来的交通安全、效率、体验的提升,必将释放出新的生产力,推动社会经济的发展。 在制造领域,AI可以帮助制造企业实现智慧化运营管理、海量数据分析挖掘以及低时延诊断预警。中国制造2025提出,制造业重点领域全面实现智能化,试点示范项目运营成本将降低50%,产品生产周期缩短50%,不良品率降低50%。 人工智能将融入千行百业的核心场景,实现多个人工智能场景的落地,带来源源不断的创新与无所不及的智能。二、行动建议 1、加速AI基础设施建设,让AI算力成为像水和电一样的公共资源作为新基建的重要组成部分,人工智能已经成为数字经济发展的重要驱动力,人工智能产业在发展过程中仍面临诸多挑战。1)人工智能产业布局不均衡通过对多个城市的实际调研发现,人工智能产业存在基础薄弱和研发实力弱等情况,无法匹配产业发展规划和战略布局2)企业使用AI算力成本高大规模预训练模型的参数量越来越大,需要的算力也从TFLOPS级别增加到PFLOPS级别,多数企业表示当前算力不足。企业使用人工智能算力成本昂贵,仅算力成本就占据企业开发成本的15%~25%3)AI人才缺口大大量人工智能企业表示缺乏AI技术人才,在全国各地都缺乏核心AI技术人才的背景下,加强培育本土AI人才非常必要。鉴于上述挑战,建议大力发展以人工智能计算中心为代表的新型基础设施,让AI成为水和电一样的基础公共资源,为数字经济发展提供新动能。人工智能计算中心建设具有技术实现复杂、建设周期长、资源投入巨大、产业辐射面广的特点,需要进一步强化战略统筹和政策保障,进行系统的组织机制和体制创新,加强关键核心技术攻关和标准化建设,以加快推动人工智能计算中心的高质量发展和网络化建设。系统总结已建成的人工智能计算中心的建设经验,持续加强人工智能计算中心的统筹建设,在确保已建成的人工智能计算中心保持高效运营的同时,顺应人工智能发展趋势和产业落地的需求,坚持以应用为导向,坚持自主创新技术路线,加强人工智能计算中心建设。2、加速人工智能进入行业关键场景,使能行业智能化升级促进人工智能与各行业融合创新,在城市、交通、制造、能源、医疗、金融等重点行业和领域开展人工智能应用试点示范,推动人工智能规模化应用,全面提升产业发展智能化水平1)智慧城市构建适用于政府服务与决策的人工智能融合赋能平台,实现AI在智慧城市建设中 “大脑”般的智慧,将人工智能技术与城市应 用场景深度融合,实现城市在各类场景下的高效治理。研制面向开放环境的决策引擎,在复杂社会问题研判、政策评估、风险预警、应急处置等重大战略决策方面推广应用2)智慧交通针对高速公路自由流收费、收费稽核、视频云联网、车路协同等典型交通AI应用场景,打造智慧交通解决方案,用人工智能技术对车辆、轨迹等进行智能分析,让出行管理更高效,让通行更通畅3)智慧制造打造数字工厂AI使能解决方案,为制造行业量身定制的质量检测、厂区安全等应用领域的一站式、高精度、支持快速换线、开箱即用的AI解决方案,打通AI落地制造行业的“最后一公里”,加速AI应用在工厂规模化部署,把AI带入每一条产线,为工厂生产和运营提质增效4)智慧巡检用人工智能的分析取代传统的人工巡检,让巡检更安全,效率和准确率更高。 结合智能电网、智能油气和智能矿山的发展需求,以AI技术为基础,打造智慧巡检解决方 案,为输电线路、变电站、配电房、油气田、加油站和煤矿等场景提供区域智能感知5)智慧医疗打造传染病AI监测预警平台、紧密型县域医共体AI解决方案、智慧医院AI 解决方案等,助力医疗行业智能化升级,将AI科技进步服务于人类健康6)智慧金融面向金融行业提供更加高效、安全、个性化的综合性金融解决方案,贯穿于金融服务垂直全流程,为银行智慧网点、金融OCR、智能双录等AI应用场景提供智慧化解决方案。三、解决方案1、以昇腾AI基础软硬件平台,构筑智能根基昇腾AI产业是以昇腾AI基础软硬件平台为基础,坚持“硬件开放、软件开源、使能伙伴、 发展人才”,联合技术和商业伙伴,打造“共建、共享、共赢”的人工智能产业,致力于让 AI“用得起、用得好、用得放心”,以人工智能赋能社会发展与产业升级,为人类社会发展带来价值。昇腾AI产业(简称昇腾AI/昇腾)是以昇腾AI基础软硬件平台为基础构建的人工智能计算产业。 昇腾AI基础软硬件平台包含Atlas系列硬件及伙伴硬件、异构计算架构CANN、全场景AI框架昇思MindSpore、昇腾应用使能MindX、全流程开发工具链MindStudio和一站式AI开发平台ModelArts等。1)Atlas系列硬件及伙伴硬件基于昇腾AI处理器,通过模组、板卡、小站、服务器、集群等丰富的产品形态,打造面向“云、 边、端”的全场景昇腾AI基础设施解决方案2)异构计算架构CANN异构计算架构CANN,北向支持业界主流AI 框架,南向支持系列化芯片的硬件差异,通过软硬协同,充分释放硬件的澎湃算力3)全场景AI框架昇思MindSpore全场景AI框架昇思MindSpore,致力于成为全球主流AI框架,具备一次开发云边端全场景部署、原生支持大模型训练、支持AI+科学计算等关键特性,加速科研创新和产业应用4)MindX昇腾应用使能昇腾应用使能MindX包含深度学习使能MindX DL、智能边缘使能MindX Edge、模型优选库ModelZoo,和行业应用开发套件MindX SDK,旨在沉淀行业知识,使能行业应用极简开发,加速人工智能应用落地昇腾AI产业2、以“一中心四平台”建设人工智能计算中心,打造人工智能算力基础设施1)人工智能计算中心是专注于AI计算的新型城市基础设施,它以昇腾AI基础软硬件平台为基础,是涵盖了从基建基础设施、硬件基础设施到软件基础设施的完整系统。作为一体化城市人工智能新型基础设施,AICC承载着“一中心四平台”的产业模式创新,解决算力普惠、科研创新、应用孵化与落地、人才培养等AI发展关键问题,旨在让AI算力像水和电一样成为城市公共基础资源,为数字经济发展提供新动能,让智能无所不及2)公共算力服务平台通过产业政策牵引,将人工智能计算中心的算力资源有序、高效、普惠地开放给当地的企业、科研机构和高校,解决当地AI技术发展和产业智能升级的算力和服务需求3)应用创新孵化平台各地AI企业、高校、科研机构,针对各地特色的AI应用场景项目机会,依托人工智能计算中心,进行科技创新成果商用转化、形成有本地特色的的重大产品创新和示范应用4)产业聚合发展平台依托计算中心,配套相关产业政策、吸引和招募AI产业链上的各类公司(算法公司、数据处理公司、行业集成公司等)入驻形成完整产业闭环,促进和推动AI产业集约集聚发展5)科研创新和人才培养平台基于人工智能计算中心充沛的算力资源,促进高校院所联合行业龙头企业,围绕产业技术创新需求,开展人工智能技术研发、科技成果转化等重点工作,落地科技创新成果的、培养关键人才。当前,在国家统筹规划下,已有20多个城市在规划和建设人工智能计算中心,华为也积极参与其中。深圳、武汉、中原、西安、成都、南京、杭州、沈阳、青岛、重庆已相继上线或试运营,已经累计为1200+企业、120+高校、70+科研单位提供了算力服务。 深圳“鹏城云脑II”于2020年10月正式上线,实现上线即饱和运营,其三项打榜获得世界第一:2021年7月,在IO500排行榜中,蝉联全系统输入输出和10节点规模系统两项世界冠军。其中,全系统输入输出性能超越第二名近20 倍,至今仍保持榜单第一。2021年11月,在AI  Perf500排行榜中,保持世界第一。依托鹏城云脑IIE级的澎湃算力,鹏城实验室与华为联合研发了全开源开放的两千亿参数中文NLP大模型鹏程.盘古,以及赋能生物医药探索的大模型鹏程神农。武汉人工智能计算中心基于昇腾AI基础软硬件建设,于2021年5月31日正式竣工并投入运营,上线即算力资源满负荷使用。于2022年2 月7日完成扩容,总算力达200P,并再次饱和运营。率先实践“一中心四平台”,开创“武 汉模式”。5个月从进场施工到正式投运,让业界见证了“武汉速度”,打造了全国人工智能示范标杆。目前,基于武汉人工智能计算中心,孵化了全球全球首个三模态大模型——紫东太初,全球首个遥感影像智能解译专用框架——武汉LuojiaNet,业界最大遥感影像样本数据集——武汉LuojiaSet,并成立多模态人工智能产业联盟和智能遥感开源生态联盟,为武汉孵化数百亿级智能遥感和多模态产业(大于300亿),截止到2022年9月底,已服务企业120+,孵化AI创新解决方案130+。3、产学研携手,共筑人工智能产业生态华为开放昇腾AI基础软硬件平台,包括Atlas系列硬件、异构计算架构CANN、全场景AI框架昇思MindSpore、昇腾应用使能MindX以及一站式开发平台ModelArts等,帮助伙伴和开发者高效使用AI能力,创新场景化AI应用,加速千行百业智能升级。 完成昇腾AI生态的初步构建,目前发展了100 万+开发者,在100多所高校开设昇腾AI相关的人工智能课程,发展700+行业合作伙伴,共同孵化了超过1600+解决方案,为中国人工智能产业繁荣提供一个强健、稳固的基石。 全场景AI框架昇思MindSpore是业界首个全自动并行的框架且具备全场景协同和全流程极简的特点。华为于2020年3月28日开源昇思 MindSpore框架,开源后获得国内外开发者的积极响应,访问量数千万,超过320万用户下载安装使用,在码云千万开源项目中综合排名第一,服务企业数量超过5500家,高校授课数量超过140所,超过40所科研机构选择昇思进行科研创新,社区贡献者达8000+,ModelZoo支持模型350+,获得业界首个AI框架类产品级CC安全认证和AI可信开源社区认证,成为国内最具创新活力的AI开源社区。 昇腾众智计划是华为围绕昇腾基础软件平台推出的一项生态合作计划,旨在汇聚高校、科研院所、企业等组织和机构的开发团队,通过项目合作方式,基于昇腾基础软硬件平台开发算子、网络模型及行业参考设计,不断丰富昇腾计算产业生态,为加速千行百业智能化升级贡献智慧与力量。目前,通过昇腾众智计划,已经完成4000多个AI模型、算子等。2022年,将继续投入2亿人民币激励基金,推出超过4000 个众智任务。 此外,在人才培养方面,教育部-华为“智能基座”产教融合协同育人基地项目由教育部、华为于2020年底联合发起,首批布局72所高校。 华为联合72所高校持续深化“智能基座”项目,在理工科专业深入实践,产教融合,把鲲鹏、昇腾、欧拉、高斯、昇思等根技术融入高校教学。目前,已赋能3000多个老师,开设 1500多门课程,覆盖了30多万学生,成立了72 个智能基座社团,出版约20本教材教辅书籍和12门精品慕课,并推出“智能基座”优秀教学资源奖励计划,激励更多教师百花齐放,自主开发教材和慕课。华为联合教育部已建设17个教育部智能基座课程虚拟教研室。大模型成为 AI 规模应用重要途径一、宏观趋势1、“大算力+大数据”正在催生大模型的快速发展,孵化系列行业新应用当前人工智能领域,大规模预训练模型得到长足发展和广泛关注,以大数据和大算力优势取代了一些小的算法模型,“大模型+大数据+大算力”成为迈向通用人工智能的一条可行路径。以GPT-3为代表的超大规模预训练模型,展示了一条通向通用人工智能的可能方向。在此背景下,我国超大规模预训练模型的发展如火如荼。2021年以来,国内相继发布了一系列大模型,华为与鹏城实验室联合发布了“鹏程盘古”系列超大规模预训练稠密模型,中科院自动化所发布了全球首个三模态大模型“紫东太初”,以及北京智源人工智能研究院发布了“悟道2.0”稀疏模型等。人工智能大模型可以实现在众多场景通用、泛化和规模化复制,减少对数据标注的依赖。随着超大规模预训练模型系统的开放,预训练基线智能水平大幅提升,行业人工智能应用不必从零开始开发,只需结合某个行业的领域数据进行调整,即可生成某个领域的相关模型,且得到良好的精度和性能。华为云发布的盘古预训练大模型已经在多个行业、100多个场景成功验证,包括能源、零售、金融、工业、医疗、环境、物流等等。其中,在能源领域,盘古预训练大模型帮助行业客户实现设备能耗的智能控制,可以节约电力成本50%;在金融行业中的异常财务检测,让模型精度提升20%以 上;在尘肺检测中,病例识别准确率提升22%等等。行业应用和算法高效流通可以让人工智 能应用和场景快速复制。2、科学计算正在从传统HPC进入科学智能新阶段 科学计算是继大模型之后,AI 发展的另一重要方向。此前,借助HPC高性能计算技术,科学计算对基础科学研究和国计民生行业发展起到重大推动作用。但是,随着求 解问题不断复杂化、高维化,科学计算仍然面临着维数灾难、计算尺度受限、理论突破与工程方法创新缓慢三大挑战。 因此,越来越多的科学家正在将AI技术引入到科学计算,科学计算正在从传统HPC进入到科学智能的新阶段。科学智能同时覆盖HPC与AI 两大技术领域,包含AI赋能机理计算、数据驱动AI计算、机理计算与AI计算相融合三大计算场景。第一个场景是AI赋能机理计算,它是将AI计算嵌入到机理计算中,实现AI对机理计算的加速。 第二个场景是数据驱动的AI计算,它则不依赖于数学机理,通过大量的数据输入,获得AI模型,通过AI计算获得结果。 第三个场景则是机理计算与AI计算相结合,它提升了科学计算的准确率和计算效率。 目前,科学计算已经进入科学智能新阶段,其创新技术已经在气象、新材料研发、生物信息等领域中得到应用。二、建议1、汇聚大模型发展要素,使能大模型从规划到落地当前人工智能技术趋势正朝着通用大模型方向发展,大模型具备更强泛化能力、可覆盖多业务场景,发展大模型也成为产学研各界共识。为了更好的推动大模型的发展,倡议汇聚大模型的发展要素,构建从规划、开发到产业化的大模型全流程使能体系,与产业界共筑中国大模型生态。 1)以大模型地图,统筹大模型有序发展首先,建议统筹规划大模型发展布局,汇聚大模型发展要素,在算力方面加强发展人工智能计算中心和算力网络,塑造我国人工智能大模型人才培养体系,同时以自主创新的人工智能根技术发展我国大模型;其次,强化场景创新,提升大模型的活跃度和影响力;最后,强化政府支持,鼓励产学研各界携手在产业条件具备的行业和区域加速大模型的产业落地。 2)打造大模型开发使能平台,让大模型易开发、易适配、易部署针对基础模型开发,建议打造大模型开发套件,通过算法开发、并行计算、存储优化等能力,实现大模型的高效开发;此外,建议开发大模型微调组件来适配行业应用,实现一键式微调和调优功能;在模型推理部署方面,还需要提供大模型部署套件,以实现分布式推理服务化、模型轻量化和动态加密部署功能。 3)成立大模型产业联盟,推动大模型应用落地技术维度端到端打通后,大模型下一个最为关键的问题是产业化落地。为了打通科研创新和产业应用的断点、促进大模型产业化落地,建议围绕大模型打通产学研用,建立大模型产业联盟,促进产业伙伴直接基于大模型孵化行业应用,实现产业聚集,让大模型真正赋能产业。 同时,产业联盟模式可以加速大模型从科研创新到行业落地的进程,在这样的大模型产业化落地过程中,各行业领域可以以更为丰富的数据和参数、更泛化的应用场景,来反哺大模型基础能力,让大模型更智能、场景适用性更好,从而迭代升级,为行业应用提供更大的支持,从而形成大模型创新-应用-迭代创新的产业正循环,开启了“炼大模型”的新范式。2、打造科学智能基础平台、携手产学研构筑科学智能生态,加速产业闭环过去单一、烟囱状的软硬件平台已无法满足科学智能需求。因此,华为建议打造原生科学智能基础软硬件平台,以实现极致性能、极简开发。华为认为,该基础平台在硬件方面应当拥有面向多样性算力的液冷整机柜,在软件方面包含业界领先的融合编程语言、编译器和操作系统,在开发使能方面则需要全场景统一的工具链,应用使能方面需要AI与HPC融合的框架和调度器。从底层硬件到上层应用协同创新,为科学研究提供“AI范式”。对于科学智能的产业生态建设,华为倡议成立科学智能创新联合体,汇聚政策、科研和产业优质资源,携手产学研伙伴,以科学智能新范式,拓展科学边界,助力技术创新,加速科研创新到产业落地进程,加强交叉学科建设和人才培养,构筑中国科学智能领先格局。三、解决方案1、基于大模型全流程使能体系, 使能大模型规划、开发、产业化 华为的人工智能大模型全流程使能体系,包含从大模型规划、大模型开发到大模型产业化的全流程,可端到端加速大模型产业落地,是以大模型产业化推动AI产业化的新范式。 1)规划大模型沙盘,与产业界共筑中国大模型创新高地从2020年开始,国内外顶尖公司的AI技术发展,越来越像一场比拼资金与人才的军备竞赛,推动AI竞争从2018年前后兴起的“大炼(小)模型”,进入到今天的“炼大模型”时代。大模型的优势不言而喻,但动则上百亿的大参数,也带来了训练成本太昂贵,模型修正不容易等难题,导致本来定位于“不再重复造 轮子”的大模型,面临重新陷入粗放式发展的境地。华为看到这一问题,积极联合产业界规划大模型沙盘,牵引产业界建设真正需要的大模型,共筑中国大模型创新高地。昇腾大模型沙盘从任务和应用类别两个维度出发,过去的一年,华为携手产业界伙伴基于昇腾AI先后推出 了各个领域有影响力的大模型,形成了基础大模型+行业大模型的整体布局。基础大模型面向多行业领域通用需求,行业大模型面向特定行业多应用场景,类似“新基建”中的信息基 础设施+融合基础设施,形成既有横向,也有纵深的立体支撑。 值得一提的是,考虑到“炼大模型”对大算力的强需求,华为与产业界在规划大模型沙盘的同时,全国20多个城市也都规划和建设了人工智能计算中心,并已开始将部分算力中心连点成片构建中国算力网——智算网络,以便基于它们的超强算力孵化AI大模型,大幅缩短大模型的训练时间。鹏程、武汉、秦岭、金陵系列大模型的快速推出,正得益于这一布局的强力支持。反过来,这些带有一定地域特色的大模型,又能够结合本地AI算力更好地服务产业。 2)打造大模型开发使能平台,让大模型易开发、易适配、易部署 依托长期的根技术积累,华为建立起了完整的大模型开发使能平台,加速从基础模型开发到推理部署的全流程,让大模型易开发、易适配、易部署。 首先,在基础模型开发方面,华为推出大模型开发套件,通过算法开发、并行计算、存储优化、断点续训重磅特性支撑大模型的高效开发。这其中,作为人工智能之“魂”,昇思 MindSpore自诞生起就有着鲜明的产业导向,可以在云、边、端等不同环境下进行开发部署,是并行维度业界最多、模型切分支持结构最全、单机容纳模型参数业界最强的的AI框架,这使其原生支持AI大模型训练,具备实现开发并行代码量降低80%、系统调整时间下降60%、仅用512卡就能完成十万亿模型参数训练的超强能力。 其次,在行业应用适配方面,华为推出基于MindX的大模型微调组件,其预置典型行业任务微调模板,通过小样本学习等手段,实现一键式微调和低参数调优,可以快速适配各种行业应用。目前紫东太初大模型就基于微调套件,提供了开放服务平台,已有40多个企业在平台上孵化了近60个产品解决方案,可以快捷的完成场景适配。 最后,在推理部署方面,推出基于MindStudio的大模型部署套件,其提供量化、剪枝、蒸馏等模型小型化能力,实现10倍级模型压缩率,同时分布式推理服务化能力还大幅提高吞吐率,此外模型动态加密技术,可在保证模型性能的同时对部署的模型进行加密,保护开发者的模型资产。3)从科研创新到行业落地,开创人工智能产业聚集新模式技术维度端到端打通后,大模型下一个最为关键的问题是产业化落地。去年底,基于全球首个智能遥感框架及数据集武汉LuoJia和全球首个三模态大模型紫东太初,产业各界成立了智能遥感开源生态联盟和多模态人工智能产业联盟,如今60余家伙伴已陆续孵化出多个行业解决方案。千博信息与中科院自动化所、华为三方联手, 基于昇腾AI基础软硬件平台以及紫东太初三模态大模型,打造出手语多模态模型并发布手语教考一体机,大幅改善了特殊人群的学习环境。此外,长安汽车、新华社技术局、浙江移动、爱奇艺等多模态人工智能产业联盟成员也分别打造了自己的多模态+智能座舱、多模态+新媒体内容检索平台、多模态+南宋御街数字人、多模态+视频摘要智能平台等场景化大模型及行业应用。智能遥感开源生态联盟下,基于武汉LuoJia的自然资源大脑、全场景类脑遥感矩阵、耕地保护自然监测平台、智能遥感解译平台等创新成果也不断涌现。大模型是AI产业加快发展的必然,也是科研创新走向产业应用的关键。华为联合产业界基于昇腾AI开启的“炼大模型”新范式,首次从大模型规划、开发到产业化构建了大模型全流程使能体系,拉通了技术生态与商业生态之间的桥梁,将加速我国大模型产业化发展,进而推动AI产业化和产业AI化,加速智能世界到来。2、打造原生支持科学智能的基础软硬件平台,原生构建科学智能新生态华为基于鲲鹏和昇腾AI,融合HPC和AI两大技术优势,通过创新的计算架构,打造原生科学智能基础软硬件平台,以全栈的创新实现科学智能基础设施的极致性能、极简开发。在硬件方面,华为推出科学智能全场景液冷“天成”多样性算力平台,其支持多样性算力灵活弹性部署,可实现液冷级能效,整系统 TCO降低20%,性能提升20~30%;在基础软件方面,华为发布毕昇C++编程语言并全面升级毕昇编译器,实现系统开发效率提升一倍,系统性能提升30~50%;在开发使能方面,华为升级全场景统一工具MindStudio,实现软件融合编程、编译和调优,可使科学智能全场景开发效率提升50%;在应用使能方面,昇思MindSpore 2.0升级为AI融合框架,原生支持科学智能以及多瑙融合调度器,其内嵌科学智能套件,让科学智能应用的开发、部署和调度更 高效,应用性能提升10~20倍,系统资源利用率提升15%。目前,科学智能基础软硬件平台已在新材料研发、大飞机设计、蛋白质结构预测等领域中应用。科学智能要实现产业化落地,还需要突破科研理论,创新工程方法,并构建产业生态,聚焦产业价值场景,打通科研创新、应用示范到产业推广的通道。在华为全联接大会2022中,华为倡议成立科学智能创新联合体,呼吁产学研各方共同携手,为大力发展科学智能生态奠定基础。科学智能基础软硬件平台绿色高效成为算力基础设施建设的关键诉求一、产业趋势1、在双碳目标下,算力基础设施的建设更加注重能耗未来算力将爆炸式增长,而数据中心是算力的主要载体,是新型基础设施节能降耗的关键环节,也是促进全社会降碳增效的有力抓手。传统数据中心能耗高、算力利用率低,在“3060 双碳”目标牵引下,国家对数据中心能耗提出更严格的要求,各省也出台了能耗指标及PUE要求,算力爆发式增长和降低碳排放的矛盾愈发突出,数据中心绿色化转型升级势在必行,算力基础设施的建设更加注重绿色高效。 2、从单领域创新走向系统级创新,实现绿色高效传统数据中心能耗控制往往是单领域创新优化,比如材料优化、供配电优化、空调制冷优化等,但提升效果有限,因此需要通过系统工程的创新,包括提升集成度、多领域全栈协同优化,比如通过AI技术对设备功率进行动态控制、IT设备与供配电及制冷设备全栈协同联动等,解决大规模数据中心建设能耗的难题,降低能耗,提高能效比和系统性能,实现绿色高效。二、建议1、建设模式从传统的部件堆叠逐步走向集群全栈一体化传统的数据中心都是分层建设、部件堆砌,导致建设周期长、能耗高、算力利用率低;集群计算中心为代表的新建数据中心,采用全栈一体化设计,从L0到L3整系统创新和协同优化,集中化建设、集约化使用,达到多样算力融合、模块化快速部署、液冷绿色高效,实现DC as a Computer。数据中心建设从部件堆叠走向全栈一体化2、散热方式逐步从传统风冷走向风液混合或全液冷数字经济时代,对高性能、高密度的计算需求逐渐增多。芯片和单机柜功率密度不断增大,传统散热方式难以为继,房间级空调方案,受限于物理空间和空气比热容低,难以支持每柜12KW以上机柜;行级空调方案,单机柜超过12KW时,需冗余配置空调以增加换热量,影响机房出柜率和TCO;超过15KW,风冷换热效率不足,难以支撑高功率元器件散热负荷,无法满足散热需求,液冷技术逐渐普及。液冷提供了高能效、高可靠、低碳环保的散热技术,逐渐成为算力基础设施的主流散热方式。 3、算力评估逐步从面向硬件的裸算力,走向面向业务的有效算力传统算力度量采用裸算力或部件级算力评估,如规格算力(芯片标称的算力规格)指标,单机或单服务器的性能评测,只关注IT计算设备的单台设备理论性能,无法完全体现集群系统或者算力中心整体性能。算力中心的真实性能需要综合考虑芯片、存储、网络以及平台软件各层协调所呈现的综合业务性能,也就是“有效算力”。有效算力通过评测真实业务性能表现,来衡量算力基础设施对业务的支撑效果,也就是业务实际可获得的算力水平。通过有效算力的模式来牵引算力基础设施的建设,提升算力的利用率,推动算力建设绿色高效诉求的落地,更好地支撑当地产业的发展。三、解决方案1、集群计算全栈协同优化,实现 DC as a computer集群计算解决方案,通过系统级工程创新,采用软硬件协同设计,包括应用软件与平台软件的协同优化,基础硬件平台及供电散热系统与平台软件的协同优化,实现从应用到平台到基础硬件平台、供电散热系统的纵向业务联动,数据中心全栈优化DC基础架构;采用数据中心整体设计,包括计算、存储、互联等各子系统协同优化,结合基础架构及通信网络优化使能平台及中间件持续提升,CPU/NPU/xPU多样性算力平台及融合调度,实现横向资源整合,突破硬件基础算力瓶颈。通过上述措施,软硬协同、纵向业务联动;整体优化、横向资源整合,提升数据中心的有效算力,提高能效比,实现DC as a Computer。集群计算解决方案整体架构算力网络将成为重要的算力供给方式一、产业趋势1、算力建设从分散化走向集约化在“东数西算”“网络强国”等战略的牵引下,在“3060双碳”目标牵引下,原来传统的分散化算力建设的弊端也越来越突出,建设周期长、能耗高、利用率低,不符合绿色高效的算力发展趋势。以人工智能计算中心、超算中心、一体化大数据中心等为代表的算力基础设施,成为国家新基建的重要组成,算力建设走向集约化,建设周期短、能耗低、算力利用率高。各地集中进行算力中心的建设,让算力像水和电一样,成为城市新型基础设施和公共资源。就像过去每个核心城市标配有机场、有高铁站,未来数字经济发展、智能化发展,核心城市都将标配一个公共算力中心,来以算力赋 能科研创新和产业发展。2 从算力中心,走向算力网络各地算力中心、算力基础设施陆续建成后,结合网络基础设施,就可以连成一张算力网络。像过去有电力网、通信网一样,在数字世界也一定会有一张算力网。以人工智能算力为例,2021年,中国科学技术信息研究所、新一代人工智能产业技术创新战略联盟(AITISA)、鹏城实验室共同发布《人工智能计算中心发展白皮书2.0》,指出了人工智能中心发展的新阶段——从人工智能计算中心走向人工智能算力网络。2021年底,在科技部的指导下,鹏城实验室牵头成立了人工智能算力网络推进联盟, 推进各地上线的人工智能计算中心连接成网上线运行。2022年6月,“中国算力网—智算网 络”一期正式上线,这是中国算力网络建设迈出的关键一步。各地的算力建设,开始从单独的算力中心,走向全国范围内的算力网络。二、行动建议1、加速算力基础设施的建设集约化建设绿色高效的算力基础设施,既是响应国家产业政策的需要,也是区域社会经济发展的需要。算力基础设施建设,需要结合当地的产业布局、科研实力及数字经济发展情况,以应用为导向,以信息技术与制造等传统技术深度融合为主线,推动人工智能计算、超级计算等先进技术的产业化与集成应用,发展高端智能产品,夯实核心基础,提升智能制造水平。促进算力服务相关各基础设施的建设,完善公共支撑体系,促进产业发展,推动制造强国和网络强国建设,助力实体经济转型升级。 结合各地实际情况,联合高校、科研院所、企业等行业技术力量,适度超前、加速建设算力基础设施,可以服务于千行百业,满足高校、 科研院所、企业不断增长的算力需求,以充沛算力,促进本地各行各业发展的诉求;同时,承担国家和区域里涉及国际民生的关键行业科研诉求,带来良好的经济效益和社会效益。 2、积极加入中国算力网,实现算力汇聚共享2022年6月,在科技部指导下,由鹏城实验室牵头的“中国算力网-智算网络”正式上线,伴随各地算力基础设施的不断建设。截止2022年11月,鹏城云脑、北京、成都、中原、合肥、 武汉、西安、济南、青岛、沈阳、广州、重 庆、昆明、福州、长沙、河北(廊坊)等20多个节点已接入中国算力网。多个人工智能计算中心间的AI算力调度与协同训练已完成初步验证,全国AI算力一张网初具雏形。 未来,各地的人工智能计算中心、超算中心、一体化大数据中心、算力枢纽、以及社会泛在云算力中心都可以接入中国算力网,共同构建一个支撑中国数字经济发展的强大算力底座,汇聚多种社会算力,实现绿色高效布局、泛在算力协同和全网交易流通,以东数西存、东数西算、东数西训为牵引,将逐步形成绿色集约的算力布局;汇聚多种社会算力,形成更加泛在的算力协同,并通过全网的算力交易流通, 弹性满足全网范围内的算力需求。让算力成为与水电一样,可“一点接入、即取即用”的社会级服务。三、解决方案1、算力网络架构创新,打造全网一台计算机算力网络需要以终为始,站在最终用户使用者的角度,打造全网一台计算机的架构,实现全程全网的社会级算力服务。算力网络的参考架构包括算网大脑及运营层、算网基础设施及使能层。1)单域自治使能层通过算力使能、网络使能和数据使能实现算力、网络和数据的单域管理与调度,确保单域独立交付与演进2)跨域编排实现跨域跨厂家的业务编排与调度,负责多云管理3)北向接口制定统一接口标准,各单域使能以服务化形式(云服务或Restful API)对外,供上层调用4)以云调算云纳管算,通过云服务来调度各种算力,重用云在大规模、跨域和异构算力的统 一调度能力非云化资源池由云管纳管,不参与全局调度;通过单域自治、跨域编排、北向接口、以云调算,实现“全网一台计算机”,为用户提供无所不在的算力服务。算力网络架构创新,打造全网一台计算机面向未来,华为将坚持围绕鲲鹏和昇腾,携手产业伙伴共建计算产业生态;坚持“硬件开 放、软件开源、使能伙伴和发展人才”,和产业伙伴共同构筑坚实的算力底座。 共建计算产业,共赢数智时代。
文章
存储  ·  人工智能  ·  算法  ·  安全  ·  大数据  ·  调度  ·  数据中心  ·  芯片  ·  开发者  ·  异构计算
2023-02-02
ADB 操作命令及用法
ADB 操作命令及用法一、ADB是什么?adb 称之为:Android 调试桥 (Android Debug Bridge )是一种允许模拟器或已连接的 Android 设备进行通信的命令行工具,可为各种设备操作提供便利,如安装和调试应用,并提供对 Unix shell(可用来在模拟器或连接的设备上运行各种命令)的访问。可以在Android SDK/platform-tools中找到 adb 工具或下载 ADB Kits 。注意: 有部分命令的支持情况可能与 Android 系统版本及定制 ROM 的实现有关。二、ADB有什么作用?ADB 是 Android SDK 里的一个工具, 用这个工具可以直接操作管理Android模拟器或者真实的Android设备。主要功能有:在设备上运行Shell命令;将本地APK软件安装至模拟器或Android设备;管理设备或手机模拟器上的预定端口;在设备或手机模拟器上复制或粘贴文件。ADB 是一个客户端-服务器程序程序,包括三个组件:客户端:该组件发送命令。客户端在开发计算机上运行。可以通过发出 adb 命令从命令行终端调用客户端。后台程序:该组件在设备上运行命令。后台程序在每个模拟器或设备实例上作为后台进程运行。服务器:该组件管理客户端和后台程序之间的通信。服务器在开发计算机上作为后台进程运行。三、ADB命令语法adb 命令的基本语法如下:adb [-d|-e|-s <serial-number>] <command>单一设备/模拟器连接若仅一个设备/模拟器连接时,可以省略掉 [-d|-e|-s <serial-number>] 这一部分,直接使用 adb <command>。多个设备/模拟器连接若有多个设备/模拟器连接,则需要为命令指定目标设备,下表是指定目标设备的命令选项:参数含义-d指定当前唯一通过 USB 连接的 Android 设备为命令目标-e指定当前唯一运行的模拟器为命令目标-s <serial-number>指定相应设备序列号的设备/模拟器为命令目标在多个设备/模拟器连接的情况下较常用的是 -s <serial-number> 参数,serial-number是指设备的设备序列号,可以通过 adb devices 命令获取。4.1 基本命令4.1.1 查看adb的版本信息adb version4.1.2 启动adbadb start-server一般无需手动执行此命令,在运行 adb 命令时若发现 adb server 没有启动会自动调起。4.1.3 停止adbadb kill-server4.1.4 以 root 权限运行 adbdadb root4.1.5 指定 adb server 的网络端口adb -P <port> start-server注意:ADB的默认端口为 5037。4.1.5 查询已连接的设备/模拟器列表adb devices4.2 应用管理4.2.1 查看应用列表查看应用列表的基本命令格式是:adb shell pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]adb shell pm list packages 后面可以跟一些可选参数进行过滤查看不同的列表,可用参数及含义如下:参数显示列表无所有应用-f显示应用关联的 apk 文件-d仅显示 disabled 的应用-e仅显示 enabled 的应用-s仅显示系统应用-3仅显示第三方应用-i显示应用的 installer-u包含已卸载应用<filter>包名包含 <filter> 字符串4.2.1.1 查看所有应用adb shell pm list packages4.2.1.2 查看系统应用adb shell pm list packages -s4.2.1.3 查看第三方应用adb shell pm list packages -34.2.1.4 查看已禁用应用adb shell pm list packages -d4.2.1.5 查看已启用应用adb shell pm list packages -e4.2.1.6 查看应用及其安装信息(安装来源)adb shell pm list packages -i4.2.1.7 查看应用及其未安装信息(安装来源)adb shell pm list packages -u4.2.1.8 查看应用及其相关联的文件adb shell pm list packages -f4.2.1.9 包名包含某字符串的应用比如要查看包名包含字符串 <package> 的应用列表,命令:adb shell pm list packages <package>4.2.2 安装应用安装应用的基本命令格式是:adb install [-l] [-r] [-t] [-s] [-d] [-g] <apk-file>adb install 后面可以跟一些可选参数来控制安装 APK 的行为,可用参数及含义如下:参数含义-l将应用安装到保护目录 /mnt/asec-r允许覆盖安装-t允许安装 AndroidManifest.xml 里 application 指定 android:testOnly="true" 的应用-s将应用安装到 sdcard-d允许降级覆盖安装-g授予所有运行时权限运行命令后可以看到输出内容,包含安装进度和状态,安装状态如下:Success:代表安装成功。Failure:代表安装失败。 APK 安装失败的情况有很多,Failure状态之后有安装失败输出代码。常见安装失败输出代码、含义及可能的解决办法如下:输出代码含义解决办法INSTALL_FAILED_ALREADY_EXISTS应用已经存在,或卸载了但没卸载干净adb install 时使用 -r 参数,或者先 adb uninstall <packagename> 再安装INSTALL_FAILED_INVALID_APK无效的 APK 文件INSTALL_FAILED_INVALID_URI无效的 APK 文件名确保 APK 文件名里无中文INSTALL_FAILED_INSUFFICIENT_STORAGE空间不足清理空间INSTALL_FAILED_DUPLICATE_PACKAGE已经存在同名程序INSTALL_FAILED_NO_SHARED_USER请求的共享用户不存在INSTALL_FAILED_UPDATE_INCOMPATIBLE以前安装过同名应用,但卸载时数据没有移除;或者已安装该应用,但签名不一致先 adb uninstall <packagename> 再安装INSTALL_FAILED_SHARED_USER_INCOMPATIBLE请求的共享用户存在但签名不一致INSTALL_FAILED_MISSING_SHARED_LIBRARY安装包使用了设备上不可用的共享库INSTALL_FAILED_REPLACE_COULDNT_DELETE替换时无法删除INSTALL_FAILED_DEXOPTdex 优化验证失败或空间不足INSTALL_FAILED_OLDER_SDK设备系统版本低于应用要求INSTALL_FAILED_CONFLICTING_PROVIDER设备里已经存在与应用里同名的 content providerINSTALL_FAILED_NEWER_SDK设备系统版本高于应用要求INSTALL_FAILED_TEST_ONLY应用是 test-only 的,但安装时没有指定 -t 参数INSTALL_FAILED_CPU_ABI_INCOMPATIBLE包含不兼容设备 CPU 应用程序二进制接口的 native codeINSTALL_FAILED_MISSING_FEATURE应用使用了设备不可用的功能INSTALL_FAILED_CONTAINER_ERROR1. sdcard 访问失败;2. 应用签名与 ROM 签名一致,被当作内置应用。1. 确认 sdcard 可用,或者安装到内置存储;2. 打包时不与 ROM 使用相同签名。INSTALL_FAILED_INVALID_INSTALL_LOCATION1. 不能安装到指定位置;2. 应用签名与 ROM 签名一致,被当作内置应用。1. 切换安装位置,添加或删除 -s 参数;2. 打包时不与 ROM 使用相同签名。INSTALL_FAILED_MEDIA_UNAVAILABLE安装位置不可用一般为 sdcard,确认 sdcard 可用或安装到内置存储INSTALL_FAILED_VERIFICATION_TIMEOUT验证安装包超时INSTALL_FAILED_VERIFICATION_FAILURE验证安装包失败INSTALL_FAILED_PACKAGE_CHANGED应用与调用程序期望的不一致INSTALL_FAILED_UID_CHANGED以前安装过该应用,与本次分配的 UID 不一致清除以前安装过的残留文件INSTALL_FAILED_VERSION_DOWNGRADE已经安装了该应用更高版本使用 -d 参数INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE已安装 target SDK 支持运行时权限的同名应用,要安装的版本不支持运行时权限INSTALL_PARSE_FAILED_NOT_APK指定路径不是文件,或不是以 .apk 结尾INSTALL_PARSE_FAILED_BAD_MANIFEST无法解析的 AndroidManifest.xml 文件INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION解析器遇到异常INSTALL_PARSE_FAILED_NO_CERTIFICATES安装包没有签名INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES已安装该应用,且签名与 APK 文件不一致先卸载设备上的该应用,再安装INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING解析 APK 文件时遇到 CertificateEncodingExceptionINSTALL_PARSE_FAILED_BAD_PACKAGE_NAMEmanifest 文件里没有或者使用了无效的包名INSTALL_PARSE_FAILED_BAD_SHARED_USER_IDmanifest 文件里指定了无效的共享用户 IDINSTALL_PARSE_FAILED_MANIFEST_MALFORMED解析 manifest 文件时遇到结构性错误INSTALL_PARSE_FAILED_MANIFEST_EMPTY在 manifest 文件里找不到找可操作标签(instrumentation 或 application)INSTALL_FAILED_INTERNAL_ERROR因系统问题安装失败INSTALL_FAILED_USER_RESTRICTED用户被限制安装应用INSTALL_FAILED_DUPLICATE_PERMISSION应用尝试定义一个已经存在的权限名称INSTALL_FAILED_NO_MATCHING_ABIS应用包含设备的应用程序二进制接口不支持的 native codeINSTALL_CANCELED_BY_USER应用安装需要在设备上确认,但未操作设备或点了取消在设备上同意安装INSTALL_FAILED_ACWF_INCOMPATIBLE应用程序与设备不兼容INSTALL_FAILED_TEST_ONLYAPK 文件是使用 Android Studio 直接 RUN 编译出来的文件通过 Gradle 的 assembleDebug 或 assembleRelease 重新编译,或者 Generate Signed APKdoes not contain AndroidManifest.xml无效的 APK 文件is not a valid zip file无效的 APK 文件Offline设备未连接成功先将设备与 adb 连接成功unauthorized设备未授权允许调试error: device not found没有连接成功的设备先将设备与 adb 连接成功protocol failure设备已断开连接先将设备与 adb 连接成功Unknown option: -sAndroid 2.2 以下不支持安装到 sdcard不使用 -s 参数No space left on device空间不足清理空间Permission denied ... sdcard ...sdcard 不可用signatures do not match the previously installed version; ignoring!已安装该应用且签名不一致先卸载设备上的该应用,再安装参考:PackageManager.javaadb install 实际是分三步完成:push apk 文件到 /data/local/tmp。调用 pm install 安装。删除 /data/local/tmp 下的对应 apk 文件。4.2.3 卸载应用卸载应用的基本命令格式是:adb uninstall [-k] <package-name><package-name> 表示应用的包名,-k 参数可选,表示卸载应用但保留数据和缓存目录。4.2.4 清理应用数据与缓存adb shell pm clear <package-name><package-name> 表示应用名包,这条命令的效果相当于在设置里的应用信息界面点击了「清除缓存」和「清除数据」。4.2.5 查看前台 Activityadb shell dumpsys activity activities | grep mFocusedActivity4.2.6 查看正在运行的 Servicesadb shell dumpsys activity services [<package-name>]<package-name> 参数不是必须的,指定 <package-name> 表示查看与某个包名相关的 Services,不指定表示查看所有 Services。<package-name> 不一定要给出完整的包名,可以仅给一部分,那么所给包名相关的 Services 都会列出来。4.2.7 查看应用详细信息adb shell dumpsys package <package-name><package-name> 表示应用包名。运行次命令的输出中包含很多信息,包括 Activity Resolver Table、Registered ContentProviders、包名、userId、安装后的文件资源代码等路径、版本信息、权限信息和授予状态、签名版本信息等。4.2.7 查看应用安装路径adb shell pm path <package-name>4.3 与应用交互与应用交互主要是使用 am <command> 命令,常用的 <command> 如下:command用途start [options] <intent>启动 <intent> 指定的 Activitystartservice [options] <intent>启动 <intent> 指定的 Servicebroadcast [options] <intent>发送 <intent> 指定的广播force-stop <package-name>停止 <package-name> 相关的进程<intent> 参数很灵活,和写 Android 程序时代码里的 Intent 相对应。用于决定 intent 对象的选项如下:参数含义-a <action>指定 action,比如 android.intent.action.VIEW-c <category>指定 category,比如 android.intent.category.APP_CONTACTS-n <component>指定完整 component 名,用于明确指定启动哪个 Activity<intent> 里还能带数据,就像写代码时的 Bundle 一样:参数含义--esn <extra-key>null 值(仅 key 名)`-e\--es `--ez <extra-key> <extra-boolean-value>boolean 值--ei <extra-key> <extra-int-value>integer 值--el <extra-key> <extra-long-value>long 值--ef <extra-key> <extra-float-value>float 值--eu <extra-key> <extra-uri-value>URI--ecn <extra-key> <extra-component-name-value>component name--eia <extra-key> <extra-int-value>[,<extra-int-value...]integer 数组--ela <extra-key> <extra-long-value>[,<extra-long-value...]long 数组4.3.1 启动应用/ 调起 Activityadb shell am start [options] <intent>例如:adb shell am start -a android.settings.SETTINGS # 打开系统设置页面 adb shell am start -a android.intent.action.DIAL -d tel:10086 # 打开拨号页面 adb shell am start -n com.android.mms/.ui.ConversationList # 打开短信会话列表options 是一些改变其行为的选项,支持的可选参数及含义如下:选项含义-D启用调试-W等待启动完成--start-profiler file启动分析器并将结果发送到 file-P file类似于 --start-profiler,但当应用进入空闲状态时分析停止-R count重复 Activity 启动次数-S启动 Activity 前强行停止目标应用--opengl-trace启用 OpenGL 函数的跟踪--user user_id \current4.3.2 调起 Serviceadb shell am startservice [options] <intent>一个典型的用例是,若设备上原本应该显示虚拟按键但是没有显示,可以试试这个:adb shell am startservice -n com.android.systemui/.SystemUIService4.3.3 停止 Serviceadb shell am stopservice [options] <intent>4.3.4 强制停止应用adb shell am force-stop <packagename>4.4 文件管理4.4.1 从模拟器/设备下载指定的文件到计算机从模拟器/设备下载指定的文件到计算机的基本命令格式是:adb pull <remote> [local]参数说明:remote: 模拟器/设备里的文件路径local:计算机上的目录,参数可以省略,默认复制到当前目录例如,将 /sdcard/<package>.apk 下载到当前目录:adb pull /sdcard/<package>.apk将 /sdcard/<package>.apk 下载到相应目录(目录需存在):adb pull /sdcard/<package>.apk D:\Download提示:该操作可结合上文所描述的查看应用安装路径的操作以达到导出相应的应用软件的目的。4.4.2 将指定的文件从计算机上传到模拟器/设备将指定的文件从计算机上传到模拟器/设备的基本命令格式是:adb push <local> <remote>参数说明:local:计算机上的文件路径remote: 模拟器/设备里的目录例如,将 D:\Download\下载到设备的/sdcard/music/目录:adb push D:\Download\ /sdcard/music/4.4.4 列出指定目录的内容列出模拟器/设备上指定目录的内容的基本命令格式是:adb shell ls [options] <directory><directory> 表示指定目录,可以省略,表示列出根目录下的所有文件和目录。 adb shell ls 后面可以跟一些可选参数进行过滤查看不同的列表,可用参数及含义如下:参数显示列表无列出目录下的所有文件和目录-a列出目录下的所有文件(包括隐藏的)-i列出目录下的所有文件和索引编号-s列出目录下的所有文件和文件大小-n列出目录下的所有文件及其 UID和 GID-R列出目录下的所有子目录中的文件4.4.5 切换到目标目录adb shell cd <directory>第一步:执行adb shell命令;第二步:执行cd <directory>命令切换到目标目录。4.4.6 删除文件或目录adb shell rm [options] <files or directory>第一步:执行adb shell命令;第二步:执行rm [options] <files or directory>命令删除文件或目录。rm 后面可以跟一些可选参数进行不同的操作,可用参数及含义如下:参数含义无删除文件-f强制删除文件,系统不提示-r强制删除指定目录中的所有文件和子目录-d删除指定目录,即使它是一个非空目录-i交互式删除,删除前提示rm -d 等同于 rmdir 命令,有些版本不包含-d 参数。4.4.7 创建目录adb shell mkdir [options] <directory-name>第一步:执行adb shell命令;第二步:执行mkdir [options] <directory-name>命令创建目录。 mkdir 后面可以跟一些可选参数进行不同的操作,可用参数及含义如下:参数含义无创建指定目录-m创建指定目录并赋予读写权限-p创建指定目录及其父目录4.4.8 创建空文件或改变文件时间戳adb shell touch [options] <file>第一步:执行adb shell命令;第二步:执行touch [options] <file>命令创建空文件或改变文件时间戳。可通过ls -n <directory> 命令查看文件的时间。4.4.9 输出当前目录路径adb shell pwd第一步:执行adb shell命令;第二步:执行pwd命令输出当前目录路径。4.4.10 复制文件和目录adb shell cp [options] <source> <dest>第一步:执行adb shell命令;第二步:执行cp [options] <source> <dest>命令复制文件和目录。参数说明:source:源文件路径dest: 目标文件路径4.4.11 移动或重命名文件adb shell mv [options] <source> <dest>第一步:执行adb shell命令;第二步:执行mv [options] <source> <dest>命令移动或重命名文件。参数说明:source:源文件路径dest: 目标文件路径4.5 网络管理4.5.1 查看网络统计信息adb shell netstat也可以将网络统计信息输出到指定文件:adb shell netstat > <file-path>例如,可以通过 adb shell netstat > D:\netstat.log 将日志输出到 D:\netstat.log 中。4.5.2 测试两个网络间的连接和延迟ping 命令的格式如下:adb shell ping [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface] [-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos] [-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option] [-w deadline] [-W timeout] [hop1 ...] destination例如,ping一个域名:adb shell ping <域名>不结束的话会一直ping下去,可以按 Ctrl + C 停止ping操作。也可以指定ping的次数:adb shell ping -c 4 <域名>4.5.3 通过配置文件配置和管理网络连接netcfg 命令的格式如下:adb shell netcfg [<interface> {dhcp|up|down}]输出示例:rmnet_ims10 DOWN 0.0.0.0/0 0x00001002 rmnet_ims00 DOWN 0.0.0.0/0 0x00001002 rmnet_tun04 DOWN 0.0.0.0/0 0x00001002 rmnet_tun03 DOWN 0.0.0.0/0 0x00001002 rmnet_tun02 DOWN 0.0.0.0/0 0x00001002 rmnet_tun01 DOWN 0.0.0.0/0 0x00001002 rmnet_tun00 DOWN 0.0.0.0/0 0x00001002 rmnet_tun14 DOWN 0.0.0.0/0 0x00001002 rmnet_tun13 DOWN 0.0.0.0/0 0x00001002 rmnet_tun12 DOWN 0.0.0.0/0 0x00001002 rmnet_tun11 DOWN 0.0.0.0/0 0x00001002 rmnet_tun10 DOWN 0.0.0.0/0 0x00001002 rmnet1 DOWN 0.0.0.0/0 0x00001002 rmnet0 DOWN 0.0.0.0/0 0x00001002 rmnet4 DOWN 0.0.0.0/0 0x00001002 rmnet3 DOWN 0.0.0.0/0 0x00001002 rmnet2 DOWN 0.0.0.0/0 0x00001002 rmnet6 DOWN 0.0.0.0/0 0x00001002 rmnet5 DOWN 0.0.0.0/0 0x00001002 dummy0 UP 0.0.0.0/0 0x000000c3 rmnet_r_ims10 DOWN 0.0.0.0/0 0x00001002 rmnet_r_ims00 DOWN 0.0.0.0/0 0x00001002 rmnet_emc0 DOWN 0.0.0.0/0 0x00001002 lo UP 127.0.0.1/8 0x00000049 sit0 DOWN 0.0.0.0/0 0x00000080 wlan0 UP 10.0.38.176/23 0x00001043 复制代码4.5.4 显示、操作路由、设备、策略路由和隧道ip 命令的格式如下:adb shell ip [ options ] objectoptions := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |-f[amily] { inet | inet6 | ipx | dnet | link } |-l[oops] { maximum-addr-flush-attempts } |-o[neline] | -t[imestamp] | -b[atch] [filename] |-rc[vbuf] [size]}object := { link | addr | addrlabel | route | rule | neigh | ntable |tunnel | tuntap | maddr | mroute | mrule | monitor | xfrm |netns | l2tp }options 是一些修改ip行为或者改变其输出的选项。所有的选项都是以-字符开头,分为长、短两种形式,支持的可选参数及含义如下:选项含义-V,-Version打印ip的版本并退出-s,-stats,-statistics输出更为详尽的信息(若这个选项出现两次或者多次,输出的信息将更为详尽)-f,-family强调使用的协议种类(包括:inet、inet6或者link)-4是-family inet的简写-6是-family inet6的简写-0是-family link的简写-o,-oneline对每行记录都使用单行输出,回行用字符代替-r,-resolve查询域名解析系统,用获得的主机名代替主机IP地址object 是要管理或者获取信息的对象。目前ip认识的对象包括:参数显示列表link网络设备address一个设备的协议(IP或者IPV6)地址neighbourARP或者NDISC缓冲区条目route路由表条目rule路由策略数据库中的规则maddress多播地址mroute多播路由缓冲区条目tuntap管理 TUN/TAP 设备netns管理网络空间例如,查看 WiFi IP 地址:adb shell ip -f inet addr show wlan04.6 模拟按键/输入在 adb shell 里有个很实用的命令叫 input,通过它可以做一些有趣的事情。可以执行adb shell input命令查看完整 help 信息如下:Usage: input [<source>] <command> [<arg>...] The sources are: dpad keyboard mouse touchpad gamepad touchnavigation joystick touchscreen stylus trackball The commands and default sources are: text <string> (Default: touchscreen) keyevent [--longpress] <key code number or name> ... (Default: keyboard) tap <x> <y> (Default: touchscreen) swipe <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen) draganddrop <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen) press (Default: trackball) roll <dx> <dy> (Default: trackball)比如使用 adb shell input keyevent <keycode> 命令,不同的 keycode 能实现不同的功能,完整的 keycode 列表详见 KeyEvent,摘引部分觉得有意思的如下:keycode含义3HOME 键4返回键5打开拨号应用6挂断电话24增加音量25降低音量26电源键27拍照(需要在相机应用里)64打开浏览器82菜单键85播放/暂停86停止播放87播放下一首88播放上一首122移动光标到行首或列表顶部123移动光标到行末或列表底部126恢复播放127暂停播放164静音176打开系统设置187切换应用207打开联系人208打开日历209打开音乐210打开计算器220降低屏幕亮度221提高屏幕亮度223系统休眠224点亮屏幕231打开语音助手276若没有 wakelock 则让系统休眠下面是 input 命令的一些用法举例。4.6.1 电源键adb shell input keyevent 26执行效果相当于按电源键。4.6.2 菜单键adb shell input keyevent 824.6.3 HOME 键adb shell input keyevent 34.6.4 返回键adb shell input keyevent 44.6.5 音量控制增加音量:adb shell input keyevent 24降低音量:adb shell input keyevent 25静音:adb shell input keyevent 164.6.6 媒体控制播放/暂停:adb shell input keyevent 85停止播放:adb shell input keyevent 86播放下一首:adb shell input keyevent 87播放上一首:adb shell input keyevent 88恢复播放:adb shell input keyevent 126暂停播放:adb shell input keyevent 1274.6.7 点亮/熄灭屏幕点亮屏幕:adb shell input keyevent 224熄灭屏幕:adb shell input keyevent 2234.6.8 滑动解锁若锁屏没有密码,是通过滑动手势解锁,那么可以通过 input swipe 来解锁。命令(向上滑动手势解锁举例):adb shell input swipe 300 1000 300 500参数 300 1000 300 500 分别表示起始点x坐标 起始点y坐标 结束点x坐标 结束点y坐标。4.6.9 输入文本在焦点处于某文本框时,可以通过 input 命令来输入文本。adb shell input text hello4.7 日志打印Android 系统的日志分为两部分,底层的 Linux 内核日志输出到 /proc/kmsg,Android 的日志输出到 /dev/log。4.7.1 Android 日志查看 Android 设备系统属性的基本命令格式是:adb logcat [option] [filter-specs]若需要停止 logcat 日志打印,可以按 Ctrl + C 停止日志监控。4.7.1.1 按级别过滤日志按级别过滤日志的基本命令格式是:adb logcat [filter-specs]Android 的日志分为如下几个优先级(priority):级别含义*:V过滤仅显示 Verbose 及以上级别(优先级最低)*:D过滤仅显示 Debug 及以上级别*:I过滤仅显示 Info 及以上级别*:W过滤仅显示 Warning 及以上级别*:E过滤仅显示 Error 及以上级别*:F过滤仅显示 Fatal 及以上级别*:S过滤仅显示 Silent 及以上级别(优先级最高,什么也不输出)按某级别过滤日志则会将该级别及以上的日志输出。比如,命令:adb logcat *:W会将 Warning、Error、Fatal 和 Silent 日志输出。注意: 在 macOS 下需要给 *:W 这样以 * 作为 tag 的参数加双引号,如 adb logcat "*:W",不然会报错 no matches found: *:W。4.7.1.2 按 tag 和级别过滤日志按 tag 和级别过滤日志的基本命令格式是:adb logcat [tag:level] [tag:level] ...比如,命令:adb logcat ActivityManager:I MyApp:D *:S表示输出 tag ActivityManager 的 Info 以上级别日志,输出 tag MyApp 的 Debug 以上级别日志,及其它 tag 的 Silent 级别日志(即屏蔽其它 tag 日志)。4.7.1.3 将日志格式化输出可以用 adb logcat -v <format> 选项指定日志输出格式。日志支持按以下几种 <format>:参数显示格式brief<priority>/<tag>(<pid>): <message>process<priority>(<pid>) <message>tag<priority>/<tag>: <message>raw<message>time<datetime> <priority>/<tag>(<pid>): <message>threadtime<datetime> <pid> <tid> <priority> <tag>: <message>long[ <datetime> <pid>:<tid> <priority>/<tag> ] <message>日志格式默认为 brief,指定格式可与上面的过滤同时使用。比如:adb logcat -v long ActivityManager:I *:S4.7.1.3 清空已存在的日志adb logcat -c4.7.1.4 将日志显示在控制台adb logcat -d4.7.1.5 将日志输出到文件adb logcat -f <file-path>4.7.1.6 加载一个可使用的日志缓冲区供查看adb logcat -b <Buffer>Android log 输出量巨大,特别是通信系统的log,因此,Android把log输出到不同的缓冲区中,目前定义了四个log缓冲区:缓冲区含义Radio输出通信系统的 logSystem输出系统组件的 logEvent输出 event 模块的 logMain所有 java 层的 log 以及不属于上面3层的 log缓冲区主要给系统组件使用,一般的应用不需要关心,应用的log都输出到main缓冲区中。默认log输出(不指定缓冲区的情况下)是输出System和Main缓冲区的log。4.7.1.7 打印指定日志缓冲区的大小adb logcat -g4.7.2 内核日志adb shell dmesg输出示例:<6>[14201.684016] PM: noirq resume of devices complete after 0.982 msecs <6>[14201.685525] PM: early resume of devices complete after 0.838 msecs <6>[14201.753642] PM: resume of devices complete after 68.106 msecs <4>[14201.755954] Restarting tasks ... done. <6>[14201.771229] PM: suspend exit 2016-08-28 13:31:32.679217193 UTC <6>[14201.872373] PM: suspend entry 2016-08-28 13:31:32.780363596 UTC <6>[14201.872498] PM: Syncing filesystems ... done. 复制代码中括号里的 [14201.684016] 代表内核开始启动后的时间,单位为秒。通过内核日志可以做一些事情,比如衡量内核启动时间,在系统启动完毕后的内核日志里找到 Freeing init memory 那一行前面的时间就是。4.8 查看 Android 设备系统属性查看 Android 设备系统属性的基本命令格式是:adb shell getprop [options]除了可以查看 Android 设备系统属性之外,还可以设置系统属性,设置系统属性的基本命令格式是:adb shell setprop <key> <value>4.8.1 查看设备型号adb shell getprop ro.product.model4.8.2 查看设备电池状况adb shell dumpsys battery输出示例:Current Battery Service state: AC powered: false USB powered: true Wireless powered: false status: 2 health: 2 present: true level: 44 scale: 100 voltage: 3872 temperature: 280 technology: Li-poly其中 scale 代表最大电量,level 代表当前电量。上面的输出表示还剩下 44% 的电量。4.8.3 查看设备屏幕分辨率adb shell wm size输出示例:Physical size: 1080x1920该设备屏幕分辨率为 1080px * 1920px。若使用命令修改过,那输出可能是:Physical size: 1080x1920 Override size: 480x1024表明设备的屏幕分辨率原本是 1080px * 1920px,当前被修改为 480px * 1024px。4.8.4 查看设备屏幕密度adb shell wm density输出示例:Physical density: 360该设备屏幕密度为 360dpi。若使用命令修改过,那输出可能是:Physical density: 420 Override density: 360表明设备的屏幕密度原来是 420dpi,当前被修改为 360dpi。4.8.5 查看设备显示屏参数adb shell dumpsys window displays输出示例:WINDOW MANAGER DISPLAY CONTENTS (dumpsys window displays) Display: mDisplayId=0 init=1080x1920 420dpi cur=1080x1920 app=1080x1794 rng=1080x1017-1810x1731 deferred=false layoutNeeded=false 复制代码其中 mDisplayId 为 显示屏编号,init 是初始分辨率和屏幕密度,app 的高度比 init 里的要小,表示屏幕底部有虚拟按键,高度为 1920 - 1794 = 126px 合 42dp。4.8.6 查看设备 android_idadb shell settings get secure android_id输出示例:5a3a3aa2c30421844.8.7 查看设备IMEI在 Android 4.4 及以下版本可通过如下命令获取 IMEI:adb shell dumpsys iphonesubinfo输出示例:Phone Subscriber Info: Phone Type = GSM Device ID = 860955027785041其中的 Device ID 就是 IMEI。而在 Android 5.0 及以上版本里这个命令输出为空,得通过其它方式获取了(需要 root 权限):adb shell su service call iphonesubinfo 1把里面的有效内容提取出来就是 IMEI 了,比如这里的是 890956027785041。参考:adb shell dumpsys iphonesubinfo not working since Android 5.0 Lollipop4.8.8 查看设备 Android 系统版本adb shell getprop ro.build.version.release4.8.9 查看设备 IP 地址adb shell ifconfig | grep Mask在有的设备上这个命令没有输出,若设备连着 WiFi,可以使用如下命令来查看局域网 IP:adb shell ifconfig wlan0若以上命令仍然不能得到期望的信息,那可以试试以下命令(部分系统版本里可用):adb shell netcfg4.8.10 查看设备 Mac 地址adb shell cat /sys/class/net/wlan0/address这查看的是局域网 Mac 地址,移动网络或其它连接的信息可以通过前面的小节「IP 地址」里提到的 adb shell netcfg 命令来查看。4.8.11 查看设备 CPU 信息adb shell cat /proc/cpuinfo4.8.12 查看设备内存信息adb shell cat /proc/meminfo4.8.13 查看设备更多硬件与系统属性设备的更多硬件与系统属性可以通过如下命令查看:adb shell cat /system/build.prop这会输出很多信息,包括前面几个小节提到的「型号」和「Android 系统版本」等。输出里还包括一些其它有用的信息,也可通过 adb shell getprop <属性名> 命令单独查看,列举一部分属性如下:属性名含义ro.build.version.sdkSDK 版本ro.build.version.releaseAndroid 系统版本ro.build.version.security_patchAndroid 安全补丁程序级别ro.product.model型号ro.product.brand品牌ro.product.name设备名ro.product.board处理器型号ro.product.cpu.abilistCPU 支持的 abi 列表[节注一]persist.sys.isUsbOtgEnabled是否支持 OTGdalvik.vm.heapsize每个应用程序的内存上限ro.sf.lcd_density屏幕密度注意:一些小厂定制的 ROM 可能修改过 CPU 支持的 abi 列表的属性名。若用 ro.product.cpu.abilist 属性名查找不到,可以这样试试:adb shell cat /system/build.prop | grep ro.product.cpu.abi4.9 修改设置注意: 修改设置之后,运行恢复命令有可能显示仍然不太正常,可以运行 adb reboot 重启设备,或手动重启。修改设置的原理主要是通过 settings 命令修改 /data/data/com.android.providers.settings/databases/settings.db 里存放的设置值。4.9.1 修改分辨率adb shell wm size 480x1024表示将分辨率修改为 480px * 1024px。恢复原分辨率命令:adb shell wm size reset4.9.2 修改屏幕密度adb shell wm density 160表示将屏幕密度修改为 160dpi。恢复原屏幕密度命令:adb shell wm density reset手机分辨率对照表宽×高(标准值)240×320320×480480×800720×12801080×19201440×2560DPI等级LDPIMDPIHDPIXHDPIXXHDPIXXXHDPIDPI数值120160240320480640对应比例34681216PX0.7511.52344.9.3 修改显示区域adb shell wm overscan 0,0,0,200四个数字分别表示距离左、上、右、下边缘的留白像素,以上命令表示将屏幕底部 200px 留白。恢复原显示区域命令:adb shell wm overscan reset4.9.4 修改关闭 USB 调试模式adb shell settings put global adb_enabled 0用命令恢复不了了,毕竟关闭了 USB 调试 adb 就连接不上 Android 设备了。去设备上手动恢复吧:「设置」-「开发者选项」-「Android 调试」。4.9.5 修改允许/禁止访问非 SDK API允许访问非 SDK API:adb shell settings put global hidden_api_policy_pre_p_apps 1 adb shell settings put global hidden_api_policy_p_apps 1禁止访问非 SDK API:adb shell settings delete global hidden_api_policy_pre_p_apps adb shell settings delete global hidden_api_policy_p_apps不需要设备获得 Root 权限。命令最后的数字的含义:值含义0禁止检测非 SDK 接口的调用。该情况下,日志记录功能被禁用,并且令 strict mode API,即 detectNonSdkApiUsage() 无效。不推荐。1仅警告——允许访问所有非 SDK 接口,但保留日志中的警告信息,可继续使用 strick mode API。2禁止调用深灰名单和黑名单中的接口。3禁止调用黑名单中的接口,但允许调用深灰名单中的接口。4.9.6 修改状态栏和导航栏的显示隐藏adb shell settings put global policy_control <key-values><key-values> 可由如下几种键及其对应的值组成,格式为 <key1>=<value1>:<key2>=<value2>。key含义immersive.full同时隐藏immersive.status隐藏状态栏immersive.navigation隐藏导航栏immersive.preconfirms?这些键对应的值可则如下值用逗号组合:value含义apps所有应用*所有界面package-name指定应用-package-name排除指定应用例如:adb shell settings put global policy_control immersive.full=*表示设置在所有界面下都同时隐藏状态栏和导航栏。adb shell settings put global policy_control immersive.status=com.package1,com.package2:immersive.navigation=apps,-com.package3表示设置在包名为 com.package1 和 com.package2 的应用里隐藏状态栏,在除了包名为 com.package3 的所有应用里隐藏导航栏。4.11 实用功能4.11.1 屏幕截图截图保存到电脑:adb exec-out screencap -p > sc.png若 adb 版本较老,无法使用 exec-out 命令,建议更新 adb 版本。无法更新的话可以使用以下麻烦点的办法:先截图保存到设备里:adb shell screencap -p /sdcard/sc.png然后将 png 文件导出到电脑:adb pull /sdcard/sc.png可以使用 adb shell screencap -h 查看 screencap 命令的帮助信息,下面是两个有意义的参数及含义:参数含义-p指定保存文件为 png 格式-d display-id指定截图的显示屏编号(有多显示屏的情况下)实测若指定文件名以 .png 结尾时可以省略 -p 参数;否则需要使用 -p 参数。若不指定文件名,截图文件的内容将直接输出到 stdout。另外一种一行命令截图并保存到电脑的方法: Linux 和 Windowsadb shell screencap -p | sed "s/\r$//" > sc.pngMac OS Xadb shell screencap -p | gsed "s/\r$//" > sc.png这个方法需要用到 gnu sed 命令,在 Linux 下直接就有,在 Windows 下 Git 安装目录的 bin 文件夹下也有。若找不到该命令,可以下载 sed for Windows 并将 sed.exe 所在文件夹添加到 PATH 环境变量里。而在 Mac 下使用系统自带的 sed 命令会报错:sed: RE error: illegal byte sequence需要安装 gnu-sed,然后使用 gsed 命令:brew install gnu-sed4.11.2 录制屏幕录制屏幕以 mp4 格式保存到 /sdcard:adb shell screenrecord /sdcard/filename.mp4需要停止时按 Ctrl-C,默认录制时间和最长录制时间都是 180 秒。若需要导出到电脑:adb pull /sdcard/<filename>.mp4可以使用 adb shell screenrecord --help 查看 screenrecord 命令的帮助信息,下面是常见参数及含义:参数含义--size WIDTHxHEIGHT视频的尺寸,比如 1280x720,默认是屏幕分辨率。--bit-rate RATE视频的比特率,默认是 4Mbps。--time-limit TIME录制时长,单位秒。--verbose输出更多信息。4.11.3 查看连接过的 WiFi 密码注:需要 root 权限。adb shell su cat /data/misc/wifi/*.conf4.11.4 设置系统日期和时间注:需要 root 权限。adb shell su date -s yyyyMMdd.HHmmss注意:表示将系统日期和时间更改为 yyyy 年 MM 月 dd 日 HH 点 mm 分 ss 秒。设置系统日期和时间后,需使用busybox hwclock -w命令以使得系统时间同步硬件时钟, 否则肯会出现重启不生效的问题。4.11.4.1 读取系统时间adb shell "date"4.11.4.2 读取系统时区adb shell "getprop persist.sys.timezone"4.11.4.3 设置系统时区adb shell "setprop persist.sys.timezone <Region>/<City>"<Region>/<City>为设置的时区,即<地区名>/<城市名>。以设置上海时区为例:Asia/Shanghai。注意:时区更新组件中平台服务功能 (timezone.RulesManagerService)默认处于停用状态。OEM 必须通过相应配置启用该功能。RulesManagerService 在系统服务器进程中运行,并通过写入 /data/misc/zoneinfo/staged 来暂存时区更新操作。RulesManagerService 还可以替换或删除已经暂存的操作。4.11.4.4 设置系统 NTP 服务器adb shell "settings put global ntp_server <NTP服务器地址>"手机重启联网后,将会自动校时。国内常见的NTP服务器地址如下:东北大学:ntp.synet.edu.cnntp.neu.edu.cn上海交大:ntp.sjtu.edu.cnpool.ntp.org:cn.pool.ntp.org阿里云NTP服务器:ntp1.aliyun.comntp2.aliyun.comntp3.aliyun.comntp4.aliyun.comntp5.aliyun.comntp6.aliyun.comntp7.aliyun.com可使用如下命令以查询是否设置成功:adb shell settings get global ntp_server4.11.5 重启手机adb reboot4.11.6 检测设备是否已 rootadb shell su此时命令行提示符是 $ 则表示没有 root 权限,是 # 则表示已 root。4.11.7 使用 Monkey 进行压力测试Monkey 可以生成伪随机用户事件来模拟单击、触摸、手势等操作,可以对正在开发中的程序进行随机压力测试。简单用法:adb shell monkey -p <packagename> -v 500表示向 <packagename> 指定的应用程序发送 500 个伪随机事件。Monkey 的详细用法参考 官方文档。4.11.8 开启/关闭 WiFi注:需要 root 权限。开启 WiFi:adb root adb shell svc wifi enable关闭 WiFi:adb root adb shell svc wifi disable若执行成功,输出为空;若未取得 root 权限执行此命令,将执行失败,输出 Killed。4.12 安全相关命令4.12.1 启用/禁用 SELinux启用 SELinuxadb root adb shell setenforce 1禁用 SELinuxadb root adb shell setenforce 04.12.2 启用/禁用 dm_verity启用 dm_verityadb root adb enable-verity禁用 dm_verityadb root adb disable-verity4.13 更多 adb shell 命令Android 系统是基于 Linux 内核的,所以 Linux 里的很多命令在 Android 里也有相同或类似的实现,在 adb shell 里可以调用。本文档前面的部分内容已经用到了 adb shell 命令。4.14.1 查看进程状态adb shell ps输出信息各列含义:列名含义USER所属用户PID进程 IDPPID父进程 IDNAME进程名4.14.2 查看处理器实时状态adb shell top [-m max_procs] [-n iterations] [-d delay] [-s sort_column] [-t] [-h]adb shell top 后面可以跟一些可选参数进行过滤查看不同的列表,可用参数及含义如下:参数含义-m最多显示多少个进程-n刷新多少次后退出-d刷新时间间隔(单位秒,默认值5)-s按某列排序(可用col值:cpu, vss, rss, thr)-t显示线程信息-h显示帮助文档输出信息各列含义:列名含义PID进程 IDPR优先级CPU%当前瞬间占用 CPU 百分比S进程状态(R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程)#THR线程数VSSVirtual Set Size 虚拟耗用内存(包含共享库占用的内存)RSSResident Set Size 实际使用物理内存(包含共享库占用的内存)PCY调度策略优先级,SP_BACKGROUND/SPFOREGROUNDUID进程所有者的用户 IDNAME进程名4.14.3 查看进程 UID有两种方案:方案一:adb shell dumpsys package <packagename> | grep userId=例如:adb shell dumpsys package org.mazhuang.guanggoo | grep userId= userId=10394方案二:通过 ps 命令找到对应进程的 pid 之后 adb shell cat /proc/<pid>/status | grep Uid 如:adb shellps | grep org.mazhuang.guanggoo u0_a394 28635 770 1795812 78736 SyS_epoll_ 0000000000 S org.mazhuang.guanggoocat /proc/28635/status | grep Uid Uid: 10394 10394 10394 10394附录:MIUI 预装软件列表(含系统组件)软件名称软件包名一体化位置信息com.android.location.fused万能遥控com.duokan.phone.remotecontroller三方应用异常分析com.miui.thirdappassistant下载管理com.android.providers.downloads.ui下载管理程序com.android.providers.downloads主屏幕提示com.android.protips主题壁纸com.android.thememanager今日头条com.ss.android.article.news传送门com.miui.contentextension健康com.mi.health全球上网com.miui.virtualsim关机闹钟com.qualcomm.qti.poweroffalarm内容中心com.miui.newhome卫星定位com.xiaomi.bsp.gps.nps双刘海屏com.android.internal.display.cutout.emulation.double垃圾清理com.miui.cleanmaster基本互动屏保com.android.dreams.basic声音com.android.soundpicker备份com.miui.backup外部存储设备com.android.externalstorage天星金融com.xiaomi.jr天气com.miui.weather2媒体存储设备com.android.providers.media.module存储已屏蔽的号码com.android.providers.blockednumber存储空间管理器com.android.storagemanager安全核心组件com.miui.securitycore密钥链com.android.keychain小爱同学com.miui.voiceassist小米SIM卡激活服务com.xiaomi.simactivate.service小米云服务com.miui.cloudservice小米云盘com.miui.newmidrive小米互传com.miui.mishare.connectivity小米互联通信服务com.xiaomi.mi_connect_service小米商城com.xiaomi.shop小米商城系统组件com.xiaomi.ab小米安全键盘com.miui.securityinputmethod小米帐号com.xiaomi.account小米换机com.miui.huanji小米支付com.miui.nextpay小米文档查看器(WPS定制)cn.wps.moffice_eng.xiaomi.lite小米智能卡com.miui.tsmclient小米有品com.xiaomi.youpin小米服务框架com.xiaomi.xmsf小米画报com.mfashiongallery.emag小米直播助手com.mi.liveassistant小米社区com.xiaomi.vipaccount小米视频com.miui.video小米设置com.xiaomi.misettings小米钱包com.mipay.wallet小米闻声com.miui.accessibility屏幕录制com.miui.screenrecorder工作设置com.android.managedprovisioning常用语com.miui.phrase应用包管理组件com.miui.packageinstaller应用商店com.xiaomi.market应用程序扩展服务com.miui.contentcatcher开机引导com.android.provision录音机com.android.soundrecorder微博com.sina.weibo快应用服务框架com.miui.hybrid急救信息com.android.emergency性能模式com.qualcomm.qti.performancemode悬浮球com.miui.touchassistant截屏com.miui.screenshot手机淘宝com.taobao.taobao手机管家com.miui.securitycenter打印处理服务com.android.printspooler打孔屏com.android.internal.display.cutout.emulation.hole扫一扫com.xiaomi.scanner投屏com.milink.service投屏服务com.xiaomi.miplay_client抖音短视频com.ss.android.ugc.aweme拼多多com.xunmeng.pinduoduo指南针com.miui.compass指纹测试com.goodix.gftest搜狗输入法小米版com.sohu.inputmethod.sogou.xiaomi搜索com.android.quicksearchbox支付宝com.eg.android.AlipayGphone收音机com.miui.fm收音机调频服务com.miui.fmservice文件com.google.android.documentsui文件管理com.android.fileexplorer日历com.android.calendar日历存储com.android.providers.calendar时钟com.android.deskclock智慧生活com.miui.hybrid.accessory智能出行com.miui.smarttravel智能助理com.miui.personalassistant智能服务com.miui.systemAdSolution服务与反馈com.miui.miservice权限控制器com.android.permissioncontroller权限管理服务com.lbe.security.miui查找手机com.xiaomi.finddevice桌面云备份com.miui.cloudbackup浏览器com.android.browser淘特com.taobao.litetao游戏中心com.xiaomi.gamecenter游戏服务com.xiaomi.gamecenter.sdk.service瀑布刘海屏com.android.internal.display.cutout.emulation.waterfall照片屏幕保护程序com.android.dreams.phototable爱奇艺com.qiyi.video生活黄页com.miui.yellowpage用户反馈com.miui.bugreport用户字典com.android.providers.userdictionary电子邮件com.android.email电话com.android.incallui电话和短信存储com.android.providers.telephony电话服务com.android.phone电量和性能com.miui.powerkeeper番茄免费小说com.dragon.read百度com.baidu.searchbox百度地图com.baidu.BaiduMap百度输入法小米版com.baidu.input_mi相册com.miui.gallery相机com.android.camera相机标定com.xiaomi.cameratools短信com.android.mms笔记com.miui.notes米家com.xiaomi.smarthome米币支付com.xiaomi.payment系统_WLAN_资源com.android.wifi.resources系统打印服务com.android.bips系统更新com.android.updater系统服务组件com.miui.securityadd系统桌面com.miui.home系统界面com.android.systemui系统语音引擎com.xiaomi.mibrain.speech系统跟踪com.android.traceur维修模式com.miui.maintenancemode网络位置服务com.xiaomi.metoknlp网络管理器com.android.networkstack.inprocess耗电检测com.xiaomi.powerchecker联系人存储com.android.providers.contacts腾讯视频com.tencent.qqlive自由窗口com.miui.freeform蓝牙com.android.bluetooth融合位置服务com.xiaomi.location.fused计算器com.miui.calculator讯飞输入法小米版com.iflytek.inputmethod.miui设备信息com.qti.qualcomm.deviceinfo设置com.android.settings设置存储com.android.providers.settings证书安装程序com.android.certinstaller语音唤醒com.miui.voicetrigger输入设备com.android.inputdevices边角刘海屏com.android.internal.display.cutout.emulation.corner运营商默认应用com.android.carrierdefaultapp通知管理com.miui.notification通讯录与拨号com.android.contacts通话管理com.android.server.telecom配套设备管理器com.android.companiondevicemanager银联可信服务安全组件小米版本com.unionpay.tsmservice.mi长型刘海屏com.android.internal.display.cutout.emulation.tall阅读com.duokan.reader音乐com.miui.player音质音效com.miui.misound驾车场景com.xiaomi.drivemodeAI虚拟助手com.xiaomi.aiasst.serviceAndroid_无障碍套件com.google.android.marvin.talkback3_Button_Navigation_Barcom.android.internal.systemui.navbar.threebuttonAdreno_Graphics_Driverscom.qualcomm.qti.gpudrivers.lito.api30AiasstVisioncom.xiaomi.aiasst.visionAnalyticscom.miui.analyticsAndroid_Services_Librarycom.google.android.ext.servicesAndroid_Shared_Librarycom.google.android.ext.sharedAndroid_System_WebViewcom.google.android.webviewAudioEffectcom.miui.audioeffectBlackcom.android.theme.color.blackBluetooth_MIDI_Servicecom.android.bluetoothmidiserviceBokehcom.miui.extraphotoBookmark_Providercom.android.bookmarkproviderCITcom.miui.citCaptivePortalLogincom.android.captiveportalloginCatchLogcom.bsp.catchlogCell_Broadcast_Servicecom.android.cellbroadcastserviceCinnamoncom.android.theme.color.cinnamonCircularcom.android.theme.icon_pack.circular.androidCircularcom.android.theme.icon_pack.circular.launcherCircularcom.android.theme.icon_pack.circular.settingsCircularcom.android.theme.icon_pack.circular.systemuiCircularcom.android.theme.icon_pack.circular.themepickerCit_QRcom.miui.qrCloudServiceSysbasecom.miui.cloudservice.sysbaseCneAppcom.qualcomm.qti.cneConference_URI_Dialercom.qti.confuridialerConfigUpdatercom.google.android.configupdaterDynamic_System_Updatescom.android.dynsystemEid-Servicecom.rongcard.eidFIDO_UAF1.0_ASMcom.fido.asmFIDO_UAF1.0_Clientcom.fido.xiaomi.uafclientFilledcom.android.theme.icon_pack.filled.androidFilledcom.android.theme.icon_pack.filled.launcherFilledcom.android.theme.icon_pack.filled.settingsFilledcom.android.theme.icon_pack.filled.systemuiFilledcom.android.theme.icon_pack.filled.themepickerFingerprintExtensionServicecom.fingerprints.extension.serviceGFManagercom.goodix.fingerprintGestural_Navigation_Barcom.android.internal.systemui.navbar.gesturalGestural_Navigation_Barcom.android.internal.systemui.navbar.gestural_extra_wide_backGestural_Navigation_Barcom.android.internal.systemui.navbar.gestural_narrow_backGestural_Navigation_Barcom.android.internal.systemui.navbar.gestural_wide_backGoogle_One_Time_Initcom.google.android.onetimeinitializerGoogle_Play_服务com.google.android.gmsGoogle_Play_服务更新程序com.android.vendingGoogle_服务框架com.google.android.gsfGoogle通讯录同步com.google.android.syncadapters.contactsHTML_查看器com.android.htmlviewerMIUI+_Beta版com.xiaomi.mirrorMIUI安全组件com.miui.guardproviderMODEM测试工具com.xiaomi.mtbMTP_主机com.android.mtpNFC服务com.android.nfcSIM卡联系人com.qualcomm.qti.simcontactsUC浏览器com.UCMobileUSIM卡应用com.android.stkWAPI证书com.wapi.wapicertmanageX-Divert设置com.qti.xdivertGreencom.android.theme.color.greenIntent_Filter_Verification_Servicecom.android.statementserviceJoyosecom.xiaomi.joyoseLive_Wallpaper_Pickercom.android.wallpaper.livepickerLocationServicescom.qualcomm.locationMConnServicecom.miui.vsimcoreMIUI_Bluetoothcom.xiaomi.bluetoothMIUI_SDKcom.miui.coreMiCloudSynccom.miui.micloudsyncMi_RCScom.xiaomi.mircsMiuiBiometriccom.miui.faceMiuiDaemoncom.miui.daemonMiuiVpnSdkManagercom.miui.vpnsdkmanagerMmsServicecom.android.mms.serviceModule_Metadatacom.android.modulemetadataNetworkStackOverlaycom.android.networkstack.overlayOceancom.android.theme.color.oceanOrchidcom.android.theme.color.orchidOsuLogincom.android.hotspot2.osuloginPacProcessorcom.android.pacprocessorPebblecom.android.theme.icon.pebblePrint_Service_Recommendation_Servicecom.google.android.printservice.recommendationProxyHandlercom.android.proxyhandlerPurplecom.android.theme.color.purpleQColorcom.qualcomm.qti.qcolorQDCM-FFcom.qti.snapdragon.qdcm_ffQualcomm_Mobile_Securitycom.qualcomm.qti.qms.service.telemetryRegServicecom.miui.dmregserviceRounded_Rectanglecom.android.theme.icon.roundedrectRoundedcom.android.theme.icon_pack.rounded.androidRoundedcom.android.theme.icon_pack.rounded.launcherRoundedcom.android.theme.icon_pack.rounded.settingsRoundedcom.android.theme.icon_pack.rounded.systemuiRoundedcom.android.theme.icon_pack.rounded.themepickerSecCamServicecom.qualcomm.qti.seccamserviceSecureElementApplicationcom.android.seSecure_UI_Servicecom.qualcomm.qti.services.secureuiSensor_Test_Toolcom.fingerprints.sensortesttoolSettings_Suggestionscom.android.settings.intelligenceShellcom.android.shellSim_App_Dialogcom.android.simappdialogSoterServicecom.tencent.soter.soterserverSpacecom.android.theme.color.spaceSquirclecom.android.theme.icon.squircleSystemHelpercom.mobiletools.systemhelperSystem_Helper_Servicecom.qualcomm.qti.services.systemhelperTAMservicecom.xiaomi.otrpbrokerTagscom.android.apps.tagTapered_Rectcom.android.theme.icon.taperedrectTeardropcom.android.theme.icon.teardropTetheringcom.android.networkstack.tethering.inprocessVesselcom.android.theme.icon.vesselVpnDialogscom.android.vpndialogsWMServicecom.miui.wmsvcWallPapercom.miui.miwallpaperWfd_Servicecom.qualcomm.wfd.serviceXiaomi_Service_Framework_Keepercom.xiaomi.xmsfkeepercom.android.backupconfirmcom.android.backupconfirmcom.android.carrierconfig.overlay.commoncom.android.carrierconfig.overlay.commoncom.android.carrierconfigcom.android.carrierconfigcom.android.cellbroadcastreceiver.overlay.commoncom.android.cellbroadcastreceiver.overlay.commoncom.android.cellbroadcastreceivercom.android.cellbroadcastreceivercom.android.cts.ctsshimcom.android.cts.ctsshimcom.android.cts.priv.ctsshimcom.android.cts.priv.ctsshimcom.android.localtransportcom.android.localtransportcom.android.onscom.android.onscom.android.overlay.gmstelecommcom.android.overlay.gmstelecommcom.android.overlay.gmstelephonycom.android.overlay.gmstelephonycom.android.phone.overlay.commoncom.android.phone.overlay.commoncom.android.providers.mediacom.android.providers.mediacom.android.provision.overlay.miuicom.android.provision.overlay.miuicom.android.server.NetworkPermissionConfigcom.android.networkstack.permissionconfigcom.android.server.telecom.overlay.commoncom.android.server.telecom.overlay.commoncom.android.server.telecom.overlay.miuicom.android.server.telecom.overlay.miuicom.android.settings.overlay.miuicom.android.settings.overlay.miuicom.android.sharedstoragebackupcom.android.sharedstoragebackupcom.android.smspushcom.android.smspushcom.android.systemui.gesture.line.overlaycom.android.systemui.gesture.line.overlaycom.android.systemui.icon.overlaycom.android.systemui.icon.overlaycom.android.systemui.navigation.bar.overlaycom.android.systemui.navigation.bar.overlaycom.android.systemui.notch.overlaycom.android.systemui.notch.overlaycom.android.systemui.overlay.commoncom.android.systemui.overlay.commoncom.android.systemui.overlay.miuicom.android.systemui.overlay.miuicom.android.wallpaperbackupcom.android.wallpaperbackupcom.android.wallpapercroppercom.android.wallpapercroppercom.android.wifi.resources.overlay.commoncom.android.wifi.resources.overlay.commoncom.android.wifi.resources.overlay.targetcom.android.wifi.resources.overlay.targetcom.android.wifi.resources.xiaomicom.android.wifi.resources.xiaomicom.google.android.cellbroadcastreceiver.overlay.miuicom.google.android.cellbroadcastreceiver.overlay.miuicom.google.android.cellbroadcastservice.overlay.miuicom.google.android.cellbroadcastservice.overlay.miuicom.google.android.overlay.gmsconfigcom.google.android.overlay.gmsconfigcom.google.android.overlay.modules.documentsuicom.google.android.overlay.modules.documentsuicom.google.android.overlay.modules.ext.servicescom.google.android.overlay.modules.ext.servicescom.miui.catcherpatch.BaseApplicationcom.miui.catcherpatchcom.miui.face.overlay.miuicom.miui.face.overlay.miuicom.miui.internal.app.SystemApplicationcom.miui.systemcom.miui.romcom.miui.romcom.miui.smsextra.internal.SmsExtraAppcom.miui.smsextracom.miui.systemui.carriers.overlaycom.miui.systemui.carriers.overlaycom.miui.systemui.devices.overlaycom.miui.systemui.devices.overlaycom.miui.systemui.overlay.devices.androidcom.miui.systemui.overlay.devices.androidcom.miui.translation.kingsoftcom.miui.translation.kingsoftcom.miui.translation.xmcloudcom.miui.translation.xmcloudcom.miui.translation.youdaocom.miui.translation.youdaocom.miui.translationservicecom.miui.translationservicecom.qti.diagservicescom.qti.diagservicescom.qti.dpmserviceappcom.qti.dpmserviceappcom.qti.qualcomm.datastatusnotificationcom.qti.qualcomm.datastatusnotificationcom.qti.service.colorservicecom.qti.service.colorservicecom.qti.slaservicecom.qti.slaservicecom.qualcomm.atfwdcom.qualcomm.atfwdcom.qualcomm.embmscom.qualcomm.embmscom.qualcomm.qcrilmsgtunnelcom.qualcomm.qcrilmsgtunnelcom.qualcomm.qti.autoregistrationcom.qualcomm.qti.autoregistrationcom.qualcomm.qti.devicestatisticsservicecom.qualcomm.qti.devicestatisticsservicecom.qualcomm.qti.dynamicddsservicecom.qualcomm.qti.dynamicddsservicecom.qualcomm.qti.lpacom.qualcomm.qti.lpacom.qualcomm.qti.qms.service.connectionsecuritycom.qualcomm.qti.qms.service.connectionsecuritycom.qualcomm.qti.qtisystemservicecom.qualcomm.qti.qtisystemservicecom.qualcomm.qti.remoteSimlockAuthcom.qualcomm.qti.remoteSimlockAuthcom.qualcomm.qti.server.wigig.tethering.rrocom.qualcomm.qti.server.wigig.tethering.rrocom.qualcomm.qti.telephonyservicecom.qualcomm.qti.telephonyservicecom.qualcomm.qti.uimGbaAppcom.qualcomm.qti.uimGbaAppcom.qualcomm.qti.uimcom.qualcomm.qti.uimcom.qualcomm.qti.workloadclassifiercom.qualcomm.qti.workloadclassifiercom.qualcomm.timeservicecom.qualcomm.timeservicecom.qualcomm.uimremoteclientcom.qualcomm.uimremoteclientcom.qualcomm.uimremoteservercom.qualcomm.uimremoteservercom.xiaomi.bluetooth.overlaycom.xiaomi.bluetooth.overlaycom.xiaomi.micloudsdk.SdkApplicationcom.xiaomi.micloud.sdkkaraokecom.miui.audiomonitormiui.external.Applicationcom.android.thememanager.module
文章
存储  ·  编解码  ·  缓存  ·  安全  ·  Shell  ·  Linux  ·  API  ·  开发工具  ·  Android开发  ·  Perl
2023-02-07
Kratos微服务框架实现IoT功能:设备实时地图
Kratos微服务框架实现IoT功能:设备实时地图IoT,也就是物联网,万物互联,在未来肯定是一个热点——实际上,现在物联网已经很热了。那好,既然这一块这么有前途。那我们就来学习怎么开发物联网系统吧。可是,作为一个小白,两眼一抹黑:我想学,可是我该如何开始?这玩意儿到底该咋整呢?于是,我各种找资料,各种学习——此处省略一亿个字,其中的艰辛,其中的曲折,总之就是:说来都是泪,欲哭却无声——总算是有了基础的认知,有了一个模糊的方向。我知道了物联网设备通讯协议MQTT、CoAP、LwM2M,知道了微服务,知道了MQ,知道了Websocket,知道了REST,知道了gRPC……有了这些认知,看起来可以开始做技术选型了。在这个时候,我发现了B站开源的微服务框架go-kratos。那么,Kratos能否实现物联网的系统和功能呢?答案是:必须可以。我们现在要开发一个物联网的系统,Kratos能够为我们提供什么技术支撑呢?有以下功能模块可供使用:MQTT,用于设备与物联网服务之间的同异步通讯;gRPC,用于微服务之间的同步通讯;MQ消息队列(RabbitMQ、Kafka、Pulsar、NATS、RocketMQ等),用于微服务之间的异步通讯;REST(基于gRPC gateway),用于后端跟前端的同步通讯;Websocket,用于后端跟前端的异步通讯。物联网一个最基础的功能就是实时地图了,也就是在地图上展现设备的动态,比如:位置、轨迹、方向……在我查找资料的时候,发现了一个实时地图的示例程序 realtimemap-go,它是Actor模型框架 Proto.Actor 的展示程序。该示例程序显示的是芬兰首都赫尔辛基公共交通车辆的实时位置。Proto.Actor,它是一种用于 Go、C# 和 Java/Kotlin 的超快速分布式 Actor 解决方案。你可能会问,那为什么不用它来进行开发?因为,它实现起来太复杂了,维护起来就更加复杂。如果你用过Erlang编程语言,那么你就能够深深体会到当中的困难。Proto.Actor该示例有一个在线演示:https://realtimemap.skyrise.cloud/该示例程序有以下特性:车辆的实时位置;车辆的轨迹;地理围栏通知(车辆进出该地理区域);每个公交公司在地理围栏区域的车辆;水平缩放。本文基于此示例程序,在Kratos下面重新实现了一遍。先决条件KratosVue.jsMQTTWebsocketgRPCRESTful示例程序的后端基于Kratos开发,需要有一定的Kratos的基础。前端基于Vue3和Typescript进行开发,需要有一定的相关基础。它是如何工作的?设备使用MQTT通讯协议将数据推送给服务端;服务端使用REST和Websocket将设备数据推送给前端。服务端基于Kratos框架进行开发,为了简便演示,本示例只有一个单体服务,实际运用时,拆分服务也是容易的。服务端接收MQTT数据数据源由于这个应用程序是关于跟踪车辆的,我们需要从某个地方获取它们的位置。在此应用程序中,位置是从赫尔辛基地区交通局的高频车辆定位 MQTT 代理接收的。有关数据的更多信息:赫尔辛基地区交通 - 开放数据。赫尔辛基地区交通局的高频定位。此数据已根据 © Helsinki Region Transport 2021、Creative Commons BY 4.0 International 获得许可Topic定义如下:0/1 /2 /3 /4 /5 /6 /7 /8 /9 /10 /11 /12 /13 /14 /15 /16 /<prefix>/<version>/<journey_type>/<temporal_type>/<event_type>/<transport_mode>/<operator_id>/<vehicle_number>/<route_id>/<direction_id>/<headsign>/<start_time>/<next_stop>/<geohash_level>/<geohash>/<sid>/#type Topic struct { Prefix string // /hfp/ is the root of the topic tree. Version string // v2 is the current version of the HFP topic and the payload format. JourneyType string // The type of the journey. Either journey, deadrun or signoff. TemporalType string // The status of the journey, ongoing or upcoming. EventType string // One of vp, due, arr, dep, ars, pde, pas, wait, doo, doc, tlr, tla, da, dout, ba, bout, vja, vjout. TransportMode string // The type of the vehicle. One of bus, tram, train, ferry, metro, ubus (used by U-line buses and other vehicles with limited realtime information) or robot (used by robot buses). // operator_id/vehicle_number uniquely identifies the vehicle. OperatorId string // The unique ID of the operator that owns the vehicle. VehicleNumber string // The vehicle number that can be seen painted on the side of the vehicle, often next to the front door. Different operators may use overlapping vehicle numbers. RouteId string // The ID of the route the vehicle is running on. DirectionId string // The line direction of the trip, either 1 or 2. Headsign string // The destination name, e.g. Aviapolis. StartTime string // The scheduled start time of the trip NextStop string // The ID of next stop or station. GeohashLevel string // The geohash level represents the magnitude of change in the GPS coordinates since the previous message from the same vehicle. Geohash string // The latitude and the longitude of the vehicle. Sid string // Junction ID, corresponds to sid in the payload. }载体数据结构定义如下:package hfp type Payload struct { Longitude *float64 `json:"long"` // 经度(WGS84) Latitude *float64 `json:"lat"` // 纬度(WGS84) Heading *int32 `json:"hdg"` // 朝向角度[0, 360] DoorState *int32 `json:"drst"` // 门状态 0:所有门都已关闭 1:有门打开 Timestamp *time.Time `json:"tst"` // 时间戳 Speed *float64 `json:"spd"` // 车速(m/s) Odometer *int32 `json:"odo"` // 里程(m) } type Event struct { VehicleId string // 车辆ID OperatorId string // 司机ID VehiclePosition *Payload `json:"VP"` // 坐标 DoorOpen *Payload `json:"DOO"` // 开门 DoorClosed *Payload `json:"DOC"` // 关门 }需要注意的是,我测试时发现,MQTT接收数据时只要接收一段时间就自动断开了,一开始我还以为是我这边出问题了,后来做了一些测试才发现,是对方限制了使用,应该是测试账号的ClientID只允许接收一定时长的数据。编写代码首先创建MQTT服务端,它本质上是一个MQTT的客户端,它具有全双工、双向的数据流,所以实现为服务端也并无问题。package server import ( "context" "github.com/go-kratos/kratos/v2/log" "github.com/tx7do/kratos-transport/transport/mqtt" "kratos-realtimemap/app/admin/internal/conf" "kratos-realtimemap/app/admin/internal/service" ) // NewMQTTServer create a mqtt server. func NewMQTTServer(c *conf.Server, _ log.Logger, svc *service.AdminService) *mqtt.Server { ctx := context.Background() srv := mqtt.NewServer( mqtt.WithAddress([]string{c.Mqtt.Addr}), mqtt.WithCodec("json"), ) _ = srv.RegisterSubscriber(ctx, "/hfp/v2/journey/ongoing/vp/bus/#", registerSensorDataHandler(svc.TransitPostTelemetry), hfpEventCreator, ) svc.SetMqttBroker(srv) return srv }以上代码创建了一个MQTT的服务器,使用JSON编解码器进行编解码,监听了Topic为/hfp/v2/journey/ongoing/vp/bus/#的MQTT推送消息。接着实现服务,对设备通过MQTT推送的消息进行处理:package service import ( "context" "github.com/tx7do/kratos-transport/broker" "kratos-realtimemap/api/hfp" "kratos-realtimemap/app/admin/internal/pkg/data" ) func (s *RealtimeMapService) SetMqttBroker(b broker.Broker) { s.mb = b } func (s *RealtimeMapService) TransitPostTelemetry(_ context.Context, topic string, headers broker.Headers, msg *hfp.Event) error { //fmt.Println("Topic: ", topic) topicInfo := hfp.Topic{} topicInfo.Parse(topic) msg.OperatorId = topicInfo.OperatorId msg.VehicleId = topicInfo.GetVehicleUID() position := msg.MapToPosition() if position != nil { s.positionHistory.Update(position) turnovers := data.AllOrganizations.Update(position) s.BroadcastVehicleTurnoverNotification(turnovers) s.BroadcastVehiclePosition(s.positionHistory.GetPositionsHistory(position.VehicleId)) } s.log.Infof("事件类型: %s 交通工具类型: %s 司机ID: %s 车辆ID: %s", topicInfo.EventType, topicInfo.TransportMode, topicInfo.OperatorId, msg.VehicleId) return nil }以上代码对Topic和载体数据进行了解析,将设备状态存入内存当中,旋即把状态通过Websocket广播给前端。好了,我们对MQTT的处理就完成了。处理MQTT的课结束,下课!嗯?这就完了?这么简单?没错,就这么点代码,就这么的容易,我也想多叨叨几句,扩充点篇幅,只可惜,它确实就是这么容易就搞定了。服务端推送数据到前端服务端与前端的通讯主要靠REST和Websocket来实现。那些更新频率不高,实时性要求也不高的数据都可以走REST,由前端主动拉取。而实时性和更新频率都比较高的数据则可以通过Websocket由服务端主动推送。数据结构别看设备与服务端的通讯很简单,但是,服务端到前端的数据就复杂多了。有以下数据:Organization(组织),指的是汽车的所属公司。Geofence(地理围栏),它是地图上的一个几何区域,用于标定汽车的停车场或者运营区域,出入都将会发送一个通知给前端。Position(汽车坐标),它是汽车的一个坐标点,包含了汽车在该点上的状态,比如:开关门,速度,朝向等。Viewport(视口),它是地图上的一个裁剪矩形,浅显的描述就是你在前端看到的地图区域,前端只接收该视口之内的汽车数据,否则服务器会向前端发送系统所有的汽车数据,不论服务器还是网络都将会吃不消。Notification(通知),服务端通知前端一些事件,主要是:汽车进出地理围栏的事件,汽车上线下线通知。其中,Position和Notification都是通过Websocket推送给前端,其他数据则是前端通过REST主动拉取。以上数据结构通过Protobuf定义:syntax = "proto3"; // 地理点 message GeoPoint { double longitude = 1;// 经度(WGS84) double latitude = 2;// 纬度(WGS84) } // 组织 message Organization { string id = 1;// 组织ID string name = 2;// 组织名称 } // 地理围栏 message Geofence { string name = 1;// 围栏名称 double radius_in_meters = 2;// 半径长度(圆形地理围栏) double longitude = 3;// 经度(WGS84) double latitude = 4;// 纬度(WGS84) string org_id = 5;// 组织ID repeated string vehicles_in_zone = 6;// 区域内所有的车辆 } // 车辆坐标 message Position { string vehicle_id = 1;// 车辆ID string org_id = 2;// 组织ID int64 timestamp = 3;// 时间戳 double longitude = 4;// 经度(WGS84) double latitude = 5;// 纬度(WGS84) int32 heading = 6;// 朝向角度[0, 360] bool doors_open = 7;// 门状态 0:所有门都已关闭 1:有门打开 double speed = 8;// 车速(m/s) } // 视口 message Viewport { GeoPoint south_west = 1;// 西南点(左下点) GeoPoint north_east = 2;// 东北点(右上点) } // 通知 message Notification { string message = 1;// 通知内容 }REST像拉取组织列表、获取某一个组织的详情、获取某一车辆的行车轨迹,都属于低频的操作,所以都走REST。REST的功能是通过gRPC的gateway实现的,所以我们可以通过protobuf来定义API:syntax = "proto3"; // 实时地图服务 service RealtimeMapService { // 获取组织列表 rpc ListOrganizations (google.protobuf.Empty) returns (ListOrganizationsReply) { option (google.api.http) = { get: "/api/organizations" }; } // 获取组织详情 rpc GetOrganization (GetOrganizationReq) returns (GetOrganizationReply) { option (google.api.http) = { get: "/api/organizations/{org_id}" }; } // 获取车辆轨迹 rpc GetVehicleTrail (GetVehicleTrailReq) returns (GetVehicleTrailReply) { option (google.api.http) = { get: "/api/trail/{id}" }; } }下面就可以创建REST服务器了:package server // NewMiddleware 创建中间件 func NewMiddleware(ac *conf.Auth, logger log.Logger) http.ServerOption { return http.Middleware( recovery.Recovery(), tracing.Server(), logging.Server(logger), ) } // NewHTTPServer new an HTTP server. func NewHTTPServer(c *conf.Server, ac *conf.Auth, logger log.Logger, s *service.RealtimeMapService) *http.Server { var opts = []http.ServerOption{ NewMiddleware(ac, logger), http.Filter(handlers.CORS( handlers.AllowedHeaders([]string{"" + "", "Content-Type", "Authorization"}), handlers.AllowedMethods([]string{"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"}), handlers.AllowedOrigins([]string{"*"}), )), } if c.Http.Network != "" { opts = append(opts, http.Network(c.Http.Network)) } if c.Http.Addr != "" { opts = append(opts, http.Address(c.Http.Addr)) } if c.Http.Timeout != nil { opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration())) } srv := http.NewServer(opts...) h := openapiv2.NewHandler() srv.HandlePrefix("/q/", h) v1.RegisterRealtimeMapServiceHTTPServer(srv, s) return srv }其服务很简单,也就是一些非常简单的内存数据查询:package service func (s *RealtimeMapService) ListOrganizations(_ context.Context, _ *emptypb.Empty) (*v1.ListOrganizationsReply, error) { reply := &v1.ListOrganizationsReply{ Organizations: data.AllOrganizations.MapToBaseInfoArray(), } return reply, nil } func (s *RealtimeMapService) GetOrganization(_ context.Context, req *v1.GetOrganizationReq) (*v1.GetOrganizationReply, error) { if org, ok := data.AllOrganizations[req.OrgId]; ok { return &v1.GetOrganizationReply{ Id: org.Id, Name: org.Name, Geofences: org.MapToGeofenceArray(), }, nil } else { return nil, v1.ErrorResourceNotFound(fmt.Sprintf("Organization %s not found", req.OrgId)) } } func (s *RealtimeMapService) GetVehicleTrail(_ context.Context, req *v1.GetVehicleTrailReq) (*v1.GetVehicleTrailReply, error) { his := s.positionHistory.GetVehicleTrail(req.Id) if his == nil { return nil, v1.ErrorResourceNotFound(fmt.Sprintf("%s positions history not found", req.Id)) } return &v1.GetVehicleTrailReply{Positions: his}, nil }WebsocketWebsocket适合需要服务端主动推送消息的应用场景之下。REST肯定是做不到的,长轮询的效率之低下,令人发指。在Kratos下创建一个Websocket的服务器是容易的,只需要以下代码即可实现:package server import ( "github.com/go-kratos/kratos/v2/log" "github.com/tx7do/kratos-transport/transport/websocket" "kratos-realtimemap/app/admin/internal/conf" "kratos-realtimemap/app/admin/internal/service" ) // NewWebsocketServer create a websocket server. func NewWebsocketServer(c *conf.Server, _ log.Logger, svc *service.RealtimeMapService) *websocket.Server { srv := websocket.NewServer( websocket.WithAddress(c.Websocket.Addr), websocket.WithPath(c.Websocket.Path), websocket.WithConnectHandle(svc.OnWebsocketConnect), websocket.WithCodec("json"), ) svc.SetWebsocketServer(srv) return srv }向前端推送消息,我简单处理了,调用Broadcast方法直接广播全部前端了:func (s *RealtimeMapService) BroadcastToWebsocketClient(eventId string, payload interface{}) { if payload == nil { return } bufPayload, _ := json.Marshal(&payload) var proto v1.WebsocketProto proto.EventId = eventId proto.Payload = string(bufPayload) bufProto, _ := json.Marshal(&proto) var msg websocket.Message msg.Body = bufProto s.ws.Broadcast(websocket.MessageType(v1.MessageType_Notify), &msg) }只有两个推送:BroadcastVehiclePosition方法是推送车辆的位置信息的:func (s *RealtimeMapService) BroadcastVehiclePosition(positions data.PositionArray) { s.BroadcastToWebsocketClient("positions", positions) }BroadcastVehicleTurnoverNotification是推送车辆进出物理围栏通知的:func (s *RealtimeMapService) BroadcastVehicleTurnoverNotification(turnovers data.TurnoverArray) { for _, turnover := range turnovers { var str string if turnover.Status { str = fmt.Sprintf("%s from %s entered the zone %s", turnover.VehicleId, turnover.OrganizationName, turnover.GeofenceName) } else { str = fmt.Sprintf("%s from %s left the zone %s", turnover.VehicleId, turnover.OrganizationName, turnover.GeofenceName) } s.BroadcastToWebsocketClient("notification", str) } }在程序里面,我们只处理了一个前端推送的消息,是前端视口改变的更新消息:func (s *RealtimeMapService) OnWebsocketMessage(sessionId websocket.SessionID, message *websocket.Message) error { s.log.Infof("[%s] Payload: %s\n", sessionId, string(message.Body)) var proto v1.WebsocketProto if err := json.Unmarshal(message.Body, &proto); err != nil { s.log.Error("Error unmarshalling proto json %v", err) return nil } switch proto.EventId { case "viewport": var msg v1.Viewport if err := json.Unmarshal([]byte(proto.Payload), &msg); err != nil { s.log.Error("Error unmarshalling payload json %v", err) return nil } _ = s.OnWsSetViewport(sessionId, &msg) } return nil } func (s *RealtimeMapService) OnWsSetViewport(sessionId websocket.SessionID, msg *v1.Viewport) error { s.viewports[sessionId] = msg return nil }到这里,服务端基本上就实现了。虽然还很粗糙,但是该有的功能是实现了。实现前端前端基于Vue.js和Typescript开发。REST客户端REST客户端基于axios封装而成:import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; import {deepMerge} from '@/util'; export interface CreateAxiosOptions extends AxiosRequestConfig { authenticationScheme?: string; } export class VAxios { private axiosInstance: AxiosInstance; private readonly options: CreateAxiosOptions; constructor(options: CreateAxiosOptions) { this.options = options; this.axiosInstance = axios.create(options); } private createAxios(config: CreateAxiosOptions): void { this.axiosInstance = axios.create(config); } getAxios(): AxiosInstance { return this.axiosInstance; } configAxios(config: CreateAxiosOptions) { if (!this.axiosInstance) { return; } this.createAxios(config); } setHeader(headers: any): void { if (!this.axiosInstance) { return; } Object.assign(this.axiosInstance.defaults.headers, headers); } get<T = any>(url: string): Promise<T> { return this.axiosInstance.get(url); } } function createAxios(opt?: Partial<CreateAxiosOptions>) { return new VAxios( deepMerge( { authenticationScheme: '', withCredentials: false, timeout: 10 * 1000, baseURL: process.env.VUE_APP_API_URL || 'http://localhost:8800/api/', headers: { 'Content-Type': 'application/json;charset=UTF-8', }, // 配置项,下面的选项都可以在独立的接口请求中覆盖 requestOptions: { // 默认将prefix 添加到url joinPrefix: true, // 是否返回原生响应头 比如:需要获取响应头时使用该属性 isReturnNativeResponse: false, // 需要对返回数据进行处理 isTransformResponse: true, // post请求的时候添加参数到url joinParamsToUrl: false, // 格式化提交参数时间 formatDate: true, // 是否加入时间戳 joinTime: true, // 忽略重复请求 ignoreCancelToken: true, // 是否携带token withToken: true, }, }, opt || {}, ), ); } export const apiInstance = createAxios();Websocket客户端Websocket基于WebSocket类开发:export interface PositionsDto { positions: PositionDto[]; } export interface PositionDto { vehicle_id: string; longitude: number; latitude: number; heading: number; speed: number; doors_open: boolean; } export interface WebsocketProto { event_id: string; payload: string; } export interface GeoPoint { longitude: number; latitude: number; } export interface Viewport { southWest: GeoPoint; northEast: GeoPoint; } export interface UpdateViewport { viewport: Viewport; } export interface Notification { message: string; } export interface HubConnection { setViewport(swLng: number, swLat: number, neLng: number, neLat: number); onPositions(callback: (positions: PositionDto[]) => void); onNotification(callback: (notification: string) => void); disconnect(): Promise<void>; } function ByteBufferToObject(buff) { const enc = new TextDecoder('utf-8'); const uint8Array = new Uint8Array(buff); const decodedString = enc.decode(uint8Array); // console.log(decodedString); return JSON.parse(decodedString); } function StringToArrayBuffer(str) { return new TextEncoder().encode(str); } class WebsocketConnect implements HubConnection { private connection: WebSocket; private onPositionsCallback?: (positions: PositionDto[]) => void; private onNotificationCallback?: (notification: string) => void; constructor() { const wsURL = `ws://localhost:7700/`; this.connection = new WebSocket(wsURL); this.connection.binaryType = 'arraybuffer'; this.connection.onopen = this.onWebsocketOpen.bind(this); this.connection.onerror = this.onWebsocketError.bind(this); this.connection.onmessage = this.onWebsocketMessage.bind(this); this.connection.onclose = this.onWebsocketClose.bind(this); } onWebsocketOpen(event) { console.log('ws连接成功', event); } onWebsocketError(event) { console.error('ws错误', event); } onWebsocketMessage(event) { const proto = ByteBufferToObject(event.data); // console.log(proto); const data = JSON.parse(proto['payload']); // console.log(data); const eventId = proto['event_id']; if (eventId == 'positions') { if (this.onPositionsCallback != null) { this.onPositionsCallback(data); } } else if (eventId == 'notification') { if (this.onNotificationCallback != null) { this.onNotificationCallback(data); } } } onWebsocketClose(event) { console.log('ws连接关闭', event); } sendMessage(eventId, data) { const x: WebsocketProto = { event_id: eventId, payload: JSON.stringify(data), }; const str = JSON.stringify(x); // console.log(str); this.connection.send(StringToArrayBuffer(str)); } setViewport(swLng: number, swLat: number, neLng: number, neLat: number) { const x: Viewport = { southWest: { longitude: swLng, latitude: swLat, }, northEast: { longitude: neLng, latitude: neLat, }, }; this.sendMessage('viewport', x); } onPositions(callback: (positions: PositionDto[]) => void) { this.onPositionsCallback = callback; } onNotification(callback: (notification: string) => void) { this.onNotificationCallback = callback; } async disconnect() { await this.connection.close(1000); } } export const connectToHub = new WebsocketConnect;地图客户端地图是使用的Mapbox开发的,这一块是直接从realtimemap-go中拷贝出来的。本来是想自己基于高德或者百度地图重新做一个,但是基于坐标系的考虑,就没有采用高德或者百度地图来开发了。要使用Mapbox,首先需要去 Mapbox 注册一个账号。然后在mapboxConfig.ts当中把你自己账号的AccessToken填写到mapboxAccessToken常量。项目代码GithubGiteeKratos 官方示例参考资料GTFS Realtime ReferenceHigh-frequency positioningrealtimemap-go
文章
消息中间件  ·  前端开发  ·  JavaScript  ·  物联网  ·  定位技术  ·  Go  ·  C#  ·  网络架构  ·  RocketMQ  ·  微服务
2023-02-02
Javaweb--狂神
JavaWeb1、基本概念1.1、前言web开发:web,网页的意思,www.baidu.com静态webhtml , css提供给所有人看的数据始终不会发生变化!动态web淘宝,几乎所有的网站:提供给所有人看的数据始终会发生变化,每个人在不同的时间,不同的地点看到的信息各不相同!技术栈:Servlet/JSP , ASP , PHP在Java中,动态web资源开发的技术统称为Javaweb;1.2、web应用程序web应用程序:可以提供浏览器访问的程序;a.html、b.html …..多个web资源,这些web资源可以被web访问你们能访问到的任何一个页面或者资源,都存在于这个世界上的某一个角落的计算机上。URL这些统一的web资源会被放在同一个文件夹上,web应用程序– –>Tomcat :服务器一个web应用由多部分组成(静态web,动态web)html,css,jsjsp,servletJava程序jar包配置文件(Properties)web应用程序编写完毕后,若想它提供给外界访问:需要一个服务器来统一管理;1.3、静态web.htm,.html,这些都是网页的后缀,如果服务器上一直存在这些东西,我们就可以直接进行读取。通网;静态web存在的缺点web页面无法更新,所有用户看到的都是同一个页面轮播图,点击特效:伪动态JavaScript(实际开发中,它用的最多)VBScript它无法和数据库交互(数据无法持久化,用户无法交互)1.4、动态web页面动态展示:“web压敏啊展示的效果因人而异”缺点加入服务器的动态web资源出现了错误,我们需要重新编写我们的后台程序,重新发布;停机维护优点:web页面可以更新,所有用户看到的都不是同一个页面它可以与数据库交互(数据持久化:注册,商品信息,用户信息….)2、web服务器2.1、技术讲解ASP微软:国内最早流行的就是ASP;在HTML中嵌入了VB的脚本,ASP + COM;在ASP开发中,基本一个页面都有几千行的业务代码,页面极其换乱维护成本高C#IIS<h1> <h1><h1> <h1> <h1> <h1> <h1> <% System.out.println("hello") %> <h1> <h1> <h1><h1> <h1>phpPHP开发速度很快,功能很强大,跨平台,代码很简单(70%,WP)无法承载大访问量的情况(局限性)JSP/Servlt:B/S:浏览器和服务器C/S:客户端和服务器sun公司主推的B/S架构基于Java语言的(所有的大公司,或者一些开源的组件,都是Java写的)可以承载三高问题带来的影响;语法像ASP,ASP– –>JSP,加强市场强度 ;……2.2、web服务器服务器是一种被动的操作,用来处理用户的一些请求和给用户一些相应信息;IIS微软的:ASP…Windows中自带Tomcat面向百度编程;Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为比较流行的Web 应用服务器。Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个Java初学web的人来说,他是最佳的选择Tomcat 实际上运行JSP 页面和Servlet。 Tomcat最新版本为10.0.5。工作3~5年之后,可以尝试手写Tomcat服务器:下载Tomcat:1.安装 or 解压2.了解配置文件及目录结构3.这个东西的作用3、Tomcat3.1、安装TomcatTomcat官网:https://tomcat.apache.org/3.2、Tomcat启动和配置文件夹作用: 启动、关闭Tomcat访问测试:https://localhost:8080/可能遇到的问题:Java环境变量没有配置闪退问题:需要配置兼容性乱码问题:配置文件中设置3.3、配置可以配置启动的端口tomcat的默认端口号:8080mysql默认端口号:3306http:80https:443<Connector port="8081" protocol="HTPP/1.1" connectionTimeout="20000" redirectPort="8443"/>可以配置主机的名称默认的主机名为:lcoalhost- ->127.0.0.1默认网站应用存放的位置为:webapps<Host name="www.wuwei.com" appBase="webapps" unpackWARs="true" autoDeploy="true">高难度面试题:请你谈一谈网站是如何进行访问的!1.输入一个域名:回车2.检查本机的C:\Windows\System32\drivers\etc\hosts配置文件下有没有这个域名映射;有:直接返回对应的ip地址,这个地址中,有我们访问的web程序,可以直接访问127.0.0.1 www.wuwei.com没有:去DNS服务器找,找到的话就返回,找不到就返回找不到;4.可以配置一下环境变量(可选性)3.4发布一个web网站不会先模仿将自己写的网站,放到服务器(Tomcat)中指定的web应用的文件夹(webapps)下,就可以访问了网站应该有的结构-- webapps : Tomcat服务器的web目录 -- ROOT -- kuangstudy:网站的目录名 - classes : java程序 - lib : web应用程序所依赖的jar包 - web.xml 网站配置文件 - index.html 默认的首页 - static - css -style.css -js -img -.......4、HTTP4.1、什么是HTTPHTTP(超文本传输协议)是一个简单的请求-响应协议,它主要运行在TCP之上。文本:html,字符串,~…超文本:图片,音乐,视频,定位,地图….80Https:安全的4434.2、两个时代http1.0HTTP/1.0:客户端可以与web服务器连接,只能获取一个web资源,断开连接http2.0HTTP/1.1:客户端可以与web服务器连接,只能获取多个web资源。4.3、Http请求客户端- -发请求(Request) - - 服务器百度:Request URL: https://www.baidu.com/ 请求地址 Request Method: GET get方法/post方法 Status Code: 200 OK 状态码:200 Remote(远程) Address: 110.242.68.3:443 Accept: text/html Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 语言 Cache-Control: max-age=0 Connection: keep-alive1、请求行请求行中的请求方式:GET请求方式:Get,Post,HEAD,DELETE,PUT,TRACT…get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但很高效post:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不很高效2、消息头Accept:告诉浏览器,它所支持的数据类型 Accept-Encoding:支持那种编码格式 GBK UTF-8 GB2312 ISO8859-1 Accept-Language 告诉浏览器:他的语言环境 Cache-Control 缓存控制 Connection 告诉浏览器,请求完成是断开还是保持连接 HOST:主机.../.4.4、Http响应服务器- - 响应- - 客户端百度:Cache-Control: private 缓存控制 Connection: keep-alive 连接 Content-Encoding: gzip 编码 Content-Type: text/html;charset=utf-8 类型1.响应体Accept:告诉浏览器,它所支持的数据类型 Accept-Encoding:支持那种编码格式 GBK UTF-8 GB2312 ISO8859-1 Accept-Language 告诉浏览器:他的语言环境 Cache-Control 缓存控制 Connection 告诉浏览器,请求完成是断开还是保持连接 HOST:主机.../. Refresh:告诉客户端,多久刷新一次; Location:让网页重新定位;2、响应状态码200:请求响应成功 2003**:请求重定向重定向:你重新到我给你的新位置去;4xx:找不到资源 404资源不存在;5xx:服务器代码错误 500 502:网关错误常见面试题:当你的浏览器中地址栏并输入地址并回车的一瞬间页面能够展示回来,经历了什么?1. 域名解析 2. 发起TCP的三次握手 3. 建立起TCP连接后发起http请求 4. 服务器响应http请求,浏览器得到html代码 5. 浏览器解析html代码,并请求html代码中的资源(css JavaScript 图片) 6. 浏览器对页面进行渲染呈现5、Maven我们为什么要学习这个技术?在Javaweb开发中,需要导入大量的jar包,我们手动去导入;如何让一个东西自动帮我们导入和配置这个jar包。​ 由此Maven诞生了!5.1、Maven项目架构管理工具我们目前就是来导jar包的!Maven的核心思想:约定大于配置有约束,不要去违反。Maven会规定你该如何去编写我们的Java代码,必须按照这个规范来;5.2、下载安装Maven官网:https://maven.apache.org/下载完成后,解压即可;建议:电脑上的所有环境放在一个文件夹下,方便管理;5.3、配置环境变量在我们的系统环境变量中配置如下配置:M2_HOME maven目录下的bin目录MAVEN_HOME maven目录在系统的path中配置 %MAVEN_HOME%\bin测试Maven是否安装成功,保证必须配置完成!5.4、阿里云镜像镜像:mirrors作用:加速我们的下载国内建议使用阿里云的镜像<mirror> <id>nexus-aliyun</id> <mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror>5.5、本地仓库在本地的仓库,远程仓库;建立一个本地仓库:localRepository <localRepository>/usr/local/apache-maven-3.8.4/maven-repo</localRepository>5.6、IDEA中启动Maven1、启动IDEA2、创建一个Mavenweb项目3、等待项目初始化完毕4、观察maven仓库中多了什么东西?5、Maven中的IDEA设置注意:IDEA项目创建成功后,看一眼Maven配置6、到这里,Maven在IDEA中的配置和使用就OK了5.7、创建一个maven项目这个只有在web应用下才会有!5.8、标记文件夹功能5.9、在IDEA中配置Tomcat解决警告问题必须要的配置:为什么会有这个问题:我们访问一个网站,需要指定一个文件夹的名字;5.10、pom文件pom.xml是Maven的核心配置文件 <build> <defaultGoal>install</defaultGoal> <finalName>JavaDevPlatform</finalName> <sourceDirectory>src/main/java</sourceDirectory> <outputDirectory>${basedir}/target/jar</outputDirectory> <resources> <resource> <directory>src/main/resources/com/szboanda</directory> <targetPath>${basedir}/target/jar/com/szboanda</targetPath> </resource> <resource> <directory>src/main/resources/sql</directory> <targetPath>${basedir}/target/jar/sql</targetPath> </resource> <resource> <directory>src/main</directory> <includes> <include>web-fragment.xml</include> </includes> <targetPath>${basedir}/target/jar/META-INF</targetPath> </resource> <resource> <directory>src/main/webapp</directory> <excludes> <exclude>WEB-INF/classes/**</exclude> <exclude>WEB-INF/lib/**</exclude> <exclude>WEB-INF/logs/**</exclude> <exclude>WEB-INF/sql/**</exclude> <exclude>WEB-INF/web.xml</exclude> </excludes> <targetPath>${basedir}/target/jar/META-INF/resources</targetPath> </resource> </resources> <plugins> <!-- 为了使项目结构更为清晰,Maven区别对待Java代码文件和资源文件,maven-compiler-plugin用来编译Java代码 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version> 3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- 为了使项目结构更为清晰,Maven区别对待Java代码文件和资源文件,maven-compiler-plugin用来编译Java代码, maven-resources-plugin则用来处理资源文件,默认的主资源文件目录是src/main/resources, 很多用户会需要添加额外的资源文件目录,这个时候就可以通过配置maven-resources-plugin来实现。 --> <plugin> <!--<groupId>org.apache.maven.plugins</groupId>--> <artifactId>maven-resources-plugin</artifactId> <version> 3.0.2</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <!-- 打包源码的插件 --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version> 3.0.1</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <addDefaultImplementationEntries>true</addDefaultImplementationEntries> <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries> </manifest> </archive> </configuration> <!-- 进行package命令时就可以将源码同时进行打包 所以我们需要绑定source插件到我们default生命周期的package阶段 --> <executions> <execution> <phase>package</phase> <!-- <phase>compile</phase> --> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> </plugins> </build>maven由于它的约定大于配置,我们之后可能会遇到我们写的配置文件,无法被导出或者生效的问题,解决方案: <!--在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <excludes> <exclude>**/*.properties</exclude> <exclude>**/*.xml</exclude> </excludes> <filtering>false</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>5.12、IDEA目录树5.13、解决遇到的问题1.Maven 3.6.2​ 解决方案:降级为3.6.12.Tomcat闪退3.IDEA中每次都要重复配置Maven​ 在IDEA中的全局默认配置中去配置4.Maven项目中Tomcat无法配置5.maven 默认web项目中web.xml版本问题6.替换为iwebapp5.0版本和tomcat一致<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0" metadata-complete="true">7.maven仓库的使用地址:https://mvnrepository.com/6、Servlet6.1、Servlet简介Servlet就是sun公司开发动态web的一门技术Sun在这些API中提供一个接口叫做:Servlet,如果你想开发Servlet程序,只需要完后两个步骤:编写一个类,实现Servlet接口把开发好的Java类部署到web服务器中。把实现了Servlet接口的Java程序叫做,Servlet6.2、HelloServletServlet接口在Sun公司有两个默认的实现类:HttpServlet1.构建一个普通Maven项目,删掉里面的src目录,以后我们的学习就在这个项目里面建立Module;这个空的工程就是Maven的主工程;2.关于Maven父子工程的理解;父项目中会有<modules> <module>servlet-01</module> </modules>子项目中<parent> <artifactId>javaweb-02-servlet</artifactId> <groupId>com.kuang</groupId> <version>1.0-SNAPSHOT</version> </parent>父项目中的java子项目可以直接使用son extends father3.Maven环境优化修改web.xml为最新的将maven的结构搭建完整4.编写一个Servlet程序编写一个普通类实现Servlet接口,这里我们直接继承HttpServletpublic class HelloServlet extends HttpServlet { //由于get或者post只是请求实现的不同方式,可以互相调用,业务逻辑都一样 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ServletOutputStream outputStream = resp.getOutputStream(); PrintWriter writer = resp.getWriter();//响应流 writer.print("Hello,Servlet"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }5.编写Servlet的映射​ 为什么需要映射:我们写的是Java程序,但是需要浏览器访问,而浏览器需要访问web服务器,所以我们需要在web服务中注册我们写的Servlet,还需给他一个浏览器访问的路径: <!--注册Servlet--> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.kuang.servlet.HelloServlet</servlet-class> </servlet> <!--Servlet的请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>6.配置tomcat​ 注意:配置项目发布的路劲就可以了7.启动测试:OK!6.3、Servlet原理Servlet是由web服务器调用,web服务器在收到浏览器请求之后,会:6.4、Mapping问题1.一个Servlet可以指定一个映射路径<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>2.一个Servlet可以指定多个映射路径<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello3</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello4</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello5</url-pattern> </servlet-mapping>3.一个Servlet可以指定通用映射路径<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>4.默认请求路径<!--默认请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>5.指定一些后缀或者前缀等等…<!--可以自定义后缀实现请求映射 注意点:*前面不能加项目映射的路径 --> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.qinjiang</url-pattern> </servlet-mapping>6.优先级问题指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求;<!--404--> <servlet> <servlet-name>error</servlet-name> <servlet-class>com.kuang.servlet.ErrorServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>error</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>6.5、ServletContextweb容器在启动的时候,他会为每个web程序都创建一个对应的ServletContext对象,他代表了当前的web应用;1、共享数据我在这个Servlet中保存的数据,可以在另外一个Servlet中拿到;public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // this.getInitParameter() 初始化参数 // this.getServletConfig() Servlet配置 // this.getServletContext() Servlet上下文 ServletContext servletContext = this.getServletContext(); String username = "qinjiang";//数据 servletContext.setAttribute("username",username);//讲一个数据保存在ServletContext中,名字为username,值 username // PrintWriter writer = resp.getWriter(); // writer.print("hello"); } }public class GetServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); String username = (String) servletContext.getAttribute("username"); resp.getWriter().print("名字:"+username); resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }<servlet> <servlet-name>hello</servlet-name> <servlet-class>com.kuang.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet> <servlet-name>getc</servlet-name> <servlet-class>com.kuang.servlet.GetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>getc</servlet-name> <url-pattern>/getc</url-pattern> </servlet-mapping>测试访问结果;2、获取初始化参数<servlet> <servlet-name>getc</servlet-name> <servlet-class>com.kuang.servlet.GetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>getc</servlet-name> <url-pattern>/getc</url-pattern> </servlet-mapping>public class ServletDemo03 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); String url = context.getInitParameter("url"); resp.getWriter().print(url); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }public class ServletDemo04 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); // RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/sd");//转发的请求路径 // requestDispatcher.forward(req,resp);//调用forword实现请求转发 servletContext.getRequestDispatcher("/sd").forward(req,resp); }3.请求转发4.读取资源文件Properties在Java目录下新建properties在resources目录下新建properties发现:都被打包到了同一个路径下:classes,我们俗称这个路径为classpath;思路:都需要一个文件流;username=root password=123456public class ServletDemo05 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties"); Properties prop = new Properties(); prop.load(is); String user = prop.getProperty("username"); String pwd = prop.getProperty("password"); resp.getWriter().print(user+":"+pwd); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); }访问测试即可OK!6.6、HttpServletResponseweb服务器接收客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequuest对象,代表响应的一个HttpServletResponse;如果要获取客户端响应过来的参数,找HttpServletRequuest如果要给客户端响应一些信息:找HttpServletResponse1、简单分类负责向浏览器发送数据的方法ServletOutputStream getOutputStream() throws IOException; PrintWriter getWriter() throws IOException;负责向浏览器发送响应头的方法void setCharacterEncoding(String var1); void setContentLength(int var1); void setContentLengthLong(long var1); void setContentType(String var1); void setDateHeader(String var1, long var2); void addDateHeader(String var1, long var2); void setHeader(String var1, String var2); void addHeader(String var1, String var2); void setIntHeader(String var1, int var2); void addIntHeader(String var1, int var2);响应的状态码int SC_CONTINUE = 100; int SC_SWITCHING_PROTOCOLS = 101; int SC_OK = 200; int SC_CREATED = 201; int SC_ACCEPTED = 202; int SC_NON_AUTHORITATIVE_INFORMATION = 203; int SC_NO_CONTENT = 204; int SC_RESET_CONTENT = 205; int SC_PARTIAL_CONTENT = 206; int SC_MULTIPLE_CHOICES = 300; int SC_MOVED_PERMANENTLY = 301; int SC_MOVED_TEMPORARILY = 302; int SC_FOUND = 302; int SC_SEE_OTHER = 303; int SC_NOT_MODIFIED = 304; int SC_USE_PROXY = 305; int SC_TEMPORARY_REDIRECT = 307; int SC_BAD_REQUEST = 400; int SC_UNAUTHORIZED = 401; int SC_PAYMENT_REQUIRED = 402; int SC_FORBIDDEN = 403; int SC_NOT_FOUND = 404; int SC_METHOD_NOT_ALLOWED = 405; int SC_NOT_ACCEPTABLE = 406; int SC_PROXY_AUTHENTICATION_REQUIRED = 407; int SC_REQUEST_TIMEOUT = 408; int SC_CONFLICT = 409; int SC_GONE = 410; int SC_LENGTH_REQUIRED = 411; int SC_PRECONDITION_FAILED = 412; int SC_REQUEST_ENTITY_TOO_LARGE = 413; int SC_REQUEST_URI_TOO_LONG = 414; int SC_UNSUPPORTED_MEDIA_TYPE = 415; int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416; int SC_EXPECTATION_FAILED = 417; int SC_INTERNAL_SERVER_ERROR = 500; int SC_NOT_IMPLEMENTED = 501; int SC_BAD_GATEWAY = 502; int SC_SERVICE_UNAVAILABLE = 503; int SC_GATEWAY_TIMEOUT = 504; int SC_HTTP_VERSION_NOT_SUPPORTED = 505;2、下载文件1.向浏览器输出消息2.下载文件​ 1.要获取下载文件的路径​ 2.下载的文件名是啥?​ 3.设置想办法让浏览器能够支持下载我们需要的东西​ 4.获取下载文件的输入流​ 5.创建缓冲区​ 6.获取OutputStream对象​ 7.将FileOutputStream流写入到buffer缓冲区​ 8.使用OutputStream将缓冲区中的数据输出到客户端!public class FileServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1.要获取下载文件的路径 String realPath = "/Users/mac/IdeaProjects/javaweb-02-servlet/response/src/main/resources/1.png"; System.out.println("下载文件的路径:"+realPath); // 2.下载的文件名是啥? String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1); // 3.设置想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件名URLEncoder.encode编码,否则有可能乱码 resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8")); // 4.获取下载文件的输入流 FileInputStream in = new FileInputStream(realPath); // 5.创建缓冲区 int len = 0; byte[] buffer = new byte[1024]; // 6.获取OutputStream对象 ServletOutputStream out = resp.getOutputStream(); // 7.将FileOutputStream流写入到buffer缓冲区 ,使用OutputStream将缓冲区中的数据输出到客户端! while ((len=in.read(buffer))>0){ out.write(buffer,0,len); } in.close(); out.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }3、验证码功能验证怎么来的?前端实现后端实现,需要用到java的图片类,生成一个图片public class ImageServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //如何让浏览器5秒自动刷新一次; resp.setHeader("refresh","3"); //在内存中创建一个图片 BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB); //得到图片 Graphics2D g = (Graphics2D) image.getGraphics();//笔 //设置图片的背景颜色 g.setColor(Color.white); g.fillRect(0,0,80,20); //给图片写数据 g.setColor(Color.BLUE); g.setFont(new Font(null,Font.BOLD,20)); g.drawString(makeNum(),0,20); //告诉浏览器,这个请求用图片的方式打开 resp.setContentType("image/jpeg"); //网站存在缓存,不让浏览器缓存 resp.setDateHeader("Expires",-1); resp.setHeader("Cache-Control","no-cache"); resp.setHeader("Pragma","no-cache"); //把图片写给浏览器 ImageIO.write(image,"jpg", resp.getOutputStream()); } //生成随机数 private String makeNum(){ Random random = new Random(); String num = random.nextInt(9999999) + ""; StringBuffer sb = new StringBuffer(); for (int i = 0; i < 7-num.length(); i++) { sb.append("0"); } num = sb.toString() + num; return num; } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }4.实现重定向B一个web资源收到客户端A请求后,B他会通知客户端A去访问另一个web资源C,这个过程叫做重定向常见场景:用户登录void sendRedirect(String var1) throws IOException;测试: @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { /* resp.setHeader("Location","/r/img"); resp.setStatus(302); */ resp.sendRedirect("/r/img");//重定向 }面试题:请你聊聊重定向与转发的区别?相同点页面都会实现跳转不同点请求转发的时候,url不会发生变化重定向的时候,url地址栏会发生变化表单的请求与响应 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //处理请求 String username = req.getParameter("username"); String password = req.getParameter("password"); System.out.println(username+":"+password); //重定向的时候一定要注意,路径问题,否则404 resp.sendRedirect("/r/success.jsp"); }<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>Success</h1> </body> </html>6.7、HttpServletRequestHttpServletRequest代表客户端的请求,用户通过Http协议访问浏览器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息;获取参数,请求转发 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); String username = req.getParameter("username"); String password = req.getParameter("password"); String[] hobbys = req.getParameterValues("hobbys"); System.out.println("====================="); //后台接收中文乱码问题 System.out.println(username); System.out.println(password); System.out.println(Arrays.toString(hobbys)); System.out.println("====================="); System.out.println(req.getContextPath()); //通过请求转发 //这里的 / 代表当前的web应用 req.getRequestDispatcher("/success.jsp").forward(req,resp); }面试题:请你聊聊重定向与转发的区别?相同点页面都会实现跳转不同点请求转发的时候,url不会发生变化 307重定向的时候,url地址栏会发生变化 3027、Cookie、Session7.1、会话会话:用户打开一个浏览器,点击了很多超链接,访问了很多web资源,关闭浏览器,这个过程可以称之为会话有状态会话::一个同学来过教室,下次再来教室,我们会知道这个同学,曾经来过你能怎么证明你是西开的学生?你 西开发票 西开给你发票学校登记 西开标记你来过了一个网站,怎么证明你来过了?客户端 服务端1.服务端给客户端一个信件,客户端下次访问服务端带上信件就可以了;cookie2.服务器登记你来过了,下次你来的时候我来匹配你;session7.2、保存会话的两种技术cookie客户端技术(响应、请求)session服务器技术,利用这个技术,可以保存用户的会话信息,我们可以把信息或者数据放在Session中!常见:网站登录之后,你下次就不用在登陆了,第二次访问直接就上去了!7.3、Cookie1.从请求中拿到cookie信息2.服务器响应给客户端cookieCookie[] cookies = req.getCookies(); //获得cookie cookie.getName(); //获得cookie中的key cookie.getValue(); //获得cookie中的值 new Cookie("lastLoginTime",System.currentTimeMillis()+""); //新建一个cookie cookie.setMaxAge(24*60*60); //设置cookie的有效期 resp.addCookie(cookie); //响应给客户端一个cookiecookie:一般会保存在本地的 用户目录下appdata;一个网站cookie是否存在上限 ! 聊聊细节问题一个cookie只能保存一个信息;一个web站点可以给浏览器发送多个cookie,最多存放20个cookieCookie大小有限制4kb;300个cookie浏览器上限删除Cookie:不设置有效期,关闭浏览器,自动失效;设置有效期时间为0 ;编码解码:URLEncoder.encode("秦疆","utf-8") URLDecoder.decode(cookie.getValue(),"utf-8"7.4、Session(重点)什么是Session:服务器会给每一个用户(浏览器)创建一个Session对象一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在;用户登录之后,整个网站都要访问!- ->保存用户的信息,保存购物车的信息.Session和Cookie的区别:Cookie是把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)Session把用户的数据写到独占Session中,服务器保存(保存重要的信息,减少服务器资源的浪费)Session对象由服务器创建;使用场景:保存一个登录用户的信息;购物车信息;在整个网站中经常会使用的数据,我们将它保存在Session中;使用Session: @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //解决乱码问题 req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html;charset=utf-8"); //得到Session HttpSession session = req.getSession(); //给Session中存东西 session.setAttribute("name",new Person("秦疆",1)); //得到Session的ID String sessionId = session.getId(); //判断Session是不是新创建 if (session.isNew()){ resp.getWriter().write("session创建成功,ID"+sessionId); }else{ resp.getWriter().write("session以及在服务器中存在了,ID"+sessionId); } // //Session创建的时候做了什么事情 // Cookie cookie = new Cookie("JSESSIONID","sessionId"); // resp.addCookie(cookie); } //得到Session HttpSession session = req.getSession(); Person person = (Person) session.getAttribute("name"); System.out.println(person.toString()); HttpSession session = req.getSession(); session.removeAttribute("name"); //手动注销Session session.invalidate();会话自动过期:web.xml中配置<!--设置Session默认失效时间--> <session-config> <!--15分钟后Session自动失效,以分钟为单位--> <session-timeout>15</session-timeout> </session-config>8、JSP8.1、什么是JSPJava Server Pages:Java服务器端页面,也和Servlet一样,用于动态web技术!最大的特点:写JSP就像在写HTML区别:HTML只给用户提供静态的数据JSP页面中可以嵌入Java代码,为用户提供动态的数据;8.2、JSP原理思路:JSP到底怎么执行的 !代码层面没有任何问题服务器内部工作​ tomcat中有一个work目录;​ IDEA中使用Tomcat的会在IDEA的Tomcat中生产一个work目录地址:/usr/local/ApacheTomcat/work/Catalina/localhost/ROOT/org/apache/jsp 发现页面转变为Java程序!浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet !JSP最终会被转换成为一个Java类!JSP本质上就是一个Servlet//初始化 public void jspInit() { } //销毁 public void jspDestroy() { } //JSPService public final void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this._jspService(request, response); }1.判断请求2.内置了一些对象final jakarta.servlet.jsp.PageContext pageContext; //页面上下文 jakarta.servlet.http.HttpSession session = null; //Session final jakarta.servlet.ServletContext application; //applicationContext final jakarta.servlet.ServletConfig config; //config jakarta.servlet.jsp.JspWriter out = null; //out final java.lang.Object page = this; //page;当前页 HttpServletRequest request //请求 HttpServletResponse response //响应 jakarta.servlet.jsp.JspWriter _jspx_out = null; jakarta.servlet.jsp.PageContext _jspx_page_context = null;3.输出页面前增加的代码response.setContentType("text/html; charset=UTF-8"); //设置响应的页面类型 pageContext = _jspxFactory.getPageContext(this, request, response, null, false, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); out = pageContext.getOut(); _jspx_out = out;4.以上的这些个对象我们可以在JSP页面中直接使用!在JSP页面中:只要是Java代码就会原封不动的输出;如果是HTML代码,就会被转换为:out.write("<html>\r\n");这样的格式,输出到前端 !8.3、JSP基础语法任何语言都有自己的语法,Java中有。JSP作为Java技术的一种应用,它拥有一些自己扩充的语法(了解,知道即可!),Java所有语法都支持!JSP表达式 <%-- JSP表达式 作用:用来将程序的输出,输出到客户端 <%= 变量或者表达式 %> --%> <%=new java.util.Date()%>jsp脚本片段<%--jsp脚本片段--%> <% int sum = 0; for (int i = 0; i < 100; i++) { sum+=i; } out.print("<h1>Sum="+sum+"</h1>"); %>脚本片段的在实现 <% int x =10; out.println(x); %> <p>这是一个JSP文档</p> <% int y =2; out.println(y); %> <hr> <%--在代码中嵌入HTML元素--%> <% for (int i = 0; i < 5; i++) { %> <h1>Hello,Word! <%=i%></h1> <% } %>JSP声明<%! static { System.out.println("Loading Servlet!"); } private int globalVar = 0; public void kuang(){ System.out.println("进去了方法Kuang!"); } %>JSP声明:会被编译到JSP生成的Java类中!其他的,就会被生成到 _jspService方法中_!在JSP,嵌入Java代码即可!<%%> <%=%> <%!%> <--注释-->JSP的注释,不会在客户端显示,HTML就会 !8.4、JSP指令<%@ page ....%> <%@include file=""%> <%-- @include会将两个页面合二为一 --%> <%@include file="/common/header.jsp"%> <h1>网页主体</h1> <%@include file="/common/footer.jsp"%> <hr> <%--jsp标签 jsp:include:拼接页面,本质还是三个 --%> <jsp:include page="/common/header.jsp"/> <h1>网页主体</h1> <jsp:include page="/common/footer.jsp"/>8.5、9大内置对象PageContext 存东西Request 存东西ResponseSession 存东西Application 【ServletContext】 存东西config 【ServletConfig】outpage 不用exception<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <%--内置对象--%> <% pageContext.setAttribute("name1","秦疆1号");//保存的数据只在一个页面中有效 request.setAttribute("name2","秦疆2号");//保存的数据只在一次请求中有效,请求转发会携带这个数据 session.setAttribute("name3","秦疆3号");//保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器 application.setAttribute("name4","秦疆4号");//保存的数据只在服务器中有效,从打开服务器到关闭服务器 %> <%--脚本片段中的代码,会被原封不动的生成到.JSP.java 要求:这里面的代码,必须保证Java语法的正确性 --%> <% //从pageContext取出,我们通过寻找的方式来 //从底层到高层(作用域):page-->request-->session-->application String name1 = (String) pageContext.findAttribute("name1"); String name2 = (String) pageContext.findAttribute("name2"); String name3 = (String) pageContext.findAttribute("name3"); String name4 = (String) pageContext.findAttribute("name4"); String name5 = (String) pageContext.findAttribute("name5");//不存在 %> <%--使用EL表达式输出 ${} --%> <h1>取出的值为:</h1> <h3>${name1}</h3> <h3>${name2}</h3> <h3>${name3}</h3> <h3>${name4}</h3> <h3> <%=name5%> </h3> </body> </html> request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的!session:客户端向服务器发送请求,产生的数据,用户看完一会还有用的,比如:购物车;application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:聊天数据;8.6、JSP标签、JSTL标签、EL表达式<!-- JSTL 表达式的依赖 --> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl-api</artifactId> <version>1.2</version> </dependency> <!-- standard 标签库 --> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency>EL表达式:${ }获取数据执行运算获取web开发的常用对象调用Java方法JSP标签<%--jsp:include--%> <%-- http://localhost:8080/jsptag.jsp?name=kuangshen&age=12 --%> <jsp:forward page="/jsptag2.jsp"> <jsp:param name="name" value="kuangshen"/> <jsp:param name="age" value="12"/> </jsp:forward>JSTL表达式JSTL标签库的使用就是为了弥补HTML标签的不足;他自定义许多标签,可以供我们使用,标签的功能和Java代码一样!格式化标签SQL标签XML标签核心标签(掌握部分)JSTL标签库使用步骤引入对应的taglib使用其中的方法在Tomcat也需要引入jstl的包,否则会报错:JSTL报错c:if c:out<body> <h4>if测试</h4> <hr> <form action="coreif.jsp" method="get"> <%-- EL表达式获取表单中的数据 ${param.参数名} --%> <input type="text" name="username" value="${param.username}"> <input type="submit" name="登录"> </form> <%--判断如果是提交的用户名是管理员,则登录成功--%> <c:if test="${param.username=='admin'}" var="isAdmin"> <c:out value="管理员欢迎您!"/> </c:if> <%--自闭合标签--%> <c:out value="${isAdmin}"/> </body>c:choose c:when<body> <%--定义一个变量score,值为85--%> <c:set var="score" value="85"/> <c:choose> <c:when test="${score>=90}"> 你的成绩优秀 </c:when> <c:when test="${score>=80}"> 你的成绩一般 </c:when> <c:when test="${score>=70}"> 你的成绩良好 </c:when> <c:when test="${score<=60}"> 你的成绩不及格 </c:when> </c:choose> </body>c:forEach<% ArrayList<String> people = new ArrayList<>(); people.add(0,"张三"); people.add(1,"李四"); people.add(2,"王五"); people.add(3,"赵六"); people.add(4,"田七"); request.setAttribute("list",people); %> <%-- var , 每一次遍历出来的变量 items , 要遍历的对象 begin , 从哪里开始 end , 到哪里结束 step , 步长 --%> <c:forEach var="people" items="${list}"> <c:out value="${people}"/> <br> </c:forEach> <hr> <c:forEach var="people" items="${list}" begin="1" end="3" step="2"> <c:out value="${people}"/> <br> </c:forEach>9、JavaBean实体类JavaBean有特定的写法:必须有一个无参构造属性必须私有化必须有对应的get/set方法;一般用来和数据库的字段做映射 ORM;ORM:对象关系映射表- ->类字段- ->属性行记录- ->对象people表idnameageaddress1秦疆1号1上海2秦疆2号2北京3秦疆3号3广州class People{ private int id; private String name; private int age; private String address; } class A{ new People(1,"秦疆1号",1,"上海"); new People(2,"秦疆2号",2,"北京"); new People(3,"秦疆3号",3,"广州"); }10、MVC三层架构什么是MVC:Model view Controller 模型、视图、控制器10.1、早些年用户直接访问控制层,控制层可以直接操作数据库;servlet-->CRUD-->数据库 弊端:程序十分臃肿,不利于维护 servlet的代码中:处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码 架构:没有什么是加一层解决不了的! 程序猿调用 | JDBC | Mysql Oracle sqlServlet10.2、MVC三层架构Model业务处理:业务逻辑(Service)数据持久层:CRUD(Dao)View展示数据提供链接发起Servlet请求(a,form,img…)Controller接收用户的请求:(req:请求参数、Session信息…)交给业务层处理对应的代码控制视图的跳转登录--->接收用户的登录请求--->处理用户的请求(获取用户登录的参数,username,password)--->交给业务层处理登录业务(判断用户名密码是否正确:事务)--->Dao层查询用户名和密码是否正确--->数据库11、Filter(重点)Filter:过滤器,用来过滤网站的数据;处理中文乱码登录验证…Filter开发步骤:1.导包2.编写过滤器​ 1.导包不要错 实现Filter接口,重写对应的方法即可public class CharacterEncodingFilter implements Filter { //初始化:web服务器启动,就已经初始化了,随时等待过滤对象出现! public void init(FilterConfig filterConfig) throws ServletException { System.out.println("CharacterEncodingFilter初始化"); } //chain:链 /* 1.过滤器中的所有代码,在过滤特定请求的时候都会执行 2.必须让过滤器继续同行 chain.doFilter(request,response); */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text-html;charset=utf-8"); System.out.println("CharacterEncodingFilter执行前..."); chain.doFilter(request,response);//让我们的程序继续走,如果不写,我们的程序到这里就停止了! System.out.println("CharacterEncodingFilter执行后..."); } //销毁:web服务器关闭的时候,过滤就会销毁 public void destroy() { System.out.println("CharacterEncodingFilter销毁"); } } 3.在web.xml中配置Filter <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.kuang.filter.CharacterEncodingFilter</filter-class> </filter> <!--只要是在/servlet下的所有请求,会经过这个过滤器--> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/servlet/*</url-pattern> </filter-mapping>12、监听器实现一个监听器的接口:(有N种)1.实现监听器的接口://统计网站在线人数:统计 Session public class OnlineCountListener implements HttpSessionListener { //创建session监听:看你的一举一动 //一旦创建session就会触发一次这个事件! public void sessionCreated(HttpSessionEvent se) { System.out.println(se.getSession().getId()); ServletContext ctx = se.getSession().getServletContext(); Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount"); if (onlineCount==null){ onlineCount = new Integer(1); }else { int count = onlineCount.intValue(); onlineCount = new Integer(count+1); } ctx.setAttribute("OnlineCount",onlineCount); } //销毁session监听! //一旦创销毁session就会触发一次这个事件! public void sessionDestroyed(HttpSessionEvent se) { ServletContext ctx = se.getSession().getServletContext(); Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount"); if (onlineCount==null){ onlineCount = new Integer(0); }else { int count = onlineCount.intValue(); onlineCount = new Integer(count-1); } ctx.setAttribute("OnlineCount",onlineCount); } /* session销毁: 1.手动销毁 :getSession().invalidate(); 2.自动销毁:web.xml配置 */ }2.在web.xml中配置监听器<!--注册监听器--> <listener> <listener-class>com.kuang.listener.OnlineCountListener</listener-class> </listener>3.看情况是否使用13、过滤器、监听器常见应用监听器:GUI编程中经常使用:public class TestPanel { public static void main(String[] args) { Frame frame = new Frame("JAVA编程") ; Panel panel = new Panel(null);//面板 frame.setLayout(null);//设置窗体的布局 frame.setBounds(300,300,500,500); frame.setBackground(new Color(0,0,255)); frame.setBounds(50,50,300,300); panel.setBackground(new Color(0,255,2));//设置背景颜色 frame.add(panel); frame.setVisible(true); //监听事件,监听关闭事件 frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { super.windowClosing(e); } }); } }1.用户登录之后才能进去主页!用户注销后就不能进去主页了!2.进入主页的时候要判断用户是否已经登录;要求:在过滤器中实现!//ServletRequest HttpServletRequest HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; if (request.getSession().getAttribute(Constant.USER_SESSION)==null){ ((HttpServletResponse) resp).sendRedirect("/error.jsp"); } chain.doFilter(request,response);14、JDBC什么是JDBC:Java连接数据库需要jar包的支持:java.sqljavax.sqlmysql-conneter-java…连接驱动(必须要导入)实验环境搭建CREATE TABLE `user`( `id` INT PRIMARY KEY, `name` VARCHAR(40), `password` VARCHAR(40), email VARCHAR(60), birthday DATE ); INSERT INTO `user`(`id`,`name`,`password`,`email`,`birthday`) VALUES(1,'张三','123456','zs@qq.com','2000-01-01'); INSERT INTO `user`(`id`,`name`,`password`,`email`,`birthday`) VALUES(2,'李四','123456','ls@qq.com','2000-01-01'); INSERT INTO `user`(`id`,`name`,`password`,`email`,`birthday`) VALUES(3,'王五','123456','ww@qq.com','2000-01-01');导入数据库依赖<!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency>IDEA连接数据库JDBC固定步骤:加载驱动连接数据库,代表数据库向数据库发送SQL的对象:CRUD编写SQL(根据业务,不同的SQL)执行SQL关闭连接public class TestJdbc { public static void main(String[] args) throws ClassNotFoundException, SQLException { //配置信息 //useUnicode=true&characterEncoding=utf-8 解决中文乱码 String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8"; String username = "root"; String password = "mysqlstart"; //1.加载驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //2.连接数据库,代表数据库 Connection connection = DriverManager.getConnection(url, username, password); //3.向数据库发送SQL的对象 Statement , prepareStatement :CRUD Statement statement = connection.createStatement(); //4.编写SQL String sql = "select * from users "; //5.执行查询SQL,返回一个 ResultSet : 结果集 ResultSet rs = statement.executeQuery(sql); while(rs.next()){ System.out.println("id="+rs.getObject("id")); System.out.println("name="+rs.getObject("name")); System.out.println("password="+rs.getObject("password")); System.out.println("email="+rs.getObject("email")); System.out.println("birthday="+rs.getObject("birthday")); } //6.关闭连接,释放资源(一定要做)先开后关 rs.close(); statement.close(); connection.close(); } }预编译SQLpublic class TestJdbc2 { public static void main(String[] args) throws ClassNotFoundException, SQLException { //配置信息 //useUnicode=true&characterEncoding=utf-8 解决中文乱码 String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8"; String username = "root"; String password = "mysqlstart"; //1.加载驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //2.连接数据库,代表数据库 Connection connection = DriverManager.getConnection(url, username, password); //3.编写sql String sql = "insert into users (id,name,password,email,birthday) values (?,?,?,?,?)"; //4.预编译 PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1,4);//给第一个占位符?的值赋值为1 preparedStatement.setString(2,"狂神说Java");//给第二个占位符?的值赋值为狂神说Java preparedStatement.setString(3,"123456");//给第三个占位符?的值赋值为123456 preparedStatement.setString(4,"24323243@qq.com");//给第四个占位符?的值赋值为24323243@qq.com preparedStatement.setDate(5,new Date(new java.util.Date().getTime()));//给第五个占位符?的值赋值为new Date(new java.util.Date().getTime()) //5.执行SQL int i = preparedStatement.executeUpdate(); if (i>0){ System.out.println("插入成功"); } //6.关闭连接,释放资源(一定要做)先开后关 preparedStatement.close(); connection.close(); } }事务要么都成功,要么都失败!ACID原则,保证数据的安全。开启事务 事务提交 commit() 事务回滚 rollback() 关闭事务 转账: A:1000 B:1000 A(900)---100--->B(1100)Junit单元测试依赖<dependencies> <!--Junit单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>简单使用@Test注解只在方法上有效,只要加了这个注解的方法,就可以直接运行!public class TestJdbc3 { @Test public void test() { Connection connection = null; //配置信息 //useUnicode=true&characterEncoding=utf-8 解决中文乱码 String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8"; String username = "root"; String password = "mysqlstart"; //1.加载驱动 try { Class.forName("com.mysql.cj.jdbc.Driver"); //2.连接数据库,代表数据库 connection = DriverManager.getConnection(url, username, password); //3.通知数据库开启事务,false 开启 connection.setAutoCommit(false); String sql = "update account set money = money - 100 where name ='A'"; connection.prepareStatement(sql).executeUpdate(); String sql2 = "update account set money = money + 100 where name ='B'"; connection.prepareStatement(sql2).executeUpdate(); connection.commit();//以上两条SQL都执行成功了,就提交事务! System.out.println("sucess"); } catch (Exception e) { e.printStackTrace(); } try { connection.rollback(); } catch (SQLException e) { e.printStackTrace(); }finally { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } } SMBMS数据库:项目如何搭建?考虑使用不使用Maven?依赖,jar项目搭建准备工作1.搭建一个maven web项目2.配置Tomcat3.测试项目是否能够跑起来4.导入项目中会遇到的jar包:​ jsp,servlet,mysql驱动,jstl,stand…5.创建项目包结构6.编写实体列;​ ORM映射:表-类映射7.编写基础公共类​ 1.数据库配置文件driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306?useUnicode=true&characterEncoding=utf-8 username=root password=mysqlstart​ 2.编写数据库的公共类package com.kunag.dao; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; //操作数据库的公共类 public class BaseDao { private static String driver; private static String url; private static String username; private static String password; //静态代码块,类加载的时候就初始化了 static { Properties properties = new Properties(); //通过类加载器读取对应的资源 InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("db.properties"); try { properties.load(is); } catch (IOException e) { e.printStackTrace(); } driver = properties.getProperty("driver"); url = properties.getProperty("url"); username = properties.getProperty("username"); password = properties.getProperty("password"); } //获取数据库的链接 public static Connection getConnection(){ Connection connection = null; try { Class.forName(driver); connection = DriverManager.getConnection(url,username,password); } catch (Exception e) { e.printStackTrace(); } return connection; } //编写查询公共类 public static ResultSet execute(Connection connection,String sql,Object[] params,ResultSet resultSet,PreparedStatement preparedStatement) throws SQLException { //预编译的SQL在后面直接执行就可以了 preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < params.length; i++) { //setObject,占位符从1开始,但是我们的数组从0 开始! preparedStatement.setObject(i+1,params[i]); } resultSet = preparedStatement.executeQuery(); return resultSet; } //编写增删改公共类 public static int execute(Connection connection,String sql,Object[] params,PreparedStatement preparedStatement) throws SQLException { //预编译的SQL在后面直接执行就可以了 preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < params.length; i++) { //setObject,占位符从1开始,但是我们的数组从0 开始! preparedStatement.setObject(i+1,params[i]); } int updateRows = preparedStatement.executeUpdate(); return updateRows; } public static boolean closeResources(Connection connection,PreparedStatement preparedStatement,ResultSet resultSet){ boolean flag = true; if (resultSet!=null){ try { resultSet.close(); //GC回收 resultSet = null; } catch (SQLException e) { e.printStackTrace(); flag = false; } } if (preparedStatement!=null){ try { preparedStatement.close(); //GC回收 preparedStatement = null; } catch (SQLException e) { e.printStackTrace(); flag = false; } } if (connection!=null){ try { connection.close(); //GC回收 connection = null; } catch (SQLException e) { e.printStackTrace(); flag = false; } } return flag; } } ​ 3.编写字符编码过滤器8.导入静态资源登录功能实现1.编写前端页面2.设置首页<!--设置欢迎页面--> <welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list>3.编写dao层用户登录的接口//得到要登录的用户 public User getLoginUser(Connection connection,String userCode) throws SQLException;4.编写dao接口的实现类public class UserDaoImpl implements UserDao { public User getLoginUser(Connection connection, String userCode) throws SQLException { PreparedStatement pstm = null; ResultSet rs = null; User user = null; if (connection != null) { String sql = "select * from smbms_user where userCode=?"; Object[] params = {userCode}; rs = BaseDao.execute(connection, pstm, rs, sql, params); if (rs.next()) { user = new User(); user.setId(rs.getInt("id")); user.setUserCode(rs.getString("userCode")); user.setUserName(rs.getString("userName")); user.setUserPassword(rs.getString("userPassword")); user.setGender(rs.getInt("gender")); user.setBirthday(rs.getDate("birthday")); user.setPhone(rs.getString("phone")); user.setAddress(rs.getString("address")); user.setUserRole(rs.getInt("userRole")); user.setCreateBy(rs.getInt("createBy")); user.setCreationDate(rs.getTimestamp("creationDate")); user.setModifyBy(rs.getInt("modifyBy")); user.setModifyDate(rs.getTimestamp("modifyDate")); } BaseDao.closeResources(null, pstm, rs); } return user; } } 5.业务层接口public interface UserService { //用户登录 public User login(String userCode,String password); }6.业务层实现类public class UserServiceImpl implements UserService{ //业务层都会调用dao层,所以我们要引入Dao层; private UserDao userDao; public UserServiceImpl(){ userDao = new UserDaoImpl(); } public User login(String userCode, String password) { Connection connection = null; User user = null; try { connection = BaseDao.getConnection(); //通过业务层调用对应的具体的数据库操作 user = userDao.getLoginUser(connection, userCode); } catch (SQLException e) { e.printStackTrace(); }finally { BaseDao.closeResources(connection,null,null); } return user; } }7.编写Servletpublic class LoginServlet extends HttpServlet { //Servlet:控制层,调用业务层代码 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("LoginServlet--start..."); //获得用户名和密码 String userCode = req.getParameter("userCode"); String userPassword = req.getParameter("userPassword"); //和数据库中的密码进行对比,调用业务层; UserServiceImpl userService = new UserServiceImpl(); User user = userService.login(userCode, userPassword);//这里已经把登录的人给查出来了 if (user!=null){//查有此人,可以登录 //将用户的信息放回到Session中; req.getSession().setAttribute(Constants.USER_SESSION,user); //跳转到主页 resp.sendRedirect("jsp/frame.jsp"); }else{//查无此人 //转发到登录页面,顺带提醒他,用户名或密码错误; req.setAttribute("error","用户名或密码错误"); req.getRequestDispatcher("login.jsp").forward(req,resp); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }8.注册Servlet<!--注册Servlet--> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.kunag.servlet.user.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login.do</url-pattern> </servlet-mapping>9.测试访问,确保以上功能成功!登录功能优化注销功能:思路:移除Session,返回登录页面public class LoginoutServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //移除用户的Constants.USER_SESSION req.getSession().removeAttribute(Constants.USER_SESSION); resp.sendRedirect("/login.jsp");//返回登录页面 } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }注册xml<servlet> <servlet-name>LoginoutServlet</servlet-name> <servlet-class>com.kunag.servlet.user.LoginoutServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginoutServlet</servlet-name> <url-pattern>/jsp/logout.do</url-pattern> </servlet-mapping>登录拦截优化编写一个过滤器,并注册public class SysFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; //过滤器,从Session中获得用户。 User user = (User) request.getSession().getAttribute(Constants.USER_SESSION); if (user==null){//已经被移除或者注销了,或者未登录 response.sendRedirect("/smbms/error.jsp"); }else { chain.doFilter(req,resp); } } public void destroy() { } }<filter> <filter-name>SysFilter</filter-name> <filter-class>com.kunag.filter.SysFilter</filter-class> </filter> <filter-mapping> <filter-name>SysFilter</filter-name> <url-pattern>/jsp/*</url-pattern> </filter-mapping>测试,登录,注销,权限,都要保证OK!密码功能修改1.导入前端素材<li><a href="${pageContext.request.contextPath }/jsp/pwdmodify.jsp">密码修改</a></li>2.写项目,建议从底层向上写3.UserDao 接口//修改当前用户密码 public int updatePwd(Connection connection,int id,int password)throws SQLException;4.UserDao 接口实现类//修改当前用户密码 public int updatePwd(Connection connection, int id, int password) throws SQLException { PreparedStatement pstm = null; int execute = 0; if (connection!=null){ String sql = "update smbms_user set userPassword = ? where id = ?"; Object params[] = {password,id}; execute = BaseDao.execute(connection, pstm, sql, params); BaseDao.closeResources(null,pstm,null); } return execute; }5.UserService层//根据用户ID修改密码 public boolean updatePwd(int id,int pwd);6.UserService实现类public boolean updatePwd(int id, int pwd) { Connection connection = null; boolean flag = false; try { connection = BaseDao.getConnection(); if (userDao.updatePwd(connection,id,pwd)>0){ flag = true; } } catch (SQLException e) { e.printStackTrace(); } finally { BaseDao.closeResources(connection,null,null); } return flag; }7.Servlet记得实现复用,需要提取出方法!@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getParameter("method"); if (method.equals("savepwd")&&method!=null){ this.updatePwd(req,resp); } } public void updatePwd(HttpServletRequest req, HttpServletResponse resp){ //从Session里面拿ID Object o = req.getSession().getAttribute(Constants.USER_SESSION); String newpassword = req.getParameter("newpassword"); boolean flag = false; if (o!=null && newpassword!=null && newpassword.length()!=0){ // if (o!=null && !StringUtils.isNullOrEmpty(newpassword)){ UserService userService = new UserServiceImpl(); flag = userService.updatePwd(((User) o).getId(), newpassword); if (flag){ req.setAttribute("message","修改密码成功,请退出,使用新密码登录"); //密码修改成功,移除当前Session req.getSession().removeAttribute(Constants.USER_SESSION); }else { req.setAttribute("message","修改密码失败"); } }else{ req.setAttribute("message","新密码有问题"); } try { req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp); } catch (ServletException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }8.测试优化密码修改使用Ajax;1.阿里巴巴的fastjson<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.4</version> </dependency>2.后台代码修改 //修改密码 public void updatePwd(HttpServletRequest req, HttpServletResponse resp){ //从Session里面拿ID Object o = req.getSession().getAttribute(Constants.USER_SESSION); String newpassword = req.getParameter("newpassword"); boolean flag = false; //if (o!=null && newpassword!=null && newpassword.length()!=0){ if (o!=null && !StringUtils.isNullOrEmpty(newpassword)){ UserService userService = new UserServiceImpl(); flag = userService.updatePwd(((User) o).getId(), newpassword); if (flag){ req.setAttribute("message","修改密码成功,请退出,使用新密码登录"); //密码修改成功,移除当前Session req.getSession().removeAttribute(Constants.USER_SESSION); }else { req.setAttribute("message","修改密码失败"); } }else{ req.setAttribute("message","新密码有问题"); } try { req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp); } catch (ServletException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } //验证旧密码,session中有用户的密码 private void pwdmodify(HttpServletRequest req, HttpServletResponse resp) { //从Session里面拿ID; Object o = (User) req.getSession().getAttribute(Constants.USER_SESSION); String oldpassword = req.getParameter("oldpassword"); //万能的Map Map<String, String> resultMap = new HashMap<String,String>(); if (o==null){//session过期了,session失效了 resultMap.put("result","sessionerror"); }else if (StringUtils.isNullOrEmpty(oldpassword)){//输入的密码为空 resultMap.put("result","error"); }else{ String userPassword = ((User) o).getUserPassword();//Session中用户的密码 if (oldpassword.equals(userPassword)){ resultMap.put("result","true"); }else { resultMap.put("result","false"); } } try { resp.setContentType("application/json"); PrintWriter writer = resp.getWriter(); //JSONArray 阿里巴巴的JSON工具类,转换格式 /* resultMap = ["result","sessionerror","result","error"] Json格式: {key,value} */ writer.write(JSONArray.toJSONString(resultMap)); writer.flush(); writer.close(); } catch (IOException e) { e.printStackTrace(); } }3.测试用户管理实现思路:1.导入分页的工具类2.用户列表页面导入userlist.jsp rollpage.jsp1.获取用户数量1.UserDao//根据用户名或者角色查询用户总数 public int getUserCount(Connection connection,String username,int userRole)throws SQLException;2.UserDaoImpl //根据用户名或者角色查询用户总数 public int getUserCount(Connection connection, String username, int userRole) throws SQLException { PreparedStatement pstm = null; ResultSet rs = null; int count = 0; if (connection!=null){ StringBuffer sql = new StringBuffer(); sql.append("select count(1) as count from smbms_user u,smbms_role r\n" + "where u.userRole = r.id"); ArrayList<Object> list = new ArrayList<Object>();//存放我们的参数 if (!StringUtils.isNullOrEmpty(username)){ sql.append(" and u.userName like ?"); list.add("%"+username+"%");//index:0 } if (userRole>0){ sql.append(" and u.userRole = ?"); list.add(userRole);//index:1 } //怎么把list转换为数组 Object[] params = list.toArray(); System.out.println("UserDaoImpl->getUserCount"+sql.toString());//输出最后完整的SQL语句 rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params); if (rs.next()){ count = rs.getInt("count");//从结果集中获得最终的数量 } BaseDao.closeResources(null,pstm,rs); } return count; }3.UserService//查询记录数 public int getUserCount(String username,int userRole);4.UserServiceImpl //查询记录数 public int getUserCount(String username, int userRole) { Connection connection =null; int count = 0; try { connection = BaseDao.getConnection(); userDao.getUserCount(connection,username,userRole); } catch (SQLException e) { e.printStackTrace(); } finally { BaseDao.closeResources(connection,null,null); } return count; } @Test public void test(){ UserServiceImpl userService = new UserServiceImpl(); int userCount = userService.getUserCount(null, 1); System.out.println(userCount); }2.获取用户列表1.userDao//通过条件查询-userlist public List<User> getUserList(Connection connection,String username,int userRole,int currentPageNo,int pageSize)throws SQLException;2.userDaoImpl public List<User> getUserList(Connection connection, String username, int userRole, int currentPageNo, int pageSize) throws SQLException { // TODO Auto-generated method stub PreparedStatement pstm = null; ResultSet rs =null; List<User> userList = new ArrayList<User>(); if (connection!=null){ StringBuffer sql = new StringBuffer(); sql.append("select u.*,r.RoleName as userRoleName from smbms_user u,smbms_role r where u.userRole = r.id"); ArrayList<Object> list = new ArrayList<Object>(); if (!StringUtils.isNullOrEmpty(username)){ sql.append(" and u.username like ?"); list.add("%"+username+"%"); } if (userRole>0){ sql.append(" and u.userRole = ?"); list.add(userRole); } sql.append(" order by creationDate DESC limit ?,?"); currentPageNo = (currentPageNo-1)*pageSize; list.add(currentPageNo); list.add(pageSize); Object[] params = list.toArray(); System.out.println("sql---->"+sql.toString()); rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params); while (rs.next()){ User user = new User(); user.setId(rs.getInt("id")); user.setUserCode(rs.getString("userCode")); user.setUserName(rs.getString("userName")); user.setGender(rs.getInt("gender")); user.setBirthday(rs.getDate("birthday")); user.setPhone(rs.getString("phone")); user.setUserRole(rs.getInt("userRole")); user.setUserRoleName(rs.getString("userRoleName")); userList.add(user); } BaseDao.closeResources(null,pstm,rs); } return userList; }3.userService//根据条件查询用户列表 public List<User> getUserList(String queryUserName,int queryUserRole,int currentPageNo,int pageSize)4.userServiceImpl public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize) { Connection connection =null; List<User> userlist = null; System.out.println("queryUserName--->"+queryUserName); System.out.println("queryUserRole--->"+queryUserRole); System.out.println("currentPageNo--->"+currentPageNo); System.out.println("pageSize---->"+pageSize); connection = BaseDao.getConnection(); try { userDao.getUserList(connection,queryUserName,queryUserRole,currentPageNo,pageSize); } catch (SQLException e) { e.printStackTrace(); }finally { BaseDao.closeResources(connection,null,null); } return userlist; }3.获取角色列表==为了我们职责统一,可以把角色的操作单独放在一个包中,和POJO类对应==RoleDaopublic interface RoleDao { //获取角色列表 public List<Role> getRoleList(Connection connection)throws SQLException; }RoleDaoImplpackage com.kunag.dao.role; import com.kunag.dao.BaseDao; import com.kunag.pojo.Role; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class RoleDaoImpl implements RoleDao{ //获取角色列表 public List<Role> getRoleList(Connection connection) throws SQLException { PreparedStatement pstm = null; ResultSet resultSet = null; ArrayList<Role> roleArrayList = new ArrayList<Role>(); if (connection!=null){ String sql = "select * from smbms_role"; Object[] params = {}; resultSet = BaseDao.execute(connection, pstm, resultSet, sql, params); while (resultSet.next()){ Role _role = new Role(); _role.setId(resultSet.getInt("id")); _role.setRoleCode(resultSet.getString("roleCode")); _role.setRoleName(resultSet.getString("roleName")); roleArrayList.add(_role); } BaseDao.closeResources(null,pstm,resultSet); } return roleArrayList; } } RoleServicepublic interface RoleService { //获取角色列表 public List<Role> getRoleList() ; }RoleServiceImplpackage com.kunag.servlet.role; import com.kunag.dao.BaseDao; import com.kunag.dao.role.RoleDao; import com.kunag.dao.role.RoleDaoImpl; import com.kunag.pojo.Role; import java.sql.Connection; import java.sql.SQLException; import java.util.List; import java.util.concurrent.Callable; public class RoleServiceImpl implements RoleService{ //引入Dao private RoleDao roleDao; public RoleServiceImpl() { roleDao = new RoleDaoImpl(); } public List<Role> getRoleList() { Connection connection = null; List<Role> roleList = null; try { connection = BaseDao.getConnection(); roleList = roleDao.getRoleList(connection); } catch (SQLException e) { e.printStackTrace(); }finally { BaseDao.closeResources(connection,null,null); } return roleList; } }4.用户显示的Servlet1.获取用户前端的数据2.判断请求是否执行,看参数的值判断3.为了实现分页,需要计算出当前页面和总页面,页面大小…4.用户列表展示5.返回前端 //重点、难点 public void query(HttpServletRequest req, HttpServletResponse resp) { //查询用户列表 //从前端获取数据: String queryUserName = req.getParameter("queryname"); String temp = req.getParameter("queryUserRole"); String pageIndex = req.getParameter("pageIndex"); int queryUserRole = 0; //获取用户列表 UserServiceImpl userService = new UserServiceImpl(); List<User> userList = null; //第一次走这个请求,一定是第一页,页面大小是固定的 int pageSize = 5;//可以把这个写在配置文件中,方便后期修改 int currentPageNo = 1; if (queryUserName==null){ queryUserName = ""; } if (temp!=null && !temp.equals("")){ queryUserRole = Integer.parseInt(temp);//给查询赋值!0,1,2,3 } if (pageIndex!=null){ currentPageNo = Integer.parseInt(pageIndex); } //获取用户的总数(分页:上一页,下一页的情况) int totalCount = userService.getUserCount(queryUserName, queryUserRole); //总页数支持 PageSupport pageSupport = new PageSupport(); pageSupport.setCurrentPageNo(currentPageNo); pageSupport.setPageSize(pageSize); pageSupport.setTotalCount(totalCount); int totalPageCount = ((int)(totalCount/pageSize))+1; // int totalPageCount = pageSupport.getTotalCount(); //控制首页和尾页 //如果页面要小于1了,就显示第一页的东西 if (totalPageCount<1){ currentPageNo = 1; }else if (currentPageNo>totalPageCount){//当前页面大于最后一页 currentPageNo = totalPageCount; } //获取用户列表展示 userList = userService.getUserList(queryUserName, queryUserRole, currentPageNo, pageSize); req.setAttribute("userList",userList); RoleServiceImpl roleService = new RoleServiceImpl(); List<Role> roleList = roleService.getRoleList(); req.setAttribute("roleList",roleList); req.setAttribute("totalCount",totalCount); req.setAttribute("currentPageNo",currentPageNo); req.setAttribute("totalPageCount",totalPageCount); req.setAttribute("queryUserName",queryUserName); req.setAttribute("queryUserRole",queryUserRole); //返回前端 try { req.getRequestDispatcher("userlist.jsp").forward(req,resp); } catch (ServletException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getParameter("method"); if (method.equals("savepwd")&&method!=null){ this.updatePwd(req,resp); }else if (method.equals("pwdmodify")&&method!=null){ this.pwdmodify(req, resp); }else if (method.equals("query")&&method!=null){ this.query(req, resp); } }
文章
SQL  ·  开发框架  ·  前端开发  ·  安全  ·  Java  ·  .NET  ·  应用服务中间件  ·  数据库连接  ·  Maven  ·  数据库
2022-10-23
干货 | Python调用百度地图API获取各点的经纬度信息(两种方式)
小伙伴们大家好,在上一期的推文中我们介绍了如何利用百度地图的API获取POI兴趣点的相关信息,详见:干货 | 10分钟教你用Python获取百度地图各点的经纬度信息但是只是简单介绍了API的调用方式。今天我们来讲讲如何在Python里面调用申请的API接口,然后利用Python进行相关的数据处理,最终得到我们想要的信息。最近大家还是要响应号召,不出门!在家好好学习吧~地点检索方式目前百度地图的地点检索服务有以下4种方式:行政区划区域检索:开发者可通过该功能,检索某一行政区划内(目前最细到城市级别)的地点信息。圆形区域检索:开发者可设置圆心和半径,检索圆形区域内的地点信息(常用于周边检索场景)。矩形区域检索:开发者可设置检索区域左下角和右上角坐标,检索坐标对应矩形内的地点信息(常用于手机或PC端地图视野内检索)地点详情检索:不同于以上三种检索功能。地点详情检索针对指定POI,检索其相关的详情信息。开发者可以通过三种区域检索(或其他服务)功能,获取POI id。使用“地点详情检索”功能,传入id,即可检索POI详情信息,如评分、营业时间等(不同类型POI对应不同类别详情数据)。常用的方式主要是第一种和第二种,今天对这两种方式都介绍一下。行政区划区域检索上次说了,API的调用方式是通过编辑好的URL,请求服务器然后返回所需要的的数据,数据是JSON或者XML类型的(别问我什么是JSON)。具体的说明大家去官网看吧balablaba的……这里我就不在BB了,直接贴上一个编辑好的URL:http://api.map.baidu.com/place/v2/search?query=超市&region=武汉市&output=json&ak=申请的AK&scope=1&page_size=20&page_num=0上面URL中,绿色标出的是需要填写的参数。各个参数的说明如下:query检索关键字。行政区划区域检索不支持多关键字检索。如果需要按POI分类进行检索,请将分类通过query参数进行设置,如query=美食。region检索行政区划区域(增加区域内数据召回权重,如需严格限制召回数据在区域内,请搭配使用city_limit参数),可输入行政区划名或对应cityCode。outpu出格式为json或者xml。ak开发者的访问密钥,必填项。v2之前该属性为key。scope检索结果详细程度。取值为1 或空,则返回基本信息;取值为2,返回检索POI详细信息。page_size单次召回POI数量,默认为10条记录,最大返回20条。多关键字检索时,返回的记录数为关键字个数*page_size。page_num分页页码,默认为0,0代表第一页,1代表第二页,以此类推。常与page_size搭配使用。关于其他可选参数更多详细信息请戳:http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-placeapi值得注意的是,page_size=20&page_num=0表示每个URL页面返回的POI数量为20个,这个是第0个页面,因为在程序中,一般都是从0开始的。好了,请求讲完了,接下来放Python代码: #coding: utf-8 import requests import json import time import csv import codecs """ 查询关键字: """ FileKey = 'preclass' KeyWord = u"超市" def getBaiduApiAk(): """ 获取配置文件中百度apikey: { "baiduak":"xx"} :return: str """ return "填写你申请的AK" def requestBaiduApi(keyWords, baiduAk, fileKey): today = time.strftime("%Y-%m-%d") pageNum = 0 count = 0 logfile = open("./" + fileKey + "-" + today + ".log", 'a+', encoding='utf-8') file = open("./" + fileKey + "-" + today + ".txt", 'a+', encoding='utf-8') file_csv = open('shops_data_青山区.csv', 'w+', encoding='utf-8') # 追加 writer = csv.writer(file_csv) writer.writerow(["no","area","name","lat","lng"]) # print('-------------') # print(index) while True: try: URL = "http://api.map.baidu.com/place/v2/search?query=" + keyWords + \ "&region=" + "武汉市青山区" + \ "&output=json" + \ "&ak=" + baiduAk + \ "&scope=1" + \ "&page_size=20" + \ "&page_num=" + str(pageNum) # print(pageNum) print(URL) resp = requests.get(URL) res = json.loads(resp.text) # print(resp.text.strip()) if len(res['results']) == 0: logfile.writelines(time.strftime("%Y%m%d%H%M%S") + " stop " + " " + str(pageNum) + '\n') break else: for r in res['results']: # print(r) count += 1 city_area = r['city']+r['area'] shop_name = r['name'] shop_lat = str(r['location']['lat']) shop_lng = str(r['location']['lng']) writer.writerow([str(count),city_area, shop_name, shop_lat, shop_lng]) # file.writelines(str(r).strip() + '\n') # print(r['city']+r['area']+" "+r['name']+" "+str(r['location']['lat']) + " " + str(r['location']['lng'])) pageNum += 1 time.sleep(1) except: print("except") logfile.writelines(time.strftime("%Y%m%d%H%M%S") + " except " + " " + str(pageNum) + '\n') break def main(): baiduAk = getBaiduApiAk() requestBaiduApi(keyWords=KeyWord, baiduAk=baiduAk, fileKey=FileKey) if __name__ == '__main__': main()(代码于2020.1.18测试无误)代码的思路也相当简单,首先是构造URL,然后请求返回JSON格式的数据,Python处理后写入CSV文件中。获取的数据详情如下所示,有需要的同学可根据说明修改相应的参数获取相应的数据:值得注意的是,在实际请求中,百度API限制了检索只能返回20个URL页面。这就意味着我们一个区域最多只能检索20*20=400个POI点。实际需求中往往不止400个点的。但人民的智慧是无穷的,我们接下来介绍第二种方式解决上面400个点的弊端。矩形区域检索所谓矩形区域检索,就是给定一个矩形范围的经纬度坐标(实际上两点即可定位一个矩形,左下点和右上点),然后在该矩形范围内进行兴趣点的检索。而矩形范围经纬度坐标的确定可以利用之前介绍的坐标拾取系统进行拾取。例如下图在武汉市(武汉加油!)拾取一个蓝色框的区域,把左下角的经纬度和右上角的经纬度记录下来即可,这样一个范围就做好啦。例如我们拾取了一个矩形:左下点的经纬度为:114.2540523, 30.471019右上点的经纬度为:114.2687126, 30.4877379现在利用矩形区域检索的URL如下:http://api.map.baidu.com/place/v2/search?query=超市&bounds=30.471019,114.2540523,30.4877379,114.2687126&output=json&ak=申请的AK&scope=1&page_size=20&page_num=0   参数无太大变化,就是region变成了bounds,并且指出了矩形区域的边界经纬度(左下点和右上点)。注意绿色处要填上你自己的AK。   好了,现在检查一下URL编辑是否正确,复制到浏览器回车一下看看:   OK,大功告成。好了,现在我们来解决400个点限制的问题。不知道聪明的你们想到了没有。没错,就是切割区域。既然百度限制了每个区域检索最多只能返回400个点,那么可以通过矩形检索的方式,将一个大矩形切割成很多小矩形,依次在每个小矩形内进行检索,最后将所有小矩形的结果加起来就有很多很多个点啦。怎样,是不是很聪明呢!例如将一个大区域分割成1234号区域分别检索,假如每个区域都返回400个点,那么总共就能获取4X400=1600个点了。而如何分割,则不必手动拾取点进行划分,可以利用程序来计算嘛!好了,下面给出一份分割区域的Python代码: #coding: utf-8 import requests import json import time import csv """ 查询关键字: """ FileKey = 'preclass' KeyWord = u"便利店" """ 关注区域的左下角和右上角百度地图坐标(经纬度) """ BigRect = { 'left': { 'x': 114.239392, 'y': 30.471019 }, 'right': { 'x': 114.385995, 'y': 30.638208 } } """ 定义细分窗口的数量,横向X * 纵向Y """ WindowSize = { 'xNum': 10.0, 'yNum': 10.0 } """ 获取AK :return: str """ def getBaiduApiAk(): return "申请的AK" def getSmallRect(bigRect, windowSize, windowIndex): """ 获取小矩形的左上角和右下角坐标字符串(百度坐标系) :param bigRect: 关注区域坐标信息 :param windowSize: 细分窗口数量信息 :param windowIndex: Z型扫描的小矩形索引号 :return: lat,lng,lat,lng """ offset_x = (bigRect['right']['x'] - bigRect['left']['x'])/windowSize['xNum'] offset_y = (bigRect['right']['y'] - bigRect['left']['y'])/windowSize['yNum'] left_x = bigRect['left']['x'] + offset_x * (windowIndex % windowSize['xNum']) left_y = bigRect['left']['y'] + offset_y * (windowIndex // windowSize['yNum']) right_x = (left_x + offset_x) right_y = (left_y + offset_y) return str(left_y) + ',' + str(left_x) + ',' + str(right_y) + ',' + str(right_x) def requestBaiduApi(keyWords, smallRect, baiduAk, index, fileKey, count): today = time.strftime("%Y-%m-%d") pageNum = 0 logfile = open("./" + fileKey + "-" + today + ".log", 'a+', encoding='utf-8') file_csv = open('testdata.csv', 'w+', encoding='utf-8',newline='') # 追加 writer = csv.writer(file_csv) writer.writerow(["no","area","name","lat","lng"]) # print('-------------') # print(index) while True: try: URL = "http://api.map.baidu.com/place/v2/search?query=" + keyWords + \ "&bounds=" + smallRect + \ "&output=json" + \ "&ak=" + baiduAk + \ "&scope=1" + \ "&page_size=20" + \ "&page_num=" + str(pageNum) print(pageNum) print(URL) resp = requests.get(URL) res = json.loads(resp.text) # print(resp.text.strip()) if len(res['results']) == 0: logfile.writelines(time.strftime("%Y%m%d%H%M%S") + " stop " + str(index) + " " + smallRect + " " + str(pageNum) + '\n') break else: for r in res['results']: # print(r) count += 1 city_area = r['city'] + r['area'] shop_name = r['name'] shop_lat = str(r['location']['lat']) shop_lng = str(r['location']['lng']) writer.writerow([str(count), city_area, shop_name, shop_lat, shop_lng]) pageNum += 1 time.sleep(1) except: print("except") logfile.writelines(time.strftime("%Y%m%d%H%M%S") + " except " + str(index) + " " + smallRect + " " + str(pageNum) + '\n') break return count def main(): baiduAk = getBaiduApiAk() count = 0 for index in range(int(WindowSize['xNum'] * WindowSize['yNum'])): smallRect = getSmallRect(BigRect, WindowSize, index) count = requestBaiduApi(keyWords=KeyWord, smallRect=smallRect, baiduAk=baiduAk, index=index, fileKey=FileKey,count= count) time.sleep(1) print(str(count)) if __name__ == '__main__': main() (代码于2020.1.18测试无误)读者只需要简单修改代码中的:查询关键字关注区域的左下角和右上角百度地图坐标(经纬度)定义细分窗口的数量,横向X * 纵向Y获取AK这几处的相关信息即可使用,生成的数据如下所示:怎样,是不是很简单呢!至此,两种方式已经介绍完毕。当然,获取经纬度信息只是我们的第一步操作,后续的过程我们将向大家展示如何根据经纬度信息获取两点之间的真实距离。希望这次的疫情尽快过去,大家都相安无事!小编还要回去上学吃热干面呢!武汉加油!
文章
XML  ·  JSON  ·  定位技术  ·  API  ·  数据处理  ·  数据格式  ·  开发者  ·  Python
2022-04-22
阿里大佬倾情力荐:Java全线成长宝典,从P5到P8一应俱全
前言对于大部分的程序员来说,清晰地规划自己的职业发展并不是一件容易的事情。作为一个曾经底子比较差,从底层摸爬滚打多年走过来的程序员,在这里分享一下对我帮助很大的一份宝典,希望同行们能快速掌握这些技术,直接弯道超车。很多程序员不知道学什么?或者说不知道从何学习?今天分享的这份宝典由阿里大佬倾情力荐,Java全线成长宝典,从P5到P8一应俱全。P5:能够独立执行既定任务1.打开程序员的大门计算机基础+CPU/内存/硬盘+软、硬件关联+2进制、位运算Windows基础+环境变量+PATH/CLASSPATH+常用命令Linux基础+进程/文件命令+网络/安全命令/常用命令虚拟机的安装与使用+VMWARE+Linux安装2.JavaSE核心面向对象和基本语法+封装+继承+多态+标识符合保留字+数据类型+流程控制集合框架+COLLECTION+LIST+SET+MAP+COLLECTIONS+ARRAYS多线程+线程进程+Tread、Runnable+线程的生命周期+线程池IO框架+字节流+字符流+缓冲流+处理流+标准流+转换流设计模式+工厂模式+单例模式+适配器模式+装饰器模式+代理模式+策略模式+模板方法模式+访问者模式设计原则+单一职责原则+里氏替换原则+依赖倒置原则+接口隔离原则+迪米特原则+开闭原则网络通讯+网络基础知识+TCP/IP+HTTP+UDP+Socket+Netty3.MySQL核心4.Web前端技术5.Java后端技术Servlet/JSP+HTTP协议+Tomcat服务器+Servlets实现原理+JSTL和EL表达式+监听器和过滤器+JSP相关知识Spring+Spring的IOC+AOP+Spring中的设计模式+Spring的事务处理+Spring的动态代理+熟练掌握Spring工作常用注解及陷阱SpringMVC+SpringMVC的实现原理+SpringMVC的相关注解+视图处理器+数据校验+拦截器+基础源码Mybatis+Mybatis的实现原理+映射文件+动态SQL+缓存机制+Mybatis的基础源码+Mybatis-Plus的应用SpringBoot+SpringBoot的基本使用+数据源配置+配置文件+SpringBoot的Web开发+SpringBoot的自动装配原理6.面试必备算法7.面试必备数据结构8.基础源码解读9.项目开发工具10.团队协作工具11.开发保障工具12.软件测试P6:能够独挡一面,在专业领域具备辅导他人的能力1.并发编程与JVM实战2.缓存中间件Redis缓存设计:Redis部署+缓存原理+Redis的VALUE类型+发布订阅+MODULE与布隆过滤器模块+LUA脚本Redis可靠性实现:Redis的持久化方案+主从复制方案+Redis的Sentinel+Redis的同步机制+Redis的新可靠性配置Redis自主集群实现:集群原理+集群搭建+槽位迁移+节点扩缩容+Redis开发实例3.消息中间件RabbitMQ:消息中间件入门+消息发布与消费权衡+消息的拒绝怎么解决+集成Spring完成应用解耦实战+集群化与镜像队列实战RocketMQ:Linux的部署、对比JMS+经典实战场景+集群+源码分析+常见面试题Kafka:Kafka框架原理+特性及实现+文件存储机制+分区及可靠性+Kafka-BROKER特性+Kafka高效性相关设计+Kafka-Consumer特性+Perducer特性+offset维护方案+Kafka-Streaming4.搜索中间件Elaticsearch:Master选举+Mapping+Aggregations+API+集群安全与调优Logstash:Beats+Filter+Plugins+PipelinesKibana:Dashboard+Graph图表+Reporting+Kibana Plugins+监控5.存储中间件MySQL集群:分库分表+主从、主主+数据备份+Mycat+Sharding-sphereFastDFS:架构原理+存储原理+同步机制+集群搭建+应用案例MongoDB:架构原理+基础操作+索引原理+备份还原6.高并发方案网络通信原理:OSI和TCP/IP对比+应用层协议讲解+传输控制层及Socket+网络层及链路层原理+NAT及路由规划四层负载均衡:四层负载原理+LVS的DR模型+LVS的TUN模型+LVS的NAT模型+LVS的负载均衡实验七层负载均衡:反向代理原理+基于反向代理的负载均衡实现+动态负载均衡实现+负载均衡算法+健康检查及熔断降级单机并发方案:ThreadLocal与强软弱虚引用+高并发容器详解+详解线程池+自定义线程池+JDK自带线程池+FORKJOIN+源码解析集群并发方案:资源静态化+CDN分发+同步转异步+多级别缓存7.高可用方案Zookeeper:架构原理+ZAB协议+NODE及Watch+2PC原理+分布式协调方案负载均衡算法:负载均衡算法之轮询+随机+源地址哈希+加权轮询+最小连接数+定向分发+区域权衡策略+可用过滤策略Nginx:Nginx基本使用+Proxy_Pass反向代理+集群健康检查+整合LUA+限流算法与高可用集群+整合注册中心自动发现服务+多Nginx同步数据+Nginx合并输出SSIHaproxy:原理特征+反向代理能力+高可用能力+高级配置+安装部署Keepalived:原理特征+安装部署+整合LVS高可用+实现Nginx高可用+高级配置及脚本开发客户端负载均衡:域名解析原理+DNS解析原理+CDN节点加速+C/S、B/S架构主动选择8.高扩展方案容器化:Docker架构+镜像+容器+仓库+存储+网络+资源限制容器与DEVOPS:Docker容器的代码挂载机制+Docker与服务发现+Dockerfile编写规则+Docker与日志+Docker与监控+Docker与CI/CD+Docker给运维团队带来的挑战容器编排Kubernetes:容器编排和容器调度+容器编排技术选型Docker Swarm+容器编排技术选型Kubernetes(k8s)+容器编排技术选型Marathon线上日志监控:Kubernetes仪表盘+监控Kubernetes、应用、主机、外部资源等+Kubernetes监控要点+Kubernetes监控实践+部署Prometheus监控+使用PROMQL+使用Grafana查看指标9.网络通信与协议Netty底层原理分析:手写理解Netty模型+Netty开发本质手写+Netty自定义Handler+Netty自定义编解码+Netty多协议通信Netty-RPC框架手写:自定义协议,连接池+协议编解码问题 粘包+拆包与内核关系+PROVIDER端简单+DISPATCHER实现+RPC调用全流程+简单重构框架分层级RPC传输的本质及有无状态的RPC区别+自定义HTTP协议解析和HTTPSERVER调用实现Dubbo实现RPC实战:框架原理+Dubbo协议+注册与发现+负载均衡+服务化最佳实践RESTFUL协议解读:RESTFUL API+RESTFUL V.S RPC+RESTFLU 接口规范+RESTFUL 实现+无状态与RESTFUL10.分布式与微服务分布式架构思维训练:主流分布式架构设计详解+架构师应具备的分布式知识+大型互联网架构演讲过程RPC从入门到精通:RPC基础入门介绍+RPC传输协议精读+RPC序列化与反序列化技术精讲SpringCloud Netflix:什么是微服务?+SpringCloud微服务组件入门级讲解+Zuul、Ribbon、Feign、Hystrix、Eureka组件搭建+快速搭建SpringCloud微服务项目+Hystrix服务熔断及服务降级实战+Bus、Sleuth、Stream及Stream消息驱动实战+微服务项目日志跟踪实战Spring Cloud Alibaba:框架技术体系讲解及背景介绍+Nacos、Seata、Sentinel、SkyWalking组件搭建+SpringCloud Alibaba项目构建实战11.分布式权限控制Oauth2.0:Oauth2.0接入流程+授权认证+服务部署OAUTH2.0Shiro与CAS:Shiro权限管理、身份认证+Shiro架构流程+Shiro关键对象+Shiro权限模型+Shiro整合CAS线上跨域管理:Session与Cookies&token+浏览器同源策略与跨域+Jsonp跨域访问原理+CORS+SSO原理JWT:微服务中高并发场景+会话处理方案+无状态会话解决方案+JWT安全机制+JWT组成结构+JWT消息校验与互联网+应用常见问题Spring Security:JDBC用户存储+记住我功能+同一用户多地点登录+踢掉其他已登录的用户+如何使用Mybatis/JPA+查询用户+禁止其他终端登录12.微服务熔断降级与限流限流算法:固定时间窗口算法+滑动时间窗口算法+令牌桶算法+漏桶算法+分布式限流算法Sentinel:Sentinel基本简介+单机流控+热点参数限流+系统自适应限流+集群流控+黑白名单控制+熔断降级+网关限流+动态规则Hystrix:Hystrix工作流程+断路器的工作原理+断路器配置+线程池隔离+信号量隔离+降级的实现+降级回退方式13.微服务链路追踪Skywalking:核心概念+服务自动打点+服务网络探针+SkyWalking on lstioALS+插件开发Zipkin:Zipkin原理+Zipkin架构+核心数据结构+主要组件构成+Zipkin的Data Model+Zipkin的持久化+Zipkin集成Spring Cloud Sleuth:基本术语Span Trace Annotation+服务的调用链路原理+Span生命周期+Sleuth跟踪原理+Sleuth采样+Brave分布式跟踪+Span上下文传播14.底层&源码深入解读Spring源码:Tomcat的SPI机制加载Spring MVC容器+RequestMappingInfo和HandlerMethod的映射关系建立+HandlrMapping和HandlerAdapter的关系+HandlerInterceptor的前置、中置、后置过滤器原理+HandlerAdapter的参数解析原理+SpringMVC的全局异常处理@ControllerAdvice原理+SpringMVC中cors的js跨域解决方案原理+SpringMVC的调用流程梳理Mybatis框架源码深入解读:Mybatis开发10个必须知道的坑+Mybatis源码架构分析+Mybatis核心模块分析+Mybatis运行流程解析+插件开发实战+Druid连接池源码解读+手写Mybatis框架实战深入Tomcat底层:理解Tomcat启动流程+Tomcat配置详解+HTTP请求解析与处理流程+Tomcat核心组件认识+Tomcat类加载机制及源码解析+Tomcat中异步Servlet实现源码分析+Tomcat BIO实现源码解读+Tomcat NIO实现源码解读+Tomcat集群与会话复制方案+Tomcat性能调优+JVM参数优化+Tomcat集群+Tomcat安全15.一线分布式场景实战分布式锁系列:手写分布式锁+DB分布式锁+Redis分布式锁+ZK分布式锁+场景与方案选择分布式事务:2/3PC方案+TCC方案+柔性事务+最终一致+可靠消息+最大努力通知+Saga+Seata落地分布式算法:分布式ID+数据库自增ID+数据库多主模式+雪花算法+美团Leaf一致性实战:分布式幂等设计+MVCC方案+去重表+悲观锁+状态机幂等+页面防重提交分布式会话:客户端存储+Session复制+Session绑定+Redis的Session方案小米B2C商城系统P7:某一领域专家,知其然知其所以然,对专业领域有影响力,可领导跨部门项目1.架构师的基本素养协议规范:Paxos+Base+Raft+Cap+Fmea应用与理论:AKF划分原则+前后端分离原则+服务无状态+通信无状态+最小知道原则架构思想:动静分离+动态解析+缓存与异步+分布式解决方案+微服务治理方案TDD设计:测试开发实践+分层自动化及报告+UI自动化+接口自动化+TDD详解DDD设计:贫血/充血模型+DDD的整洁架构之道+领域的延展-领域事件+领域事件与CQRS+VENTSTORMING领域建模2.架构师技术导向存储高性能:数据库性能压测+NoSQL数据库+缓存中间件+分库分表中间件+分布式文件系统计算机高性能:单机高性能+集群高性能+调用高性能+网络服务高性能+虚拟、容器高性能Servicemesh:理论与落地+微服务Servicemesh在理论上优于Microservice+Servicemesh之基础理论解析+servicemesh之技术选型+Servicemesh实战落地之ISTIO流量、安全、故障、实战3.性能调优技术JVM调优:JVM调优必备理论知识-GC Collector-三色标记+垃圾回收算法串讲+JVM常见参数总结+JVM调优实战网络调优:TCP内核参数+Java API参数+IO模型 Trade off+资源隔离优化+网卡、CPU配置调优+网络安全的加密算法与数字签名+网络故障分析与问题解决+XSS攻击的危害和规避方法数据库调优:MySQL的性能监控+Schema与数据类型优化+通过执行计划优化+通过索引进行优化+查询优化+参数优化+分布式MySQL优化+SQL注入、WebShell攻击的危害和规避方法Linux内核调优:单进程最大打开文件限制+内核内存参数调优+TCP发送Keepalive消息频度+Tcp fin_wait_2状态时间+定义UDP和TCP链接的本地端口取值范围+优化TCP接收缓存的最大值、最小值、默认值容器环境调优:镜像体积调优+镜像体积最小化+构建速度最快化+使用CMD VOLUME指令+Docker网络方案优化4.高并发线上“填坑”实战5.底层&源码深入解读SpringBoot:SpringBoot启动器原理+SpringBoot核心源码解读+自动配置原理+SpringBoot启动流程源码分析+SpringBoot中的@Conditional原理+自定义功能启动器Spring Cloud Netflix:Zuul路由网关详解及源码探析+Ribbon客户端负载均衡原理与算法详解+Feign声明式服务调用方式实现+Eureka注册中心构建分析+Config配置服务中心与svn、git快速集成+自定义分布式配置中心实现+微服务项目Docker化+Shiro+Oauth2.0解析Spring Cloud Alibaba:Nacos底层实现详解+Seata底层实现详解+Sentinel底层实现详解+链路跟踪+SkyWalking底层实现详解Dubbo:Dubbo架构原理和内核深入剖析+Dubbo SPI原理和实战+Dubbo IOC和AOP原理精讲+Dubbo动态编译服务发布原理剖析+服务引用原理剖析+集群容错设计解剖+服务降级设计解剖+网络通信架构剖析+网络通信编码解码剖析+手写Dubbo框架6.算法深入分析分布算法解析:布隆过滤器+布谷鸟过滤器+一致性哈希原理化解资源限制:哈希分流法+分段统计思想+内存限制下的功能建设+位图+对文件进行排序数据组织方式:哈希表+有序表+位图+链表+平衡搜索二叉树7.实战项目驱动(业务中台+数据中台+技术中台/线上百万并发Spring Cloud Alibaba脱敏生产项目)P8:在专业领域有一定的前瞻性,推动业务创新,参与能够影响事业部层面的,能够做策略及统筹策划1.硬实力:亿级流量架构架构理论和算法基础:分布式理论基石之CAP定理+数据最终一致性之BASE理论+分布式一致性算法+负载均衡算法和原理+分布式调度和协调算法+分布式存储算法架构核心要素:高性能架构+高可用架构+高伸缩性架构+高扩展架构+安全性架构架构基础设施:多地多活方案+负载均衡方案+服务故障自动感知+CDN云服务和资源静态化原理和源码剖析:Linux Kernel源码+JVM HotSpot源码+Redis源码+MySQL源码+MQ源码+Spring系列源码微服务与中台:微服务全家桶+从微服务到ServiceMesh+从ServiceMesh到Serverless+业务驱动中台落地+性能测试和持续测试+全链路压测+自动化运维云原生架构:Docker原理与搭建+Kubernetes+容器编排、容器网络+持续集成和部署+弹性扩缩容+服务网络lstioDDD设计:领域模型的选择+DDD的整洁架构之道+领域事件+CQRS+领域建模TDD模式:分层自动化及报告+UI自动化+接口自动化+TDD详解+测试开发实践数据库前沿:图数据库Neo4j+分布式关系型数据库TiDB+分布式列存储时序数据库Apache Druid2.软实力:管理和格局管理方法论:自我管理(集中精力做好需要做的事)+向上管理(管理你的老板)+向下管理(管理你的部署)+横向管理(管理你的同事)+对外管理(管理外部关系)开发团队管理:理解程序员+寻找并招聘优秀的程序员+帮助新员工顺利入职+成为高效的程序设计经理+激励团队+建立成功的开发文化+管理成功的软件交付管理实践:成为新项目团队的领导,如何入手?+接管正在进行的项目,如何入手?+如何指导他人设计和编程+如何检查别人的工作?+如何进行面试+如何奖励出色的工作?管理技能:如何做演讲?+如何主持会议?+找到自己正确的领导风格?大视野:理解商业的本质+创业与创新+真正高效的工作+理性的投资观+如何做减法?大格局:提升自己的格局+看清能力圈的边界+跳出定势思维+掌控生活和工作的节奏+寻找未来的特征智能时代:大数据和机器智能+智能时代的思维革命+大数据与商业+智能革命的技术挑战+未来智能化产业+智能革命和未来社会+智能架构3.项目实战和案例剖析再造拼多多:技术支撑-完整搭建DevOps+统一规则-代码规范落地+搭建基础服务+用户中心+商品中心+库存中心+营销中心+订单中心+搜索中心+评价中心+推荐中心+支付中心+商户中心+拼多多超高并发秒杀系统百度地图如何支撑过亿日活?+亿级流量接入层服务的演化之路美团网Kubernetes容器化服务+集群管理实战京东日志系统+架构设计与应用淘宝亿级流量商品详情页总结我们要明白的一点是,程序员的career实际上是在混这个行业,而不是某个固定的公司,现在几乎不存一个工作做一辈子的情况,所以变数是比较大的,我们要做的就是随时准备好应对“变数”的方案。因此,不断更新自己的知识库,确保自己一定能保持在这个行业的头部,要具有忧患意识是非常重要的。本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。
文章
负载均衡  ·  算法  ·  NoSQL  ·  Java  ·  应用服务中间件  ·  程序员  ·  Redis  ·  微服务  ·  容器  ·  Spring
2022-12-20
微信小程序的相关配置
微信小程序介绍什么是小程序?2017年度百度百科十大热词之一微信小程序(wei xin xiao cheng xu),简称小程序,英文名Mini Program,是一种不需要下载安装即可使用的应用( 张小龙对其的定义是无需安装,用完即走,实际上是需要安装的,只不过小程序的体积特别小, 下载速度很快,用户感觉不到下载的过程 )限制:同一个分包中的页面享有共同的预下载大小限额 2M,限额会在工具中打包时校验。2017年1月9日0点,万众瞩目的微信第一批小程序正式低调上线。小程序可以干什么?同App进行互补,提供同app类似的功能,比app操作更加简洁的轻应用通过扫一扫或者在微信搜索即可下载用户使用频率不高,但又不得不用的功能软件,目前看来小程序是首选连接线上线下开发门槛低, 成本低相关资料1) 官网:https://mp.weixin.qq.com/第一个小程序开发小程序的第一步,你需要拥有一个小程序帐号,通过这个帐号你就可以管理你的小程序。跟随这个教程,开始你的小程序之旅吧!申请帐号进入小程序注册页 根据指引填写信息和提交相应的资料,就可以拥有自己的小程序帐号。在这个小程序管理平台,你可以管理你的小程序的权限,查看数据报表,发布小程序等操作。登录 小程序后台 ,我们可以在菜单 “开发”-“开发设置” 看到小程序的 AppID 了 。小程序的 AppID 相当于小程序平台的一个身份证,后续你会在很多地方要用到 AppID (注意这里要区别于服务号或订阅号的 AppID)。有了小程序帐号之后,我们需要一个工具来开发小程序。测试号申请请大家先完成测试号的申请任务。安装开发工具前往 开发者工具下载页面 ,根据自己的操作系统下载对应的安装包进行安装,有关开发者工具更详细的介绍可以查看 《开发者工具介绍》 。打开小程序开发者工具,用微信扫码登录开发者工具,准备开发你的第一个小程序吧!你的第一个小程序新建项目选择小程序项目,选择代码存放的硬盘路径,填入刚刚申请到的小程序的 AppID,给你的项目起一个好听的名字,勾选 "不使用云服务" (注意: 你要选择一个空的目录才可以创建项目),点击新建,你就得到了你的第一个小程序了,点击顶部菜单编译就可以在微信开发者工具中预览你的第一个小程序。接下来我们来预览一下这个小程序的效果。编译预览点击工具上的编译按钮,可以在工具的左侧模拟器界面看到这个小程序的表现,也可以点击预览按钮,通过微信的扫一扫在手机上体验你的第一个小程序。通过这个章节,你已经成功创建了你的第一个小程序,并且在微信客户端上体验到它流畅的表现。目录结构小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:文件必需作用app.js是小程序逻辑app.json是小程序公共配置app.wxss否小程序公共样式表一个小程序页面由四个文件组成,分别是:文件类型必需作用js是页面逻辑wxml是页面结构json否页面配置wxss否页面样式表注意:为了方便开发者减少配置项,描述页面的四个文件必须具有相同的路径与文件名。第一个小程序项目里边生成了不同类型的文件:.json 后缀的 JSON 配置文件.wxml 后缀的 WXML 模板文件.wxss 后缀的 WXSS 样式文件.js 后缀的 JS 脚本逻辑文件接下来我们分别看看这4种文件的作用。JSON 配置JSON 是一种数据格式,并不是编程语言,在小程序中,JSON扮演的静态配置的角色。我们可以看到在项目的根目录有一个 app.json 和 project.config.json,此外在 pages/logs 目录下还有一个 logs.json,我们依次来说明一下它们的用途。小程序配置 app.jsonapp.json 是当前小程序的全局配置,包括了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。QuickStart 项目里边的 app.json 配置内容如下:{ "pages":[ "pages/index/index", "pages/logs/logs" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "Weixin", "navigationBarTextStyle":"black" } }我们简单说一下这个配置各个项的含义:pages字段 —— 用于描述当前小程序所有页面路径,这是为了让微信客户端知道当前你的小程序页面定义在哪个目录。window字段 —— 定义小程序所有页面的顶部背景颜色,文字颜色定义等。其他配置项细节可以参考文档 小程序的配置 app.json 。工具配置 project.config.json通常大家在使用一个工具的时候,都会针对各自喜好做一些个性化配置,例如界面颜色、编译配置等等,当你换了另外一台电脑重新安装工具的时候,你还要重新配置。考虑到这点,小程序开发者工具在每个项目的根目录都会生成一个 project.config.json,你在工具上做的任何配置都会写入到这个文件,当你重新安装工具或者换电脑工作时,你只要载入同一个项目的代码包,开发者工具就自动会帮你恢复到当时你开发项目时的个性化配置,其中会包括编辑器的颜色、代码上传时自动压缩等等一系列选项。其他配置项细节可以参考文档 开发者工具的配置 。页面配置 page.json这里的 page.json 其实用来表示 pages/logs 目录下的 logs.json 这类和小程序页面相关的配置。如果你整个小程序的风格是蓝色调,那么你可以在 app.json 里边声明顶部颜色是蓝色即可。实际情况可能不是这样,可能你小程序里边的每个页面都有不一样的色调来区分不同功能模块,因此我们提供了 page.json,让开发者可以独立定义每个页面的一些属性,例如刚刚说的顶部颜色、是否允许下拉刷新等等。其他配置项细节可以参考文档 页面配置 。JSON 语法这里说一下小程序里JSON配置的一些注意事项。JSON文件都是被包裹在一个大括号中 {},通过key-value的方式来表达数据。JSON的Key必须包裹在一个双引号中,在实践中,编写 JSON 的时候,忘了给 Key 值加双引号或者是把双引号写成单引号是常见错误。JSON的值只能是以下几种数据格式,其他任何格式都会触发报错,例如 JavaScript 中的 undefined。数字,包含浮点数和整数字符串,需要包裹在双引号中Bool值,true 或者 false数组,需要包裹在方括号中 []对象,需要包裹在大括号中 {}Null还需要注意的是 JSON 文件中无法使用注释,试图添加注释将会引发报错。WXML从事过网页编程的人知道,网页编程采用的是 HTML + CSS + JS 这样的组合,其中 HTML 是用来描述当前这个页面的结构,CSS 用来描述页面的样子,JS 通常是用来处理这个页面和用户的交互。同样道理,在小程序中也有同样的角色,其中 WXML 充当的就是类似 HTML 的角色。打开 pages/index/index.wxml,你会看到以下的内容:<view class="container"> <view class="userinfo"> <button wx:if="{{!hasUserInfo && canIUse}}"> 获取头像昵称 </button> <block wx:else> <image src="{{userInfo.avatarUrl}}" background-size="cover"></image> <text class="userinfo-nickname">{{userInfo.nickName}}</text> </block> </view> <view class="usermotto"> <text class="user-motto">{{motto}}</text> </view> </view>和 HTML 非常相似,WXML 由标签、属性等等构成。但是也有很多不一样的地方,我们来一一阐述一下:标签名字有点不一样往往写 HTML 的时候,经常会用到的标签是 div, p, span,开发者在写一个页面的时候可以根据这些基础的标签组合出不一样的组件,例如日历、弹窗等等。换个思路,既然大家都需要这些组件,为什么我们不能把这些常用的组件包装起来,大大提高我们的开发效率。从上边的例子可以看到,小程序的 WXML 用的标签是 view, button, text 等等,这些标签就是小程序给开发者包装好的基本能力,我们还提供了地图、视频、音频等等组件能力。更多详细的组件讲述参考下个章节 小程序的能力多了一些 wx:if 这样的属性以及 {{ }} 这样的表达式在网页的一般开发流程中,我们通常会通过 JS 操作 DOM (对应 HTML 的描述产生的树),以引起界面的一些变化响应用户的行为。例如,用户点击某个按钮的时候,JS 会记录一些状态到 JS 变量里边,同时通过 DOM API 操控 DOM 的属性或者行为,进而引起界面一些变化。当项目越来越大的时候,你的代码会充斥着非常多的界面交互逻辑和程序的各种状态变量,显然这不是一个很好的开发模式,因此就有了 MVVM 的开发模式(例如 React, Vue),提倡把渲染和逻辑分离。简单来说就是不要再让 JS 直接操控 DOM,JS 只需要管理状态即可,然后再通过一种模板语法来描述状态和界面结构的关系即可。小程序的框架也是用到了这个思路,如果你需要把一个 Hello World 的字符串显示在界面上。WXML 是这么写 :<text>{{msg}}</text>JS 只需要管理状态即可:this.setData({ msg: "Hello World" })通过 {{ }} 的语法把一个变量绑定到界面上,我们称为数据绑定。仅仅通过数据绑定还不够完整的描述状态和界面的关系,还需要 if/else, for等控制能力,在小程序里边,这些控制能力都用 wx: 开头的属性来表达。更详细的文档可以参考 WXMLWXSS 样式WXSS 具有 CSS 大部分的特性,小程序在 WXSS 也做了一些扩充和修改。新增了尺寸单位。在写 CSS 样式时,开发者需要考虑到手机设备的屏幕会有不同的宽度和设备像素比,采用一些技巧来换算一些像素单位。WXSS 在底层支持新的尺寸单位 rpx ,开发者可以免去换算的烦恼,只要交给小程序底层来换算即可,由于换算采用的浮点数运算,所以运算结果会和预期结果有一点点偏差。提供了全局的样式和局部样式。和前边 app.json, page.json 的概念相同,你可以写一个 app.wxss 作为全局样式,会作用于当前小程序的所有页面,局部页面样式 page.wxss 仅对当前页面生效。此外 WXSS 仅支持部分 CSS 选择器更详细的文档可以参考 WXSS 。JS 逻辑交互一个服务仅仅只有界面展示是不够的,还需要和用户做交互:响应用户的点击、获取用户的位置等等。在小程序里边,我们就通过编写 JS 脚本文件来处理用户的操作。<view>{{ msg }}</view> <button bindtap="clickMe">点击我</button>点击 button 按钮的时候,我们希望把界面上 msg 显示成 "Hello World",于是我们在 button 上声明一个属性: bindtap ,在 JS 文件里边声明了 clickMe 方法来响应这次点击操作:Page({ clickMe: function() { this.setData({ msg: "Hello World" }) } })响应用户的操作就是这么简单,更详细的事件可以参考文档 WXML - 事件 。此外你还可以在 JS 中调用小程序提供的丰富的 API,利用这些 API 可以很方便的调起微信提供的能力,例如获取用户信息、本地存储、微信支付等。在前边的 QuickStart 例子中,在 pages/index/index.js 就调用了 wx.getUserInfo 获取微信用户的头像和昵称,最后通过 setData 把获取到的信息显示到界面上。更多 API 可以参考文档 小程序的API 。通过这个章节,你了解了小程序涉及到的文件类型以及对应的角色,在下个章节中,我们把这一章所涉及到的文件通过 “小程序的框架” 给 “串” 起来,让他们都工作起来。util公共函数定义及读取util目录下的util.js文件专用于小程序项目中的公共函数定义,可以将项目开发过程中的公共代码方法提取封装到util.js文件中。1.util/util.js中定义公共方法const toStr = s =>{ s="你好,"+s; return s; } module.exports = { formatTime,toStr }2.在指定页面中的js中模块化引入utils的js文件//通过require方式引入整个util //const util=require("../../utils/util") //通过import方式引入整个util //import util from "../../utils/util" //引入指定的模块 import {formatTime} from "../../utils/util"采用整体引入方式,在调用时请使用实例名.模块名,例如:util.formatDate(new Date());3.利用页面的onLoad事件完成数据赋值展示onLoad() { this.setData({ loginTime:formatTime(new Date()) }); if (wx.getUserProfile) { this.setData({ canIUseGetUserProfile: true }) } } 小程序配置全局配置小程序根目录下的 app.json 文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。完整配置项说明请参考小程序全局配置以下是一个包含了部分常用配置选项的 app.json :{ "pages": [ "pages/index/index", "pages/logs/index" ], "window": { "navigationBarTitleText": "Demo" }, "tabBar": { "list": [{ "pagePath": "pages/index/index", "text": "首页" }, { "pagePath": "pages/logs/index", "text": "日志" }] }, "networkTimeout": { "request": 10000, "downloadFile": 10000 }, "debug": true } 完整配置项说明请参考小程序全局配置页面配置每一个小程序页面也可以使用同名 .json 文件来对本页面的窗口表现进行配置,页面中配置项会覆盖 app.json 的 window 中相同的配置项。完整配置项说明请参考小程序页面配置例如:{ "navigationBarBackgroundColor": "#ffffff", "navigationBarTextStyle": "black", "navigationBarTitleText": "微信接口功能演示", "backgroundColor": "#eeeeee", "backgroundTextStyle": "light" } 配置小程序 /sitemap 配置微信现已开放小程序内搜索,开发者可以通过 sitemap.json 配置,或者管理后台页面收录开关来配置其小程序页面是否允许微信索引。当开发者允许微信索引时,微信会通过爬虫的形式,为小程序的页面内容建立索引。当用户的搜索词条触发该索引时,小程序的页面将可能展示在搜索结果中。 爬虫访问小程序内页面时,会携带特定的 user-agent:mpcrawler 及场景值:1129。需要注意的是,若小程序爬虫发现的页面数据和真实用户的呈现不一致,那么该页面将不会进入索引中。具体配置说明页面收录设置:可对整个小程序的索引进行关闭,小程序管理后台-功能-页面内容接入-页面收录开关;详情sitemap 配置:可对特定页面的索引进行关闭sitemap 配置小程序根目录下的 sitemap.json 文件用来配置小程序及其页面是否允许被微信索引。完整配置项说明请参考小程序 sitemap 配置例1:{ "rules":[{ "action": "allow", "page": "*" }] } 所有页面都会被微信索引(默认情况)例2:{ "rules":[{ "action": "disallow", "page": "path/to/page" }] } 配置 path/to/page 页面不被索引,其余页面允许被索引例3:{ "rules":[{ "action": "allow", "page": "path/to/page" }, { "action": "disallow", "page": "*" }] } 配置 path/to/page 页面被索引,其余页面不被索引注:没有 sitemap.json 则默认所有页面都能被索引注:{"action": "allow", "page": "\*"} 是优先级最低的默认规则,未显式指明 "disallow" 的都默认被索引如何调试当在小程序项目中设置了 sitemap 的配置文件(默认为 sitemap.json)时,便可在开发者工具控制台上显示当前页面是否被索引的调试信息( 最新版本的开发者工具支持索引提示)注:sitemap 的索引提示是默认开启的,如需要关闭 sitemap 的索引提示,可在小程序项目配置文件 project.config.json 的 setting 中配置字段 checkSiteMap 为 false注: sitemap 文件内容最大为 5120 个 UTF8 字符
文章
数据采集  ·  JSON  ·  小程序  ·  JavaScript  ·  前端开发  ·  搜索推荐  ·  API  ·  开发者  ·  数据格式  ·  索引
2022-12-15
【JavaWeb】之富文本编辑器
前言本文为JavaWeb基础富文本编辑器的介绍,使用与常用富文本编辑器,Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~一、富文本编辑器介绍富文本编辑器,Rich Text Editor, 简称 RTE, 它提供类似于 Microsoft Word 的编辑功能,容易被不会编写 HTML 的用户并需要设置各种文本格式的用户所喜爱。它的应用也越来越广泛。最先只有 IE 浏览器支持,其它浏览器相继跟进,在功能的丰富性来说,还是 IE 强些。虽然没有一个统一的标准,但对于最基本的功能,各浏览器提供的 API 基本一致,从而使编写一个跨浏览器的富文本编辑器成为可能。抛开专业的定义,用自己的话来介绍一下到底什么是富文本编辑器。先举个简单的例子,大家大多都使用过网上的一些博客系统或者论坛贴吧吧,那我们要发布一则文章或者消息的时候我们需要在后台设置一下这段文本的格式还有字体的大小粗细颜色等样式,此时网页上会有一个设置这些信息的菜单或者是一个网页的文本编辑器,这个就是富文本编辑器的常见应用,如下图:富文本编辑器不同于我们平时的文本编辑器,但是其功能确实和我们的平时使用的word的是类似的,只不过富文本编辑器设置是解决不会编写 HTML 的用户并需要设置各种文本格式在我们的网页上。程序员可到网上下载免费的富文本编辑器内嵌于自己的网站或程序里(当然付费的功能会更强大些),方便用户编辑文章或信息。比较好的文本编辑器有kindeditor,fckeditor等。二、富文本编辑器使用接下来以wangeditor为例介绍富文本编辑器的使用步骤:1.引入编辑器(多种引入方式)包管理工具例如node下载:npm install wangeditorbower install wangEditor下载源文件js引入( https://github.com/wangfupeng1988/wangEditor/releases):<script src="/static/assets/plugins/wangEditor/wangEditor.min.js"></script>在线cdn引入(https://www.bootcdn.cn/wangEditor/):网站链接选择版本复制引入即可2.使用编辑器(1)创建容器<div id="wangeditor"> <div ref="editorElem"></div> </div>(​2)创建并且实例化组件vue的使用方法//vue的使用 import E from "wangeditor”;//导入组件 // 相当于js的变量设置 data() { return { editor: null, }} //methods里创建调用、或是mounted里面直接生产 this.editor = new E(_this.$refs.editorElem);//获取组件并构造编辑器 this.editor.create(); // 创建富文本实例js的使用方式//js的使用 var E = window.wangEditor var editor = new E('#editor') // 或者 var editor = new E( document.getElementById('editor') ) editor.create()react使用方式// 创建组件 <div id="wangeditor" ref={editor}></div> // 组件声明 const editor = useRef(); useEffect(()=>{ //获取组件并构造编辑器 const Edit = new E(editor.current); Edit.create(); }[])(3)基础配置配置菜单// 这是默认的菜单配置就是全部的功能、不需要的话将其去掉即可 this.editor.customConfig.menus = [ 'head', // 标题 'bold', // 粗体 'fontSize', // 字号 'fontName', // 字体 'italic', // 斜体 'underline', // 下划线 'strikeThrough', // 删除线 'foreColor', // 文字颜色 'backColor', // 背景颜色 'link', // 插入链接 'list', // 列表 'justify', // 对齐方式 'quote', // 引用 'emoticon', // 表情 'image', // 插入图片 'table', // 表格 'video', // 插入视频 'code', // 插入代码 'undo', // 撤销 'redo' // 重复 ]自定义设置// 自定义颜色 this.editor.customConfig.colors = [ "#000000", "#333333", ]; // 自定义字体 this.editor.customConfig.fontNames = [ "PingFangSC", ]; //配置多种语言--就是将编辑器原本文字配置成你需要的文字、 //***链接文字一定要在链接的上面 this.editor.customConfig.lang = { '设置标题': 'title', '正文': 'p', '链接文字': 'link text', '链接': 'link', '上传图片': 'upload image', '上传': 'upload', '创建': 'init' // 还可自定添加更多 }(4)常用功能设置//设置编辑器内容 this.editor.txt.html(“yanyanyan”); // 编辑器的事件,每次改变会获取其html内容(html内容是带标签的) this.editor.customConfig.onchange = html => { _this.formValidate.content = html; }; // 设置编辑器层级 this.editor.customConfig.zIndex = 10; // 去除复制过来文本的默认样式 this.editor.customConfig.pasteFilterStyle = false; //用户点击富文本区域会触发onfocus函数执行 this.editor.customConfig.onfocus = function () { console.log("onfocus") } // 将图片大小限制为 3M this.editor.customConfig.uploadImgMaxSize = 3 * 1024 * 1024 // 限制一次最多上传 5 张图片 this.editor.customConfig.uploadImgMaxLength = 5 //上传图片的错误提示默认使用alert弹出,也可以自定义用户体验更好的提示方式 editor.customConfig.customAlert = function (info) { // info 是需要提示的内容 alert('自定义提示:' + info) }(5)本地上传图片编辑器自带图片上传的网站链接图片,本地上传图片需要自己设置// 上传图片到服务器,对应的是controller层的@RequestMapping("/upload") this.editor.customConfig.uploadImgServer = "/api/file/upload”;//接口名称 //自定义name,接收的时候图片文件的那么用这个,对应的是参数中的MultipartFile upimg名称,这个名称即上传到浏览器的参数名称 this.editor.customConfig.uploadFileName = "file_key”;//这个需要和后台商量上传图片的名称 // 上传图片的结果反馈 this.editor.customConfig.uploadImgHooks = { before: function(xhr, editor, files) { // 图片上传之前触发 // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,files 是选择的图片文件 // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传 // return { // prevent: true, // msg: '放弃上传' // } // console.log("before:",xhr) }, success: function(xhr, editor, result) { // 图片上传并返回结果,图片插入成功之后触发 // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果 // console.log("success:",result) }, fail: function(xhr, editor, result) { // 图片上传并返回结果,但图片插入错误时触发 // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果 }, error: function(xhr, editor) { // 图片上传出错时触发 // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象 }, // 如果服务器端返回的不是 {errno:0, data: [...]} 这种格式,可使用该配置 // (但是,服务器端返回的必须是一个 JSON 格式字符串!!!否则会报错) customInsert: function(insertImg, result, editor) { // 图片上传并返回结果,自定义插入图片的事件(而不是编辑器自动插入图片!!!) // insertImg 是插入图片的函数,参数editor 是编辑器对象,result 是服务器端返回的结果 // 举例:假如上传图片成功后,服务器端返回的是 {url:'....'} 这种格式,即可这样插入图片: var url = result.result.remote_path; insertImg(url); // result 必须是一个 JSON 格式字符串!!!否则报错 } }; // }(6)常用 API属性获取编辑器的唯一标识: editor.id 获取编辑区域 DOM 节点: editor.$textElem[0] 获取菜单栏 DOM 节点: editor.$toolbarElem[0] 获取编辑器配置信息: editor.config 获取编辑区域 DOM 节点 ID: editor.textElemId 获取菜单栏 DOM 节点 ID: editor.toolbarElemId 获取菜单栏中“图片”菜单的 DOM 节点 ID: editor.imgMenuId方法选取操作获取选中的文字: editor.selection.getSelectionText() 获取选取所在的 DOM 节点: editor.selection.getSelectionContainerElem()[0] 开始节点: editor.selection.getSelectionStartElem()[0] 结束节点: editor.selection.getSelectionEndElem()[0] 折叠选取: editor.selection.collapseRange(更多可参见源码中定义的方法编辑内容操作插入 HTML: editor.cmd.do(‘insertHTML’, ‘< p>…< /p>’) 可通过editor.cmd.do(name, value)来执行document.execCommand(name, false, value)的操作三、主流富文本编辑器推荐1.TinyMCETinyMCE是一个开源的所见即所得的HTML编辑器,界面相当清新,界面模拟本地软件的风格,顶部有菜单栏。支持图片在线处理,插件多,功能非常强大,易于集成,并且拥有可定制的主题。支持目前流行的各种浏览器,它可以达到微软Word类似的编辑体验。而且还是开源免费的,目前一直有人维护,这款编辑器使用的人非常多。更多介绍及下载地址: https://www.tiny.cloud/docs/demo/full-featured/2.CKEditorCkeditor也是一款非常经典的富文本编辑器,官方下载量过千万。它是在非常著名的FCkEditor基础上开发的新版本,FckEditor的用户现在基本都转移到Ckeditor了。Ckeditor有高性能的实时预览,它特有行内编辑功能,使得编辑内容更加直观,仿佛是在编辑网页一样,有很强的可扩展性,被各大网站广泛运用。更多介绍及下载地址: https://ckeditor.com/3.UEditorUEditor 是由百度出品的富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源免费。这款编辑器用过的人也非常多,功能非常全面,插件很多,还可以很方便插入百度地图,接入十分简单。16年之后百度没有怎么更新了,不过现有的功能也足够用了。UEditor分为UE版(全功能版)和UM版(精简版),大家可以根据自己使用环境选择不同的版本。更多介绍及下载地址: http://ueditor.baidu.com/website/4.wangEditorwangEditor轻量级,小巧实用,配置方便,使用简单。可以自定义皮肤功能,免费开源。用户数量也很多,尤其是用在一些轻型环境,比如论坛社区回帖。wangEditor是国人出品的开源项目。更多介绍及下载地址: http://www.wangeditor.com/5.kindeditorKindEditor历史也很长了,用户数也不少,国内某公司出品。免费开源。界面类似于office word,界面和功能中规中矩,文档齐全,使用还算方便。更多介绍及下载地址: http://kindeditor.net/demo.php6.simditorsimditor是Tower平台使用的富文本编辑器,是一款轻量化的编辑器,界面简约,功能实用,插件不是很多,功能要求不高的可以使用。虽然是国内出品,但文档是英文的。开源免费。更多介绍及下载地址: https://simditor.tower.im/7.bootstrap-wysiwygbootstrap-wysiwyg是基于Bootstrap的轻型、免费开源的富文本编辑器,界面简洁大方。使用需要先引入bootstrap。bootstrap-wysiwyg这一长串像乱码一样的名字影响了它的推广和使用~ ~毕竟轻型使用环境还是很多的。更多介绍及下载地址: http://mindmup.github.io/bootstrap-wysiwyg/8.summernotesummernote是一款轻量级的富文本编辑器,比较容易上手,使用体验流畅,支持各种主流浏览器。summernote开源免费,该项目一直比较活跃,一直都有人在维护。summernote同样依赖于jquery和bootstrap,使用前先引入这两项。更多介绍及下载地址: https://summernote.org/9.FroalaFroala是一款功能丰富的富文本编辑器,界面分类清晰,容易集成,容易升级,支持主流浏览器,具有行内编辑功能。Froala代码示例很多,可以集成在很多js框架里如React.js,Aurelia,Angular,Ionic,Django等。插件很多,易于扩充功能。Froala是收费的,不过前端是开源的,如果要使用后台是要交费的(如果你使用国人强大的crack技能那就是另外一回事了~ ~)。目前有三个定价方式:基础版(239美元)、专业版(1199美元)和企业版(1999美元)。更多介绍及下载地址: https://www.froala.com/wysiwyg-editor10.QuillQuill是轻型的编辑器,样式一般(黑白风),功能中等,它的代码高亮功能比较强,同样支持行内编辑模式,工具条可自定义。开源免费,项目活跃,一直有人维护。更多介绍及下载地址: https://quilljs.com/11.FreeTextBoxFreeTextBox功能强大,前端支持主流浏览器,但后台只支持.NET。它的外观和使用风格都和微软 Word很类似。工具条可以定制,运行速度一般。FreeTextBox分为免费版和pro收费版,一般情况免费版本功能已经足够用了。更多介绍及下载地址: http://freetextbox.com/12.dhtmlxEditorDHTMLX组件是一整套基于JS的UI库,功能强大,其中包含编辑器dhtmlxEditor。该编辑器可以直接集成到Angular,React和Vue.js框架中,该编辑器还同时支持Markdown和富文本。支持普通视图和类似word一样的页面视图,支持全屏幕,工具支持自定义,包括工具条颜色和样式等。dhtmlxEditor分为免费版和收费版,免费版具有绝大多数功能,而且免费版是开源的你可以在代码级别随便扩充。更多介绍及下载地址: https://dhtmlx.com/docs/products/dhtmlxRichText/13.eWebEditoreWebEditor外观和使用风格都和微软 Word很类似,功能很多。工具条可以定制,运行速度很快。导入文件接口很多,支持word、excel、pdf、ppt直接导入,目前版本不支持代码高亮,不适合纯技术平台使用,适合内容编辑人员使用。eWebEditor有很长的历史了,是典型的传统富文本编辑器,不论是界面,还是功能都比较传统。eWebEditor是收费的,但也有免费的精简版,精简版没有后台功能。更多介绍及下载地址: http://www.ewebeditor.net/demo/后记Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
文章
JavaScript  ·  前端开发  ·  Java  ·  程序员  ·  API  ·  定位技术  ·  UED  ·  CDN  ·  Python  ·  容器
2022-11-24
商圈库_思路梳理 | 学习笔记
开发者学堂课程【2020版大数据实战项目之DMP广告系统(第六阶段):商圈库_思路梳理】学习笔记,与课程紧密联系,让用户快速学习知识。课程地址:https://developer.aliyun.com/learning/course/681/detail/11821商圈库_思路梳理 内容介绍一、模板二、步骤三、高德API四、总结 商圈库正常情况下应该放在“打标签”里,但这个功能也是比较大的。所以我们给它单独设立一个大章节 一、目标1.例子(1)例如一个有实体店的电商公司(他有一个或很多实体店)在投放广告的时候,应该会优先选择附近的用户进行投放(2)如果要对一个市场营销活动投放广告的话,比如在宝马工作,然后他们在做这个活动的时候一般都会有地点的。比如说北京的某一个郊县或者说是上海的某一个商区。那么这时他们在做活动之前肯定会希望更多的人来看。那么他们在投放广告的时候应该也会优先选择活动场地附近的人,其实 DSP 来去投放某一个广告的时候有一个很重要的标签维度,就是要根据地理位置以及商圈信息来进行广告的投放。比如,现在要投放给80、90年的一些小伙伴,家住在一个商圈附近。这个商圈指的是比如说西单、东单等等都算商圈,所以在投放的时候比较倾向于投放有对应商圈信息的一些用户,这样投放广告会精准度更高一些所以对于广告业务来说,商圈所代表的地理位置信息是非常重要的,这个小节我们就对整个数据集获取一下商圈库的信息,我们为什么做商圈库其实原因很简单,如果你要想把商圈库中的数据打上商圈的信息。那么你就应该有一个商圈库,当我们拥有一个商圈库以后,在打标签的时候就可以查询到它属于这个商圈。那么把这个商圈加进来,它的作用就是为了我们打标签的时候提供一些数据 二、步骤1.高德 API 介绍,要有一个商圈的数据库,要根据地理位置获取到商圈的位置信息,要通过高德这样的地图的功能来获取信息2.通过高德获取地理位置信息,获取到了以后我们就了解这个代码该怎么写3.解析高德所返回的 JSON,在这之前首先要了解JSON是一个什么格式,如何解析 JSON ,然后我们再去解析。通过这两个步骤,就是通过解析高德返回的JSON就能学习到什么是 JSON,它里面是什么,什么是 JSON,里面有什么数据类型。然后常见的在如何解析,这些非常的重要,比我们商圈库本身的功能要重要的多。因为基本上在任何公司做任何项目都逃不过 JSON 这个东西。如果不能理解JSON怎么解析,那么就会是一个不合格的开发者。4.生成商圈库 三、高德API1.目标通过这个小节了解如何使用高德逆地理位置 API 来通过位置信息获取其位置名称,你是怎么给定一个经纬度去获取其地理位置信息的,以及高德提供给我们了一个什么样的API让我们能做到这件事2.步骤(1)申请高德 API. 这步完成以后呢,你就拥有了高德API的权限 API 介绍(2)并且访问,通过这一小节就会了解API该怎么调用3.申请高德API(1)注册高德开发者账号①进入https://lbs.amap.com/②右上角点击注册,按照流程进行注册即可(2)创建新的应用点击进入高德开放平台的网站当中,正常情况下大家需要点击右上角的注册。注册后要给定你是个人开发者还是企业开发者,我们选择个人。然后输入个人信息就注册完成了。有账号的话就不用注册了,可以直接进行登陆登陆成功后点击控制台,进入到界面当中。首先要创建一个应用,创建以后我们才能进行后续操作。所以我们先点击应用管理,然后找到我的应用,点击创建新应用这时要写一个应用名称,应用类型可以随意选择,然后点击创建,创建完了以后就会得到如下图,但是少了一个 K ,其实创建应用一个应用里可以有多个 K 。一个K其实就是一个身份识别的一个 token,因此我们先要进行一个K的添加,点击右上角加号,然后 K 的名称我们随便定义一下叫做 dmp ,然后服务平台一定要选择 web 服务,然后再去选择,最后点击提交,这时就申请出来了一个K,然后就可以进行后续的操作注意:在使用第三方的 API 时,一定要先看开发文档,点击完开发文档以后,注意刚才选择的是 web 服务,因此也要使用 web 服务的 API点击web服务,我们看到它支持这么多东西,什么叫做地理逆地理编码API点进去查看,地理和逆地理编码它的意思就是说通过 HTTP 和 HTTPS 访问远程,提供结构化地址,与经纬度之间的相互转换。什么叫相互转化,和什么叫之间。就是结构化地址转为经纬度,经纬度转为结构化地址。什么叫做结构化地址呢?它首先是一串字符,然后里面包含国家、省份、城市、区景、城镇、乡村、街道、门牌号码、大厦等建筑物名称。这就是经纬度信息,但是我们要商圈信息,商圈就是一个结构化的地理位置。恰好找到了逆地理位置编码这个要用的 API。那么路径规划就是定两个点,然后规划路线类似于在高德百度进行导航的功能。然后行政区就是你给定一个地址查询一下哪个区的。搜索 pui 就是给定一个范围,在这个范围内的一些商户他的信息返回给你。ip定位就是给一个 ip,返回一个经纬度。批量请求接口就是你可以一次性做一批事情。要用到的就是逆地理位置这个编码。图片右侧是这个API的目录,使用限制就是说在使用的时候会有一些限制就是必须使用 UTF-8,基本上是默认的。使用说明有两个API,一个是地理编码,一个是逆地理编码。地理编码是给定一个商圈的名称,返回对应的经纬度范围,那么逆地理编码是给一个经纬度,返回你这个经纬度在哪个商圈内。因此选择逆地理编码逆地理编码其实就是一个 URL,它的请求方式必须是 Get,就是必须要直接访问这个 URL 才可以,那么必须要传一个参数,假如是 Get 请求,那么这些参数全都拼到URL后面。我们至少需要挑两个参数,一个是K一个是 location,如果是这样的话我们先去拷贝k,拷贝以后在后面加上,再拼上一个东西的话两个参数之间应该有&这个符号来作为分隔第二个参数是必须作为一个 location , 进入笔记当中拷贝一个地理位置,在文档中找到服务事例,里面就有一个 location 。复制,第一位是经度100度,第二位是纬度30多。接下来就回车返回查看结果,得到的结果是 JSON 格式。接下来打开百度搜索 json format。会有一个 json 格式化教验工具叫做 bJSON ,将刚才复制的拷贝在里面,拷贝后就可以格式化教验,这时已经转化为格式良好的 json 串。包括请求状态是否成功,regecode 是真正需要的数据,有位置信息。位置信息中存在 businessareas,即商圈的意思。给出的地理位置有很多的商圈。操作比较简单,发 http 请求后解析 Jason 。接下来要详细介绍 http 以及工具、详细学习 Jason 解析,以及功能。http 访问和 Jason 能力是基础并且很重要。 四、总结1.当前的功能是需要根据数据集中的经纬度信息,找到对应的商圈信息,高德提供了大量的地图相关的 API ,我们可以免费使用,其中这个功能应该使用高德逆地理位置API2.逆地理位置的API其实就是 Http 的一个接口,使用 GET 请求,发送对应的参数即可调用3.后续需要做的事情(1)通过 Http 客户端在程序里调用高德API(2)获取数据后,使用 JSON 解析为对象 
文章
JSON  ·  API  ·  定位技术  ·  数据库  ·  数据格式  ·  开发者
2022-11-22
跳转至:
阿里云开发者学堂
130337 人关注 | 7314 讨论 | 12051 内容
+ 订阅
  • 企业运维训练营之云上监控运维最佳实践启动!参营送好礼
  • 可观测Grafana入门训练营,帮助同学们由浅入深的对阿里云Grafana服务拥有全面了解
  • 【开发者7日学】求职达人训练营上线啦~快来打卡赢好礼
查看更多 >
开发与运维
5768 人关注 | 133248 讨论 | 318580 内容
+ 订阅
  • Linux如何查看centos版本?
  • Python3 notes
  • Python3 notes
查看更多 >
IoT
123068 人关注 | 2907 讨论 | 24744 内容
+ 订阅
  • 前缀和算法练习集
  • 【ANFIS 回归预测】基于平均定位误差 D 的 ANFIS 实现数据非线性回归附matlab代码
  • 【二维装箱】基于BL算法求解矩形地块二维装箱放置优化问题附matlab代码
查看更多 >
人工智能
2864 人关注 | 12304 讨论 | 102352 内容
+ 订阅
  • Python3 notes
  • Python3 notes
  • PHP的组件是什么意思?底层原理是什么?
查看更多 >
大数据
188703 人关注 | 30735 讨论 | 83757 内容
+ 订阅
  • Linux脚本是干什么的?底层原理是什么?
  • composer.json 文件是干什么的?底层原理是什么?
  • 使用树状图可视化聚类
查看更多 >