我眼中的服务提供和服务消费

 

服务提供和消费脑图

服务提供和消费脑图

 

参见: 服务提供者, 服务消费者, 服务注册中心

服务提供者

1.服务提供者启动,解析xml文件中配置的服务,这里使用Dom4j解析。

2.将服务的一些相关信息注册到 服务注册中心。

注:服务相关信息:服务中心接口url,接口名称,方法名称,参数信息。

3.提供一个接口,服务消费者通过调用这个接口url来调用相应的服务。

参见: 服务提供和消费脑图, 服务注册中心 (1.注册服务), 服务消费者 (3.调用服务)

服务消费者

1.服务消费者启动,使用dom4j解析xml获取要消费的服务相关接口。

2.根据接口信息去服务注册中心判断是否有对应的注册信息,如果有则通过jdk动态代理生成相应的代理类并注册到spring中(代理方法中会根据服务中心返回的信息(服务提供者的url)去调用服务提供者对应的服务)。

参见: 服务提供和消费脑图, 服务注册中心 (2.消费服务), 服务提供者 (3.调用服务)

服务注册中心

1.将来自服务提供者信息存储到redis。

2.将服务信息提供给服务消费者。

参见: 服务提供者 (1.注册服务), 服务消费者 (2.消费服务), 服务提供和消费脑图

工程示例

  注:示例中为了简单,采用rest请求方式来代替socket连接

  注册中心

@RestController
@RequestMapping("index")
public class IndexController {@Autowiredprivate RedisCacheTemplate redisCacheTemplate;//注册服务提供者信息,将信息放到redis中@RequestMapping(value = "register", method = RequestMethod.POST)public SimpleResponse register(@RequestBody RegisterMessage registerMessage) {try {Map<String, Object> map = new HashMap<>();for (InterfaceMessage interfaceMessage : registerMessage.getInterfaceMessageList()) {interfaceMessage.setProviderUrl(registerMessage.getProviderUrl());map.put(ToStringBuilder.reflectionToString(interfaceMessage, ToStringStyle.SHORT_PREFIX_STYLE), true);}redisCacheTemplate.batchPut(map);return SimpleResponse.success(map.size());} catch (Exception e) {e.printStackTrace();return SimpleResponse.error(e.getMessage());}}//消费者拿到配置的服务信息到注册中心来匹配,验证是否存在这个服务@RequestMapping(value = "contains", method = RequestMethod.POST)public SimpleResponse contains(@RequestBody InterfaceMessage interfaceMessage) {try {if(redisCacheTemplate.exist(ToStringBuilder.reflectionToString(interfaceMessage, ToStringStyle.SHORT_PREFIX_STYLE))) {return SimpleResponse.success(true);} else {return SimpleResponse.error(null);}} catch (Exception e) {e.printStackTrace();return SimpleResponse.error(e.getMessage());}}@RequestMapping(value = "test", method = {RequestMethod.GET, RequestMethod.POST})public SimpleResponse test(@RequestParam String providerUrl){return SimpleResponse.success(providerUrl);}
}

  服务提供者

<?xml version="1.0" encoding="UTF-8"?>
<services-provider><service id="testService" interface="com.hjzgg.simulation.api.ITestService"/>
</services-provider>

  自定义xml,配置将要注册的服务id及对应的接口类。

