首页 文章 精选 留言 我的

精选列表

搜索[深度集成],共10008篇文章
优秀的个人博客,低调大师

JSBridge深度剖析

概述 做过混合开发的人都知道Ionic和PhoneGap之类的框架,这些框架在web基础上包装一层Native,然后通过Bridge技术的js调用本地的库。 在讲JSBridge技术之前,我们来看一下传统的实现方式。 Android端 Native调JS native调用js比较简单,只要遵循:"javascript: 方法名('参数,需要转为字符串')"的规则即可。 在4.4之前,调用的方式: // mWebView = new WebView(this); mWebView.loadUrl("javascript: 方法名('参数,需要转为字符串')"); //ui线程中运行 runOnUiThread(new Runnable() { @Override public void run() { mWebView.loadUrl("javascript: 方法名('参数,需要转为字符串')"); Toast.makeText(Activity名.this, "调用方法...", Toast.LENGTH_SHORT).show(); } }); 4.4以后(包括4.4),使用以下方式: mWebView.evaluateJavascript("javascript: 方法名('参数,需要转为字符串')", new ValueCallback() { @Override public void onReceiveValue(String value) { //这里的value即为对应JS方法的返回值 } }); 说明: 4.4之前Native通过loadUrl来调用JS方法,只能让某个JS方法执行,但是无法获取该方法的返回值 4.4之后,通过evaluateJavascript异步调用JS方法,并且能在onReceiveValue中拿到返回值 不适合传输大量数据(大量数据建议用接口方式获取) mWebView.loadUrl("javascript: 方法名('参数,需要转为字符串')");函数需在UI线程运行,因为mWebView为UI控件 JS调Native Js调用Native需要对WebView设置@JavascriptInterface注解,这里有个漏洞,后面会给大家说明。要想js能够Native,需要对WebView设置以下属性。 WebSettings webSettings = mWebView.getSettings(); //Android容器允许JS脚本 webSettings.setJavaScriptEnabled(true); //Android容器设置侨连对象 mWebView.addJavascriptInterface(getJSBridge(), "JSBridge"); 这里我们看到了getJSBridge(),Native中通过addJavascriptInterface添加暴露出来的JS桥对象,然后再该对象内部声明对应的API方法。 private Object getJSBridge(){ Object insertObj = new Object(){ @JavascriptInterface public String foo(){ return "foo"; } @JavascriptInterface public String foo2(final String param){ return "foo2:" + param; } }; return insertObj; } 那么Html怎么调用Native的方法呢? //调用方法一 window.JSBridge.foo(); //返回:'foo' //调用方法二 window.JSBridge.foo2('test');//返回:'foo2:test' 说明: 在Android4.2以上(api17后),暴露的api要加上注解@JavascriptInterface,否则会找不到方法。 在api17以前,addJavascriptInterface有风险,hacker可以通过反编译获取Native注册的Js对象,然后在页面通过反射Java的内置 静态类,获取一些敏感的信息和破坏 JS调用Native暴露的api,并且能得到相应返回值 注:说到WebView中接口隐患的问题,这里大家可以参考WebViw漏洞利用,不过Android发展到现在,这个漏洞基本没有了。 iOS端 Native调JS Native调用js的方法比较简单,Native通过stringByEvaluatingJavaScriptFromString调用Html绑定在window上的函数。不过应注意Oc和Swift的写法。 //Swift webview.stringByEvaluatingJavaScriptFromString("方法名(参数)") //OC [webView stringByEvaluatingJavaScriptFromString:@"方法名(参数);"]; 说明: Native调用JS方法时,能拿到JS方法的返回值 不适合传输大量数据(大量数据建议用接口方式获取) JS调Native Native中通过引入官方提供的JavaScriptCore库(iOS7以上),然后可以将api绑定到JSContext上(然后Html中JS默认通过window.top.*可调用)。 引入官方的库文件 #import <JavaScriptCore/JavaScriptCore.h> Native注册api函数(OC) -(void)webViewDidFinishLoad:(UIWebView *)webView{ [self hideProgress]; [self setJSInterface]; } -(void)setJSInterface{ JSContext *context =[_wv valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; // 注册名为foo的api方法 context[@"foo"] = ^() { //获取参数 NSArray *args = [JSContext currentArguments]; NSString *title = [NSString stringWithFormat:@"%@",[args objectAtIndex:0]]; //做一些自己的逻辑 //返回一个值 'foo:'+title return [NSString stringWithFormat:@"foo:%@", title]; }; } Html中JS调用Native方法 window.top.foo('test'); 说明: iOS7才出现这种方式,在这之前,js无法直接调用Native,只能通过JSBridge方式简介调用 JS能调用到已经暴露的api,并且能得到相应返回值 iOS原生本身是无法被JS调用的,但是通过引入官方提供的第三方"JavaScriptCore",即可开放api给JS调用 JSBridge 什么是JSBridge JSBridge:听其取名就是js和Native之前的桥梁,而实际上JSBridge确实是JS和Native之前的一种通信方式。简单的说,JSBridge就是定义Native和JS的通信,Native只通过一个固定的桥对象调用JS,JS也只通过固定的桥对象调用Native。JSBridge另一个叫法及大家熟知的Hybrid app技术。 流程:H5->通过某种方式触发一个url->Native捕获到url,进行分析->原生做处理->Native调用H5的JSBridge对象传递回调。 我们前面讲过了原生的WebView/UIWebView控件已经能够和Js实现数据通信了,那为什么还要JSBridge呢?其实使用JSBridge有很多方面的考虑: Android4.2以下,addJavascriptInterface方式有安全漏掉 iOS7以下,JS无法调用Native url scheme交互方式是一套现有的成熟方案,可以完美兼容各种版本,对以前老版本技术的兼容。 url scheme url scheme是一种类似于url的链接,是为了方便app直接互相调用设计的。具体来讲如果是系统的url scheme,则打开系统应用,否则找看是否有app注册这种scheme,打开对应app。注:这种scheme必须原生app注册后才会生效。 而在我们实际的开发中,app不会注册对应的scheme,而是由前端页面通过某种方式触发scheme(如用iframe.src),然后Native用某种方法捕获对应的url触发事件,然后拿到当前的触发url,根据定义好的协议,分析当前触发了那种方法。 JSBridge技术实现 要实现JSBridge,我们需要按以下步骤分析: 第一步:设计出一个Native与JS交互的全局桥对象 第二步:JS如何调用Native 第三步:Native如何得知api被调用 第四步:分析url-参数和回调的格式 第五步:Native如何调用JS 第六步:H5中api方法的注册以及格式 JSBridge的完整流程可总结为: 设计Native与JS交互的全局桥对象 我们规定,JS和Native之间的通信必须通过一个H5全局对象JSbridge来实现。该对象有如下特点:该对象名为"JSBridge",是H5页面中全局对象window的一个属性,形如: var JSBridge = window.JSBridge || (window.JSBridge = {}); 该对象有如下方法: registerHandler( String,Function )H5调用注册本地JS方法,注册后Native可通过JSBridge调用。调用后会将方法注册到本地变量messageHandlers 中。 callHandler( String,JSON,Function )H5调用 调用原生开放的api,调用后实际上还是本地通过url scheme触发。调用时会将回调id存放到本地变量responseCallbacks中 _handleMessageFromNative( JSON )Native调用 原生调用H5页面注册的方法,或者通知H5页面执行回调方法 JS调用Native 我们定义好了全局桥对象,可以通过它的callHandler方法来调用原生的api。 callHandler函数内部实现过程 在执行callHandler时,内部经历了以下步骤: 判断是否有回调函数,如果有,生成一个回调函数id,并将id和对应回调添加进入回调函数集合responseCallbacks中。 通过特定的参数转换方法,将传入的数据,方法名一起,拼接成一个url scheme //url scheme的格式如 //基本有用信息就是后面的callbackId,handlerName与data //原生捕获到这个scheme后会进行分析 var uri = CUSTOM_PROTOCOL_SCHEME://API_Name:callbackId/handlerName?data 使用内部早就创建好的一个隐藏iframe来触发scheme //创建隐藏iframe过程 var messagingIframe = document.createElement('iframe'); messagingIframe.style.display = 'none'; document.documentElement.appendChild(messagingIframe); //触发scheme messagingIframe.src = uri; 注:正常来说是可以通过window.location.href达到发起网络请求的效果的,但是有一个很严重的问题,就是如果我们连续多次修改window.location.href的值,在Native层只能接收到最后一次请求,前面的请求都会被忽略掉。所以JS端发起网络请求的时候,需要使用iframe,这样就可以避免这个问题。 Native通知api被调用 上一步,我们已经成功在H5页面中触发scheme,那么Native如何捕获scheme被触发呢? 根据系统不同,Android和iOS分别有自己的处理方式。 Android 在Android中(WebViewClient里),通过shouldoverrideurlloading可以捕获到url scheme的触发。 public boolean shouldOverrideUrlLoading(WebView view, String url){ //如果返回false,则WebView处理链接url,如果返回true,代表WebView根据程序来执行url return true; } iOS iOS中,UIWebView有个特性:在UIWebView内发起的所有网络请求,都可以通过delegate函数在Native层得到通知。这样,我们可以在webview中捕获url scheme的触发(原理是利用 shouldStartLoadWithRequest) - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSURL *url = [request URL]; NSString *requestString = [[request URL] absoluteString]; //获取利润url scheme后自行进行处理 分析url-参数和回调的格式 在前面的步骤中,Native已经接收到了JS调用的方法,那么接下来,原生就应该按照定义好的数据格式来解析数据了,Native接收到Url后,可以按照这种格式将回调参数id、api名、参数提取出来,然后按如下步骤进行。 根据api名,在本地找寻对应的api方法,并且记录该方法执行完后的回调函数id 根据提取出来的参数,根据定义好的参数进行转化 原生本地执行对应的api功能方法 功能执行完毕后,找到这次api调用对应的回调函数id,然后连同需要传递的参数信息,组装成一个JSON格式的参数 通过JSBridge通知H5页面回调 Native调用JS 到了这一步,就该Native通过JSBridge调用H5的JS方法或者通知H5进行回调了。其中的messageJSON数据格式根据两种不同的类型。 JSBridge._handleMessageFromNative(messageJSON); Native通知H5页面进行回调:数据格式为: Native通知H5回调的JSON格式。Native主动调用H5方法:Native主动调用H5方法时,数据格式是:{handlerName:api名,data:数据,callbackId:回调id}: handlerName String型 需要调用的,h5中开放的api的名称 data JSON型 需要传递的数据,固定为JSON格式(因为我们固定H5中注册的方法接收的第一个参数必须是JSON,第二个是回调函数) callbackId String型 原生生成的回调函数id,h5执行完毕后通过url scheme通知原生api成功执行,并传递参数 H5中api方法的注册以及格式 前面有提到Native主动调用H5中注册的api方法,那么h5中怎么注册供原生调用的api方法呢? JSBridge.registerHandler('testH5Func',function(data,callback){ alert('测试函数接收到数据:'+JSON.stringify(data)); callback&&callback('测试回传数据...'); }); 如上代码,其中第一个data即原生传过来的数据,第二个callback是内部封装过一次的,执行callback后会触发url scheme,通知原生获取回调信息. 完善JSBridge方案 github上有一个开源项目,它里面的JSBridge做法在iOS上进一步优化了,所以参考他的做法,这里进一步进行了完善。地址marcuswestin/WebViewJavascriptBridge JSBridge对象图解: JSBridge实现完整流程: 总结 那么我们在实际的开发中,如何针对Android和iOS的不同情况,统一出一种完整的方案。 另类实现:不采用url scheme方式 前面提到的JSBridge都是基于url scheme的,但其实如果不考虑Android4.2以下,iOS7以下,其实也可以用另一套方案的。 Native调用JS的方法不变 JS调用Native是不再通过触发url scheme,而是采用自带的交互具体来讲: Android中,原生通过 addJavascriptInterface开放一个统一的api给JS调用,然后将触发url scheme步骤变为调用这个api,其余步骤不变。 OS中,原生通过JavaScriptCore里面的方法来注册一个统一api,其余和Android中一样。

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

