商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
For commercial use, please contact the author for authorization. For non-commercial use, please indicate the source.
協議(License):署名-非商業性使用-相同方式共享 4.0 國際 (CC BY-NC-SA 4.0)
作者(Author):Waste Code
連結(URL):https://waste-code.tech/archi...
來源(Source):Waste Code
文章概覽
專案模組
- common模組——實現實體類以及宣告暴露的api介面
- provider模組——暴露的api介面的業務實現
- consumer模組——請求介面的實現,將會待用暴露的api介面
- GITHUB: Dubbo的簡單使用以及Triple協議的Streaming通訊的實現
- 官方文件: Triple協議
- Blog目的: 記錄實現過程及出現的問題
Dubbo的簡單使用
- 在common模組中定義實體類User
在common模組中宣告暴露出的介面,實現介面UserService
public interface UserService { /** * 獲取使用者資訊 * @param name * @return */ User getUserInfo(String name); }
在provider和consumer模組中引入相關依賴
<dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>3.0.7</version> </dependency> <!-- 下面這個包必須引用,服務註冊到zookeeper中使用,之前沒有引用這個包,結果應用起不來 --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-zookeeper</artifactId> <version>3.0.7</version> </dependency> <dependency> <groupId>com.sample</groupId> <artifactId>common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
在provider和consumer模組中建立application.yml檔案並編寫相關配置
server: port: 8082 # 這裡填寫埠號,provider和consumer不同, spring: application: name: consumer dubbo: protocol: name: dubbo # 選擇通訊協議 port: -1 registry: id: zk-zookeeper address: zookeeper://127.0.0.1:2181
在provider和consumer中編寫啟動類,這裡以consumer模組為例,這裡要加上EnableDubbo註解
@SpringBootApplication @EnableDubbo public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
在provider中對UserService進行實現
// 這裡注意使用註解@DubboService,時dubbo中的Service註解,主要在對外提供服務的實現類上 @DubboService public class UserServiceImpl implements UserService { @Override public User getUserInfo(String name) { User user = new User(); user.setName("dubbo"); user.setAge(12); return user; } }
在consumer中實現請求介面, 引用provider模組暴露出的介面要使用DubboReference註解
@RestController @RequestMapping("/user") public class UserController { @DubboReference private UserService userService; @GetMapping("/info") public User getUserInfo() { return userService.getUserInfo("xxx"); } }
編寫完成程式碼後,啟動provider和consumer模組,然後透過Postman工具呼叫介面,發現可以正常使用就完成了
Triple協議的Streaming通訊實現
Triple協議的Stream通訊主要分為三種:服務端流、客戶端流、雙向流
應用場景
- 介面需要傳送大量資料,無法被放到一次請求中,需要分批次傳送
- 流式場景,資料需要按照傳送順序處理, 資料本身是沒有確定邊界的
推送類場景,多個訊息在同一個呼叫的上下文中被髮送和處理
流的語義保證(優點)
- 提供訊息邊界,可以方便的對訊息進行單獨處理
- 嚴格有序,傳送端的順序和接收端的順序是一致的
- 全雙工,傳送不需要等待
- 支援取消和超時
Streaming流通訊實現
服務端流(SERVER_STREAM)請求流程
服務端流(SERVER_STREAM)的Java實現
在provider和consumer模組中新增相關依賴
<dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> </dependency>
修改provider和consumer模組中的相關配置
dubbo: #此處僅擷取需要變更的配置,其他配置預設為有原有的就行 protocol: name: tri # 修改dubbo的通訊協議,當然triple協議同樣支援之前的dubbo的簡單使用
在common模組的UserService中宣告相關api介面
/** * 服務端流 * @param name * @param response */ void sayHelloServerStream(String name, StreamObserver<String> response) throws InterruptedException;
在provider模組中實現相關功能
//StreamObserver是接收訊息的觀察者, //在onNext方法呼叫後,consumer模組中的消費者會獲取相關的資料, //當onCompleted方法呼叫後,consumer模組進行最後的處理後,整個服務端流才會結束 @Override public void sayHelloServerStream(String name, StreamObserver<String> response) throws InterruptedException { response.onNext("Hallo, " + name); // 這裡延遲10s,主要測試,provider模組接收資料會不會有10s的延時 Thread.sleep(10 * 1000); response.onNext("Hallo, " + name + ", 第二次"); response.onCompleted(); }
在consumer模組編寫請求方法
/** * 測試服務端流 * @param name * @return * @throws InterruptedException */ @GetMapping("/sayHallo/{name}") public List<String> sayHallo(@PathVariable("name") String name) throws InterruptedException { List<String> list = new ArrayList<>(); userService.sayHelloServerStream(name, new StreamObserver<String>() { // 每次provider模組呼叫一次onNext時,該方法會執行一次 @Override public void onNext(String data) { System.out.println("onNext:" + data); list.add(data); } @Override public void onError(Throwable throwable) { System.out.println("報錯了"); } // 當provider模組的onCompleted方法呼叫後,執行該方法 @Override public void onCompleted() { System.out.println("結束"); } }); return list; }
客戶端(CLIENT_STREAM)流請求流程
雙向流(BIDIRECTIONAL_STREAM)請求流程
客戶端流(CLIENT_STREAM)/雙向流(BIDIRECTIONAL_STREAM)的Java實現
- 客戶端流和雙向流在Java中的實現方式是同一種
- 引用pom和修改配置與服務端流相同
在common模組中宣告相關介面
/** * 客戶端流/雙向流, 這裡返回的StreamObserver類裡的處理實在provider模組中實現, * 而引數StreamObserver則是在consumer模組中實現,雖然是consumer呼叫該方法 * @param response * @return */ StreamObserver<String> sayHelloStream(StreamObserver<String> response);
在provider模組中實現相關方法
@Override public StreamObserver<String> sayHelloStream(StreamObserver<String> response) { return new StreamObserver<String>() { @Override public void onNext(String data) { System.out.println("服務端請求引數:" + data); response.onNext("Hello, " + data); } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { System.out.println("provider關閉"); response.onCompleted(); } }; }
在consumer模組中實現方法的呼叫
@PostMapping("/sayHallo") public List<String> sayHallo(@RequestBody List<String> names) { List<String> list = new ArrayList<>(); StreamObserver<String> request = userService.sayHelloStream(new StreamObserver<String>() { @Override public void onNext(String data) { System.out.println("說了啥?" + data); list.add(data); } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { System.out.println("結束了"); } }); // 上面定義了StreamObserver並呼叫了方法後,在下邊透過onNext方法呼叫傳送請求 names.forEach(item -> { request.onNext(item); try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { throw new RuntimeException(e); } }); request.onCompleted(); return list; }