# 内置tomcat服务器配置
server.port=8088
server.context-path=/provider-server#打印彩色日志
spring.output.ansi.enabled=always# 日志打印级别
logging.level.root=debug# service
service.xml.path=classpath:service-provider.xml 自定义服务提供者配置文件 位置
service.provider.path=http://localhost:8088/provider-server/index/provider  服务提供者执行相应服务接口
service.register.path=http://localhost:8090/register-server/index/register  调用注册中心 接口
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hjzgg.simulation.common.node.InterfaceMessage;
import com.hjzgg.simulation.common.node.RegisterMessage;
import com.hjzgg.simulation.common.parsexml.BeanNode;
import com.hjzgg.simulation.common.parsexml.ParseServiceXML;
import com.hjzgg.simulation.common.response.ReturnCode;
import com.hjzgg.simulation.common.utils.RestTemplateUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;import java.util.ArrayList;
import java.util.List;public class Registrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {private static Logger logger = LoggerFactory.getLogger(Registrar.class);private String servicesXmlPath;private String serviceProviderPath;private String serviceRegisterPath;@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {List<BeanNode> beanNodes = ParseServiceXML.getProviderServices(servicesXmlPath); 解析自定义服务提供配置文件List<InterfaceMessage> list = new ArrayList<>();for(BeanNode beanNode : beanNodes) { 根据服务对应id去 寻找实现的 beanif(!registry.containsBeanDefinition(beanNode.getBeanName())) {logger.error("接口" + beanNode.getBeanName() + " " + beanNode.getInterfaceCls().getTypeName() + " 没有对应的实现类");} else {InterfaceMessage interfaceMessage = new InterfaceMessage();interfaceMessage.setBeanName(beanNode.getBeanName());interfaceMessage.setInterfacType(beanNode.getInterfaceCls().getTypeName());list.add(interfaceMessage);}}if(!CollectionUtils.isEmpty(list)) { 将配置的服务信息发送的注册中心RegisterMessage registerMessage = new RegisterMessage();registerMessage.setProviderUrl(this.serviceProviderPath);registerMessage.setInterfaceMessageList(list);try {String result = RestTemplateUtils.post(this.serviceRegisterPath, (JSONObject) JSON.toJSON(registerMessage), MediaType.APPLICATION_JSON_UTF8);JSONObject retJson = JSONObject.parseObject(result);if(retJson.getInteger("code") == ReturnCode.SUCCESS.getValue()) {logger.debug("服务注册成功...");} else {logger.error("服务注册失败..." + retJson.getString("msg"));}} catch (Exception e) {e.printStackTrace();logger.error("服务注册失败..." + e.getMessage());}}}@Overridepublic void setEnvironment(Environment environment) { 获取环境变量this.servicesXmlPath = environment.getProperty("service.xml.path");this.serviceProviderPath = environment.getProperty("service.provider.path");this.serviceRegisterPath = environment.getProperty("service.register.path");assert(StringUtils.isNotEmpty(this.serviceProviderPath) && StringUtils.isNotEmpty(serviceRegisterPath) &&StringUtils.isNotEmpty(this.servicesXmlPath));}
}
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;/*** Created by hujunzheng on 2017/7/7.*/@Configuration
@Import(Registrar.class)
public class Config { 注册服务配置启动
}
import com.hjzgg.simulation.common.node.ServiceMessage;
import com.hjzgg.simulation.common.response.SimpleResponse;
import com.hjzgg.simulation.common.utils.ContextUtils;
import com.hjzgg.simulation.common.utils.SerializeUtil;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;/*** Created by hujunzheng on 2017/7/8.*/
@RestController
@RequestMapping("index")
public class IndexController {

   服务提供者执行相应服务接口@RequestMapping(value
= "invoke", method = RequestMethod.POST)public Object invoke(@RequestParam String serviceMessageBody) {try {
       根据消费者传递的服务信息 找到对应的服务bean以及方法,并利用反射执行方法,最后返回结果ServiceMessage serviceMessage
= (ServiceMessage) SerializeUtil.unserialize(Hex.decodeHex(serviceMessageBody.toCharArray()));Object bean = null;if((bean = ContextUtils.getBean(serviceMessage.getBeanName(), serviceMessage.getRequireType())) != null) {List<Class<?>> classList = new ArrayList<>();if(serviceMessage.getArgs() != null) {for (Object obj : serviceMessage.getArgs()) {classList.add(obj.getClass());}}Method method = ReflectionUtils.findMethod(bean.getClass(), serviceMessage.getMethodName(), classList.toArray(new Class<?>[0]));if(method != null) {return method.invoke(bean, serviceMessage.getArgs());} else {return SimpleResponse.error("服务" + serviceMessage.getRequireType().getTypeName() + "中没有对应参数"+ ToStringBuilder.reflectionToString(classList) + "的" + serviceMessage.getMethodName() + "方法");}} else {return SimpleResponse.error("没有名称为" + serviceMessage.getBeanName() + "且类型为"+ serviceMessage.getRequireType().getTypeName() + "对应的bean");}} catch (Exception e) {e.printStackTrace();return SimpleResponse.error(e.getMessage());}}}

  服务消费者

