|
2013年3月17日
现在做互联网产品的团队都比较小,也可能没有特别多运维人员。因此特别需要用一些系统或是工具来监控服务器或者是服务是否正常。之前比较直接的做法是自己搭建一套开源的监控系统,现在随着云服务器的流行,也有越来越多的人用户会使用云端的监控平台。
从我的经验来看,云服务器监控是有些特别的好处的:
1.自建的监控平台,有一部分问题是没办法发出警报。比如,一般监控服务器也会在内部网络中,如果出现外部网络问题,监控服务是没办法通知到相关人员(因为发邮件或者是发短信的通路也会出现问题)。
2.自建的监控平台,只能检测到一个点的访问情况。阿里云监控平台可以检测杭州和青岛两个节点(这是我的帐号看到的),可以比较有效地了解非监测点的一个访问情况。
3.云监控平台都有一套不错的管理界面,可有效减少部署维护和使用成本。
下面我介绍一下,我使用过的一些云监控平台,并对他们的优点和缺点进行比较。
阿里云监控
阿里的云服务器在市场中是做的很不错的,我的很多朋友都购买了阿里的云服务器。以阿里的公司实力,做一款云监控的产品应该不会差到哪里去。
优点:
1.产品体验好,进入住界面就能轻而易举找到你想要的功能。
2.监控功能全,包括站点监控、服务器监控和自定义监控。
3.多点监控,可以在全国提供几个点的监控。从用户的角度了解服务是否可用。
4.站点监控功能也很全面,包含SMTP、POP3、FTP监控。
5.免费,且监控站点数没限制。
缺点:
1.服务器监控和自定义监控,要求是云服务器,如果不是云服务器,只能使用站点检测功能。
360网站服务监控
360的个人用户产品很少使用,但是360的企业产品还真有不少做的不错的。说说360监控的优缺点。
优点:
1.提供服务器监控。可以监控到服务器的磁盘,CPU,内存等情况。
2.UI和告警都设计的不错。
3.免费。
缺点:
1.需要在服务器上开SNMP协议。(不过360提供很多脚本工具,可以一键安装)
2.有20台服务器的限制。(这个基本上够用,超过20台服务器,可以购买服务了)
监控宝
名字很专一,一看就知道是专业做监控的。
优点:
1.专注监控,界面设计还算可以。
2.监控功能全面,内网采集的方式较多。可以采集数据库数据。
缺点:
1.免费用户,服务器和网站监控都有限制,而且数量极少2台服务器监控,5台网站监控。
作者简介: qiyadeng(www.qiyadeng.com)对互联网技术、运营及市场领域有浓厚的兴趣,喜爱思考、阅读、讨论;擅长Java开发及分布式技术。现专注于互联网的创新产品– 老来宝(http://www.laolaibao.com),帮助年轻用户获得补充养老金,并提供养老金增值产品。
比较少参加这类大型的会议,进到会场的第一眼,发现会议室已经全部坐满,后来主办方发现站的人太多了,找来了一些小板凳,我快速找到一个小板凳坐下。坐下开始认真听,非常开心地听到广告时间结束,和我计划的时间完美一致。
回顾一下我比较关系的几个主题
基于用户画像的大数据实例
演讲嘉宾是联通沃商店的大数据技术经理,该大数据实例主要是通过联通营运商的数据和沃商店进行分析,通过绘制用户画像的形式,在其他应用场景,如广告、游戏下载中为用户推荐用户喜欢的产品。可以看出来嘉宾技术实例及基础功是十分不错,至少是一个硕士毕业。近些年被大家挂在嘴边的机器学习算法、推荐算法、语义分析都有部分介绍,实在是接受不过来;比较熟悉的还是我们当初硕士的专业方向推荐算法,看到了简单的介绍觉得很亲切。不过后来提问环节看,现场还是很多高人,有不少是做这个领域的。不过归根是国有企业和类似研究机构,是否能产生非常大的价值,我表示怀疑,不过这些算法一罗列,对经费的分配还是很有好处的。
电商系统的心得分享
这又是一个国有企业,号称是线上卖大力丸的人(国药1健康)。从技术成长为总经理,有很多心得体会。感觉和我有那么一点像,有一些体会也迫不及待的分析给这些年轻的IT从业者,为人严肃,总是会把困难估计的充分一点(估计年轻也没少教学费)。演讲中说了构建系统中的四个原则
权限独立,相互制约
非常务实的看到某些大型企业的,部门斗争。从系统层面开始设计制约(这个应该非常符合老板心意)。这个对很多小型企业在成才过程中是非常有帮助的。
设计流程 减少犯错
在电商行业非常清楚客服和仓库的员工流动性,以及普遍受教育程度偏低,通过流程设计,而不是提高对用人的要求。这也是非常务实的方法。回顾之前在系统层面独自设计支持中央预订系统,设计出来的自动传真(当时网络不如现在易得)及新订单提醒(感谢施总的支持,增加音响进行声音)等等,简直觉得找到了知音。
多了解一些财务知识。
谈到的两点是数据之间需要有勾稽关系和不能修改历史数据,很骄傲我对财务的理解还是不错,从未犯过这种不靠谱的错误。
跨平台大型在线客服系统的技术构架
嘉宾谈了的是一套客服系统,比较多的关键字是客服妹子,可以看出IT从业者苦中作乐的精神。给我的体会是,客服系统都可以做成这样。从一个项目到一个产品,在云计算的世界,可以好一个客服的组件,也是有很大的价值。和我的理想事业很接近,可以花上一生中最精华的时间,做好一个有价值的小众专业的行业。
阿里分布式数据库服务实践
阿里的人就是高调,上场就调戏京东双11前系统崩溃。我也经历过很多系统崩溃,简直是开发人员的噩梦,也是IT人员信用受损的严重事件(因此我一直比较注意防止崩溃及崩溃后的快速恢复)。回到分布式数据库,这个是收获最大的一个演讲。虽然这个演讲看上去是再给阿里云的DRDS做宣传,但是嘉宾演讲的很进行,深入浅出地介绍了分布式数据库和单机数据库的区别。对分布式事务的重新认识是一个很大的收获,以前一直把教程中的数据库原理中的事务定义,作为分布式事务需要解决的问题,其实不是。需要更加务实,在淘宝阿里这类订单处理系统中,有一类对分布式事务的模式(异步消息机制);在其他领域会有其他模式分布式的事务模型,这些分布式模型肯定都不满足单机的事务模型,但是可以满足和解决相应领域的问题。
平台架构的服务器监控
一个APP的监控模型,猜测项目立项的原因,有两个。一个是和竞争对手的数据比较(UPYUN的对手主要是七牛),一个其实可以真正从用户的角度看,用户的体检速度如何,以及影响用户体验速度的真实原因。目前一般行业还不会做的这么细,因为UPYUN是技术支持公司,因此一定需要用这些数据去说服和支持用户。我们现在做的比较多的服务器的可用性、性能和应用的可用性、性能监控。前端时间刚好再比较,发现互联网上有不少好的监控平台,一般的创业公司,可以无需自己搭建监控平台,接入到相应的监控平台即可。下次再开文进行讨论。
作者简介:qiyadeng(www.qiyadeng.com)对互联网技术、运营及市场领域有浓厚的兴趣,喜爱思考、阅读、讨论;擅长Java开发及分布式技术。现专注于互联网的创新产品--老来宝(http://www.laolaibao.com),立志于帮助广大凤凰(diao)男(si)提供补充养老金管理平台。
摘要: guava是Java的一个扩展类库,在google的许多项目中使用过了,现在最为一个 开源的Java类库广泛使用(http://code.google.com/p/guava-libraries/)。
guava类库扩展的主要是这些相关类:collections(集合类),concurrency(并发),primitives,reflection(反射),comparison,I/O,hashi... 阅读全文
简单介绍一下8个Java牛人,他们为Java社区,创建了框架(framework),产品或者是写书,影响甚至改变了Java开发的方法(根据个人喜好排序)。 8.Tomcat创始人 James Duncan Davidson,是当时Sun公司的软件工程师(1997-2001),创建了Java的Web服务器Tomcat,Tomcat广泛应用于Java Web开发的各个领域。 7.测试驱动开发JUnit创始人 Kent Beck,极限编程和测试驱动开发方法的缔造者。此外,他还创造了JUnit,JUnit目前一次成为Java开发测试的事实标准。基于测试驱动的开发方法和JUnit给Java开发的方法带了巨大的变化。 6.Java Collections框架设计者 Joshua Bloch,领导设计了Java平台的许多功能,包括Java 5.0 版本中饱受赞誉的Java Collections框架。2004年他离开Sun公司,成为Google的首席Java架构师,此外他的著作“Effective Java”基本上是学习Java的必读之书。 5.JBoss创始人 Marc Fleury,在2001年创造了JBoss,JBoss是一个Java开源的应用服务器,也已经成为Java Web应用部署中的事实标准。后来他把JBoss买给了RedHat,之后继续从事JBoss的开发工作。不过2007年他离开了RedHat去追求他的个人爱好。 4.Struts创始人 Craig Mcclanahan,创建了Struts,一个流行的基于Java的MVC开源框架,基本上很多Java开发者都知道如何开发Struts的应用程序。 3.Spring创始人 Rod Johnson,Spring框架的创始人,Spring Source的CEO。Spring是一个非常流行的Java应用程序开发的开源框架。此外,他的著作Expert One-to-One J2EE Design and Development,是J2EE最有影响力的一本书。 2.Hibernate创始人 Gavin King,Hibernate的创始人,一个流行的Java ORM解决方案;同时他也是Seam的创始人,此外他为EJB3.0和JPA也做出了突出的贡献。 1.Java之父 James Gosling,1994年发明了Java语言,他创建了Java编译器和虚拟机。在2010年,当Oracle收购Sun公司时,他离开了Sun公司。 原创文章,作者:qiyadeng,转载请注明: 转载自http://www.qiyadeng.com/ 本文链接地址: 你应该知道的8个Java牛人
1.申请开发者帐号 首先注册百度的帐号,然后申请成为百度开发者(需要通过手机进行身份证验证)。 2.新建应用 点击菜单中的创建应用,我们目前选择的是Web应用。 应用创建之后,选择左边菜单的云环境,环境类型需要选择JAVA。 并新创建一个版本,输入1作为版本号 3.在百度集成开发环境中开始开发 百度提供了基于Eclipse的插件,由于该插件不能支持最新的Eclipse版本。建议下载百度的一键安装版本。百度文档中介绍了如何使用开发环境,详细请看集成开发环境使用。 打开百度集成开发环境,在Eclipse左下角点击Login to Baidu,使用你的账号登陆。然后点击工具栏中百度Logo,选择Import BAE Project,填入application和version 之后选择Java作为Project Language。 4.解决项目错误 刚导入的BAE project,在Eclipse中会报错。通过problems view可以看到是因为JRE环境配置不正确和Web运行环境设置不正确。 A.右键项目属性--选择JavaBuildPath,在Libraries中选择Add Library,之后再选择JRE System Library。 B.接下来把Java project转换为Java Web Project(Eclipse中Java Project转换为Java web Project),注意如果你的tomcat是6版本的话,请注意选择Dynamic web Module的版本不超过2.5。 设置Web应用的运行环境,在servers view中新建一个tomcat服务器。 C.和A类似,在Java Build Path中加入 Server Runtime,选择Tomcat。 D.修改hello.jsp,在hello.jsp中加入如下代码 <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
E.把项目部署到tomcat中。
至此项目错误全部解决,应该可以看到运行结果。
5.新建Servert测试
新建一个Servlet,HomeServlet,Eclipse会自动在web.xml中加入配置信息,HomeServet.java和web.xml的部分代码如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); }
/** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("<h1>BAE Servlet Test.</h1>"); }
web.xml中部分代码
<servlet> <description></description> <display-name>HomeServlet</display-name> <servlet-name>HomeServlet</servlet-name> <servlet-class>com.qiyadeng.HomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HomeServlet</servlet-name> <url-pattern>/HomeServlet</url-pattern> </servlet-mapping> 运行tomcat,可以看到如下运行成功,这样你就可以像一般的Java Web Project一样进行开发。
6.最后
BAE中Java环境中百度使用的Jetty,而不是tomcat,Jetty的好处是不需要频繁的重启,修改的代码即时就可看到运行结果。
通过SVN提交代码到BAE,如果有需要做小的修改,可以通过百度的在线编辑工具直接修改。
原创文章,转载请注明: 转载自http://www.qiyadeng.com/
本文链接地址: 百度开发者中心BAE新建Java应用
1.申请开发者帐号 首先注册百度的帐号,然后申请成为百度开发者(需要通过手机进行身份证验证)。 2.新建应用 点击菜单中的创建应用,我们目前选择的是Web应用。 应用创建之后,选择左边菜单的云环境,环境类型需要选择JAVA。 并新创建一个版本,输入1作为版本号 3.在百度集成开发环境中开始开发 百度提供了基于Eclipse的插件,由于该插件不能支持最新的Eclipse版本。建议下载百度的一键安装版本。百度文档中介绍了如何使用开发环境,详细请看集成开发环境使用。 打开百度集成开发环境,在Eclipse左下角点击Login to Baidu,使用你的账号登陆。然后点击工具栏中百度Logo,选择Import BAE Project,填入application和version 之后选择Java作为Project Language。 4.解决项目错误 刚导入的BAE project,在Eclipse中会报错。通过problems view可以看到是因为JRE环境配置不正确和Web运行环境设置不正确。 A.右键项目属性--选择JavaBuildPath,在Libraries中选择Add Library,之后再选择JRE System Library。 B.接下来把Java project转换为Java Web Project(Eclipse中Java Project转换为Java web Project),注意如果你的tomcat是6版本的话,请注意选择Dynamic web Module的版本不超过2.5。 设置Web应用的运行环境,在servers view中新建一个tomcat服务器。 C.和A类似,在Java Build Path中加入 Server Runtime,选择Tomcat。 D.修改hello.jsp,在hello.jsp中加入如下代码 <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
E.把项目部署到tomcat中。
至此项目错误全部解决,应该可以看到运行结果。
5.新建Servert测试
新建一个Servlet,HomeServlet,Eclipse会自动在web.xml中加入配置信息,HomeServet.java和web.xml的部分代码如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); }
/** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("<h1>BAE Servlet Test.</h1>"); }
web.xml中部分代码
<servlet> <description></description> <display-name>HomeServlet</display-name> <servlet-name>HomeServlet</servlet-name> <servlet-class>com.qiyadeng.HomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HomeServlet</servlet-name> <url-pattern>/HomeServlet</url-pattern> </servlet-mapping> 运行tomcat,可以看到如下运行成功,这样你就可以像一般的Java Web Project一样进行开发。
6.最后
BAE中Java环境中百度使用的Jetty,而不是tomcat,Jetty的好处是不需要频繁的重启,修改的代码即时就可看到运行结果。
通过SVN提交代码到BAE,如果有需要做小的修改,可以通过百度的在线编辑工具直接修改。
空指针异常(Null Pointer Exception)是我们平时最容易碰到的,也是最令人讨厌的异常。本文介绍如何避免出现空指针异常。 首先我们看如下的示例 private Boolean isFinished(String status) { if (status.equalsIgnoreCase("Finish")) { return Boolean.TRUE; } else { return Boolean.FALSE; } } 如果status的值为空的话,那么将会出现空指针异常(本例第2行)。所以我们应该使用如下的方法
private Boolean isFinished(String status) { if ("Finish".equalsIgnoreCase(status)) { return Boolean.TRUE; } else { return Boolean.FALSE; } } 这样的话,如果status为空,也不会出现空指针异常。相信我们大多数朋友已经知道这样的方法了,如果一个对象可能为null,那么不需要直接调用它的方法。
接下来我将接着提供几种避免空指针的建议。
1.判断Collection是否为空。
2.使用一些判断方法。
3.assert关键字。
4.Assert类。
5.异常处理。
6.太多的点.操作语法。
7.使用StringUtils类
1.判断Collection是否为空
Collection 为空是指Collection中没有元素。一些开发者如果碰到Collection中没有元素的时候,经常return null,更好的做法是,你应该return Collections.EMPTY_LIST,Collections.EMPTY_SET或者是Collections.EMPTY_MAP.
错误的代码
public static List getEmployees() { List list = null; return list; }
正确的代码
public static List getEmployees() { List list = Collections.EMPTY_LIST; return list; }
2.使用一些判断方法
使用一些方法如contains(),indexOf(),isEmpty(),containsKey(),ContainsValue和hasNext()等来判断,确保不存在空值。
示例:
String myName = "qiyadeng"; List list = Collections.EMPTY_LIST; boolean exist = list.contains(myName); int index = list.indexOf(myName); boolean isEmpty =list.isEmpty(); Map map =Collections.EMPTY_MAP; exist=map.containsKey(myName); exist=map.containsValue(myName); isEmpty=map.isEmpty(); Set set=Collections.EMPTY_SET; exist=set.contains(myName); isEmpty=set.isEmpty(); Iterator iterator; exist = iterator.hasNext();
3.assert关键字
在Java1.4版本之后,提供了断言assert来确定你的代码中的假设。使用的语法如下:
expression1是一个boolean表达式,如果expression1返回的false,系统将会抛出AssertError(没有详细信息)。
另外一种使用方法
assert expression1:expression2 如果expression1返回false,那么系统将会抛出AssertError,并且详细信息为expression2。
示例:
public static String getManager(String employeeId) { assert (employeeId != null) : "employeeId must be not null"; return "qiyadeng"; } 我使用getManager(null)来调用getManger方法,最后运行的结果是"java.lang.AssertionError:employeedId must be not null"
注意记得使用java选项中加入-enableassertion开启assertion功能。
4.Assert类
Assert类在com.bea.core.repackaged.springframework.util包中,有许多方法可以用于断言。
public static String getManager(String employeeId) { Assert.notNull(employeeId, "employeeId must be not null"); Assert.hasLength(employeeId, "employeeId must has length greater than 0"); return "qiyadeng"; } 当我同样使用getManager(null)来调用getManager方法,将获得信息"java.lang.IllegalArgumentException: employeeId must be not null"。
5.异常处理
使用try catch处理异常或是检查变量是否为空。
public static String getManager(String employeeId) { return null; } 如上代码,我使用下面方法调用
String managerId = getManager("A015"); System.out.println(managerId.toString()); 将会发生"java.lang.NullPointerException",为了处理这个异常,我们应该使用try catch来处理异常或者是检查变量是否为null。
try-catch方法
String managerId = getManager("A015"); try { System.out.println(managerId.toString()); } catch (NullPointerException npe) { //write your code here } 或者是对变量进行检查
String managerId = getManager("A015"); if (managerId != null) { System.out.println(managerId.toString()); } else { //write your code here }
6.不要太多的点.操作语法
一些开发者使用太多的这样的方法来减少代码,但是这个对后面的维护和异常处理都是不太好的。
错误的写法
String attrValue = (String)findViewObject("VO_NAME").getCurrentRow().getAttribute("Attribute_NAME"); 正确的写法
ViewObject vo = findViewObject("VO_NAME"); Row row = vo.getCurrentRow(); String attrValue = (String)row.getAttribute("Attribute_NAME");
7.使用StringUtils类
StringUtil是org.apache.commns.lang包中的类,我们可以使用该类来避免空指针异常。
例如 StringUtils.isEmpty(),StringUtils.isBlank,StringUtils.equals()等等,更多的你可以参考文档。
为了不出现空指针异常,在写代码的过程中需要时刻检查你的代码是否会抛出NullPointerException,如果你没有时间及时调整的话,使用//TODO标记,便于你后面解决问题。
现在经常需要根据用户提供的位置,提供一些和位置相关的信息。有时可以直接确定用户的经度和纬度,有时不一定可以确定用户的经度和纬度信息,用户是 通过输入一些路名、标志性建筑或是商场名等位置,但是我们的数据库可能并没有存法用户可能输入的这些位置信息的经度纬度,这时候可以使用一些地图提供的 API来确定,用户所输入的位置信息的经度和纬度。 我们使用百度地图提供的GeoCoding API实现从位置信息到经度纬度的转换,详细的使用说明可以参考 GeoCoding API。我们这里做一个简单的演示 public String getGeoCode(String query) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = geoCodeRequestUrl(query); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler);//百度返回的经度纬度信息xml logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String geoCodeRequestUrl(String query) throws UnsupportedEncodingException{ String url = WeChatConstant.BASEURL + "geocoder?address=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY + "&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 使用JUnit进行测试 @Test public void testGeoCode() throws Exception { BaiduMapService bms = new BaiduMapService(); String response = bms.getGeoCode("上地十街十号"); BaiduGeoCodeResponse res = BaiduGeoCodeResponse.getBaiduGeoCode(response);//解析xml System.out.println(res.toString()); } 输出的结果 <GeocoderSearchResponse> <status>OK</status> <result> <location> <lat>40.057098</lat> <lng>116.307175</lng> </location> <precise>1</precise> <confidence>80</confidence> <level>道路</level> </result> </GeocoderSearchResponse> BaiduGeoCodeResponse [lat=40.057098, lng=116.307175]
到了一个较陌生的环境,经常会在周边找一些基础设施,比如银行,商场,餐厅等(还有一种更急切的是找厕所)。通过百度提供的地图API,可以在你的应用中简单做到,详情可阅读 Place API。我们以查找周边银行作为示例,需确定的参数至少有三个,要查找的位置的经度和纬度,需要查找的内容的类型或是关键字。 public String getPalace(String query,String lat,String lng) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = palceRequestUrl(query,lat,lng); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler);//位置xml logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String palceRequestUrl(String query,String lat,String lng) throws UnsupportedEncodingException { String url = WeChatConstant.BASEURL + "place/search?query=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY +"&location="+lat+","+lng +"&radius=2000"+"&output=" + WeChatConstant.OUTPUTFORMAT; return url; } Junit测试 @Test public void testGetBaiduPlace() throws Exception{ BaiduMapService bms = new BaiduMapService(); String response = bms.getPalace("银行", "39.915", "116.404"); List<BaiduPlaceResponse> list = BaiduPlaceResponse.getBaiduPlace(response); for(BaiduPlaceResponse res:list){ System.out.println(res.toString()); } } 输出内容(省略部分内容) <?xml version="1.0" encoding="utf-8" ?> <PlaceSearchResponse> <status>OK</status> <results> <result> <name>中国工商银行东长安街支行</name> <location> <lat>39.915891</lat> <lng>116.41867</lng> </location> <address>东城区东长安街1号东方广场西三办公楼1楼</address> <uid>a025683c73033c35a21de987</uid> <detail_url>http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&amp;output=html&amp;source=placeapi</detail_url> <tag>银行,王府井/东单</tag> </result> </results> </PlaceSearchResponse> BaiduPlaceResponse [name=中国工商银行东长安街支行, telephone=null, address=东城区东长安街1号东方广场西三办公楼1楼, lat=39.915891, lng=116.41867, tag=null, detailUrl=http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&output=html&source=placeapi]
位置识别这是实际应用经常应用的消息,特别是很多商家,通过了解用户位置,给用户提供特别的产品或是商场的推荐。其中用户可能发送两种类型的消息: 1.微信地理位置信息 2.路名、标志性建筑或是商场名称 1.微信地理位置消息认识一下,微信地理位置消息,包含一些什么信息 <xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1351776360</CreateTime> <MsgType><![CDATA[location]]></MsgType> <Location_X>23.134521</Location_X> <Location_Y>113.358803</Location_Y> <Scale>20</Scale> <Label><![CDATA[位置信息]]></Label> <MsgId>1234567890123456</MsgId> </xml>
包含的主要信息有经度纬度和Label的位置。可以根据label中描述的位置信息,提供给用户对应的服务。也可根据用户的经度纬度信息,提供你最近的产品或是有地域性的产品。 首先根据微信的地理位置信息,定义WeChatLocationMessage类,并能把Xml转换为WeChatLocationMessage对象 public class WeChatLocationMessage { private String toUserName; private String fromUserName; private String createTime; private String msgType; private String locationx; private String localtiony; private String scale; private String label; private String msgId; public static WeChatLocationMessage getWeChatLocationMessage(String xml){ XStream xstream = new XStream(new DomDriver()); WeChatLocationMessage message = null; xstream.alias("xml", WeChatLocationMessage.class); xstream.aliasField("ToUserName", WeChatLocationMessage.class, "toUserName"); xstream.aliasField("FromUserName", WeChatLocationMessage.class, "fromUserName"); xstream.aliasField("CreateTime", WeChatLocationMessage.class, "createTime"); xstream.aliasField("MsgType", WeChatLocationMessage.class, "msgType"); xstream.aliasField("Location_X", WeChatLocationMessage.class, "locationx"); xstream.aliasField("Location_Y", WeChatLocationMessage.class, "localtiony"); xstream.aliasField("Scale", WeChatLocationMessage.class, "scale"); xstream.aliasField("Label", WeChatLocationMessage.class, "label"); xstream.aliasField("MsgId", WeChatLocationMessage.class, "msgId"); message = (WeChatLocationMessage)xstream.fromXML(xml); return message; } //getter and setter } 本文利用百度的地图API,查找最近的银行做为示例。 public String getPalace(String query,String lat,String lng) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = palceRequestUrl(query,lat,lng); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler); logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String palceRequestUrl(String query,String lat,String lng) throws UnsupportedEncodingException { String url = WeChatConstant.BASEURL + "place/search?query=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY +"&location="+lat+","+lng +"&radius=2000"+"&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 输出的结果 <PlaceSearchResponse> <status>OK</status> <results> <result> <name>中国工商银行东长安街支行</name> <location> <lat>39.915891</lat> <lng>116.41867</lng> </location> <address>东城区东长安街1号东方广场西三办公楼1楼</address> <uid>a025683c73033c35a21de987</uid> <detail_url>http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&amp;output=html&amp;source=placeapi </detail_url> <tag>银行,王府井/东单</tag>
</result> </results> </PlaceSearchResponse> 接下来,把百度地图反映出来的最近位置信息,以图文消息的格式展示给微信用户 public static String getWeChatReplyNewsMessageByBaiduPlace(List<BaiduPlaceResponse> placeList, double lat, double lng,String userName, int size){ WeChatReplyNewsMessage newsMessage = new WeChatReplyNewsMessage(); List<Item> items = new ArrayList<Item>(); StringBuffer strBuf = new StringBuffer(); logger.log(Level.INFO,"placeList count="+placeList.size()); newsMessage.setItems(items); if(placeList.size()>size){ newsMessage.setArticleCount(size); } else{ newsMessage.setArticleCount(placeList.size()); } logger.log(Level.INFO,"article count="+newsMessage.getArticleCount()); newsMessage.setCreateTime(new Date().getTime()+""); newsMessage.setMsgType("news"); newsMessage.setFuncFlag("0"); newsMessage.setToUserName(userName); newsMessage.setFromUserName(WeChatConstant.FROMUSERNAME); for(int i = 0;i <newsMessage.getArticleCount();i++){ BaiduPlaceResponse place = placeList.get(i); Double distance = GeoUtil.DistanceOfTwoPoints(Double.valueOf(place.getLng()), Double.valueOf(place.getLat()), lng, lat, GaussSphere.Beijing54); Item item = new Item(); item.setTitle(place.getName()+"["+distance+"米]"+"\n"+place.getAddress()+"\n"+place.getTelephone()); item.setPicUrl(""); item.setUrl(place.getDetailUrl()); item.setDescription(""); items.add(item); } logger.log(Level.INFO,"newMessage="+newsMessage.toString()); strBuf = strBuf.append(getWeChatNewsMessage(newsMessage)); return strBuf.toString(); } public static String getWeChatNewsMessage(WeChatReplyNewsMessage newsMessage){ XStream xstream = new XStream(new DomDriver()); xstream.alias("xml", WeChatReplyNewsMessage.class); xstream.aliasField("ToUserName", WeChatReplyNewsMessage.class, "toUserName"); xstream.aliasField("FromUserName", WeChatReplyNewsMessage.class, "fromUserName"); xstream.aliasField("CreateTime", WeChatReplyNewsMessage.class, "createTime"); xstream.aliasField("MsgType", WeChatReplyNewsMessage.class, "msgType"); xstream.aliasField("ArticleCount", WeChatReplyNewsMessage.class, "articleCount"); xstream.aliasField("Content", WeChatReplyNewsMessage.class, "content"); xstream.aliasField("FuncFlag", WeChatReplyNewsMessage.class, "funcFlag"); xstream.aliasField("Articles", WeChatReplyNewsMessage.class, "items"); xstream.alias("item", Item.class); xstream.aliasField("Title", Item.class, "title"); xstream.aliasField("Description", Item.class, "description"); xstream.aliasField("PicUrl", Item.class, "picUrl"); xstream.aliasField("Url", Item.class, "url"); return xstream.toXML(newsMessage); } 2.路名、标志性建筑或是商场名称对路名、标志性建筑等信息,方法还是通过第三方地图信息,确定输入的位置信息的经度纬度。 本文使用百度地图API,确定所查找的位置的经度和纬度。 public String getGeoCode(String query) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = geoCodeRequestUrl(query); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler); logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String geoCodeRequestUrl(String query) throws UnsupportedEncodingException{ String url = WeChatConstant.BASEURL + "geocoder?address=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY + "&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 确定了经度和纬度,问题就变成和第1种消息类型一致了,根据经度纬度去做相应处理。 3.源代码本文的代码较长,提供源代码下载。 WeChatDemo下载
本文介绍,如果把Java Project转换为Java Web Project,应该在多数的Eclipse的版本都类似。 1.Java Project一个Java Projec,在Eclipse中显示的是一个“J”的蓝色文件夹。 2.Project Facets右键项目属性Properties,右侧选择菜单Project Facets,点击converted to faceted form... 勾选dynamic web module 选择下面的further configuration available 项目中的Web目录和设置保持一致。 3.Java Web Project这样你就转换到了Java Web Project。
1.新建Project 新建Java Project,并把mongo-java-driver驱动加入到项目bulid path中,如果你使用的是maven增加依赖。 <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>2.10.1</version> </dependency> 2.连接上MongoDB //>2.10版本 MongoClient mongo = new MongoClient( "localhost" , 27017 );
//老版本 Mongo mongo = new Mongo("localhost", 27017); 如果需要验证,需要输入用户名和密码 MongoClient mongoClient = new MongoClient(); DB db = mongoClient.getDB("database name"); boolean auth = db.authenticate("username", "password".toCharArray()); 3.MongoDB数据库 得到MongoDB中的数据库,如果数据库名不存在,MongoDB会自动创建 DB db = mongo.getDB("database name"); 显示所有的数据库 List<String> dbs = mongo.getDatabaseNames(); for(String db : dbs){ System.out.println(db); } 4.MongoDB Collection(MongoDB表) 得到数据库中的表 DB db = mongo.getDB("testdb"); DBCollection table = db.getCollection("user"); 显示数据库中的所有表 DB db = mongo.getDB("testdb"); Set<String> tables = db.getCollectionNames(); for(String coll : tables){ System.out.println(coll); } 5.插入、查找、更新、删除操作 插入数据,向Collection(表)中插入一个Document DBCollection table = db.getCollection("user"); BasicDBObject document = new BasicDBObject(); document.put("name", "qiyadeng"); document.put("age", 30); document.put("createdDate", new Date()); table.insert(document); 更新Document中的name="qiyadeng.com" DBCollection table = db.getCollection("user");
BasicDBObject query = new BasicDBObject(); query.put("name", "qiyadeng");
BasicDBObject newDocument = new BasicDBObject(); newDocument.put("name", "qiyadeng.com");
BasicDBObject updateObj = new BasicDBObject(); updateObj.put("$set", newDocument); table.update(query, updateObj); 从Collection中查找name="qiyadeng.com"的Document DBCollection table = db.getCollection("user"); BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng.com"); DBCursor cursor = table.find(searchQuery); while (cursor.hasNext()) { System.out.println(cursor.next()); } 删除name="qiyadeng"的Document DBCollection table = db.getCollection("user"); BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng.com"); table.remove(searchQuery);
6.完整的例子 package com.qiyadeng.mongodb;
import java.util.Date;
import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.MongoClient;
public class MongoDBSample {
public static void main(String[] args) throws Exception{ /**** Connect to MongoDB ****/ //2.10.0后,使用MongoClient MongoClient mongo = new MongoClient("localhost", 27017); /**** Get database ****/ // if database doesn't exists, MongoDB will create it for you DB db = mongo.getDB("testdb"); /**** Get collection / table from 'testdb' ****/ // if collection doesn't exists, MongoDB will create it for you DBCollection table = db.getCollection("user"); /**** Insert ****/ // create a document to store key and value BasicDBObject document = new BasicDBObject(); document.put("name", "qiyadeng"); document.put("age", 30); document.put("createdDate", new Date()); table.insert(document); /**** Find and display ****/ BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng"); DBCursor cursor = table.find(searchQuery); while (cursor.hasNext()) { System.out.println(cursor.next()); } /**** Update ****/ // search document where name="qiyadeng" and update it with new values BasicDBObject query = new BasicDBObject(); query.put("name", "qiyadeng"); BasicDBObject newDocument = new BasicDBObject(); newDocument.put("name", "qiyadeng.com"); BasicDBObject updateObj = new BasicDBObject(); updateObj.put("$set", newDocument); table.update(query, updateObj); /**** Find and display ****/ BasicDBObject searchQuery2 = new BasicDBObject().append("name", "qiyadeng.com"); DBCursor cursor2 = table.find(searchQuery2); while (cursor2.hasNext()) { System.out.println(cursor2.next()); } } }
输出 { "_id" : { "$oid" : "51444c88874c79654063356b"} , "name" : "qiyadeng" , "age" : 30 , "createdDate" : { "$date" : "2013-03-16T10:42:16.555Z"}} { "_id" : { "$oid" : "51444c88874c79654063356b"} , "age" : 30 , "createdDate" : { "$date" : "2013-03-16T10:42:16.555Z"} , "name" : "qiyadeng.com"}
使用mongo验证创建的数据库testdb,collection user是否存在。
本文介绍如何安装在windows 7中安装MongoDB。 注:MongoDB并不像Windows上安装其他软件,只需要下载Zip包并解压,然后配置数据存放目录并启动即可。 1.下载MongoDB从MongoDB官方网站,根据你的平台选择对应的windows的压缩包并解压,本文解压到D:\mongodb\。
注:如果需要在命令行中快速使用MongoDB bin目录下的命令,可以将D:\mongoDB\bin加入到Window环境变量。 2.配置数据文件在D:\mongodb\创建mongo.config文件,如下(并在d:\mongodb目录下新建data,log文件夹) ##数据存储的位置 dbpath=D:\mongodb\data ##所有的输出位置 logpath=D:\mongodb\log\mongo.log ##日志读写操作 diaglog=3 3.运行MongoDB Server在命令控制行,切换到d:\mongodb\bin目录下,使用命令mongod.exe --config d:\mongdb\mongo.config启动MongoDb Server。 D:\mongodb\bin>mongod.exe --config d:\mongodb\mongo.config all output going to: D:\mongodb\log\mongo.log 4.连接MongoDB新开启一个命令行控制窗口,使用mongo.exe连接MongoDB Server. 5.设置MongoDB为Windows服务在命令行控制窗口,加入--install选项可以把MongoDB安装为Windows服务。 D:\mongodb\bin>mongod.exe --config d:\mongodb\mongo.config 启动MongoDB的命令为:net start MongoDB 停车MongODB的命令为:net stop MongoDB 删除MongoDB的命令为:mongod --remove
1.设置成为开发者模式
登录微信工作平台,选择高级功能-进入开发模式,成为开发者。需要做如下图配置。URL配置的信息是指,微信的后台服务器把您的用户消息发送到该URL处理。Token是你和微信之间的一个密码,用来验证消息是否是从微信的服务发送而来,而不是其他来攻击你的系统。
现在你还不能设置,在设置时微信会GET请求你设置的URL,已检测接口是否可以使用。只有等你准备好GET方法之后才可以进行设置。
2.实现GET方法
从文档中知道,我们需要实现POST和GET方法,GET方法用于验证微信和你的通讯验证,POST用于消息处理。
新建Servlet HelloWeChat,先实现其中的GET方法
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO 为了简单起见,先不对消息来源进行校验
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String echo = request.getParameter("echostr");
echo = new String(echo.getBytes("ISO-8859-1"),"UTF-8");
pw.println(echo);
}
可以在本地使用http://localhost:8080/QiyadengWeb/HelloWeChat?echostr=hello中文,先进行测试,如果没有问题,可以部署到服务器上,然后在微信公众平台进行设置了。
3.实现POST方法
POST方法首先接收到微信公众平台传送过来的XML,从中提取消息发送人和消息内容。更加消息发送内容,你可以增加自己的处理逻辑,最后拼装成回复消息XML,返回给微信公众平台。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8");
WeChatTextMessage textMsg = null;
try {
textMsg = getWeChatTextMessage(wxMsgXml);
} catch (Exception e) {
e.printStackTrace();
}
StringBuffer replyMsg = new StringBuffer();
if(textMsg != null){
//增加你所需要的处理逻辑,这里只是简单重复消息
replyMsg.append("您给我的消息是:");
replyMsg.append(textMsg.getContent());
}
else{
replyMsg.append(":)不是文本的消息,我暂时看不懂");
}
String returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName());
pw.println(returnXml);
}
关于调试,这里推荐一个工具Fiddler,你可以模拟微信的POST消息到你的本地,而不必每次部署到服务器上进行调试。关于Fiddler的POST数据使用方法,可以参考下图标注内容。
4.部署并测试
完成第一步,并和你的公众帐号好进行对话,回复消息没有问题的话,那就恭喜你了。
5.依赖库
使用maven的同学,添加以下依赖即可。非maven用户,找到这些库添加到buider path中即可。
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.3</version>
</dependency>
6.完整的代码
package com.qiyadeng.wechat;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
/**
* Servlet implementation class HelloWeChat
*/
public class HelloWeChat extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public HelloWeChat() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO 为了简单起见,先不对消息来源进行校验
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String echo = request.getParameter("echostr");
echo = new String(echo.getBytes("ISO-8859-1"),"UTF-8");
pw.println(echo);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8");
WeChatTextMessage textMsg = null;
try {
textMsg = getWeChatTextMessage(wxMsgXml);
} catch (Exception e) {
e.printStackTrace();
}
StringBuffer replyMsg = new StringBuffer();
if(textMsg != null){
//增加你所需要的处理逻辑,这里只是简单重复消息
replyMsg.append("您给我的消息是:");
replyMsg.append(textMsg.getContent());
}
else{
replyMsg.append(":)不是文本的消息,我暂时看不懂");
}
String returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName());
pw.println(returnXml);
}
private WeChatTextMessage getWeChatTextMessage(String xml){
XStream xstream = new XStream(new DomDriver());
xstream.alias("xml", WeChatTextMessage.class);
xstream.aliasField("ToUserName", WeChatTextMessage.class, "toUserName");
xstream.aliasField("FromUserName", WeChatTextMessage.class, "fromUserName");
xstream.aliasField("CreateTime", WeChatTextMessage.class, "createTime");
xstream.aliasField("MsgType", WeChatTextMessage.class, "messageType");
xstream.aliasField("Content", WeChatTextMessage.class, "content");
xstream.aliasField("MsgId", WeChatTextMessage.class, "msgId");
WeChatTextMessage wechatTextMessage = (WeChatTextMessage)xstream.fromXML(xml);
return wechatTextMessage;
}
private String getReplyTextMessage(String content, String weChatUser){
WeChatReplyTextMessage we = new WeChatReplyTextMessage();
we.setMessageType("text");
we.setFuncFlag("0");
we.setCreateTime(new Long(new Date().getTime()).toString());
we.setContent(content);
we.setToUserName(weChatUser);
we.setFromUserName("shanghaiweather");//TODO 你的公众帐号微信号
XStream xstream = new XStream(new DomDriver());
xstream.alias("xml", WeChatReplyTextMessage.class);
xstream.aliasField("ToUserName", WeChatReplyTextMessage.class, "toUserName");
xstream.aliasField("FromUserName", WeChatReplyTextMessage.class, "fromUserName");
xstream.aliasField("CreateTime", WeChatReplyTextMessage.class, "createTime");
xstream.aliasField("MsgType", WeChatReplyTextMessage.class, "messageType");
xstream.aliasField("Content", WeChatReplyTextMessage.class, "content");
xstream.aliasField("FuncFlag", WeChatReplyTextMessage.class, "funcFlag");
String xml =xstream.toXML(we);
return xml;
}
}
开始微信公众平台的开发,我们首先要了解微信平台可以帮助我们做哪些事情? 使用您的公众账号登陆http://mp.weixin.qq.com/,选择菜单--高级功能-开发模式--查看文档,即能看到微信公众平台目前所能开发的功能。 一、通讯机制 公众平台的主要内容是 - 接受用户发送给您公众账号的消息
- 给您的用户回复消息
需要特别说明的是,发送消息和回复消失是一个连贯的过程,只能在一个对话中完成。也就是说您的用户不找您说话,您是不能主动发送消息给你的客户(群发是另外一种情况,有次数限制。你也可以申请付费使用微信CRM平台)。所有的发送消息和接受消息,都需要微信平台进行中转。 二、消息类型 下面介绍用户能给您发送的消息类型,也就是目前接受到的消息类型。 1.接受消息类型 1.1文本消息: 这也是我们平时碰到最多的,可以根据文本中提到的一些关键字,进行判断,判断用户的含义,并进行回复。 1.2图片消息: 目前通过图片理解用户想表达的意思,还是有较大难度,因此多数的公众账号,会选择忽略图片信息或选择由人工来处理。只能说一句:图片很美,但是我看不懂。 1.3地理位置消息: 用户把他的位置发给您,这对大多数公众账号来说,是一个重要的信息。可以提供一些基于位置信息的服务,比如酒店预订公众账号,可以给你推荐你周边的酒店。 另外一个补充是,可以在文本消息中分析出位置信息,并加以利用。比如用户输入“南京路步行街”,可以提供用户南京路步行街的相关商户。 1.4链接消息: 目前还没有看到开发模式中特别有效的使用方法。使用比较多的可能会是购物时或是咨询时,对所谈论的对象进行明确。 1.5事件推送消息: 当用户进入到和你对话的过程中,可以先和用户打招呼等。这个消息目前只支持4.5版本,且暂时还没有开发。后续可想想的空间很大,比如用户进入到会话之后,摇一摇会发生什么呢? 2.回复消息类型 2.1文本消息 这是我们平时发送最多的一类消息,当只需要简单的文字即可回答用户的消息时,可用文本消息。文本消息中可以带有链接地址。 2.2图文消息 图文消息,这是我们在推送消息中经常看到的消息格式。每项内容可以点击查看更详细信息(当然你也可以把链接设置为空,使其不能跳转) 2.3音乐消息 在你的答复中给用户一个语音消息或是音乐,可以获得不少用户的亲睐。 了解了公众平台的通讯机制和消息类型,接下来,我们开始准备开发环境了。
|