[Dart, Flutter] gRPC + Dart, Сервис + Клиент, напишем? Часть 2
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Первая часть находится здесьПривет, я Андрей, работаю Flutter разработчиком в компании Финам.Давайте развивать сервис Umka основы которого мы заложили в первой части.Реализация отправки ответа на полученный вопросДля начала чуть изменим нашу "базу вопросов", таким образом, чтобы она содержала правильный ответ к каждому вопросу:
[
{
"id": 0,
"text": "7 x 5 = ?",
"answer": "35"
},
{
"id": 1,
"text": "12 x 13 = ?",
"answer": "156"
},
{
"id": 2,
"text": "2 ** 5 = ?",
"answer": "32"
},
{
"id": 3,
"text": "2 ** 10 = ?",
"answer": "1024"
},
{
"id": 4,
"text": "2 ** 11 = ?",
"answer": "2048"
}
]
В файл lib/questions_db_driver.dart добавим метод getCorrectAnswerById, для получения корректного ответа по идентификатору вопроса и сделаем небольшой рефакторинг кода:
import 'dart:io';
import 'dart:convert';
import 'generated/umka.pb.dart';
final List<Question> questionsDb = _readDb();
List _getQuestionsList() {
final jsonString = File('db/questions_db.json').readAsStringSync();
return jsonDecode(jsonString);
}
List<Question> _readDb() => _getQuestionsList()
.map((entry) => Question()
..id = entry['id']
..text = entry['text'])
.toList();
String? getCorrectAnswerById(int questionId) {
final jsonList = _getQuestionsList();
final correctAnswer = jsonList.firstWhere(
(element) => element['id'] == questionId,
orElse: () => null,
);
return correctAnswer?['answer'];
}
В класс UmkaService добавим реализацию метода sendAnswer в котором:
- получим из "базы" правильный ответ
- если по какой-то причине "клиент" передал несуществующий идентификатор вопроса "выбросим" ошибку throw grpc.GrpcError.invalidArgument('Invalid question id!');
- оценим ответ (за правильный в поле mark запишем 5, за неверный "влепим двойку") и вернём оценку "клиенту"
@override
Future<Evaluation> sendAnswer(ServiceCall call, Answer request) async {
print('Received answer for the question: $request');
final correctAnswer = getCorrectAnswerById(request.question.id);
if (correctAnswer == null) {
throw grpc.GrpcError.invalidArgument('Invalid question id!');
}
final evaluation = Evaluation()
..id = 1
..answerId = request.id;
if (correctAnswer == request.text) {
evaluation.mark = 5;
} else {
evaluation.mark = 2;
}
return evaluation;
}
Остальной код в файле lib/service.dart без изменений.Реализация метода sendAnswer на стороне клиентского приложения такая:
Future<void> sendAnswer(Student student, Question question) async {
final answer = Answer()
..question = question
..student = student;
print('Enter your answer: ');
answer.text = stdin.readLineSync()!;
final evaluation = await stub.sendAnswer(answer);
print('Evaluation for the answer: ${answer.text} '
'\non the question ${question.text}:'
'\n$evaluation');
}
- создаем "экземпляр" класса Answer
- добавляем в него текст ответа введённый "студентом" в терминал
- отправляем ответ на "оценку"
- дождавшись оценки от сервиса выводим её в консоль
Также чуть изменим метод обращения к сервису Umka callService:
Future<void> callService(Student student) async {
final question = await getQuestion(student);
await sendAnswer(student, question);
await channel.shutdown();
}
Здесь все просто:
- запрашиваем у сервиса вопрос
- отправляем на него ответ
- закрываем соединение
Запускаем сервисДля запуска сервиса на localhost из директории проекта выполним команду:dart lib/service.dartВ окне терминала сервиса будут видны логи отправленных ответов. Чтобы завершить работу сервиса, можно нажать ctrl+c.Подключаемся к сервису терминальным клиентомКомандой dart lib/client.dart в соседнем окне терминала из папки проекта запустим нашего "клиента" и представим себя студентом, которому нужно ответить на полученный вопрос. Для этого читаем вопрос, и в терминал в виде числа вводим ответ. После этого нам "прилетит" оценка mark: 5 или mark: 2.Демонстрация вышеописанного:
Ошибки gRPCДавайте "заставим" вызов sendAnswer прислать нам ошибку. Для этого подменим question.id на нелепый, например так:
Future<void> callService(Student student) async {
final question = await getQuestion(student);
question.id = 777;
await sendAnswer(student, question);
await channel.shutdown();
}
}
Сервис пришлет нам ошибку
Unhandled exception:
gRPC Error (code: 3, codeName: INVALID_ARGUMENT, message: Invalid question id!, details: [], rawResponse: null)
Демонстрация:
Ошибки, конечно же, требуют корректной обработки.Отправка потокa данных клиентскому приложениюДавайте добавим нашему сервису возможность обучать "студентов". Для этого организуем периодическую отправку вопросов клиентскому приложению вместе с ответом на него.Здесь нам и пригодится возможность gRPC отправлять stream c сервера "клиентам".Добавим к описанию нашего сервиса в файл protos/umka.proto один тип:
message AnsweredQuestion {
Question question = 1;
string answer = 2;
}
И один удалённый вызов:
rpc getTutorial(Student) returns (stream AnsweredQuestion) {}
Обратите внимание на аннотацию stream перед возвращаемым типом. Именно она "решает", что при вызове данной процедуры клиент будет получать поток данных, а не одиночный ответ.Теперь описание выглядит так:
Мы изменили описание сервиса, поэтому нужна "регенерация" gRPC Dart кода, и мы в папке проекта просто запускаем знакомую уже команду:protoc -I protos/ protos/umka.proto --dart_out=grpc:lib/generatedМожно заглянуть во вновь сгенерированный код и убедиться, что там появился новый "вызов" в виде метода getTutorial класса UmkaServiceBase в файле umka.pbgrpc.dart и класс AnsweredQuestion в файле umka.pb.dart.Код метода getTutorial для сервисаПерейдя в файл lib/service.dart обнаруживаем "ворчание" компилятора на то, что в классе UmkaService отсутствует реализация метода getTutorial.Напишем его код следующим образом:
@override
Stream<AnsweredQuestion> getTutorial(
ServiceCall call, Student request) async* {
for (var question in questionsDb) {
final answeredQuestion = AnsweredQuestion()
..question = question
..answer = getCorrectAnswerById(question.id)!;
yield answeredQuestion;
await Future.delayed(Duration(seconds: 2));
}
}
}
В Dart для того, чтобы функция возвращала стрим её нужно пометить ключевым словом async*. После этого в стрим объекты указанного типа Stream<AnsweredQuestion> отправляются с помощью ключевого слова yield.Метод getTutoriad по одному будет брать вопросы из "базы", отправлять их "студенту" в клиентское приложение, делать паузу 2 секунды для "подумать". Процесс будет повторяться пока не закончатся данные. После этого соединение, установленное при вызове метода getTutorial, будет прервано.Доработка клиентского приложенияИзменения здесь небольшие:
- добавим в UmkaTerminalClient метод запроса урока takeTutorial,
- изменим обращение к сервису сосредоточившись только на "уроке".
Future<void> takeTutorial(Student student) async {
await for (var answeredQuestion in stub.getTutorial(student)) {
print(answeredQuestion);
}
}
Future<void> callService(Student student) async {
await takeTutorial(student);
await channel.shutdown();
}
}
Dart конструкция await for позволяет удобно брать данные из потока до тех пор, пока поток не "иссякнет", после чего метод takeTutorial завершится. Текст вопросов с ответами на них просто печатаем в консоль.Запускаем dart lib/service.dart в одном терминальном окне и dart lib/client.dart в другом, и наблюдаем поток вопросов в клиентский терминал:
На этом вторая и завершим вторую часть.Мы поработали над развитием нашей системы добавив полезные "фичи":
- Отправка на сервер ответа на полученный вопрос.
- Получение обучающего материала в виде потока задачек с ответами.
Посмотрели как "прилетает" ошибка.Думаю, стало понятно, что в gRPC предусмотрена удобная возможность развития системы.До встречи в части №3 где мы продолжим добавлять полезные возможности нашему сервису на основе потока данных от клиента к сервису и двунаправленного потока данных.
===========
Источник:
habr.com
===========
Похожие новости:
- [JavaScript, API, Стандарты связи] Консорциум Всемирной паутины принял стандарт Web Audio в качестве официального
- [Dart, Flutter] gRPC + Dart, Сервис + Клиент, напишем
- [Стандарты связи, Смартфоны, Сотовая связь] Смартфоны с поддержкой 5G заняли 8% российского рынка, операторы связи подогревают интерес к технологии
- [Беспроводные технологии, Стандарты связи, Интернет вещей, Сотовая связь, Будущее здесь] SIMCom Wirelss Solutions совместно с МТС протестировали работу технологии “eSIM M2M”
- [IT-инфраструктура, Производство и разработка электроники, Компьютерное железо, Процессоры] Внедрение DDR5 будет молниеносным: к 2026 году новая память займет 90% рынка
- [Go] Настройка инструментов стандартной библиотеки Golang для production
- [Dart, C, Go, Flutter] Вызов кода Go из Dart с использованием cgo и Dart FFI на простом примере
- [Программирование, Разработка мобильных приложений, Dart, Flutter] Flutter 2.2: что нового (перевод)
- [Разработка систем связи, Стандарты связи, Сотовая связь] В Москве будет создан первый индустриальный 5G-полигон
- [Программирование, C++, Компиляторы, IT-стандарты] С++23 WIP: онлайн-встреча международного комитета по C++
Теги для поиска: #_dart, #_flutter, #_dart, #_flutter, #_grpc, #_protobuf, #_dart (дарт), #_flatter (флаттер), #_dart, #_flutter
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:17
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Первая часть находится здесьПривет, я Андрей, работаю Flutter разработчиком в компании Финам.Давайте развивать сервис Umka основы которого мы заложили в первой части.Реализация отправки ответа на полученный вопросДля начала чуть изменим нашу "базу вопросов", таким образом, чтобы она содержала правильный ответ к каждому вопросу: [
{ "id": 0, "text": "7 x 5 = ?", "answer": "35" }, { "id": 1, "text": "12 x 13 = ?", "answer": "156" }, { "id": 2, "text": "2 ** 5 = ?", "answer": "32" }, { "id": 3, "text": "2 ** 10 = ?", "answer": "1024" }, { "id": 4, "text": "2 ** 11 = ?", "answer": "2048" } ] import 'dart:io';
import 'dart:convert'; import 'generated/umka.pb.dart'; final List<Question> questionsDb = _readDb(); List _getQuestionsList() { final jsonString = File('db/questions_db.json').readAsStringSync(); return jsonDecode(jsonString); } List<Question> _readDb() => _getQuestionsList() .map((entry) => Question() ..id = entry['id'] ..text = entry['text']) .toList(); String? getCorrectAnswerById(int questionId) { final jsonList = _getQuestionsList(); final correctAnswer = jsonList.firstWhere( (element) => element['id'] == questionId, orElse: () => null, ); return correctAnswer?['answer']; }
@override
Future<Evaluation> sendAnswer(ServiceCall call, Answer request) async { print('Received answer for the question: $request'); final correctAnswer = getCorrectAnswerById(request.question.id); if (correctAnswer == null) { throw grpc.GrpcError.invalidArgument('Invalid question id!'); } final evaluation = Evaluation() ..id = 1 ..answerId = request.id; if (correctAnswer == request.text) { evaluation.mark = 5; } else { evaluation.mark = 2; } return evaluation; } Future<void> sendAnswer(Student student, Question question) async {
final answer = Answer() ..question = question ..student = student; print('Enter your answer: '); answer.text = stdin.readLineSync()!; final evaluation = await stub.sendAnswer(answer); print('Evaluation for the answer: ${answer.text} ' '\non the question ${question.text}:' '\n$evaluation'); }
Future<void> callService(Student student) async {
final question = await getQuestion(student); await sendAnswer(student, question); await channel.shutdown(); }
Ошибки gRPCДавайте "заставим" вызов sendAnswer прислать нам ошибку. Для этого подменим question.id на нелепый, например так: Future<void> callService(Student student) async {
final question = await getQuestion(student); question.id = 777; await sendAnswer(student, question); await channel.shutdown(); } } Unhandled exception:
gRPC Error (code: 3, codeName: INVALID_ARGUMENT, message: Invalid question id!, details: [], rawResponse: null) Ошибки, конечно же, требуют корректной обработки.Отправка потокa данных клиентскому приложениюДавайте добавим нашему сервису возможность обучать "студентов". Для этого организуем периодическую отправку вопросов клиентскому приложению вместе с ответом на него.Здесь нам и пригодится возможность gRPC отправлять stream c сервера "клиентам".Добавим к описанию нашего сервиса в файл protos/umka.proto один тип: message AnsweredQuestion {
Question question = 1; string answer = 2; } rpc getTutorial(Student) returns (stream AnsweredQuestion) {}
Мы изменили описание сервиса, поэтому нужна "регенерация" gRPC Dart кода, и мы в папке проекта просто запускаем знакомую уже команду:protoc -I protos/ protos/umka.proto --dart_out=grpc:lib/generatedМожно заглянуть во вновь сгенерированный код и убедиться, что там появился новый "вызов" в виде метода getTutorial класса UmkaServiceBase в файле umka.pbgrpc.dart и класс AnsweredQuestion в файле umka.pb.dart.Код метода getTutorial для сервисаПерейдя в файл lib/service.dart обнаруживаем "ворчание" компилятора на то, что в классе UmkaService отсутствует реализация метода getTutorial.Напишем его код следующим образом: @override
Stream<AnsweredQuestion> getTutorial( ServiceCall call, Student request) async* { for (var question in questionsDb) { final answeredQuestion = AnsweredQuestion() ..question = question ..answer = getCorrectAnswerById(question.id)!; yield answeredQuestion; await Future.delayed(Duration(seconds: 2)); } } }
Future<void> takeTutorial(Student student) async {
await for (var answeredQuestion in stub.getTutorial(student)) { print(answeredQuestion); } } Future<void> callService(Student student) async { await takeTutorial(student); await channel.shutdown(); } } На этом вторая и завершим вторую часть.Мы поработали над развитием нашей системы добавив полезные "фичи":
=========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:17
Часовой пояс: UTC + 5