<?xml version="1.0" encoding="UTF-8"?>
<services-consumer><service ref="testService" interface="com.hjzgg.simulation.api.ITestService" url="http://localhost:8088/provider-server/index/provider"/>
</services-consumer>

  自定义服务消费者配置,服务引用名称,接口类型,调用服务提供者URL

# 内置tomcat服务器配置
server.port=8089
server.context-path=/consumer-server#打印彩色日志
spring.output.ansi.enabled=always# 日志打印级别
logging.level.root=debug# service xml
service.xml.path=classpath:service-consumer.xml 自定义服务消费配置文件位置
service.contains.url=http://localhost:8090/register-server/index/contains 注册中心服务查询接口
service.invoke.url=http://localhost:8088/provider-server/index/invoke 服务提供者执行相应服务接口
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hjzgg.simulation.common.dynamic.JdkDynamicProxy;
import com.hjzgg.simulation.common.node.InterfaceMessage;
import com.hjzgg.simulation.common.parsexml.BeanNode;
import com.hjzgg.simulation.common.parsexml.ParseServiceXML;
import com.hjzgg.simulation.common.register.SpringBeanRegister;
import com.hjzgg.simulation.common.response.ReturnCode;
import com.hjzgg.simulation.common.utils.RestTemplateUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.http.MediaType;import java.util.Iterator;
import java.util.List;public class Registrar implements ImportBeanDefinitionRegistrar, EnvironmentAware{private Logger logger = LoggerFactory.getLogger(Registrar.class);private String servicesXmlPath;private String serviceContainsPath;@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
     解析自定义服务消费配置文件List
<BeanNode> beanNodes = ParseServiceXML.getConsumerServices(servicesXmlPath); 判断注册中心 是否注册了这个服务了for(Iterator<BeanNode> it = beanNodes.iterator(); it.hasNext(); ) {BeanNode beanNode = it.next();InterfaceMessage interfaceMessage = new InterfaceMessage();interfaceMessage.setProviderUrl(beanNode.getUrl());interfaceMessage.setInterfacType(beanNode.getInterfaceCls().getTypeName());interfaceMessage.setBeanName(beanNode.getBeanName());try {String result = RestTemplateUtils.post(this.serviceContainsPath, (JSONObject) JSON.toJSON(interfaceMessage), MediaType.APPLICATION_JSON_UTF8);JSONObject retJson = JSON.parseObject(result);if (retJson.getInteger("code") == ReturnCode.FAILURE.getValue()) {it.remove();logger.error(interfaceMessage.getBeanName() + "对应类型" + interfaceMessage.getInterfacType() + "的服务在" +interfaceMessage.getProviderUrl() + "上没有注册");}} catch (Exception e) {e.printStackTrace();logger.error("服务" + interfaceMessage.getBeanName() + "对应类型" + interfaceMessage.getInterfacType() + "查找失败..." + e.getMessage());}}
     将与注册中心一直的服务 以 动态代理的方式 注册到spring中SpringBeanRegister.registerBean(importingClassMetadata, registry, beanNodes);}@Override
public void setEnvironment(Environment environment) { 设置环境变量this.servicesXmlPath = environment.getProperty("service.xml.path");this.serviceContainsPath = environment.getProperty("service.contains.url");String serviceInvokePath = environment.getProperty("service.invoke.url");assert(StringUtils.isNotEmpty(serviceContainsPath) && StringUtils.isNotEmpty(this.servicesXmlPath)&& StringUtils.isNotEmpty(serviceInvokePath));JdkDynamicProxy.setServerProviderInvokeUrl(serviceInvokePath);} }
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;/*** Created by hujunzheng on 2017/7/7.*/@Configuration
@Import(Registrar.class)
public class Config {
}

   测试一下

  api接口

import com.alibaba.fastjson.JSONObject;/*** Created by hujunzheng on 2017/7/7.*/
public interface ITestService {JSONObject testService();
}

  服务提供者对应的实例

import com.alibaba.fastjson.JSONObject;
import com.hjzgg.simulation.api.ITestService;
import org.springframework.stereotype.Service;/*** Created by hujunzheng on 2017/7/8.*/
@Service("testService")
public class TestServiceImpl implements ITestService {@Overridepublic JSONObject testService() {JSONObject result = new JSONObject();result.put("name", "hujunzheng");result.put("age", 25);return result;}
}

  消费者对应的测试

