---
title: "Solon Rpc 应用开发 - 跑通第一次调用"
---

Solon Rpc 应用通常拆成三部分：双方共享的服务接口、服务端实现、客户端消费方。体验与 Dubbo 相似。

### 1、 三个部分（模块）的职责

推荐把项目拆成：

```text
user-api       共享接口和 DTO
user-provider  Solon 服务端，实现接口
user-consumer  Solon 客户端，调用接口
```

客户端只依赖 `user-api` 和 RPC 客户端依赖，不要把服务端数据库、Web 容器实现传递给客户端。

### 2、 共享接口和 DTO

```java
package demo.api;

import java.io.Serializable;

public interface UserService {
    User getById(long userId);
    void add(User user);
}

public class User implements Serializable {
    private long userId;
    private String name;
    private int level;

    public long getUserId() { return userId; }
    public void setUserId(long userId) { this.userId = userId; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getLevel() { return level; }
    public void setLevel(int level) { this.level = level; }
}
```

`Serializable` 对多种序列化方案更稳妥；如果明确只使用 JSON，DTO 通常不要求实现它。示例不用 Lombok，避免读者还要额外寻找 Lombok 依赖。

### 3、 服务端

服务端依赖 `org.noear:solon-web`。当前仓库的 `solon-web` shortcut 默认组合用了 snack4 序列化插件。

```java
package demo.provider;

import demo.api.User;
import demo.api.UserService;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.annotation.Remoting;

@Remoting
@Mapping("/rpc/v1/user")
public class UserServiceImpl implements UserService {
    @Override
    public User getById(long userId) {
        User user = new User();
        user.setUserId(userId);
        user.setName("demo");
        user.setLevel(1);
        return user;
    }

    @Override
    public void add(User user) {
        // 实际项目中在这里调用 service/repository
    }
}
```

`@Mapping` 是远程入口路径；`@Remoting` 表明这是远程服务。

入口和配置：

```java
import org.noear.solon.Solon;

public class ProviderApp {
    public static void main(String[] args) {
        Solon.start(ProviderApp.class, args);
    }
}
```

```yaml
# provider/src/main/resources/app.yml
server.port: 9001
solon.app:
  group: demo
  name: userapi
```

### 4、 客户端

客户端依赖共享接口，以及当前仓库中的 `org.noear:nami`、`org.noear:nami-channel-http` 和 `org.noear:nami-coder-snack4`。在 Solon Bean 中注入代理：

```java
package demo.consumer;

import demo.api.User;
import demo.api.UserService;
import org.noear.nami.annotation.NamiClient;
import org.noear.nami.common.ContentTypes;
import org.noear.solon.annotation.Component;

@Component
public class UserCaller {
    @NamiClient(url = "http://localhost:9001/rpc/v1/user", headers = ContentTypes.JSON)
    private UserService userService;

    public User load(long id) {
        return userService.getById(id);
    }
}
```

`@NamiClient` 支持标在接口和字段上；在 Solon 中标在字段上时由 Nami 插件注入代理。接口必须是 interface，Builder 最终也只为接口创建 JDK Proxy。

客户端如果也启动 Web 服务，使用不同端口：

```yaml
server.port: 8081
solon.app:
  group: demo
  name: userconsumer
```

### 5、 验证调用链

1. 启动 provider，确认 `9001` 没有端口冲突。
2. 启动 consumer。
3. 调用 `userService.getById(1)`。
4. 若失败，先把问题限定在固定 URL；不要马上加入注册中心。

一次调用的关键对应关系：

| 客户端 | 服务端 |
|---|---|
| `url` 的 `/rpc/v1/user` | 类上的 `@Mapping` |
| 方法名 `getById` | 服务端接口实现方法 |
| `headers = ContentTypes.JSON` | 服务端的 snack4/JSON 序列化支持 |
| 返回类型 `User` | 服务端返回的 DTO |


