【手写dubbo-4】想了解dubbo的Invocation模型?先创造个私有协议吧!

        上个版本只是为了实现简单的远程调用,只能请求指定类,并且返回字符串。本次优化,使得可以请求服务端开放的任意服务,并且返回任意对象。
        优化请求信息,请求信息中携带消息id(作用很大,稍后会有介绍)、接口名、方法名、参数信息等。响应信息中有消息id、响应信息等。对象序列化方式暂时采用jdk自带序列化,因序列化不影响使用,因此暂不优化,以后版本提供扩展入口。

优化思路图

image-20210512164115390

项目结构

  • dubbo-server: 服务端
  • dubbo-client: 消费端
  • dubbo-server-service: 接口信息,消费端引入此模块
  • dubbo-framework: dubbo框架模块。

核心代码

        因请求和响应传输的对象不同。因此定义为request/response两个对象名称为DubboRequestDubboResponse

        每次请求都需要有一个id,也许这个id不需要全局唯一,但是在同一时间,不能存在同样id的请求,这个原因,我会在下一篇文章种重点说明。这里采用AtomicgetAndIncrement方法获取id,主要是为了即使在多线程的情况下,也可以安全的获取到唯一的标识,这里不了解Atomic包下类作用的,可以自行百度下。

public class DubboRequest implements Serializable
{
	private static final AtomicLong INVOKE_ID = new AtomicLong(0);
	private final long mId;
	private Object mData;
	public DubboRequest ()
	{
		this.mId = newId();
	}
	private static final long newId ()
	{
		//每次请求都有一个请求编号,每次获取一次请求编号+1。
		//getAndIncrement 获取并+1。 当到最大值时再次获取,会自动更新到最小值。因此始终不会重复
		return INVOKE_ID.getAndIncrement();
	}
}

        在DubboRequest的mData中存放RpcInvocation对象。这个对象的名字也很有深意。有兴趣的小伙伴可以百科下Invacation模型。

public class RpcInvocation implements Serializable
{
	private static final long serialVersionUID = -4355285085441097045L;
	//接口路径
	private String interfacePath;
	//方法名称
	private String methodName;
	//参数类型
	private Class<?>[] parameterTypes;
	//参数值
	private Object[] arguments;
	//附加属性
	private Map<String, String> attachments;
}
public class DubboResponse implements Serializable
{
	private  long mId;
	private Object mData;
}

        为了容易理解,协议的信息,还继续采用简单方式。一个Int类型的值存放总数据大小,然后后面是字节数组。

image-20210512172009080

DubboClientEncoder.java编码代码。

public class DubboClientEncoder extends MessageToByteEncoder<DubboRequest>
{
	@Override
	protected void encode(ChannelHandlerContext ctx, DubboRequest req, ByteBuf out) throws Exception {
		byte[] serialize = SerializeUtil.serialize (req);
		out.writeInt(serialize.length);
		out.writeBytes(serialize);
	}
}

        服务开放方式,为了使得协议更加容易理解,暂时使用简陋的方式,简单存放在map中。

    private static final Map<String,Class> exportServiceMap = new HashMap<> ();
    static {
        //假设此为开放的服务,稍后优化。
        exportServiceMap.put ("com.test.dubbo.service.UserService",UserServiceImpl.class);
        exportServiceMap.put ("com.test.dubbo.service.OrderService", OrderServiceImpl.class);
    }

        服务端接收到请求信息时,对请求信息进行解码,并且找到对应的接口调用并且返回。

   @Override
    protected void channelRead0 (ChannelHandlerContext ctx, DubboRequest req)
            throws Exception
    {
        //获取客户端发送的消息,并调用服务
        DubboResponse rsp  = new DubboResponse ();
        RpcInvocation rpc =(RpcInvocation) req.getMData ();
        rsp.setMId (req.getMId ());
        Class aClass = exportServiceMap.get (rpc.getInterfacePath ());
        if(aClass!=null){
            Method method = aClass.getMethod (rpc.getMethodName (), rpc.getParameterTypes ());
            Object invoke = method.invoke (aClass.newInstance (), rpc.getArguments ());
            rsp.setMData (invoke);
        }
        ctx.writeAndFlush (rsp);
    }

客户端调用代码,调用服务端的两个接口,响应结果打印。

public static void main (String[] args)
	{
		RpcProxy rpcProxy = new RpcProxy();
		UserService bean = rpcProxy.getBean (UserService.class);
		User user = bean.getUserNameById (12l);
		System.out.println ("userName:"+user.getName ());


		OrderService orderService = rpcProxy.getBean (OrderService.class);
		List<Order> orderList = orderService.getOrderList ();
		System.out.println ("orders:"+orderList);
	}

如此一来,通过RpcInvocation更加深入了解了Invocation的作用。
源码地址:gitee

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叁滴水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值