import com.hjzgg.simulation.api.ITestService;
import com.hjzgg.simulation.common.response.SimpleResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** Created by hujunzheng on 2017/7/9.*/
@RestController
@RequestMapping("index")
public class ConsumerController {@Autowiredprivate ITestService testService;@RequestMapping("test")public SimpleResponse test() {try {return SimpleResponse.success(testService.testService());} catch (Exception e) {e.printStackTrace();return SimpleResponse.error(e.getMessage());}}
}

  这就是我的实现方式,就说到这里了。最后我想说,思路很重要,掌握的知识很重要,多积累,多思考,任重而道远。最后附上我从中积累的知识和经验。

知识和经验

  执行顺序及ProxyFactoryBean实现

  Public class ProxyFactoryBean implements FactoryBean, InitializingBean;
  方法执行顺序 getObjectType->afterPropertiesSet->getObject
bean 的属性设置的 先于 getObjectType

  Springboot 工程自定义jar包中获取上下文工具类

要加上 @Component注解

  实体类型(例如下面)网络传输方法,避免字符串编码格式问题

发送请求
ServiceMessage serviceMessage = new ServiceMessage();
。。。。。

JSONObject params = new JSONObject();
params.put("serviceMessageBody", Hex.encodeHexString(SerializeUtil.serialize(serviceMessage)));

Class<?> returnType = method.getReturnType();
return RestTemplateUtils.post(SERVER_PROVIDER_INVOKE_URL, params, MediaType.APPLICATION_FORM_URLENCODED, returnType);
接收请求
@RequestMapping(value = "invoke", method = RequestMethod.POST)
public Object invoke(@RequestParam String serviceMessageBody) {
try {
ServiceMessage serviceMessage = (ServiceMessage) SerializeUtil.unserialize(Hex.decodeHex(serviceMessageBody.toCharArray()));
。。。。。 
}
参考工具类
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
</dependency>
Hex实现十六进制字符串和byte[]之间的转换,另附RestTemplateUtils工具链接
  完整项目下载,请点这里!

转载于:https://www.cnblogs.com/hujunzheng/p/7131212.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/531249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

shiro整合oauth

前言 如果oauth原理还不清楚的地方&#xff0c;其参考这里。 一、基本思路脑图 二、客户端shiro配置 shiro配置文件 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:util&q…

springmvc+swagger2

一、swagger2依赖 <!--swagger--> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><exclusions><exclusion><artifactId>spring-aop</artifactId><groupId>org.s…

获取资源文件工具类

如果没有依赖spring&#xff0c;可以将分割线下的方法去掉 import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframe…

无状态shiro认证组件(禁用默认session)

准备内容 简单的shiro无状态认证 无状态认证拦截器 import com.hjzgg.stateless.shiroSimpleWeb.Constants; import com.hjzgg.stateless.shiroSimpleWeb.realm.StatelessToken; import org.apache.shiro.web.filter.AccessControlFilter;import javax.servlet.ServletRequest;…

Spring根据包名获取包路径下的所有类

参考mybatis MapperScannerConfigurer.java 最终找到 Spring的一个类 ClassPathBeanDefinitionScanner.java 参考ClassPathBeanDefinitionScanner 和它的父类 ClassPathScanningCandidateComponentProvider&#xff0c;将一些代码进行抽取&#xff0c;得到如下工具类。 import…

idea模板注释

类文件头部的注释 #if (${PACKAGE_NAME} && ${PACKAGE_NAME} ! "")package ${PACKAGE_NAME};#end #parse("File Header.java") /** * ${DESCRIPTION} * author ${USER} hujunzheng * create ${YEAR}-${MONTH}-${DAY} ${TIME} **/ public class ${N…

redis分布式锁小试

一、场景 项目A监听mq中的其他项目的部署消息&#xff08;包括push_seq, status, environment&#xff0c;timestamp等&#xff09;&#xff0c;然后将部署消息同步到数据库中&#xff08;项目X在对应环境[environment]上部署的push_seq[项目X的版本]&#xff09;。那么问题来了…

Jackson ObjectMapper readValue过程

1.整体调用栈 2.看一下调用栈的两个方法 resolve 方法中通过 Iterator i$ this._beanProperties.iterator() 遍历属性的所有子属性&#xff0c;缓存对应的 deserializer。观察调用栈的方法&#xff0c;可以发现是循环调用的。 3.比如寻找自定义的 LocalDateTime类的序列化实现…

java如何寻找main函数对应的类

参考springboot Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())…