2020:“深度伪造”与“深度欺骗”的一年

在过去的一年中,可用于生成逼真、虚假或人为操控的音视频机器学习模型deepfake逐渐见诸报端,成为媒体争相报道的对象,并成为新兴的网络安全威胁。公众接触到deepfake首先是通过娱乐视频,可用deepfake工具生成,将明星的脸部叠加在色情视频中。 尽管这些视频低质且明显可以看出来是不合法的,但是这项新技术对于人们区分虚拟和现实还是有一定的潜在影响。在政治世界中,尤其值得人们关注,deepfake可以成为一种攻击政客或政党的武器,操纵公众舆论来影响选举甚至股市。 几年前,deepfake技术研发有限,这种工具只为资源丰富且技术先进的国家使用,但是现在deepfakes工具免费提供、随处可得,且简单易学,因此任何能够上网、有时间和目的的人都能够随时制作deepfake视频,并将虚假内容视频在社交媒体中传播开来。 进一步来说,deepfake工具越来越智能,生成视频所需要的素材也越来越少。早期需要几个小时的视频和音频或者是大数据集,才可以进行机器分析和生成。这意味着较高关注度的人比如政客、名人、知名CEO或拥有大量网络资源的人容易被盯上,成为虚假视频的主角。 现在,只要有一张照片就可以生成这类虚假视频了。未来生成虚假视频只会越来越容易,比如Facebook上的个人资料图片或者Instagram的音频片段,由此可见,人人都应该提高警惕了,防止自己成为下一个目标。 虚假的现实 Deepfake之所以强大,是因为它颠覆了人类对现实的基本理解:眼见为实。Deepfake让真相与现实脱节,此外还能够引起情感上的回应。比如你看到一些令人不快的东西,即使之后你知道它是假的,但是你依然会有负面情绪,并且将所见所感和潜意识联系起来。 去年10月,州长Gavin Newsom签署了加利福尼亚州的AB 730法案(称为“反Deepfake法案”),希望在2020年大选之前减少恶意deepfake虚假视频的传播。虽然这一举措值得称赞,但法案本身没有任何进展。它人为地设置了一个时间线,这个时间线只适用于在选举的60天内具有“真实恶意”原则的deepfake虚假视频,这就免除了传播平台监管和删除deepfake虚假视频的责任,相反地,法律依赖于虚假视频生产者的自我承认和所有权声明,而对于“真实 恶意”的证据查找过程不是那么明确的。 制定该法律的初衷并非去执行它,更可能的是立法者向人们展示自己已经迈出的第一步,表明他们知道deepfake对民主的严重威胁。而这场斗争才刚刚开始。在理想情况下,该法律将影响并指导联邦政府和其他州的工作,以此为初始模板进行更有效和更具执行力的立法。 Deepfake技术将成为2020年的商业威胁 迄今为止,和deepfake相关的讨论主要集中在通过社交媒体(尤其是在政治领域)推动的虚假信息运动和大规模人为操纵的潜在影响上。2020年将是deepfake成为企业真正威胁的一年,而企业的网络防御团队还没有能力应对这种威胁。 鱼叉式网络攻击针对的是高级员工,通常会诱使他们手动完成任务,比如支付伪造发票,发送实体文件或者手动设置网络犯罪分子的登录凭证。从技术角度来看,这些邮件通常更难检测,因为该电子邮件不包含任何可疑的链接或附件,并且通常与BEC攻击结合使用(当黑客控制了员工的电子邮件时,他们可以从合法地址发送电子邮件)。 根据FBI的数据,在过去三年中,BEC攻击给全球组织造成的损失超过260亿美元。而deepfake能够加剧这些攻击,比如利用语音邮件模拟高层欺骗员工,这就让网络犯罪更加容易。 有一种观点认为,攻击打破了真相壁垒,与其认为攻击请求是虚假的,接受请求并相信它是真实可信的做法反倒更有意义,能够直接地揭露它。 然而,当deepfake技术进一步发展时,攻击的真实和虚假反而难以辨别了。比如员工与其认为的CEO进行视频通话,屏幕后的犯罪分子实时生成deepfake视频。去年早些时候,一位CEO被AI产生的声音欺骗,将243000美元转移到他认为是公司供应商的银行账户中。 当前,安全行业还没有设备、电子邮件筛选或任何技术来防御deepfake技术。但是,也不是一无所获。例如,Facebook,微软和大学研究人员发起了Deepfake检测挑战赛,呼吁推动开源deepfake检测工具的开发。国防高级研究计划局(DARPA)宣布了语义取证或”SemaFor计划”,该计划旨在开发“语义取证”,作为过去使用的统计检测技术的另一种防御方法。 当前存在的唯一防范方法是教育用户,让他们了解这些新型攻击的方法,并警惕任何看起来不同寻常的行为或者活动。 信任早已不再是我们负担得起的奢侈品了。

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

