疯狂创客圈,一个Java 高并发研习社群 【博客园 总入口 】html
疯狂创客圈,倾力推出: 《Netty Zookeeper Redis 高并发实战》一书, 面试必备 + 面试必备 + 面试必备git
你们好,我是做者尼恩。目前和几个小伙伴一块儿,组织了一个高并发的实战社群【疯狂创客圈】。正在开始高并发、亿级流程的 IM 聊天程序 学习和实战github
顺便说明下:
本文的内容只是一个初稿、初稿,本文的知识,在《Netty Zookeeper Redis 高并发实战》一书时,进行大篇幅的完善和更新,而且进行的源码的升级。 博客和书不同,书的内容更加系统化、全面化,更加层层升入、井井有条、更屡次的错误排查,请你们以书的内容为准。
本文的最终内容, 具体请参考疯狂创客圈 倾力编著,机械工业出版社出版的 《Netty Zookeeper Redis 高并发实战》一书 。
web
在疯狂创客圈的 亿级流程的 IM 聊天程序 学习项目中,短链接Web服务器和长链接IM服务器之间,是相互配合的。在分布式集群的环境下,用户首先经过短链接登陆Web服务器。Web服务器在完成用户的帐号/密码验证,返回uid和token时,还须要经过必定策略,获取目标IM服务器的IP地址和端口号列表,返回给客户端。客户端开始链接IM服务器,链接成功后,发送鉴权请求,鉴权成功则受权的长链接正式创建。面试
短链接的调用,使用Feign 技术。下面是详解。api
看完以后,Feign 独立使用,彻底能够替换掉目前的Http 客户端调用方法。服务器
1.1. Feign短链接Restful调用并发
通常来讲,短链接的服务接口,都是基于应用层Http协议的Http api 或者RESTful api实现,经过JSON文本格式返回数据。如何在Java服务端调用其余节点的Http api 或者RESTful api呢?app
至少有如下几种方式:负载均衡
(1)JDK原生的URLConnection
(2)Apache的Http Client/HttpComponents
(3)Netty的异步HTTP Client
(4)Spring的RestTemplate
目前用的最多的,基本上是第二种,这也是在单体服务时代,最为成熟和稳定的方式,也是效率较高的短链接方式。
插入一个解释: 什么是RESTful api。REST的全称是 Representational State Transfer,它是一种API接口的风格、也而不是标准,只是提供了一组调用的原则和约束条件。也就是说,在短链接服务的领域,它算是一种特殊格式的HTTP api。
回到前面的话题,若是同一个Http api/RESTful api 接口,假若不止一个短链接服务器提供,而是有多个节点提供服务,那么,简单的使用Http Client调用,就无能为力了。
Http Client/HttpComponents调用不能根据接口的负载、或者其余的条件,去判断哪个接口应该调用,哪个接口不该该调用。解决这个问题的方式是啥呢?
可使用Feign来调用多个服务器的同一个接口。Feign不只仅能够进行同接口多服务器的负载均衡,一旦使用了Feign做为http api的客户端,调用远程的http接口就会变得像调用本地方法同样简单。
Feign是何方神圣?它是Netflix开发的一个声明式、模板化的HTTP客户端, Feign的目标是帮助Java工程师更快捷、优雅地调用HTTP API//RESTful api。另外,Feign被无缝集成到了SpringCloud微服务框架,使用Feign后,能够很是方便项目SpringCloud微服务技术。
若是项目使用了SpringCloud技术,那就就能够更加方便的声明式使用Feign。若是没有使用SpringCloud,使用Feign也很是之简单。Netflix Feign目前更名为OpenFeign,最新版本是2018.5发布的9.7.0。OpenFeign在Java应用中,负责处理与远程Web服务的请求响应,最大限度下降编码复杂性。能够说是Java应用中调用Web服务的客户端的利器。
下面就看看在单独使用Feign的场景下,是怎么调用远程的http服务。
引入Feign依赖的jar包到pom.xml:
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-core</artifactId> <version>9.7.0</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-gson</artifactId> <version>9.7.0</version> </dependency>
接下来,就能够开始使用Feign来调用远程的Http API了。
前面讲到,高并发的IM系统中,用户的登陆与认证、好友的更新与获取等等一些低频的请求,这些都使用短链接来实现。
做为演示,这里仅仅列举两个短链接的API接口:
(1) http://localhost:8080/user/{userid}
这个接口的功能,是用户获取用户信息,是一个典型的RESTful类型的接口。{userid}是一个占位符,调用的时候,须要替换成用户id。 好比说若是用户id为1,调用的连接为:http://localhost:8080/user/1 。
(2) http://localhost:8080/login/{username}/{password}
这个接口的功能,用户登陆的认证。占位符{username}是表示用户名称,占位符{password}表示用户密码。 好比说若是用户名称为zhangsan,密码为123调用的连接为:http://localhost:8080/login/zhangsan/123 。
上面的接口很简单,仅仅是为了演示,不能用于生产场景。这些API可使用Spring MVC 等常见的WEB技术来实现。
如何经过Feign技术,来调用上面的这些Http API呢?
第一步,须要建立一个本地的API的代理接口。具体以下:
package com.crazymakercircle.imServer.feignClient; import feign.Param; import feign.RequestLine; public interface UserAction { @RequestLine("GET /login/{username}/{password}") public String loginAction( @Param("username") String username, @Param("password") String password); @RequestLine("GET /user/{userid}") public String getById( @Param("userid") Integer userid); }
在代理接口中,为每个远程Http API定义一个方法。
如何将方法对应到远程接口呢?
在方法的前面,加上一个@RequestLine 注解,注明远程Http API的请求地址。这个地址不须要从域名和端口开始,只须要从URI的根目录“/”开始便可。
好比,若是远程Http API的URL为:http://localhost:8080/user/{userid} ,@RequestLine 声明的值,只须要配成 /user/{userid} 便可。
如何给接口传递参数值呢?
在方法的参数前面, 加上一个 @Param 注解便可。 @Param内容为HTTP连接中参数占位符的名称。绑定好以后,实际这个Java接口中的参数值,会替换到@Param注解中的占位符。
好比:因为getById的惟一参数 userid的 @Param注解中,用到的占位符是userid。那么,经过调用 userAction.getById(100) ,那么userid的值100,就会用来替换掉请求连接http://localhost:8080/user/{userid} 中占位符userid,最终获得的请求连接为:http://localhost:8080/user/100。
在完成远程API的本地代理接口的定之后,接下来的工做就是调用本地代理,这个工做也是很是的简单。
仍是以疯狂创客圈的实战项目,获取用户信息、和用户登陆两个API的代理接口的调用为例。
实战的代码以下:
import com.crazymakercircle.imServer.feignClient.UserAction; import feign.Feign; import feign.Request; import feign.Retryer; import feign.codec.StringDecoder; import org.junit.Test; /** * Created by 尼恩 at 疯狂创客圈 */ //@ContextConfiguration( // locations = { "classpath:application.properties" }) //@RunWith(SpringJUnit4ClassRunner.class) //@Configuration //自动加载配置信息 //@EnableAutoConfiguration public class LoginActionTest { /* // 服务器ip地址 @Value("${server.web.user.url}") private String userBase;*/ @Test public void testLogin() { UserAction action = Feign.builder() // .decoder(new GsonDecoder()) .decoder(new StringDecoder()) .options(new Request.Options(1000, 3500)) .retryer(new Retryer.Default(5000, 5000, 3)) .target( UserAction.class, // userBase "http://localhost:8080/" ); String s = action.loginAction( "zhangsan", "zhangsan" ); System.out.println("s = " + s); } @Test public void testGetById() { UserAction action = Feign.builder() // .decoder(new GsonDecoder()) .decoder(new StringDecoder()) .target( UserAction.class, "http://localhost:8080/" ); String s = action.getById(2); System.out.println("s = " + s); } }
主要的也是最为核心的就一步,构建一个远程代理接口的本地实例。使用Feign.builder() 构造器模式方法,带上一票配置方法的链式调用。主要的链式调用的配置方法介绍以下:
(1)target 配置方法
为构造器配置本地的代理接口,和远程的根目录。代理接口类的每个接口方法前@RequestLine 声明的值,最终都会加上这个根目录。这个是最为重要的一个配置方法。代理接口类很重要,最终Feign.builder() 构造器返回的本地代理实例类型,就这个接口。
(2)options配置方法
options方法指定链接超时时长及响应超时时长。
(3)retryer配置方法
retryer方法主要是指定重试策略。
(4)decoder配置方法
decoder方法指定对象解码方式,这里用的是基于String字符串的解码方式。若是须要使用Jackson的解码方式,须要在pom.xml中添加Jackson的依赖。
主要的配置方法,就介绍这些,具体使用和其余的方法,请参见官网。
经过Feign.builder() 构造完成代理实例后,调用远程API,就变成了调用Java函数同样的简单。
建议,若是是独立调用Http服务,尽可能使用Feign。
一是简单,
二是若是采用httpclient或其余相对较重的框架,对初学者来讲编码量与学习曲线都会是一个挑战。
三是,既能够独立使用Feign,又能够方便后续的和Spring Could微服务框架继承。
总之,何乐而不为呢?
下一篇: zookeeper + netty 实现高并发IM 聊天
Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战
疯狂创客圈 【 博客园 总入口 】