java 微服务框架 redkale 入门介绍-亚博电竞手机版
redkale 功能
redkale虽然只有1.xm大小,但是麻雀虽小五脏俱全。既可作为服务器使用,也可当工具包使用。作为独立的工具包提供以下功能:
1、convert包提供json的序列化和反序列化功能,类似gson、jackson。
2、convert包提供java对象二进制的序列化和反序列化功能,类似protobuf。
3、source包提供很简便的数据库操作功能,类似jpa、hibernate。
4、net包提供tcp/udp服务功能, 类似mina。
5、net.http提供http服务, 类似tomcat、netty。
6、resourcefactory提供轻量级的依赖注入功能, 类似google guice。
redkale 服务器
redkale作为服务器的目录如下:
bin : 存放启动关闭脚本(start.sh、shutdown.sh、start.bat、shutdown.bat)
conf : 存放服务器所需配置文件:
application.xml: 服务配置文件 (必需);
logging.properties:日志配置文件 (可选);
persistence.xml:数据库配置文件 (可选);
lib : 存放服务所依赖的第三方包,redkale.jar 放在此处。
logs : logging.properties 配置中默认的日志存放目录。
root : application.xml 配置中http服务所需页面的默认根目录。
redkale启动的流程如下:
1、加载 application.xml 并解析。
2、初始化
3、解析所有的
4、初始化并启动所有
5、初始化单个server:
5.1、扫描classpath加载所有可用的service实现类(没有标记为@autoload(false)的类)并实例化,然后相互依赖注入。
5.2、service实例在依赖注入过程中加载所需的datasource、cachesource资源。
5.3、调用所有本地模式service的init方法。
5.4、扫描classpath加载所有可用的servlet实现类(没有标记为@autoload(false)的类)并实例化 (优先实例化websocketservlet)。
5.5、给所有servlet依赖注入所需的service。
5.6、调用所有servlet的init方法。
5.7、启动server的服务监听。
6、启动进程本身的监听服务。
基于redkale的开发与调试
基于redkale创建一个java应用程序工程(即使是web项目也不要创建java-web工程),引用redkale.jar 并创建redkale所需的几个目录和文件。一个普通的web项目只需要编写业务层的service和接入层的httpservlet的代码。数据库datasource通过配置文件进行设置。
编写完代码可以通过启动脚本进行调试, 也可以在ide设置项目的主类为 org.redkale.boot.application 或者工程内定义主类进行启动调试:
public final class bootstrap { public static void main(string[] args) throws exception { org.redkale.boot.application.main(args); } }
若需要调试单个service,可以通过 application.singleton 方法进行调试:
public static void main(string[] args) throws exception { userservice service = application.singleton(userservice.class); loginbean bean = new loginbean(); bean.setaccount("myaccount"); bean.setpassword("123456"); system.out.println(service.login(bean)); }
application.singleton 运行流程与通过bin脚本启动的流程基本一致,区别在于singleton运行时不会启动server和application自身的服务监听。redkale提倡接入层(servlet)与业务层(service)分开,service在代码上不能依赖于servlet,因此调试service自身逻辑时不需要启动接入层服务(类似websocket依赖servlet的功能除外)。
redkale的依赖注入
redkale内置的依赖注入实现很简单,只有三个类: javax.annotation.resource、org.redkale.util.resourcetype、org.redkale.util.resourcefactory,采用反射技术,由于依赖注入通常不会在频繁的操作中进行,因此性能要求不会很高。其中前两个是注解,resourcefactory是主要操作类,主要提供注册和注入两个接口。resourcefactory的依赖注入不仅提供其他依赖注入框架的常规功能,还能动态的自动更新通过inject注入的资源。
public class aservice { @resource(name = "property.id") private string id; @resource(name = "property.id") //property.开头的资源名允许string自动转换成primitive数值类型 private int intid; @resource(name = "bigint") private biginteger bigint; @resource(name = "seqid") private int seqid; @resource private resourcetest.bservice bservice; @override public string tostring() { return "{id:/"" id "/", intid: " intid ", bigint:" bigint "}"; } /** 以下省略getter setter方法 */ } public class bservice { @resource(name = "property.id") private string id; @resource private aservice aservice; private string name = ""; @java.beans.constructorproperties({"name"}) public bservice(string name) { this.name = name; } @override public string tostring() { return "{name:/"" name "/", id: " id ", aserivce:" aservice "}"; } /** 以下省略getter setter方法 */ } public static void main(string[] args) throws exception { resourcefactory factory = resourcefactory.root(); factory.register("property.id", "2345"); //注入string类型的property.id aservice aservice = new aservice(); bservice bservice = new bservice("eee"); factory.register(aservice); //放进resource池内,默认的资源名name为"" factory.register(bservice); //放进resource池内,默认的资源名name为"" factory.inject(aservice); //给aservice注入id、bservice,bigint没有资源,所以为null factory.inject(bservice); //给bservice注入id、aservice system.out.println(aservice); //输出结果为:{id:"2345", intid:2345, bigint:null, bservice:{name:eee}} system.out.println(bservice); //输出结果为:{name:"eee", id:2345, aserivce:{id:"2345", intid:2345, bigint:null, bservice:{name:eee}}} factory.register("seqid", 200); //放进resource池内, 同时resourcefactory会自动更新aservice的seqid值 system.out.println(factory.find("seqid", int.class)); //输出结果为:200 factory.register("bigint", new biginteger("66666")); //放进resource池内, 同时resourcefactory会自动更新aservice对象的bigint值 system.out.println(aservice); //输出结果为:{id:"2345", intid:2345, bigint:66666, bservice:{name:eee}}可以看出seqid与bigint值都已自动更新 factory.register("property.id", "6789"); //更新resource池内的id资源值, 同时resourcefactory会自动更新aservice、bservice的id值 system.out.println(aservice); //输出结果为:{id:"6789", intid:6789, bigint:66666, bservice:{name:eee}} system.out.println(bservice); //输出结果为:{name:"eee", id:6789, aserivce:{id:"6789", intid:6789, bigint:66666, bservice:{name:eee}}} bservice = new bservice("ffff"); factory.register(bservice); //更新resource池内name=""的bservice资源, 同时resourcefactory会自动更新aservice的bservice对象 factory.inject(bservice); system.out.println(aservice); //输出结果为:{id:"6789", intid: 6789, bigint:66666, bservice:{name:ffff}} }
如上例,通过resourcefactory.inject注入的对象都会自动更新资源的变化,若不想自动更新可以使用带boolean autosync参数的register系列方法(autosync传false)注册新资源。
redkale 架构部署
通常一个系统会分为三层:接入层、业务层、数据层。对应到redkale的组件是: servlet、service、source。大部分系统提供的是http服务,为了方便演示redkale从集中式到分布式的变化,以一个简单的http服务作为范例。
开发一个极简单的小论坛系统。包含三个模块:
用户模块 userserivice: 提供用户注册、登录、更新资料等功能, userservlet作为接入层。
帖子模块 forumserivice: 提供看帖、发帖、删帖等功能, forumservlet作为接入层。
通知模块 notifyserivice: 提供用户操作、回帖等消息通知功能, notifywebsocket是websocket的servlet, 且name为 ws_notify,
作为接入层。
其中数据源有:
datasource: 在persistence.xml里配置的数据库source的name为demodb ,三个模块都需要使用demodb。
cachesource: 仅供userserivice用于存放session的缓存service,name为 usersessions, 且session只存放用户id( int 类型)。
1、单点部署
在早期用户量很少或者开发、调试环境中只需部署一个进程就可满足需求。
如上图,所有模块的httpservlet、service与source数据库操作全部署在一起。 application.xml作简单的配置即可:
2、多点部署
在生产环境需要避免单点问题,一个服务一般会部署多套。在此做个简单的容灾部署,最前端部署一个nginx作反向代理和负载均衡服务器,后面部署两套系统。
如上图,两个进程间的serivce都是本地模式,两者会通过sncp服务保持数据同步,若datasource开启了数据缓存也会自动同步。两套的配置文件相同,配置如下:
3、分层部署
随着业务的复杂度增加,接入层与业务层混在一起会越来越难部署和维护,因此需要进行分层部署。
如上图,对httpservlet与service进行了分离。每个接入层的service都是远程模式,业务层只需提供sncp供远程调用。
接入层中每个进程的配置相同,配置如下:
业务层中每个进程的配置相同,配置如下:
4、微服务部署
当用户量和发帖量增加到上百万的时候,明显地将所有模块的服务部署到一个进程里是不行的。 因此需要将service服务都独立部署形成微服务架构。
如上图,将serivice都独立部署并进行容灾部署,当然如果有需要,servlet之间、source都可以各自分离独立部署。不同类型的service之间都是远程模式调用。
接入层中每个进程的配置相同,配置如下:
用户模块userservice服务群中各个进程的配置相同,配置如下:
通知模块notifyservice服务群中各个进程的配置相同,配置如下:
帖子模块forumservice服务群中各个进程的配置相同,配置如下:
5、api网关式部署
随着用户量到了上千万时,一个userservice的服务进程是无法提供全部用户服务。 因此可以考虑按用户段进行分布式部署。将192.168.50.110、192.168.50.111上的userservice服务改成网关式的服务。下面是以 service本地模式介绍中的userservice 为范例进行编写:
@resourcetype({userservice.class}) public class userservicegateway extends userservice { @resource(name = "userservice_reg") private userservice reguserservice; //只用于注册的服务节点 @resource(name = "userservice_mob") private userservice mobuserservice; //只用于查询手机号码对应的userid的服务节点 @resource(name = "userservice_node01") private userservice userservice01; //userid小于2000000的用户的服务节点 @resource(name = "userservice_node02") private userservice userservice02; //userid小于4000000的用户的服务节点 @resource(name = "userservice_node03") private userservice userservice03; //userid小于6000000的用户的服务节点 @resource(name = "userservice_node04") private userservice userservice04; //userid大于6000000的用户的服务节点 private userservice getservice(int userid) { if (userid <= 200_0000) return userservice01; if (userid <= 400_0000) return userservice02; if (userid <= 600_0000) return userservice03; return userservice04; } @override public userinfo finduserinfo(int userid) { return this.getservice(userid).finduserinfo(userid); } @override public retresultlogin(loginbean bean) { //手机号码用long存储,0表示无手机号码 int userid = mobuserservice.finduserid(bean.getmobile()); if (userid < 1) return new retresult<>(10001, "not found mobile " bean.getmobile()); return this.getservice(userid).login(bean); } @override public void register(userinfo user) { reguserservice.register(user); //会生成userid this.getservice(user.getuserid()).putuserinfo(user); } @override public userinfo updateusername(int userid, string username) { return this.getservice(userid).updateusername(userid, username); } }
从代码看出,userservicegateway继承了userservice, 确保了userservice对外的服务接口不变,上面代码是用户量在600-800万之间的写法,通过简单的用户id分段,根据不同用户id调不同的服务节点。
如上图,网关下的userservice部署分三类: userservice_reg只用于注册用户;userservice_mob提供查询手机号码与用户id间的关系的服务;userservice_node按用户段提供已有用户的服务。且每个userservice的实例在userservicegateway都是远程模式。每种类型可以部署多个节点(为了结构图简单,上图每个类型只部署一个节点)。userservicegateway(192.168.50.110、192.168.50.111)的配置如下:
由以上几种部署方式的范例可以看出,redkale提供了非常强大的架构,集中式到微服务架构不需要增加修改一行代码即可随意切换,即使网关式部署也只是新增很少的代码就可切换,且不影响其他服务。真正可以做到敏捷开发,复杂的系统都可如小系统般快速地开发出来。
为了降低接入层与业务层代码的耦合, 可以将service分接口与实现两个类,接入层只加载接口包、业务层使用实现包。