Spring Boot项目集成 UidGenerator

前言 UidGenerator 基于snowflake算法实现 UidGenerator 由百度开发,基于SnowFlake算法的唯一ID生成器。UidGenerator 已组件的形式工作在应用项目中,支持自定义workeid位数和初始化策略,从而适用docker等虚拟化环境下实例自动重启等场景。 准备一个maven项目,构建两个模块。分别作为使用方和提供方。(建两个模块主要是为了“造轮子”,其他模块或项目可以直接引用,无需关心uid配置,如果没有分模块,可以忽略构建两个模块) 下载uid源码,放在项目中,开源地址https://github.com/baidu/uid-generator 数据库建表 DROP TABLE IF EXISTS WORKER_NODE;CREATE TABLE WORKER_NODE ( ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id', HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name', PORT VARCHAR(64) NOT NULL COMMENT 'port',TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER', LAUNCH_DATE DATE NOT NULL COMMENT 'launch date', MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time', CREATED TIMESTAMP NOT NULL COMMENT 'created time',PRIMARY KEY(ID)) COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB; Spring 配置 CachedUidGennerator: UidGenerator 有两个具体的实现类,分别是 DefaultUidGenerator 和 CachedUidGenerator, 官方推荐使用性能较强的 CachedUidGenerator。 我们直接引用 UdiGenerator源码中的 cached-uid-spring.xml文件,使用默认配置 <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <!-- UID generator --> <bean id="disposableWorkerIdAssigner" class="com.baidu.fsg.uid.worker.DisposableWorkerIdAssigner" /> <bean id="cachedUidGenerator" class="com.baidu.fsg.uid.impl.CachedUidGenerator"> <property name="workerIdAssigner" ref="disposableWorkerIdAssigner" /> <!-- 以下为可选配置, 如未指定将采用默认值 --> <!-- RingBuffer size扩容参数, 可提高UID生成的吞吐量. --> <!-- 默认:3, 原bufferSize=8192, 扩容后bufferSize= 8192 << 3 = 65536 --> <!--<property name="boostPower" value="3"></property>--> <!-- 指定何时向RingBuffer中填充UID, 取值为百分比(0, 100), 默认为50 --> <!-- 举例: bufferSize=1024, paddingFactor=50 -> threshold=1024 * 50 / 100 = 512. --> <!-- 当环上可用UID数量 < 512时, 将自动对RingBuffer进行填充补全 --> <!--<property name="paddingFactor" value="50"></property>--> <!-- 另外一种RingBuffer填充时机, 在Schedule线程中, 周期性检查填充 --> <!-- 默认:不配置此项, 即不实用Schedule线程. 如需使用, 请指定Schedule线程时间间隔, 单位:秒 --> <!--<property name="scheduleInterval" value="60"></property>--> <!-- 拒绝策略: 当环已满, 无法继续填充时 --> <!-- 默认无需指定, 将丢弃Put操作, 仅日志记录. 如有特殊需求, 请实现RejectedPutBufferHandler接口(支持Lambda表达式) --> <!--<property name="rejectedPutBufferHandler" ref="XxxxYourPutRejectPolicy"></property>--> <!-- 拒绝策略: 当环已空, 无法继续获取时 --> <!-- 默认无需指定, 将记录日志, 并抛出UidGenerateException异常. 如有特殊需求, 请实现RejectedTakeBufferHandler接口(支持Lambda表达式) --> <!--<property name="rejectedPutBufferHandler" ref="XxxxYourPutRejectPolicy"></property>--> </bean> </beans> 引入cached-uid-spring.xml配置文件,在我们自己新建的 UidConfig中 package com.xxx.uid.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; /** * @author lishuzhen * @date 2020/8/11 16:10 */ @Configuration @ImportResource(locations = {"classpath:/uid/cached-uid-spring.xml"}) public class UidConfig { } 在另一个模块中maven引入,创建一个UidGenUtils工具类,方便使用 package com.xxxx.utils; import com.xxx.uid.UidGenerator; import org.springframework.stereotype.Component; import javax.annotation.Resource; /** * @author lishuzhen * @date 2020/8/11 16:13 */ @Component public class UidGenUtils { @Resource private UidGenerator uidGenerator; public long getUid() { return uidGenerator.getUID(); } public String getUidStr() { return String.valueOf(uidGenerator.getUID()); } }

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

