# 深入RPC原理（上）


### 2.3  Dubbo Spring Cloud调用



1. sentinel启动命令：

   ```sh
   java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090  -jar D:/TestCode/sources/sentinel-dashboard-1.6.3.jar
   ```

2. Nacos Sentinel配置：

   sentinel-degrade为降级配置策略， 内容：

   ```json
   [
     {
       "resource": "placeOrder",
       "count": 0.2,
       "grade": 1,    
       "timeWindow": 4
     }
   ]
   ```

   sentinel-user-flow为限流配置策略： 内容：

   ```json
   [
      {
       "resource": "placeOrder",
       "controlBehavior": 0,
       "count": 2,
       "grade": 1,
       "limitApp": "default",
       "strategy": 0
     }
   ]
   ```






### 2.4 RPC深入解析

+ ​
  一个对象是如何进行序列化? 下面以User对象例举讲解:

  User对象:

  ```java
  package com.itcast;
  public class User {

      /**
       * 用户编号
       */
      private String userNo = "0001";

      /**
       * 用户名称
       */
      private String name = "zhangsan";

  }

  ```

  ​

+ **常用的序列化技术**

  1. **JDK原生序列化**

     代码:

     ```java
     ...
         public static void main(String[] args) throws IOException, ClassNotFoundException {
             String basePath =  "D:/TestCode";
             FileOutputStream fos = new FileOutputStream(basePath + "tradeUser.clazz");
             TradeUser tradeUser = new TradeUser();
             tradeUser.setName("Mirson");
             ObjectOutputStream oos = new ObjectOutputStream(fos);
             oos.writeObject(tradeUser);
             oos.flush();
             oos.close();
             FileInputStream fis = new FileInputStream(basePath + "tradeUser.clazz");
             ObjectInputStream ois = new ObjectInputStream(fis);
             TradeUser deStudent = (TradeUser) ois.readObject();
             ois.close();
             System.out.println(deStudent);
         }
     ...
     ```

     ​

  2. **Hessian2序列化**

     ​

     代码示例:

     ```java
     ...
     TradeUser tradeUser = new TradeUser();
     tradeUser.setName("Mirson");

     //tradeUser对象序列化处理
     ByteArrayOutputStream bos = new ByteArrayOutputStream();
     Hessian2Output output = new Hessian2Output(bos);
     output.writeObject(tradeUser);
     output.flushBuffer();
     byte[] data = bos.toByteArray();
     bos.close();

     //tradeUser对象反序列化处理
     ByteArrayInputStream bis = new ByteArrayInputStream(data);
     Hessian2Input input = new Hessian2Input(bis);
     TradeUser deTradeUser = (TradeUser) input.readObject();
     input.close();

     System.out.println(deTradeUser);
     ...
     ```

  3. **Protobuf序列化**

     ​

     Protobuf脚本定义:

     ```Protobuf
     // 定义Proto版本
     syntax = "proto3";
     // 是否允许生成多个JAVA文件
     option java_multiple_files = false;
     // 生成的包路径
     option java_package = "com.itcast.bulls.stock.struct.netty.trade";
     // 生成的JAVA类名
     option java_outer_classname = "TradeUserProto";


     // 预警通知消息体
     message TradeUser {

         /**
          * 用户ID
          */
         int64 userId = 1 ;
         
         /**
          * 用户名称
          */
         string userName = 2 ;
     }
     ```

     代码操作:

     ```java
     // 创建TradeUser的Protobuf对象
     TradeUserProto.TradeUser.Builder builder = TradeUserProto.TradeUser.newBuilder();
     builder.setUserId(101);
     builder.setUserName("Mirson");

     //将TradeUser做序列化处理
     TradeUserProto.TradeUser msg = builder.build();
     byte[] data = msg.toByteArray();

     //反序列化处理, 将刚才序列化的byte数组转化为TradeUser对象
     TradeUserProto.TradeUser deTradeUser = TradeUserProto.TradeUser.parseFrom(data);
     System.out.println(deTradeUser);
     ```



+ **JDK动态代理的如何实现?**

  实例代码:

  ```java
  public class JdkProxyTest {

      /**
       * 定义用户的接口
       */
      public interface User {
          String job();
      }


      /**
       * 实际的调用对象
       */
      public static class Teacher {

          public String invoke(){
              return "i'm Teacher";
          }
      }


      /**
       * 创建JDK动态代理类
       */
      public static class JDKProxy implements InvocationHandler {
          private Object target;

          JDKProxy(Object target) {
              this.target = target;
          }

          @Override
          public Object invoke(Object proxy, Method method, Object[] paramValues) {
              return ((Teacher)target).invoke();
          }
      }


          public static void main(String[] args){
              // 构建代理器
              JDKProxy proxy = new JDKProxy(new Teacher());
              ClassLoader classLoader = ClassLoaderUtils.getClassLoader();

              // 生成代理类
              User user = (User) Proxy.newProxyInstance(classLoader, new Class[]{User.class}, proxy);

              // 接口调用
              System.out.println(user.job());
          }
  }

  ```

  ​



#### 3.1.4 优雅启动

  在Dubbo框架中也引入了"warmup"特性，核心源码是在**"com.alibaba.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance.java"**中：

  ```java
  protected int getWeight(Invoker<?> invoker, Invocation invocation) {
      // 先得到Provider的权重
      int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT);
      if (weight > 0) {
          // 得到provider的启动时间戳
          long timestamp = invoker.getUrl().getParameter(Constants.REMOTE_TIMESTAMP_KEY, 0L);
          if (timestamp > 0L) {
              // provider已经运行时间
              int uptime = (int) (System.currentTimeMillis() - timestamp);
              // 得到warmup的值，默认为10分钟
              int warmup = invoker.getUrl().getParameter(Constants.WARMUP_KEY, Constants.DEFAULT_WARMUP);
              // provider运行时间少于预热时间，那么需要重新计算权重weight（即需要降权）
              if (uptime > 0 && uptime < warmup) {
                  weight = calculateWarmupWeight(uptime, warmup, weight);
              }
          }
      }
      return weight;
  }
  
  static int calculateWarmupWeight(int uptime, int warmup, int weight) {
      // 随着provider的启动时间越来越长，慢慢提升权重weight
      int ww = (int) ( (float) uptime / ( (float) warmup / (float) weight ) );
      return ww < 1 ? 1 : (ww > weight ? weight : ww);
  }
  ```



  


