Consul-Proxy:使用netty实现快速服务注册(三)使用第三方数据源

news/2024/7/5 1:46:44 标签: consul-proxy, 数据源, consul, netty

netty_1">Consul-Proxy:使用netty实现快速服务注册

使用第三方数据源

一、背景

Springcloud+consul作为微服务的注册已经见怪不怪了,试下也很流行,在我个人云服务器上,我也是这样做的。

然而,我的云服务器内存比较小,很快内存就被cloud全家桶吃光了,没办法部署其他应用了,因此,我觉得将一些服务独立出去,放弃cloud全家桶。

Consul-proxy使用netty+consul实现服务注册,并提供了若干简单的注解实现了http的mapping映射处理。

简单来说,没错,是因为穷,才有了这个组件。

品茗IT-首发

二、Maven配置

要使用consul-proxy>consul-proxy,只需要加入下面依赖即可。

<dependency>
	<groupId>cn.pomit</groupId>
	<artifactId>consul-proxy>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>consul-proxy-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>consul-proxy>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>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>		              
		<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。

这里的@InitConfiguration,是为了使用第三方数据源初始化myabtis-proxy工具。

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 })
@InitConfiguration(configurations = { DataSourceConfiguration.class })
public class ConsulApp {
	public static void main(String[] args) {
		ConsulProxyApplication.run(ConsulApp.class, args);
	}

}

3.2 第三方数据源初始化

consul-proxy>consul-proxy工具可以自动读取解析@InitConfiguration注解,并调用注解指定的配置类的initConfiguration方法,这个方法要声明为static方法,并有Properties 参数,consul-proxy>consul-proxy会自动将程序的配置传递给这个initConfiguration方法。

这里,使用dbcp2生成了DataSource,并调用

MybatisConfiguration.initConfiguration(packageName, dataSource);

对mybatis-proxy工具进行初始化。

DataSourceConfiguration:

import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSourceFactory;

import cn.pomit.mybatis.configuration.MybatisConfiguration;

public class DataSourceConfiguration {
	public static final String DATASOURCE_PREFIX = "datasource.";
	public static void initConfiguration(Properties properties) {
		String packageName = "cn.pomit.consulproxy.mapper";
		try {
			Properties dataSourceProperties = new Properties();
			for (Object key : properties.keySet()) {
				String tmpKey = key.toString();
				if(tmpKey.startsWith(DATASOURCE_PREFIX)){
					String datasourceKey = tmpKey.replace(DATASOURCE_PREFIX, "");
					dataSourceProperties.put(datasourceKey, properties.get(key));
				}
			}
			DataSource dataSource = BasicDataSourceFactory.createDataSource(dataSourceProperties);
			MybatisConfiguration.initConfiguration(packageName, dataSource);
		} catch (Exception e) {
			e.printStackTrace();
			MybatisConfiguration.initConfiguration(packageName, properties);
		}
	}

	
}

3.3 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.4 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.5 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.6 过程中使用到的实体

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
datasource.driverClassName=com.mysql.jdbc.Driver
datasource.url=jdbc:mysql://127.0.0.1:3306/feiyun?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
datasource.username=cff
datasource.password=123456

datasource.maxIdle=10
datasource.maxActive=80
datasource.testOnBorrow=true
datasource.testWhileIdle=true
datasource.initialSize=10
datasource.maxWait=30000
datasource.removeAbandoned=true
datasource.removeAbandonedTimeout=300
datasource.logAbandoned=true
datasource.validationQuery=SELECT 1

如果不带环境,直接把application-loc.properties的配置写在application.properties也是一样的。

这里面,以datasource.开发的配置,是我们在DataSourceConfiguration里面需要的自定义配置。其他配置为consul-proxy>consul-proxy需要的配置。

如果我们不想自定义数据源,可以参考《Consul-Proxy:使用netty实现快速服务注册(三)使用mybatis》一篇。

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交流群


http://www.niftyadmin.cn/n/1051022.html

相关文章

还是关于Linux

ps &#xff1a;显示系统进程 -a :显示所有进程&#xff0c;&#xff08;包括其他用户的进程&#xff09; -u &#xff1a;用户以及其他详细信息 -x &#xff1a;显示没有控制终端的进程 -ef &#xff1a;显示所有 pidof &#xff1a;用来查询某个指定服务进程的PID值。每个进…

Struts2中Action访问Servlet API的两种方法

Struts2的Action并未直接与任何Servlet API偶合&#xff0c;这也是Struts2的一个改良的地方。但如何进行访问&#xff1f; 方法一&#xff1a;.[一般推荐使用](只能获得request,而response则得不到)Struts2提供了一个ActionContext类&#xff0c;Struts2中的Action可以通过它…

分布式协议学习笔记(二) 日志复制

一旦Leader选定之后&#xff0c;我们将复制所有的操作到每个节点上。 这个是通过心跳包中的Append Entries message结构体来进行实现的 客户端发送一个操作到Leader中(即使客户端发送操作到Follower&#xff0c;操作也会被转发到Leader处理) Leader将操作附加到自己的Log日志中…

七牛云 上传图片 https 修改Nginx 注意事项

仅在这记录下&#xff0c;今天的事情。 问题出自于Nginx 设置http 强制跳转 https设置 1.上午&#xff0c;出于某些需求&#xff0c;我将服务器Nginx 设置http 强行跳转 https server {listen 80;server_name www.server_name.com;rewrite ^(.*)$ https://$host$1 perm…

oracle 11g即时客户端配置详解

1. 从OTN下载几个压缩包 下载地址为: http://www.oracle.com/technology/software/tech/oci/instantclient/index.html 然后根据你的平台&#xff0c;选择不同的下载&#xff0c;我是安装在rhel5下&#xff0c;所以选择了linux x86 并下载了下面2个包&#xff1a; instantcl…

Struts 2中实现文件上传

前一阵子有些朋友在电子邮件中问关于Struts 2实现文件上传的问题&#xff0c; 所以今天我们就来讨论一下这个问题。 实现原理 Struts 2是通过Commons FileUpload文件上传。Commons FileUpload通过将HTTP的数据保存到临时文件夹&#xff0c;然后Struts使用fileUpload拦截器将文…

Spring和ActiveMq消息队列整合详解

Spring和ActiveMq消息队列整合详解 官方主页 Spring ActiveMq 一、概述 消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流&#xff0c;并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型&#xff0c;它可以在分布式环境下扩展进程间的通…

How does a single thread handle asynchronous code in JavaScript?

原文&#xff1a; https://www.quora.com/How-does-a-single-thread-handle-asynchronous-code-in-JavaScript -------------------------------------------------------------------------------- Well, arguably its not true that Javascript is single threaded if you se…