EFK实战二 - 日志集成

前言 在EFK基础架构中,我们需要在客户端部署Filebeat,通过Filebeat将日志收集并传到LogStash中。在LogStash中对日志进行解析后再将日志传输到ElasticSearch中,最后通过Kibana查看日志。 上文已经搭建好了EFK的基础环境,本文我们通过真实案例打通三者之间的数据传输以及解决EFK在使用过程中的一些常见问题。 首先看一下实际的业务日志 2020-01-0910:03:26,719INFO========GetCostCenterStart=============== 2020-01-09 10:03:44,267 WARN 成本中心编码少于10位!{"deptId":"D000004345","companyCode":"01"} 2020-01-0910:22:37,193ERRORjava.lang.IllegalStateException:SessionImpl[abcpI7fK-WYnW4nzXrv7w,]:can'tcallgetAttribute()whensessionisnolongervalid. atcom.caucho.server.session.SessionImpl.getAttribute(SessionImpl.java:283) atweaver.filter.PFixFilter.doFilter(PFixFilter.java:73) atcom.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87) atweaver.filter.MonitorXFixIPFilter.doFilter(MonitorXFixIPFilter.java:30) atweaver.filter.MonitorForbiddenUrlFilter.doFilter(MonitorForbiddenUrlFilter.java:133) 「日志组成格式为:」 时间 日志级别 日志详情 那么我们的主要任务就是将这段日志正常写入EFK中。 filebeat安装配置 下载filebeat7.5.1 将下载后的文件上传至服务器并解压tar -zxvf filebeat-7.5.1-linux-x86_64.tar.gz 修改filebeat.yml, filebeat.inputs: -type:log enabled:true paths: -/app/weaver/Resin/log/xxx.log 此段配置日志输入,指定日志存储路径 output.logstash: #TheLogstashhosts hosts:["172.31.0.207:5044"] 此段配置日志输出,指定Logstash存储路径 启动filebeat ./filebeat -e -c filebeat.yml 如果需要静默启动,则使用 nohup ./filebeat -e -c filebeat.yml & 命令启动即可。 logstash配置 logstash的配置主要分为三段 input,filter,output。input用于指定输入,主要是开放端口给Filebeat用于接收日志filter用于指定过滤,对日志内容进行解析过滤。output用于指定输出,直接配置ES的地址即可 input{ beats{ port=>5044 } } output{ elasticsearch{ hosts=>["http://172.31.0.127:9200"] index=>"myindex-%{+YYYY.MM.dd}" user=>"elastic" password=>"xxxxxx" } } 我们配置好logstash后通过命令重启logstash docker-compose -f elk.yml restart logstash 经过上述两步配置后应用程序往日志文件写入日志,filebeat会将日志写入logstash。在kibana查看写入的日志结果如下: 日志显示有2个问题: 由于错误日志堆栈信息有多行,在kibana中展示成了多行,数据查看很乱。需要将堆栈异常整理成一行显示。 需要对日志进行解析,拆成“时间 日志级别 日志详情”的显示格式。 优化升级 在filebeat中设置合并行 filebeat默认是行传输的,但是我们的日志肯定是多行一个日志,我们要把多行合并到一起就要找到日志的规律。比如我们的日志格式全都是以时间格式开头,所以我们在filebeat中 filebeat.inputs区域添加如下几行配置 #以日期作为前缀 multiline.pattern:^\d{4}-\d{1,2}-\d{1,2} #开启多行合并 multiline.negate:true #合并到上一行之后 multiline.match:after 在logstash中设置对日志的解析 将日志解析成“时间 日志级别 日志详情”的展示格式,所以我们需要在logstash配置文件中添加filter段 filter { grok{ match => { "message" => "(?<date>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}),\d{3} %{LOGLEVEL:loglevel} (?<des>.*)" } } } 这里主要是使用grok语法对日志进行解析,通过正则表达式对日志进行过滤。大家可以通过kibana里的grok调试工具进行调试 配置完成后我们重新打开kibana Discover界面查看日志,符合预期,完美! 常见问题 kibana 乱码 这个主要原因还是客户端日志文件格式有问题,大家可以通过 file xxx.log查看日志文件的编码格式,如果是ISO8859的编码基本都会乱码,我们可以在filebeat配置文件中通过encoding指定日志编码进行传输。 filebeat.inputs: -type:log enabled:true paths: -/app/weaver/Resin/log/xxx.log encoding:GB2312 kibana 提取字段出错 如上所示,打开kibana Discover面板时出现此异常,大家只要删除ES中的 .kibana_1索引然后重新访问Kibana即可。 查看周围文件 我们在终端查看日志某关键字时一般会查上下文信息便于排查问题,如经常用到的指令 cat xxx.log | grep -C50 keyword,那么在Kibana中如何实现这功能呢。 在Kibana中搜索关键字,然后找到具体日志记录,点击左边向下箭头,然后再点击“查看周围文档”即可实现。 动态索引 我们日志平台可能需要对接多个业务系统,需要根据业务系统建立不同的索引。 在filebeat中给日志打上标记 -type:log ...... fields: logType:oabusiness 在logstash中根据标记生成索引 input { beats { port => 5044 } } filter { if [fields][logType] == "oabusiness" { grok{ match => { "message" => "(?<date>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}),\d{3} %{LOGLEVEL:loglevel} (?<des>.*)" } } } } output { elasticsearch { hosts => ["http://172.31.0.207:9200"] index => "%{[fields][logType]}-%{+YYYY.MM.dd}" user => "elastic" password => "elastic" } } 如果本文对你有帮助, 别忘记来个三连: 点赞,转发,评论 本文分享自微信公众号 - JAVA日知录(javadaily)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

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

