博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
我眼中的服务提供和服务消费
阅读量:6240 次
发布时间:2019-06-22

本文共 15343 字,大约阅读时间需要 51 分钟。

 

服务提供和消费脑图

服务提供和消费脑图

 

参见: , ,

服务提供者

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

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

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

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

参见: , ,

服务消费者

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

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

参见: , ,

服务注册中心

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

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

参见: , ,

工程示例

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

  注册中心

@RestController@RequestMapping("index")public class IndexController {    @Autowired    private RedisCacheTemplate redisCacheTemplate;   //注册服务提供者信息,将信息放到redis中    @RequestMapping(value = "register", method = RequestMethod.POST)    public SimpleResponse register(@RequestBody RegisterMessage registerMessage) {        try {            Map
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,配置将要注册的服务id及对应的接口类。

# 内置tomcat服务器配置server.port=8088server.context-path=/provider-server#打印彩色日志spring.output.ansi.enabled=always# 日志打印级别logging.level.root=debug# serviceservice.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;    @Override    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {        List
beanNodes = ParseServiceXML.getProviderServices(servicesXmlPath); 解析自定义服务提供配置文件 List
list = new ArrayList<>(); for(BeanNode beanNode : beanNodes) {
根据服务对应id去 寻找实现的 bean if(!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()); } } } @Override public 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
> 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()); } }}

  服务消费者

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

# 内置tomcat服务器配置server.port=8089server.context-path=/consumer-server#打印彩色日志spring.output.ansi.enabled=always# 日志打印级别logging.level.root=debug# service xmlservice.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;    @Override    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
     解析自定义服务消费配置文件 List
beanNodes = ParseServiceXML.getConsumerServices(servicesXmlPath); 判断注册中心 是否注册了这个服务了 for(Iterator
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 {    @Override    public 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 {    @Autowired    private 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[]之间的转换,另附 工具链接
 
  完整项目下载,!

转载地址:http://sdcia.baihongyu.com/

你可能感兴趣的文章
【转】在控制台、WinForm项目中的嵌入mdf文件的烦恼
查看>>
【转】C51中断函数的写法
查看>>
django无法加载admin的静态内容的问题(Centos7+Nginx+uwsgi环境下)
查看>>
windows 2008 启用.NET Framework 3.5
查看>>
Linux -- Ubuntu搭建java开发环境
查看>>
MVC视图中Html常见的辅助方法
查看>>
分享一下刚刚HP电话面试。。。。。。。。我估计我挂了,不过还是要来分享一下...
查看>>
PT 转 PX
查看>>
平凡世界里的万千思绪
查看>>
(二)java环境搭建
查看>>
深入推荐引擎相关算法 - 协同过滤2
查看>>
mybatis逆向工程之配置
查看>>
使用.NET 4.0+ 操作64位系统中的注册表
查看>>
剑指offer——面试题26:判断二叉树B是否为二叉树A的子结构
查看>>
scrapy主动退出爬虫的代码片段
查看>>
ny12 喷水装置(二)
查看>>
C\C++语言细节(2)
查看>>
Jenkins持续部署-自动生成版本号
查看>>
设计模式--代理模式
查看>>
javascript基础知识--最基础的
查看>>