jooq实践

用法 sql语句 SELECT AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, COUNT(*)FROM AUTHORJOIN BOOK ON AUTHOR.ID BOOK.AUTHOR_IDWHERE BOOK.LANGUAGE DEAND BOOK.PUBLISHED > DATE 2008-01-01 GROUP BY AUTHOR.FIRST_NAME, AUTHOR.LAST_NAMEHAVING COUNT(*) > 5 ORDER BY AUT…

git根据用户过滤提交记录

使用SourceTree 使用gitk 转载于:https://www.cnblogs.com/hujunzheng/p/8398203.html

cglib动态代理导致注解丢失问题及如何修改注解允许被继承

现象 SOAService这个bean先后经过两个BeanPostProcessor&#xff0c;会发现代理之后注解就丢失了。 开启了cglib代理 SpringBootApplication EnableAspectJAutoProxy(proxyTargetClass true) public class Application {public static void main(String[] args) {SpringApplic…

spring AbstractBeanDefinition创建bean类型是动态代理类的方式

1.接口 Class<?> resourceClass 2.获取builder BeanDefinitionBuilder builder BeanDefinitionBuilder.genericBeanDefinition(resourceClass); 3.获取接口对应的动态代理class Class<?> targetProxyClass Proxy.getProxyClass(XXX.class.getClassLoader(), ne…

微信小程序:一起玩连线,一个算法来搞定

微信小程序&#xff1a;一起玩连线 游戏玩法 将相同颜色的结点连接在一起&#xff0c;连线之间不能交叉。 算法思想 转换为多个源点到达对应终点的路径问题&#xff0c;且路径之间不相交。按照dfs方式寻找两个结点路径&#xff0c;一条路径探索完之后&#xff0c;标记地图并记录…

IntelliJ IDEA关于logger的live template配置

1.安装 log support2插件 2.配置log support2 由于项目中的日志框架是公司自己封装的&#xff0c;所以还需要自己手动改一下 log support2插件生成的live template 当然也可以修改 Log support global的配置 包括 Logger Field、Logger class、Logger Factory class都可以修改。…

springboot项目接入配置中心,实现@ConfigurationProperties的bean属性刷新方案

前言 配置中心&#xff0c;通过keyvalue的形式存储环境变量。配置中心的属性做了修改&#xff0c;项目中可以通过配置中心的依赖&#xff08;sdk&#xff09;立即感知到。需要做的就是如何在属性发生变化时&#xff0c;改变带有ConfigurationProperties的bean的相关属性。 配置…

简单封装kafka相关的api

一、针对于kafka版本 <dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>0.8.2.2</version></dependency><dependency><groupId>org.apache.kafka</groupId>…

springmvc controller动态设置content-type

springmvc RequestMappingHandlerAdapter#invokeHandlerMethod 通过ServletInvocableHandlerMethod#invokeAndHandle调用目标方法&#xff0c;并处理返回值。 如果return value &#xff01; null&#xff0c;则通过returnvalueHandlers处理&#xff0c;内部会调用MessageConv…

springboot2.0 redis EnableCaching的配置和使用

一、前言 关于EnableCaching最简单使用&#xff0c;个人感觉只需提供一个CacheManager的一个实例就好了。springboot为我们提供了cache相关的自动配置。引入cache模块&#xff0c;如下。 二、maven依赖 <dependency><groupId>org.springframework.boot</groupId…

依赖配置中心实现注有@ConfigurationProperties的bean相关属性刷新

配置中心是什么 配置中心&#xff0c;通过keyvalue的形式存储环境变量。配置中心的属性做了修改&#xff0c;项目中可以通过配置中心的依赖&#xff08;sdk&#xff09;立即感知到。需要做的就是如何在属性发生变化时&#xff0c;改变带有ConfigurationProperties的bean的相关属…