阿里云AMQP Springboot集成

概述 本文主要介绍如何通过 Spring boot 连接阿里云AMQP服务。 操作步骤 1、示例程序下载,下载地址。 2、参数配置,AMQP管理控制台获取。 resources -> application.properties spring.application.name=rabbitmq-demo spring.rabbitmq.host=18********617278.mq-amqp.cn-hangzhou-a.aliyuncs.com spring.rabbitmq.port=5672 spring.rabbitmq.username=****** spring.rabbitmq.password=****** spring.rabbitmq.virtual-host=****** spring.rabbitmq.temp

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

SpringBoot之集成Spring AOP

在开始之前,我们先把需要的jar包添加到工程里。新增Maven依赖如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 接下来,我们进入正题。这里的涉及的通知类型有:前置通知、后置最终通知、后置返回通知、后置异常通知、环绕通知,下面我们就具体的来看一下怎么在SpringBoot中添加这些通知。 首先我们先创建一个Aspect切面类: @Component @Aspect publicclassWebControllerAop{ } 指定切点: //匹配com.zkn.learnspringboot.web.controller包及其子包下的所有类的所有方法 @Pointcut("execution(*com.zkn.learnspringboot.web.controller..*.*(..))") publicvoidexecuteService(){ } 接着我们再创建一个Controller请求处理类: packagecom.zkn.learnspringboot.web.controller; importorg.springframework.web.bind.annotation.RequestMapping; importorg.springframework.web.bind.annotation.RestController; /** *Createdbyzknon2016/11/19. */ @RestController @RequestMapping("/aop") publicclassAopTestController{ } 前置通知 配置前置通知: /** *前置通知,方法调用前被调用 *@paramjoinPoint */ @Before("executeService()") publicvoiddoBeforeAdvice(JoinPointjoinPoint){ System.out.println("我是前置通知!!!"); //获取目标方法的参数信息 Object[]obj=joinPoint.getArgs(); //AOP代理类的信息 joinPoint.getThis(); //代理的目标对象 joinPoint.getTarget(); //用的最多通知的签名 Signaturesignature=joinPoint.getSignature(); //代理的是哪一个方法 System.out.println(signature.getName()); //AOP代理类的名字 System.out.println(signature.getDeclaringTypeName()); //AOP代理类的类(class)信息 signature.getDeclaringType(); //获取RequestAttributes RequestAttributesrequestAttributes=RequestContextHolder.getRequestAttributes(); //从获取RequestAttributes中获取HttpServletRequest的信息 HttpServletRequestrequest=(HttpServletRequest)requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); //如果要获取Session信息的话,可以这样写: //HttpSessionsession=(HttpSession)requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION); Enumeration<String>enumeration=request.getParameterNames(); Map<String,String>parameterMap=Maps.newHashMap(); while(enumeration.hasMoreElements()){ Stringparameter=enumeration.nextElement(); parameterMap.put(parameter,request.getParameter(parameter)); } Stringstr=JSON.toJSONString(parameterMap); if(obj.length>0){ System.out.println("请求的参数信息为:"+str); } } 注意:这里用到了JoinPoint和RequestContextHolder。通过JoinPoint可以获得通知的签名信息,如目标方法名、目标方法参数信息等。通过RequestContextHolder来获取请求信息,Session信息。 接下来我们在Controller类里添加一个请求处理方法来测试一下前置通知: @RequestMapping("/testBeforeService.do") publicStringtestBeforeService(Stringkey,Stringvalue){ return"key="+key+"value="+value; } 前置通知拦截结果如下所示: 后置返回通知 配置后置返回通知的代码如下: /** *后置返回通知 *这里需要注意的是: *如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息 *如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数 *returning限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,对于returning对应的通知方法参数为Object类型将匹配任何目标返回值 *@paramjoinPoint *@paramkeys */ @AfterReturning(value="execution(*com.zkn.learnspringboot.web.controller..*.*(..))",returning="keys") publicvoiddoAfterReturningAdvice1(JoinPointjoinPoint,Objectkeys){ System.out.println("第一个后置返回通知的返回值:"+keys); } @AfterReturning(value="execution(*com.zkn.learnspringboot.web.controller..*.*(..))",returning="keys",argNames="keys") publicvoiddoAfterReturningAdvice2(Stringkeys){ System.out.println("第二个后置返回通知的返回值:"+keys); } Controller里添加响应的请求处理信息来测试后置返回通知: @RequestMapping("/testAfterReturning.do") publicStringtestAfterReturning(Stringkey){ return"key=:"+key; } @RequestMapping("/testAfterReturning01.do") publicIntegertestAfterReturning01(Integerkey){ returnkey; } 当发送请求为:http://localhost:8001/aop/testAfterReturning.do?key=testsss&value=855sss时,处理结果如图所示: 当发送请求为:http://localhost:8001/aop/testAfterReturning01.do?key=55553&value=855sss时,处理结果如图所示: 后置异常通知 后置异常通知的配置方式如下: /** *后置异常通知 *定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法; *throwing限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行, *对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。 *@paramjoinPoint *@paramexception */ @AfterThrowing(value="executeService()",throwing="exception") publicvoiddoAfterThrowingAdvice(JoinPointjoinPoint,Throwableexception){ //目标方法名: System.out.println(joinPoint.getSignature().getName()); if(exceptioninstanceofNullPointerException){ System.out.println("发生了空指针异常!!!!!"); } } Controller里配置响应的请求处理类: @RequestMapping("/testAfterThrowing.do") publicStringtestAfterThrowing(Stringkey){ thrownewNullPointerException(); } 后置异常通知方法的处理结果如下所示: 后置最终通知 后置最终通知的配置方式如下: /** *后置最终通知(目标方法只要执行完了就会执行后置通知方法) *@paramjoinPoint */ @After("executeService()") publicvoiddoAfterAdvice(JoinPointjoinPoint){ System.out.println("后置通知执行了!!!!"); } Controller类配置相应的请求处理类: @RequestMapping("/testAfter.do") publicStringtestAfter(Stringkey){ thrownewNullPointerException(); } @RequestMapping("/testAfter02.do") publicStringtestAfter02(Stringkey){ returnkey; } 当发送请求为:http://localhost:8001/aop/testAfter.do?key=55553&value=855sss 当发送请求为:http://localhost:8001/aop/testAfter02.do?key=55553&value=855sss 环绕通知 环绕通知的配置方式如下: /** *环绕通知: *环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 *环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型 */ @Around("execution(*com.zkn.learnspringboot.web.controller..*.testAround*(..))") publicObjectdoAroundAdvice(ProceedingJoinPointproceedingJoinPoint){ System.out.println("环绕通知的目标方法名:"+proceedingJoinPoint.getSignature().getName()); try{ Objectobj=proceedingJoinPoint.proceed(); returnobj; }catch(Throwablethrowable){ throwable.printStackTrace(); } returnnull; } Controller对应的请求处理类如下: @RequestMapping("/testAroundService.do") publicStringtestAroundService(Stringkey){ return"环绕通知:"+key; } 当发送请求为:http://localhost:8001/aop/testAroundService.do?key=55553 当发送请求为:http://localhost:8001/aop/testAfter02.do?key=55553&value=855sss时,不符合环绕通知的切入规则,所以环绕通知不会 执行。 完整的AOP配置代码如下: packagecom.zkn.learnspringboot.aop; importcom.alibaba.fastjson.JSON; importcom.google.common.collect.Maps; importorg.aspectj.lang.JoinPoint; importorg.aspectj.lang.ProceedingJoinPoint; importorg.aspectj.lang.Signature; importorg.aspectj.lang.annotation.*; importorg.springframework.stereotype.Component; importorg.springframework.web.context.request.RequestAttributes; importorg.springframework.web.context.request.RequestContextHolder; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpSession; importjava.util.Enumeration; importjava.util.Map; /** *Createdbyzknon2016/11/18. */ @Component @Aspect publicclassWebControllerAop{ //匹配com.zkn.learnspringboot.web.controller包及其子包下的所有类的所有方法 @Pointcut("execution(*com.zkn.learnspringboot.web.controller..*.*(..))") publicvoidexecuteService(){ } /** *前置通知,方法调用前被调用 *@paramjoinPoint */ @Before("executeService()") publicvoiddoBeforeAdvice(JoinPointjoinPoint){ System.out.println("我是前置通知!!!"); //获取目标方法的参数信息 Object[]obj=joinPoint.getArgs(); //AOP代理类的信息 joinPoint.getThis(); //代理的目标对象 joinPoint.getTarget(); //用的最多通知的签名 Signaturesignature=joinPoint.getSignature(); //代理的是哪一个方法 System.out.println(signature.getName()); //AOP代理类的名字 System.out.println(signature.getDeclaringTypeName()); //AOP代理类的类(class)信息 signature.getDeclaringType(); //获取RequestAttributes RequestAttributesrequestAttributes=RequestContextHolder.getRequestAttributes(); //从获取RequestAttributes中获取HttpServletRequest的信息 HttpServletRequestrequest=(HttpServletRequest)requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); //如果要获取Session信息的话,可以这样写: //HttpSessionsession=(HttpSession)requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION); Enumeration<String>enumeration=request.getParameterNames(); Map<String,String>parameterMap=Maps.newHashMap(); while(enumeration.hasMoreElements()){ Stringparameter=enumeration.nextElement(); parameterMap.put(parameter,request.getParameter(parameter)); } Stringstr=JSON.toJSONString(parameterMap); if(obj.length>0){ System.out.println("请求的参数信息为:"+str); } } /** *后置返回通知 *这里需要注意的是: *如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息 *如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数 *returning限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,对于returning对应的通知方法参数为Object类型将匹配任何目标返回值 *@paramjoinPoint *@paramkeys */ @AfterReturning(value="execution(*com.zkn.learnspringboot.web.controller..*.*(..))",returning="keys") publicvoiddoAfterReturningAdvice1(JoinPointjoinPoint,Objectkeys){ System.out.println("第一个后置返回通知的返回值:"+keys); } @AfterReturning(value="execution(*com.zkn.learnspringboot.web.controller..*.*(..))",returning="keys",argNames="keys") publicvoiddoAfterReturningAdvice2(Stringkeys){ System.out.println("第二个后置返回通知的返回值:"+keys); } /** *后置异常通知 *定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法; *throwing限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行, *对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。 *@paramjoinPoint *@paramexception */ @AfterThrowing(value="executeService()",throwing="exception") publicvoiddoAfterThrowingAdvice(JoinPointjoinPoint,Throwableexception){ //目标方法名: System.out.println(joinPoint.getSignature().getName()); if(exceptioninstanceofNullPointerException){ System.out.println("发生了空指针异常!!!!!"); } } /** *后置最终通知(目标方法只要执行完了就会执行后置通知方法) *@paramjoinPoint */ @After("executeService()") publicvoiddoAfterAdvice(JoinPointjoinPoint){ System.out.println("后置通知执行了!!!!"); } /** *环绕通知: *环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 *环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型 */ @Around("execution(*com.zkn.learnspringboot.web.controller..*.testAround*(..))") publicObjectdoAroundAdvice(ProceedingJoinPointproceedingJoinPoint){ System.out.println("环绕通知的目标方法名:"+proceedingJoinPoint.getSignature().getName()); try{//obj之前可以写目标方法执行前的逻辑 Objectobj=proceedingJoinPoint.proceed();//调用执行目标方法 returnobj; }catch(Throwablethrowable){ throwable.printStackTrace(); } returnnull; } } 完整的Controller类代码如下: packagecom.zkn.learnspringboot.web.controller; importorg.springframework.web.bind.annotation.RequestMapping; importorg.springframework.web.bind.annotation.RestController; /** *Createdbyzknon2016/11/19. */ @RestController @RequestMapping("/aop") publicclassAopTestController{ @RequestMapping("/testBeforeService.do") publicStringtestBeforeService(Stringkey,Stringvalue){ return"key="+key+"value="+value; } @RequestMapping("/testAfterReturning.do") publicStringtestAfterReturning(Stringkey){ return"key=:"+key; } @RequestMapping("/testAfterReturning01.do") publicIntegertestAfterReturning01(Integerkey){ returnkey; } @RequestMapping("/testAfterThrowing.do") publicStringtestAfterThrowing(Stringkey){ thrownewNullPointerException(); } @RequestMapping("/testAfter.do") publicStringtestAfter(Stringkey){ thrownewNullPointerException(); } @RequestMapping("/testAfter02.do") publicStringtestAfter02(Stringkey){ returnkey; } @RequestMapping("/testAroundService.do") publicStringtestAroundService(Stringkey){ return"环绕通知:"+key; } } 本文转自:http://lib.csdn.net/article/microservice/60453?knId=1899

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

MyEclipse离线集成Maven插件

MyEclipse自带有Maven4MyEclipse插件,如果没有可以在线和离线安装的方式,在线安装由于网络问题或者插件下载地址的问题导致安装失败,现在给大家介绍离线安装的方式。 1、Maven插件下载地址:http://pan.baidu.com/s/1mi94Efe 2、在MyEclipse 安装目录下新建maven文件夹,比如我的安装目录为:C:\MyEclipseSpr10.6\MyEclipse for Spring 10\dropins\目录下新建maven文件夹,然后解压下载到Maven插件,把目录下的features文件夹和plugins文件夹拷贝到新建的maven目录下。 3、在MyEclipse安装目录下的dropins目录新建txt文本,并重命名为maven.link。 4、编辑maven.link文件,输入path=C:\MyEclipseSpr10.6\MyEclipse for Spring 10\dropins\maven。 5、重启MyEclipse,查看MyEclipse工具栏Window/Preferences下就会出现Maven的选项,到此为止你已成功安装maven插件。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

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

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册