mif é uma estrutura de aplicativo da web C++ 11 projetada para o desenvolvimento de microsserviços de back-end. A estrutura faz uso de metainformações de tipo adicionais.
1.5.1
NOTA: O branch master é desenvolvido ativamente, use a versão mais recente para uso em produção.
Testado pela última vez no gcc 13.2
Linux (testado pela última vez no Ubuntu 23.10)
git clone https://github.com/tdv/mif.git
cd mif
./download_third_party.sh
mkdir build
cd build
cmake ..
make
make install
NOTA: No Ubuntu você pode ter que instalar alguns pacotes adicionais: liblz4-dev, pkgconf, bison, flex
Você pode tentar usar CMAKE_INSTALL_PREFIX para selecionar o diretório de instalação
Após a construção mif , você pode criar exemplos
cd mif /examples/{sample_project}
mkdir build
cd build
cmake ..
make
NOTA: Para desenvolver seus aplicativos, você pode usar o modelo de aplicativo. Após baixar o projeto mif , siga os passos
cd mif /template
mkdir build
cd build
cmake ..
make
Depois disso você pode alterar este modelo para criar seu próprio aplicativo. Além disso, você pode usar os exemplos retirados do modelo. Os exemplos estão totalmente concluídos e possuem um script para construção propriamente dita. Todos eles estão na pasta _docs. Você pode encontrar documentação detalhada dos exemplos no wiki.
Servidor:
cd mif /exammples/{sample_project}/bin
./{sample_project}_server
Cliente:
cd mif /exammples/{sample_project}/bin
./{sample_project}_client
Por favor use --help para obter mais informações sobre como executar um exemplo
Código fonte
Descrição
O exemplo demonstra o trabalho do servidor de eco HTTP simples. Servidor de eco HTTP
// mif
# include < mif /application/http_server.h >
# include < mif /common/log.h >
# include < mif /net/http/constants.h >
class Application
: public mif ::Application::HttpServer
{
public:
using HttpServer::HttpServer;
private:
// mif .Application.HttpServer
virtual void Init ( mif ::Net::Http::ServerHandlers &handlers) override final
{
handlers[ " / " ] = [] ( mif ::Net::Http::IInputPack const &request,
mif ::Net::Http::IOutputPack &response)
{
auto data = request. GetData ();
mif _LOG (Info) << " Process request " " << request. GetPath () << request. GetQuery () << " "t Data: "
<< (data. empty () ? std::string{ " null " } : std::string{ std::begin (data), std::end (data)});
response. SetCode ( mif ::Net::Http::Code::Ok);
response. SetHeader ( mif ::Net::Http::Constants::Header::Connection::Value,
mif ::Net::Http::Constants::Value::Connection::Close::Value);
response. SetData ( std::move (data));
};
}
};
int main ( int argc, char const **argv)
{
return mif ::Application::Run<Application>(argc, argv);
}
Teste
curl -iv -X POST " http://localhost:55555/ " -d ' Test data '
Código fonte
Descrição
O exemplo demonstra o trabalho do servidor HTTP com interface dupla para processamento de solicitações HTTP brutas e mif RPC por HTTP. Mais você pode ver no capítulo "Serviço Web. Adicional"
Código fonte
Descrição
O exemplo "Hello World" demonstra um aplicativo cliente-servidor básico com empacotamento RPC baseado em interface e comunicação TCP com o uso de boost.archives para serialização de dados
Etapas básicas para construir um aplicativo cliente-servidor com RPC
Interface comum
// STD
# include < string >
// mif
# include < mif /service/iservice.h >
namespace Service
{
struct IHelloWorld
: public mif ::Service::Inherit< mif ::Service::IService>
{
virtual void AddWord (std::string const &word) = 0;
virtual std::string GetText () const = 0;
virtual void Clean () = 0;
};
} // namespace Service
Meta-informação de interface comum
// STD
# include < string >
// mif
# include < mif /remote/ps.h >
// THIS
# include " common/interface/ihello_world.h "
namespace Service
{
namespace Meta
{
using namespace ::Service ;
mif _REMOTE_PS_BEGIN (IHelloWorld)
mif _REMOTE_METHOD (AddWord)
mif _REMOTE_METHOD (GetText)
mif _REMOTE_METHOD (Clean)
mif _REMOTE_PS_END ()
} // namespace Meta
} // namespace Service
mif _REMOTE_REGISTER_PS (Service::Meta::IHelloWorld)
Implementação de interface de servidor
...
// mif
# include < mif /service/creator.h >
// THIS
# include " common/id/service.h "
# include " common/interface/ihello_world.h "
namespace Service
{
namespace Detail
{
namespace
{
class HelloWorld
: public IHelloWorld
{
public:
...
private:
...
// IHelloWorld
virtual void AddWord (std::string const &word) override final
{
...
}
virtual std::string GetText () const override final
{
...
}
virtual void Clean () override final
{
...
}
};
} // namespace
} // namespace Detail
} // namespace Service
mif _SERVICE_CREATOR
(
::Service::Id::HelloWorld,
::Service::Detail::HelloWorld
)
Aplicativo de servidor
// mif
# include < mif /application/tcp_service.h >
// COMMON
# include " common/id/service.h "
# include " common/ps/ihello_world.h "
class Application
: public mif ::Application::TcpService
{
public:
using TcpService::TcpService;
private:
// mif .Application.TcpService
virtual void Init ( mif ::Service::FactoryPtr factory) override final
{
factory-> AddClass <Service::Id::HelloWorld>();
}
};
int main ( int argc, char const **argv)
{
return mif ::Application::Run<Application>(argc, argv);
}
Aplicativo cliente
// mif
# include < mif /application/tcp_service_client.h >
# include < mif /common/log.h >
// COMMON
# include " common/ps/ihello_world.h "
class Application
: public mif ::Application::TcpServiceClient
{
public:
using TcpServiceClient::TcpServiceClient;
private:
// mif .Application.TcpServiceClient
virtual void Init ( mif ::Service::IFactoryPtr factory) override final
{
auto service = factory-> Create <Service::IHelloWorld>( " HelloWorld " );
mif _LOG (Info) << " Add words. " ;
service-> AddWord ( " Hello " );
service-> AddWord ( " World " );
service-> AddWord ( " !!! " );
mif _LOG (Info) << " Result from server: " " << service-> GetText () << " " " ;
mif _LOG (Info) << " Clean. " ;
service-> Clean ();
mif _LOG (Info) << " Result from server: " " << service-> GetText () << " " " ;
}
};
int main ( int argc, char const **argv)
{
return mif ::Application::Run<Application>(argc, argv);
}
Código fonte
Descrição
Este exemplo é igual a "Hello World". A diferença está em chamar métodos remotos com estruturas de dados definidas pelo usuário como parâmetros e retornar um valor. A estrutura do projeto é a mesma do exemplo de projeto anterior, apenas adicionamos a definição de estruturas de dados e metainformações definidas pelo usuário.
Estruturas de dados do usuário
// STD
# include < cstdint >
# include < map >
# include < string >
namespace Service
{
namespace Data
{
using ID = std::string;
struct Human
{
std::string name;
std::string lastName;
std:: uint32_t age = 0 ;
};
enum class Position
{
Unknown,
Developer,
Manager
};
struct Employee
: public Human
{
Position position = Position::Unknown;
};
using Employees = std::map<ID, Employee>;
} // namespace Data
} // namespace Service
Meta-informação
// mif
# include < mif /reflection/reflect_type.h >
// THIS
# include " common/data/data.h "
namespace Service
{
namespace Data
{
namespace Meta
{
mif _REFLECT_BEGIN (Human)
mif _REFLECT_FIELD (name)
mif _REFLECT_FIELD (lastName)
mif _REFLECT_FIELD (age)
mif _REFLECT_END ()
mif _REFLECT_BEGIN (Position)
mif _REFLECT_FIELD (Unknown)
mif _REFLECT_FIELD (Developer)
mif _REFLECT_FIELD (Manager)
mif _REFLECT_END ()
mif _REFLECT_BEGIN (Employee, Human)
mif _REFLECT_FIELD (position)
mif _REFLECT_END ()
} // namespace Meta
} // namespace Data
} // namespace Service
mif _REGISTER_REFLECTED_TYPE (::Service::Data::Meta::Human)
mif _REGISTER_REFLECTED_TYPE(::Service::Data::Meta::Position)
mif _REGISTER_REFLECTED_TYPE(::Service::Data::Meta::Employee)
Código fonte
Descrição
Comparado aos exemplos anteriores, este adiciona a herança da interface. Na implementação você pode consultar uma interface que está fora da hierarquia.
Código fonte
Descrição
O exemplo "Visitante" demonstra o mecanismo de retornos de chamada remotos para métodos de interface. Isso pode ser usado como ponto de partida para aplicativos baseados em publicação/assinatura.
Código fonte
Descrição
O exemplo demonstra a API Json (operações CRUD) no servidor HTTP.
Teste
curl -i -X POST " http://localhost:55555/employee/create " -d ' {"name":"Ivan", "lastName":"Ivanov","age":33,"email":"[email protected]","position":"Developer","rate":200000.00} '
curl -i " http://localhost:55555/employee/read?id=1 "
curl -i " http://localhost:55555/employee/update?id=1 " -d ' {"name":"Ivan", "lastName":"Ivanov","age":33,"email":"[email protected]","position":"Developer","rate":220000.00} '
curl -i " http://localhost:55555/employee/list?limit=2&offset=0 "
curl -i " http://localhost:55555/employee/delete?id=1 "
Código fonte
Descrição
O exemplo demonstra a comunicação entre dois microsserviços (o exemplo é uma versão mais poderosa do http crud). Teste
./storage —config=storage.xml
./service --config=service.xml
curl -i -X POST " http://localhost:55555/employee/create " -d ' {"name":"Ivan", "lastName":"Ivanov","age":33,"email":"[email protected]","position":"Developer","rate":200000.00} '
curl -i " http://localhost:55555/employee/read?id=1 "
curl -i " http://localhost:55555/employee/update?id=1 " -d ' {"name":"Ivan", "lastName":"Ivanov","age":33,"email":"[email protected]","position":"Developer","rate":220000.00} '
curl -i " http://localhost:55555/employee/list?limit=2&offset=0 "
curl -i " http://localhost:55555/employee/delete?id=1 "
Código fonte
Descrição
O exemplo demonstra o mecanismo de reflexão da estrutura de dados C++. Isso pode ser usado como ponto de partida para construir aplicativos com serialização, ORM e API REST.