Consul-Proxy:使用netty实现快速服务注册
使用mybatis进行数据库操作
一、背景
Springcloud+consul作为微服务的注册已经见怪不怪了,试下也很流行,在我个人云服务器上,我也是这样做的。
然而,我的云服务器内存比较小,很快内存就被cloud全家桶吃光了,没办法部署其他应用了,因此,我觉得将一些服务独立出去,放弃cloud全家桶。
Consul-proxy使用netty+consul实现服务注册,并提供了若干简单的注解实现了http的mapping映射处理。
简单来说,没错,是因为穷,才有了这个组件。
二、Maven配置
要使用consul-proxy,只需要加入下面依赖即可。
<dependency>
<groupId>cn.pomit</groupId>
<artifactId>consul-proxy</artifactId>
<version>1.3</version>
</dependency>
但是要完整的运行,还是需要其他依赖的,比如netty和json相关的jar包。
如果想使用mybatis连接数据库,还需要引入mybatis,这里使用了mybatis-proxy工具,mybatis-proxy工具对mybatis做了简单的封装,方便在非spring环境下使用mybatis。
完整的依赖如下:
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.pomit</groupId>
<artifactId>consul-proxy-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>consul-proxy-demo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<dbcp.version>2.4.0</dbcp.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.40</version>
</dependency>
<dependency>
<groupId>cn.pomit</groupId>
<artifactId>consul-proxy</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>cn.pomit</groupId>
<artifactId>mybatis-proxy</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>${dbcp.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>cn.pomit.consulproxy.ConsulApp</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
以上maven的配置中,除了依赖以外,还有shade插件的配置,方便打包成可执行jar包。
说到这里,需要提一下的是,如果打包后运行报错:java.lang.SecurityException: Invalid signature file digest for Manifest main attributes 异常,可以在<configuration> 节点下增加<filters>即可。
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>cn.pomit.consulproxy.ConsulApp</mainClass>
</transformer>
</transformers>
</configuration>
三、使用方法
3.1 启动类
启动类要加上@EnableServer,指明被管理的handler,每个被管理的handler就相当于spring中的一个controller。
import cn.pomit.consul.ConsulProxyApplication;
import cn.pomit.consul.annotation.EnableServer;
import cn.pomit.consul.annotation.InitConfiguration;
import cn.pomit.consulproxy.config.DataSourceConfiguration;
import cn.pomit.consulproxy.handler.AdviceHandler;
@EnableServer(handler = { AdviceHandler.class })
@EnableMybatis(mapperScan = "cn.pomit.consulproxy.mapper")
public class ConsulApp {
public static void main(String[] args) {
ConsulProxyApplication.run(ConsulApp.class, args);
}
}
3.2 Handler处理类
下面是AdviceHandler类,使用json处理相关数据,并返回json结果。
AdviceHandler:
import java.nio.charset.Charset;
import java.util.List;
import com.alibaba.fastjson.JSONObject;
import cn.pomit.consul.annotation.Mapping;
import cn.pomit.consul.handler.resource.AbstractResourceHandler;
import cn.pomit.consul.http.HttpRequestMessage;
import cn.pomit.consul.http.HttpResponseMessage;
import cn.pomit.consulproxy.domain.advice.FAdviceInfo;
import cn.pomit.consulproxy.dto.ResultCode;
import cn.pomit.consulproxy.dto.ResultModel;
import cn.pomit.consulproxy.dto.advice.AdviceAuthReq;
import cn.pomit.consulproxy.service.advice.FAdviceInfoService;
import cn.pomit.mybatis.util.StringUtil;
public class AdviceHandler extends AbstractResourceHandler {
@Mapping(value = "/advice/submit")
public HttpResponseMessage submit(HttpRequestMessage httpRequestMessage) {
try {
String content = httpRequestMessage.getBody().toString(Charset.defaultCharset());
FAdviceInfo fAdviceInfo = JSONObject.parseObject(content, FAdviceInfo.class);
if (StringUtil.isEmpty(fAdviceInfo.getContent()) || StringUtil.isEmpty(fAdviceInfo.getSummary())) {
return HttpResponseMessage.responeseBody(ResultModel.error("参数错误"));
}
fAdviceInfo.setStatus(1);
FAdviceInfoService fAdviceInfoService = new FAdviceInfoService();
fAdviceInfoService.save(fAdviceInfo);
return HttpResponseMessage.responeseBody(ResultModel.ok());
} catch (Exception e) {
e.printStackTrace();
return HttpResponseMessage.responeseBody(new ResultModel(ResultCode.CODE_00004));
}
}
@Mapping(value = "/advice/list")
public HttpResponseMessage list(HttpRequestMessage httpRequestMessage) {
FAdviceInfoService fAdviceInfoService = new FAdviceInfoService();
List<FAdviceInfo> list = fAdviceInfoService.findByStatus(1);
return HttpResponseMessage.responeseBody(new ResultModel(ResultCode.CODE_00000, list));
}
@Mapping(value = "/advice/auth")
public HttpResponseMessage auth(HttpRequestMessage httpRequestMessage) {
String content = httpRequestMessage.getBody().toString(Charset.defaultCharset());
AdviceAuthReq adviceAuthReq = JSONObject.parseObject(content, AdviceAuthReq.class);
FAdviceInfoService fAdviceInfoService = new FAdviceInfoService();
FAdviceInfo fAdviceInfo = fAdviceInfoService.findById(adviceAuthReq.getId());
if (fAdviceInfo == null)
return HttpResponseMessage.responeseBody(ResultModel.error("留言信息不存在!"));
if (adviceAuthReq.getType() == 0) {
fAdviceInfo.setStatus(0);
fAdviceInfoService.update(fAdviceInfo);
} else {
fAdviceInfoService.deleteById(adviceAuthReq.getId());
}
return HttpResponseMessage.responeseBody(new ResultModel(ResultCode.CODE_00000));
}
}
3.3 Service处理逻辑层
由于不是spring环境,Service每次使用需要自己new一个对象了。但是service使用mybatis的时候,要用ProxyHandlerFactory生成代理,作为属性声明即可,无需每次都new。
FAdviceInfoService:
import java.util.List;
import cn.pomit.consulproxy.domain.advice.FAdviceInfo;
import cn.pomit.consulproxy.mapper.advice.FAdviceInfoDao;
import cn.pomit.mybatis.ProxyHandlerFactory;
public class FAdviceInfoService {
FAdviceInfoDao fAdviceInfoDao = ProxyHandlerFactory.getMapper(FAdviceInfoDao.class);
public void save(FAdviceInfo fAdviceInfo) {
fAdviceInfoDao.save(fAdviceInfo);
}
public void deleteById(Integer id) {
fAdviceInfoDao.deleteById(id);
}
public void update(FAdviceInfo fAdviceInfo) {
fAdviceInfoDao.update(fAdviceInfo);
}
public List<FAdviceInfo> findByStatus(Integer status) {
return fAdviceInfoDao.findByStatus(status);
}
public FAdviceInfo findById(int id) {
return fAdviceInfoDao.findOne(id);
}
}
3.4 Mapper接口
FAdviceInfoDao,常规的mybatis写法:
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import cn.pomit.consulproxy.domain.advice.FAdviceInfo;
@Mapper
public interface FAdviceInfoDao {
@Select({
"<script>",
"select id id,name name,contract contract,",
"summary summary,content content,status status",
"from f_advice_info ",
" where status = #{status, jdbcType=INTEGER}",
"</script>"
})
List<FAdviceInfo> findByStatus(@Param("status") Integer status);
@Insert({"<script>",
"INSERT INTO f_advice_info(name , contract , summary , content , status )",
"values ",
"(#{name},#{contract},#{summary},#{content},#{status})",
"</script>"})
@Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id")
int save(FAdviceInfo fAdviceInfo);
@Update({
"<script>",
" update f_advice_info set",
"<if test='name != null and name != \"\" '> name = #{name, jdbcType=VARCHAR}, </if>",
"<if test='contract != null and contract != \"\" '> contract = #{contract, jdbcType=VARCHAR}, </if>",
"<if test='content != null and content != \"\" '> content = #{content, jdbcType=VARCHAR}, </if>",
"<if test='status != -1 '> status = #{status, jdbcType=INTEGER}, </if>",
"<if test='summary != null and summary != \"\" '> summary = #{summary, jdbcType=VARCHAR} </if>",
" where id=#{id}",
"</script>"
})
int update(FAdviceInfo fAdviceInfo);
@Select({
"<script>",
"select id id,name name,contract contract,",
"summary summary,content content,status status",
"from f_advice_info ",
" where id = #{id, jdbcType=INTEGER}",
"</script>"
})
FAdviceInfo findOne(@Param("id") int id);
@Delete({
"<script>",
" delete from f_advice_info",
" where id=#{id}",
"</script>"
})
int deleteById(@Param("id") Integer id);
}
3.5 过程中使用到的实体
FAdviceInfo:
public class FAdviceInfo {
private int id;
private String name;
private String contract;
private String summary;
private String content;
private Integer status;
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setContract(String contract) {
this.contract = contract;
}
public String getContract() {
return contract;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getSummary() {
return summary;
}
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
}
AdviceAuthReq:
public class AdviceAuthReq {
private Integer id;
private Integer type;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
}
ResultCode:
public enum ResultCode {
/**
* 通用
*/
CODE_00000("00000", "操作成功"),
CODE_00001("00001", "请求失败"),
CODE_00002("00002", "未授权的请求"),
CODE_00003("00003", "非法的参数字段"),
CODE_00004("00004", "异常抛出"),
CODE_00005("00005", "权限不足"),
CODE_00006("00006", "分页limit参数错误"),
CODE_00007("00007", "分页offset参数错误"),
CODE_00009("00009", "未登录或登录状态已失效"),
CODE_00010("00010", "数据已存在"),
CODE_00011("00011", "数据不存在"),
CODE_00012("00012", "参数缺失"),
CODE_00013("00013", "系统维护中"),
CODE_00014("00014", "登录失败"),
CODE_00015("00015", "token失效"),
CODE_00016("00016", "签名错误"),
CODE_99999("99999", "签名无效");
private String code;
private String desc;
ResultCode(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public String getDesc() {
return desc;
}
/**
* 根据code匹配枚举
*
* @param code
* @return
*/
public static ResultCode getResultCodeByCode(String code) {
for (ResultCode resultCode : ResultCode.values()) {
if (code.equals(resultCode.getCode())) {
return resultCode;
}
}
return null;
}
public static ResultCode getResultCodeByDesc(String desc) {
for (ResultCode resultCode : ResultCode.values()) {
if (desc.equals(resultCode.getDesc())) {
return resultCode;
}
}
return null;
}
}
ResultModel :
public class ResultModel {
private String errorCode;
private String message;
private Object remark;
private Object data;
public ResultModel(String errorCode, String message) {
this.errorCode = errorCode;
this.message = message;
}
public ResultModel() {
}
public ResultModel(String errorCode, String message, Object data) {
this.errorCode = errorCode;
this.message = message;
this.data = data;
}
public ResultModel(ResultCode resultCodeEnum, Object data) {
this.errorCode = resultCodeEnum.getCode();
this.message = resultCodeEnum.getDesc();
this.data = data;
}
public ResultModel(ResultCode resultCodeEnum, Object data, Object remark) {
this.errorCode = resultCodeEnum.getCode();
this.message = resultCodeEnum.getDesc();
this.data = data;
this.remark = remark;
}
public ResultModel(ResultCode resultCodeEnum) {
this.errorCode = resultCodeEnum.getCode();
this.message = resultCodeEnum.getDesc();
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static ResultModel ok() {
return new ResultModel(ResultCode.CODE_00000);
}
public static ResultModel ok(Object data) {
return new ResultModel(ResultCode.CODE_00000, data);
}
public static ResultModel unAuth() {
return new ResultModel(ResultCode.CODE_00002);
}
public static ResultModel unAuth(Object data) {
return new ResultModel(ResultCode.CODE_00002, data);
}
public static ResultModel error(String message) {
return new ResultModel(ResultCode.CODE_00001.getCode(), message);
}
public Object getRemark() {
return remark;
}
public void setRemark(Object remark) {
this.remark = remark;
}
}
四、配置文件
4.1 application.properties
在classpath下添加application.properties
spring.profiles.active=loc
application.name=consulProxy
application.port=8999
这个指定了带环境的配置文件可以读取classpath下的application-loc.properties
application-loc.properties:
consul.host=127.0.0.1
consul.port=8500
#consul.instanceId=cunsul-proxy-8888
#consul.scheme=https
#consul.healthCheckUrl=/health
consul.healthCheckPath=/health
#consul.healthCheckInterval=10s
#consul.healthCheckTimeout=100s
consul.preferIpAddress=true
mybatis.configuration=cn.pomit.mybatis.configuration.MybatisConfiguration
#mybatis.mapper.scan=cn.pomit.consulproxy.mapper
mybatis.transation.scan=cn.pomit.consulproxy.service
mybatis.datasource.type=POOLED
mybatis.datasource.driver=com.mysql.jdbc.Driver
mybatis.datasource.url=jdbc:mysql://127.0.0.1:3306/boot?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
mybatis.datasource.username=cff
mybatis.datasource.password=123456
如果不带环境,直接把application-loc.properties的配置写在application.properties也是一样的。
这里面,以mybatis.开发的配置,可以自动加载并配置成mybatis自己定义的数据库连接池,这里面的参数不能多,只能是mybatis要的那几个参数。按照mybatis的config的xml要求的那些参数。
如果我们想自定义数据源,可以参考《Consul-Proxy:使用netty实现快速服务注册(三)使用第三方数据源》一篇。
4.2 log4j.properties
使用log4j,log4j的配置文件写在classpath下即可:
# DEBUG,INFO,WARN,ERROR,FATAL
LOG_LEVEL=DEBUG
log4j.rootLogger=${LOG_LEVEL},CONSOLE,FILE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Encoding=utf-8
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
#log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss} %C{8}@(%F:%L):%m%n
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss} %C{1}@(%F:%L):%m%n
log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.FILE.File=logs/workorder.log
log4j.appender.FILE.Encoding=utf-8
log4j.appender.FILE.DatePattern='.'yyyy-MM-dd
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
#log4j.appender.FILE.layout=org.apache.log4j.HTMLLayout
log4j.appender.FILE.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH\:mm\:ss} %C{8}@(%F\:%L)\:%m%n
喜欢这篇文章么,喜欢就加入我们一起讨论netty技术吧!
品茗IT交流群
网友评论