服务器端接入华为推送遇到的connection reset问题
整个四月份都在做服务器端推送接入的工作,第一期接入的平台只包括小米,魅族,华为。
其中,小米魅族是支持用户全量推送的,不需要客户端上报用户的pushId,接入过程比较方便快速。
华为则比较坑了:
开发文档写的烂;
不支持全量推送,需要客户端上报一个推送一个;
为了解决开发中遇到的一些问题,专门加了华为官方的开发者群,但是提的问题基本上没人回答,导致开发进度一度暂停。
通过其他渠道解决了开发中遇到的问题后,就把项目部署到测试服务器了,但是测试服务器上,项目并不像在本地开发时一样良好运行,在调用推送接口时出现了这个错误。
在华为的群里问了官方,但是没人理,其他开发者也没有遇到这种情况,只有自己找出原因了。
测试服务器与本地开发环境主要的不同有两点:
①测试服务器在日本,而我在中国大陆
②测试服务器是linux,我的电脑是Windows
接下来就是使用排除法排除了。
刚好公司在阿里云上还有一台闲置的linux服务器,在简单搭建环境后,我把项目部署到了这台阿里云服务器上,发现可以正常推送。
可以看出,同是linux服务器,中国区的ip访问华为推送后台是没有问题的,但是日本东京的ip不可以。那就应该是华为的后台限制了日本的ip访问了。
不过这个需要华为官方的确认。于是我找到产品经理,产品经理通过公司运营那边的关系,终于得到了华为的回复:不支持日本。
那就很尴尬了,毕竟公司的测试服以及以后生产服务器都在东京。
和其他同事讨论这个情况后,他们提出了一种解决办法:在国内搭建一个转发的服务,华为推送请求先从日本发到这个转发服务,通过转发服务发到华为后台,华为的响应也是按照这个路径处理。
在仔细考虑可行性后,认为可以这么处理,于是开干。
这个中转服务器要做的非常简单,就是原封不动的转发我们服务器的请求及华为服务器的响应。一个action类就可以满足了,具体代码如下:
@Controller @RequestMapping(value = "push") public class PushCtrl { private Logger logger = LoggerFactory.getLogger(this.getClass()); private static String apiUrl = "https://api.push.hicloud.com/pushsend.do"; // 应用级消息下发API @ResponseBody @RequestMapping(value = "doTransfer",method=RequestMethod.POST) public void doTransfer(HttpServletRequest request,HttpServletResponse httpServletResponse) throws Exception { byte[] data = FileCopyUtils.copyToByteArray(request.getInputStream()); String jsonData = new String(data, "UTF-8"); JSONObject jo = JSONObject.fromObject(new String(request.getParameter("nsp_ctx"))); String appId = (String)jo.get("appId"); String postUrl = apiUrl + "?nsp_ctx=" + URLEncoder.encode("{\"ver\":\"1\", \"appId\":\"" + appId + "\"}", "UTF-8"); String response = httpPost(postUrl, jsonData, 50000, 50000); httpServletResponse.setContentType("text/plain;charset=utf-8"); PrintWriter responseStream = httpServletResponse.getWriter(); responseStream.println(response); responseStream.close(); } @ResponseBody @RequestMapping(value = "test",method=RequestMethod.GET) public ModelMap test(ModelMap m) { m.put("key", "value"); return m; } public String httpPost(String httpUrl, String data, int connectTimeout, int readTimeout) throws Exception { OutputStream outPut = null; HttpURLConnection urlConnection = null; InputStream in = null; try { URL url = new URL(httpUrl); urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setRequestMethod("POST"); urlConnection.setDoOutput(true); urlConnection.setDoInput(true); urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); urlConnection.setConnectTimeout(connectTimeout); urlConnection.setReadTimeout(readTimeout); urlConnection.connect(); // POST data outPut = urlConnection.getOutputStream(); outPut.write(data.getBytes("UTF-8")); outPut.flush(); // read response if (urlConnection.getResponseCode() < 400) { in = urlConnection.getInputStream(); } else { in = urlConnection.getErrorStream(); } List<String> lines = IOUtils.readLines(in, urlConnection.getContentEncoding()); StringBuffer strBuf = new StringBuffer(); for (String line : lines) { strBuf.append(line); } logger.info(strBuf.toString()); return strBuf.toString(); } finally { IOUtils.closeQuietly(outPut); IOUtils.closeQuietly(in); if (urlConnection != null) { urlConnection.disconnect(); } } } }
接下来,就是部署,测试,竟然真的可行。那这个问题就算是解决了。