spring cloud feign 拦截器配置

问题 公司大规模推全链路,要求所有应用都接入。手头一个新的项目,采用spring cloud框架。通过 feign 调用rest接口。但是支持提供的支持 HttpClient4 的方式无法对 feign 起作用。 思路 想着 feign 也是采用 HttpClient 的。按理对 HttpClient4 起作用的也应该同样有效。然后去看了下架构组全链路配置代码,原来他们是在初始化 HttpCLient 实例时候,加入了拦截器。通过拦截器,在 header 中加入链路信息。但是这个方式似乎对 feign 没啥作用。那我就自己实现添加拦截器呗。 查了下 feign 添加拦截器,网络上提供的方式都是通过自定义 FeignConfiguration 的方式来实现。方法如下: 新建 FeignConfiguration类 public class FeignConfiguration { @Bean public FeignBasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new FeignBasicAuthRequestInterceptor(); } } 然后在配置客户端调用时候加入配置中 @FeignClient(value = "xxx-service", path = "/xxx", configuration = FeignConfiguration.class) 只是这种方法无法做到全局。找了好久,看了文档,都没有办法配置全局。 无奈,去翻源码吧。找到了 FeignAutoConfiguration 类。看到了初始化配置。 protected static class HttpClientFeignConfiguration { @Autowired( required = false ) private HttpClient httpClient; protected HttpClientFeignConfiguration() { } @Bean @ConditionalOnMissingBean({Client....

January 25, 2018

javabean字段文字拼接

问题 接了一个需求,从数据库拿出患者的记录,这些记录都是一些吸烟年份、饮酒年份、是否糖尿病等,不是数字就是布尔值,需要通过代码转化为普通人能理解的文字。 思前想后,总不能使用大量 if-else 判断来拼接字符串吧。可以采用模板,但是那样除了必要判空,代码和模板也将分离,不方便维护。而且有些字段存在依赖关系。 解决 刚好之前看了使用注解来替代枚举的方式。想着是否可以在每个需要拼接的字段上打上注解,通过判断注解的方式拼接。注解中也可以放入模板、依赖关系等配置。 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface StringAssemble { /** * 模板 */ String template(); /** * 分类 */ String type() default ""; /** * 上级依赖 */ String dependency() default ""; /** * 相互依赖的内容之间的分割符号 */ String dependencySplit() default ","; } 上面注解中,templete 主要是字段值需要填入的文字模板,type 用于不同的字段组合,dependency 主要是有依赖关系的字段之间的内容组合,最后一个是有依赖关系的字段字段拼接后用于隔离的标点。 实现代码如下: private static String stringAssemble(Object data, String type, String splitPunctuation) { final Map<String, String> infoMap = new HashMap<>(); final Map<String, String> dependencyMap = new HashMap<>(); try { Field[] fields = data....

January 20, 2018

Dubbo统一异常拦截

问题 最近换了岗位,工作内容改成跟着架构师完善公司的技术架构。第一个任务就是做一个统一的异常拦截并返回相关错误码的方案。 公司采用dubbo做分布式框架,按dubbo的推荐,异常已经直接抛给调用方(consumer),只是公司现有的约定是使用错误码。问了几个前辈后才知道,原来dubbo在处理自定义异常的时候有些限制。 public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { try { Result result = invoker.invoke(invocation); if (result.hasException() && GenericService.class != invoker.getInterface()) { try { Throwable exception = result.getException(); // 如果是checked异常,直接抛出 if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) { return result; } // 在方法签名上有声明,直接抛出 try { Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); Class<?>[] exceptionClassses = method.getExceptionTypes(); for (Class<?> exceptionClass : exceptionClassses) { if (exception.getClass().equals(exceptionClass)) { return result; } } } catch (NoSuchMethodException e) { return result; } // 未在方法签名上定义的异常,在服务器端打印ERROR日志 logger....

October 9, 2017

hexo3搭建博客

前几天心血来潮,翻出了不知道那年的博客,用户hexo重新搭建了下。把这个是记录下来。 安装 这个不多说,hexo 官方文档有介绍。 配置 基本的一些配置上面,上面也有,还有一些我参考了这篇博客,在此先谢过这位朋友的分享。 遇到的问题及解决 CNAME 文件 因为 Github 需要 CNAME 文件,用于指定域名。不过放在 public 文件夹中的话,只要 clean 一次就没了。网站找了下资料,应该将其放在 source 目录总,这样在生成时候就会一起带到 public 文件夹中。一些博文中需要的图片(images)和favicon.ico等不变的内容也可以一起放在 source 目录下。 README.md文件不渲染 在站点配置文件中添加 skip_render: README.md stiemap 的生成 需要安装插件 npm install hexo-generator-sitemap --save 站点配置文件中添加配置 sitemap: path: sitemap.xml 博文永久连接 默认是时间分开。但是这样路径比较长。不方便管理。网上有种方法是urlname。使用urlname来作为链接,避免使用文件名。这样文件名就可以自己定义,方便本地管理。 具体做法是,在站点配置文件中,修改配置如下: permalink: post/:urlname.html 然后在每一篇 post 中都需要加一个urlname,如这篇文章的: title: 20171001-hexo3搭建博客 date: 2017-10-01 03:44:13 tags: hexo urlname: blog-with-hexo3 缺点是,如果那篇忘记了这个,那生成的 html 文件将会是未知 图片保存 hexo官网有推荐的方法,文档。当时我的觉这个太依赖hexo,不符合markdown的理念。所以,还是选择土办法管理图片,放在 source/images 目录下,post中直接用相对路径应用。亲测可行。 博客的版本控制 hexo 的部署功能,只能建 public 文件夹中的内容推送到 Github 上。而配置文件和博客源文件都无法提交。如果换了电脑或电脑换了,那就什么都要从新开始了。...

October 1, 2017

SpringMVC没有拦截根路径的问题

在开发tomcat + spring mvc的web工程时候,发现在controller中配置的根路劲不会被拦截到,或者说前段返回的根本就不是controller中所配置的。比如,我在controller中配置根路径的返回为一个欢迎页面: @RequestMapping(value = "/", method = RequestMethod.GET) public ModelAndView main() { ModelAndView mv = new ModelAndView("main"); return mv; } 但访问“http://localhost:8080/project_name/”,返回的却是404. 在对该单元测试中,对根路径的访问等得到了正确的结果,这证明spring的配置是正确的。于是乎想起来web.xml中的配置可能存在为题,自然最有可能有问题的是web.xml对默认页面的设置了。及welcome-file-list。但是自己并没有配置这个。后来看了tomcat官网对tomcat目录下conf/web.xml的说明才知道,原来此处的web.xml中的配置将被当作默认配置,而部署的工程内的web.xml中的配置会覆盖默认配置,但是如果向我那样没有配置welcome-file-list,那么就会使用默认的了。为此当访问根目录的时候,tomcat试图去寻找默认的index.html等资源,而不是把请求交给spring的拦截器。 知道了这些,解决问的就简单了,直接修改工程下的web.xml,加入如下配置: <welcome-file-list> <welcome-file></welcome-file> </welcome-file-list> 这样就配置后,tomcat就知道不需要对根路径的访问做处理了,而将请求交给了spring的拦截器。

December 2, 2014

Spring4定时任务的注解配置

不想写繁琐的xml文件,所以试着用Spring的注解来配置定时任务。按网上好多写的那样,使用@Scheduled注解: @Scheduled(fixedRate=5000) public void doSomething() { // something that should execute periodically } 但是怎么也无法让定时任务跑起来。最后无奈,只有去官网看英语了。 原来,还需要有一个允许定时任务的注解,放在一个有@configuration注解类中。 @Configuration @EnableAsync @EnableScheduling public class AppConfig { } 其中,@EnableAsysnc是允许异步任务。至于@Configuration的说明,见Spring的最新文档。

November 3, 2014

webpy源码阅读之HelloWorld

使用过Django和web.py,只是一直没有好好深入学习过,Django使用时间比较长,不过Django庞大了点,不适合我这种没耐心的人去学习,所以选择了web.py。 Hello World! 先看下web.py官方cookbook的“Hello World!”例子 import web urls = ("/.*"), "hello") app = web.application(urls, globals()) class hello: def GET(self): return 'Hello, world!' if __name__ == "__main__": app.run() 在例子中可以看到app.run()是启动了服务,而app的是application的一个实例,初始化工作是web.application(urls, globals()),就拿这个做入口,来看下application类的源码吧。 初始化 初始化的流程: 初始化mapping 把_load和_unload变成钩子加入到processors 把Reloader和reload_mapping变成钩子加入到processors 载入main所在模块 init的参数有三个,第一个mapping,上面例子中的urls;第二个fvars,例子中的globals(),globals()是python自带的一个函数,其返回值是全局名字空间,包括了函数、类、导入模块等等,是一个字典类型;第三个autoreload,是否允许自动重新载入。 mapping初始化 self.init_mapping(mapping) def init_mapping(self, mapping): self.mapping = list(utils.group(mapping, 2)) 最后mapping的形式会是[[‘1’,‘2’],[‘1’,‘2’]]这样,这边就可以理解urls列表的样式了。 _load和_unload变成钩子加入到processors 关于钩子和处理器详见官网。 self.add_processor(loadhook(self._load)) self.add_processor(unloadhook(self._unload)) loadhook函数的作用就是在执行其参数传入的函数前执行一些操作,unloadhook则是在其后执行一些操作。这两个都类似于python的decorator。而_load和_unload则是将application实例本身加入web.ctx.app_stack或从其中移除。 如果autoreload不为True则初始化就到此为止了。在初始化的时候,如果autoreload为None,则回去参数读取web.config内的debug的值,默认为False,所以如果autoreload参数没有传入,一般都是不会自动加载,后面操作不会再去进行了。 Reloader和reload_mapping变成钩子加入到processors 和第二步相似,reload_mapping就是用来从新载入主的app,并初始化mapping。而Reloader则是在检查磁盘上任何模块是否有变化,如果有则重新加载。 重新import 通过main获取主app所在的模块名称和所在文件名,然后通过文件名重新import。这步还是简单的,不过代码值得一看,可以学好如sys.modules、getattr()及__import__()的用法。 启动服务 def run(self, *middleware): return wsgi.runwsgi(self.wsgifunc(*middleware)) run函数调用wsgi模块的runwsgi函数,在hello world中就是启动一个http simple服务。具体内容等看到wsgi模块和httpserver模块再说了。

April 3, 2014

推荐程序员阅读的书籍 -- 编程实践

忘记是哪里看到的这份书单了,那时就用思维导图记录了下来,以后相继看了编程实践下的几本书,感觉作者推荐和分类的相当好。编程实践的下书我没有全部阅读过,每个阶段阅读了那么一两本书,在这里简单介绍下那些我阅读过的书。 《编写可读代码的艺术》 这是一本适合刚开始自己的编程生涯的人阅读的书,书的中心内容就是教我们编写出可读性高、整洁、优美的代码,提高代码质量,并把编写更容易理解的代码变成一种习惯。当然,也不是说有经验的程序员就不用看这本书了。我阅读这本书的时候已经不是新手了,不过依然可以从书中学到很多。对于有经验的程序员,很容易忽视一些问题,也更容易养成一些恶习。 《测试驱动开发》 一本很薄的书,如果没有其他干扰,集中注意力的话,一天读完不是问题。书中介绍的形式很特别,作者让我们跟随他,一步一步的去进行测试优先的开发,之后作者还为我们展示了如何使用测试优先的方法一步一步的开发一个类似JUnit的Python单元测试框架。不懂Python语言也没问题,里面的代码很简单,看完后自己的Python入门了。本书还有一个重点,就是作者的任务列表,我现在也在使用这种方法,能在任务中断后继续时帮我快速了解之前所完成的以及接下来需要做的,不至于还要花时间去思考之前做了什么。 《代码大全》 这本书的厚度完全可以和大全两个字联想起来,内容丰富,质量也高,可问题是要看完这么厚的一本书确实难了的。不过还好,作者给出了每个章节适合的人群,根据自己的情况和需要,可以选择性阅读,如果时间多,读读这本也是个好选择。 《程序员修炼之道》 强烈推荐的好书,主要是本人从这本书中受益匪浅。这本书的英文名意思是高效的程序员,所以全书都在讲如何提高自己的效率,从各种习惯到工具再到团队合作,方方面面提高自己的效率。如书的副标题–从小工到专家,等你看完这书,按照书中的方法去实践时,专家可能还远,但是绝对不会再是菜鸟。不管是学生还是富有经验的发开人员都可以去阅读,就算读过一遍,再去随手翻翻也能带来一些感悟。 《计算机程序的构造和解释》 上述的书都是在讲述如何成为好程序员,如何写出好的代码,如何提高自己效率,但这本书就是教我们理解计算机语言和程序本身。这本书曾经是MIT的入门级教材,所以更偏重理论。也许我们学习了不止一门计算机语言,也许我们被那些不同语言的不同特性吸引着或是困恼着,也许我们还只是浅浅的理解了编译器、解析器之类的概念,那么这本书完全可以帮我们换个角度看到编程和语言。它会让我们学到函数式编程、虚拟机、解析器、编译器等等,如果觉得这些太高深了,那么它同样也在讲解语言的组成、基本表达式以及其组合、抽象等等。这本书很适合有一定编程经验的人阅读,可以结合自己的经验学习书中的知识。当然新手也完全可以学习,最大的好处是建立了一套知识体系,对以后学习编程语言和相关内容时候变得更简单高效。不过话说后面,这本书不好消化,看起来费时费脑力。 未提到的书籍 这些书都是具有代表意义的,还有许多经典的书籍值得程序员去阅读。 《编译原理》 (龙书)这类书籍太过高深了,除非向编程语言更深入的发展或是工作领域需要,否则没必要去阅读。 《编程珠玑》 个人感觉值得一读,书中好多都在提如很灵机一动的想出一个好的算法,里面的算法很值得学习,我也在实践中使用过一些。 《算法导论》 里面的算法的确不是一个普通程序员很好理解的,阅读这本书需要高等数据知识,我读了第一章递归求时间复杂度后就放弃了这本书。 《编码:隐匿在计算机软硬件背后的语言》 这本书从小学生之间使用手电筒光来聊天开始,讲到了继电器,使用继电器实现数学逻辑中的各种门,然后利用这些做一个继电器实现的加减法电路,然后…这本书可以让我们明白计算机工作原理,即使没了计算机,我们也可以自己做一台可编程的机械出来。我觉得给我儿子当计算机入门教材不错。 最后说的是自己的提升还是靠自己的努力,书籍只是起引导作用。而且并不是这么多书都要去看,找适合自己的。

February 28, 2014

Win8.1下安装OpenVPN

我在win8.1下安装OpenVPN遇到了两个问题,一是安装时候TAP设备驱动程序安装失败,二是因为系统临时目录带中文而无法正确连接,下面是解决方法。 系统及软件版本 Win8.1 64位系统 OpenVPN 2.3.2 64位 ##一、安装TAP设备驱动程序失败 在安装OpenVPN时候,在最后报了**“An error occurred installing the TAP device driver”**这样的错误。这是说安装TAP驱动时候错误,可以不理会,这个时候OpenVPN其实已经安装完成。后面要做的事就是自己动手来安装TAP驱动了。 不过手动安装虚拟网络设备的时候需要知道失败的原因,解决后才能在安装。在Win7下安装是正常的,而Win8/8.1就不行了,主要是因为系统开启了“驱动程序强制签名”,而在Win8.1下不认OpenVPN的TAP驱动程序签名导致的。这个不太清楚是OpenVPN版本有关还是和系统版本有关系。解决办法就是禁用驱动程序强制签名,步骤如下: 1、按住Shift建不放,然后用鼠标点击右侧的Charm菜单中的重启按钮(注意,这里会重启系统,有文档没有保存的保存下)。 2、在出现的界面中依次选择:疑难解答->高级选项->启动设置->重新启动 3、在重启后的界面上选择“禁用驱动强制签名” 4、用Charm菜单中的搜索功能搜索“add a new TAP virtual ethernet adapter”,以管理员身份运行,看到系统提示后点“始终…”,之后就算不在禁用驱动程序强制签名的模式下也可以正确安装。 ##二、系统临时目录中文问题 我的微软账号中的名字是中文,所以在生成的用户目录也是中文的,在用户目录下的临时文件夹自然在路径上也带了中文,而OpenVPN的GUI程序默认在使用临时文件夹。看来OpenVPN的GUI程序也不支持中文啊。失败后在日志中看到的错误信息就是“–tmp-dir”这个参数指定的目录错误。如果使用命令行的话完全可以通过“–tmp–dir”参数指定其他的,不过那样不方便。 其实自己的client.ovpn文件也同样可以指定参数,在里面加入一行“tmp-dir “e:\temp””(注意,这个路径必须有效),然后再重新打开OpenVPN就可以正常连接。再多说一句,Win7/8/8.1下,需要以管理员身份运行OpenVPN,不然显示连接上了,但是依然无法连接远程服务器。

February 11, 2014

PuTTY下如何使用agent forwarding

公司大部分同事都使用SecureCRT来远程访问服务器,我比较另类,用了个PuTTY。最近公司把访问服务器改成了需要使用SSH Keys来访问,而且还需要通过跳板机才可以。这样一来就需要使用agent forwarding。可问题来了,SecureCRT只需要选择允许agent forwarding就可以了。而putty在“Connection” -> “SSH” -> “Auth”下勾选了“Allow agent forwarding”后,依然无法访问。 谷歌了好久也没法找到问题所在,后来无奈,去看了下putty找了文档,找到了问题所在。原来还需要Pageant一起使用,步骤如下: 一、开启Pageant,(可以在putty官网下载到),Windows下开始后会自动最小化在托盘,找到后双击打开即可。 二、添加Key,在Pageant界面点击Add Key,选择密钥即可。 三、启动PuTTY,在“Connection” -> “SSH” -> “Auth”下勾选了“Allow agent forwarding”。 接下去就可以连接跳板机,然后通过跳板机访问服务器啦。 PuTTY在这边不是一款很大众的软件,使用起来遇到问题自然比较难找到办法解决,而且他的文档还都是英文。所以把这些记下来,方便自己以后忘记了后还能再找到,也方便其他和我一样的人。

January 17, 2014