RPC
или социализация приложений
RPC
- Remote Procedure Call
- Удалённый вызов процедур
RPC зачем?
- Уменьшить использование ресурсов
- Разнести логику
RPC что?
- это не протокол!
- это набор технологий
RPC из чего состоит
- протокол обмена
- сериализатор данных
RPC протокол обмена
- TCP/UDP
- HTTP(S) [хотя и не является транспортным уровнем ISO/OSI]
Особенности вызова
- Асимметричность
- Синхронность
Более сложная реализация
- Разделение адресного пространства
- Транспорт
- аварийные ситуации на одном из концов
- Неоднородность
RMI
Remote Method Invocation
RMI
- API Java
- служит для выполнения кода на другой JVM
- Связь клиент-серверная
RMI что кого
- Объекты передаются по значению (Serializeble)
- Удаленные объекты передаются в виде Stub
- RemoteException
- всё можно найти в java.rmi.*
RMI интерфейс
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RMIInterface extends Remote {
String sayHello(String name) throws RemoteException;
}
RMI сервер
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
public class RMIServer implements RMIInterface {
public String sayHello(String name) {
String string = "Hello, " + name + "!";
return string;
}
public static void main(String... args) throws Exception {
final Registry registry = LocateRegistry.createRegistry(2099);
final RMIInterface service = new RMIServer();
Remote stub = UnicastRemoteObject.exportObject(service, 0);
registry.bind("sample/HelloService", stub);
}
}
RMI клиент
import java.rmi.registry.*;
public class RMIClient {
public static void main(String... args) throws Exception {
Registry registry = LocateRegistry.getRegistry("localhost", 2099);
RMIInterface service = (RMIInterface) registry
.lookup("sample/HelloService");
String[] names = { "John", "Jan", "Иван"};
for (String name : names) {
System.out.println(service.sayHello(name));
}
}
}
RMI безопасность
grant {
permission java.net.SocketPermission "127.0.0.1:*", "connect,resolve";
permission java.net.SocketPermission "127.0.0.1:*", "accept";
};
grant {
permission java.security.AllPermission;
};
SOAP
Simple Object Access Protocol
SOAP
- расширение XML-RPC
- транспорт HTTP(S), SMTP, FTP, TCP, UDP
- WSDL [Web Service Destribution Language]
SOAP RPC
encoded |
literal |
5
5.0
|
5
5.0
s
|
SOAP минусы
- избыточость информации
- неторопливсть
SOAP плюсы
- кроссплатформенность
- расширяемость (WS-адресация)
- нейтралитет (протоколы)
- независимость (модель программирования)
SOAP примеры
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
@WebService
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface SOAPInterface {
String sayHello(String name);
}
SOAP примеры
import javax.jws.WebService;
@WebService(endpointInterface = "ru.matmex.rpc.soap.SOAPInterface")
public class SOAPImpl implements SOAPInterface {
@Override
public String sayHello(String name) {
return "Hello " + name + "!";
}
}
SOAP примеры
import javax.xml.ws.Endpoint;
public class SOAPServer {
public static void main(String... args) {
Endpoint.publish("http://localhost:9999/ws/hello", new SOAPImpl());
}
}
SOAP примеры
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class SOAPClient {
public static void main(String... args) throws Exception {
URL url = new URL("http://localhost:9999/ws/hello?wsdl");
QName qname = new QName("http://soap.rpc.matmex.ru/", "SOAPImplService");
Service service = Service.create(url, qname);
SOAPInterface hello = service.getPort(SOAPInterface.class);
System.out.println(hello.sayHello("Magaz"));
}
}
SOAP примеры
wsimport -keep http://localhost:9999/ws/hello?wsdl
SOAP примеры
@WebServiceClient(name = "SOAPImplService",
targetNamespace = "http://soap.rpc.matmex.ru/",
wsdlLocation = "http://localhost:9999/ws/hello?wsdl")
public class SOAPImplService extends Service {/**/}
@WebService(name = "SOAPInterface", targetNamespace = "http://soap.rpc.matmex.ru/")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface SOAPInterface {/**/}
SOAP примеры
public class SOAPClientWSImport {
public static void main(String... args) {
SOAPImplService helloService = new SOAPImplService();
SOAPInterface hello = helloService.getSOAPImplPort();
System.out.println(hello.sayHello("Magaz"));
}
}
HTTP
HyperText Transfer Protocol
протокол передачи гипертекста
HTTP
- протокол прикладного уровня
- клиент-север, запрос-ответ
HTTP методы
- GET
- POST
- PUT
- DELETE
- HEAD
- OPTIONS
HTTP заголовки
- Host
- User-Agent
- Accept
- Content-Type
- DoNotTrack
HTTP коды ответа
- 1xx - информационные
- 2xx - успешно
- 3xx - перенаправление
- 4xx - ошибка клиента
- 5xx - ошибка сервера
HTTP запрос
GET /search?q=REST HTTP/2
Host: www.google.com
Accept-Encoding: deflate, gzip, br
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
Connection: keep-alive
HTTP ответ
HTTP/2 200
content-type: text/html; charset=UTF-8
date: Tue, 12 Mar 2019 20:12:12 GMT
expires: -1
cache-control: private, max-age=0
trailer: X-Google-GFE-Current-Request-Cost-From-GWS
strict-transport-security: max-age=31536000
content-encoding: br
server: gws
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
REST
Representational State Transfer
передача состояния представления
Требования к REST
- Клиент-сервер
- Отсутствие состояния
- Кэширование
- Единообразие интерфейса
- Идентификация ресурсов
- Манипуляция ресурсами через представление
- «Самоописываемые» сообщения
- Гипермедиа (HATEOAS)
- Слои
Характеризуется
метод запроса |
GET, POST, DELETE |
путь запроса |
/objects/list, /objects/100500 |
тело запросв |
форма, json, xml |
код ответа |
200 OK, 404 NotFound |
тело ответа |
json, html, xml |
REST и Java
- JAX-RS
- Spring Framework
- и другое
JAX-RS
- Java API for RESTful Web Services
- JavaEE
JAX-RS аннотации
- @Path
- @GET, @PUT, @POST, @DELETE, @HEAD
- @Produces
- @Consumes
JAX-RS аннотации
- @PathParam
- @QueryParam
- @HeaderParam
- @CookieParam
- @FormParam
- @DefaultValue
- @Context
SpringFramework
- @RequestMapping
- @GetMapping, @PostMapping, @PutMapping, @PatchMapping, @DeleteMapping
- @PathVariable
- @RequestAttribute
- @RequestBody
- @RequestParam
- @SessionAttribute(s)
JAX-RS пример
@Path("/greetings")
public class JaxRsController {
@GET
@Path("/{name}")
@Produces(MediaType.TEXT_PLAIN)
public Response greeting(@PathParam("name") String name) {
String greeting = "Hello " + name;
return Response.ok(greeting).build();
}
}
Spring пример 1
@RestController
@RequestMapping("/greetings")
public class SpringRestController {
@RequestMapping(method = RequestMethod.GET,
value = "/{name}",
produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity< ? > greeting(@PathVariable String name) {
String greeting = "Hello " + name;
return new ResponseEntity<>(greeting, HttpStatus.OK);
}
}
Spring пример 2
@RestController
public class GreetingController {
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
return new Greeting("Hello " + name);
}
}
Spring пример. Клиент
public static void main(String... args) {
RestTemplate restTemplate = new RestTemplate();
String message = restTemplate.getForObject("http://localhost/greetings/Bob", Sting.class);
log.info(message);
Greeting greeting = restTemplate.getForObject("http://localhost/greeting?name=Bob", Greeting.class);
// {"message": "Hello Bob"}
log.info(greeting.getMessage());
}
HATEOAS
Hypermedia as the Engine of Application State
- клиент обращается к фиксированному URL
- клиент может ничего не знать о сервере, кроме входящего URL
HATEOAS пример
{
"content":"Hello, User!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting/User"
},
"farewell":{
"href":"http://localhost:8080/farewell/User"
}
}
}
gRPC
- Google
- Protocol Buffers v3
- HTTP/2
Protocol Buffers
- протокол сериализации
- проще, быстрее, однозначнее XML
- необходима реализация под каждый язык
HTTP/2
- только HTTPS
- одно соединение для всех запросов
gRPC пример описания
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
Типы gRPC
- Unary RPC
- Server streaming RPC
- Client streaming RPC
- Bidirectional streaming
Что есть еще в gRPC
- балансировка
- Interceptors
gRPC клиент
public class HelloWorldClient {
public static void main(String[] args) throws Exception {
ManagedChannel channel = ManagedChannelBuilder
.forAddress("localhost", 50051).usePlaintext().build();
GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(channel);
try {
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response;
try {
response = blockingStub.sayHello("Magaz");
} catch (StatusRuntimeException e) {
System.err.println("RPC failed: " + e.getStatus());return;
}
System.out.println("Greeting: " + response.getMessage());
} finally {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
}
}
gRPC сервер
import io.grpc.*;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
public class HelloWorldServer {
public static void main(String[] args) throws IOException, InterruptedException {
Server server = ServerBuilder.forPort(50051)
.addService(new GreeterImpl()).build().start();
server.awaitTermination();
}
static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(HelloRequest req, StreamObserver< HelloReply > responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
}
JMS
- Это не RPC
- Является частью JDK
- Топики
- Очереди
JMS основа
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
class ConsumerOrProducer {
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory
= new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);
connection.start();
Session session = connection
.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = null;
if("QUEUE".equalsIgnoreCase(System.getProperties("amq"))) {
destination = session.createQueue("LolQueue");
}else{
destination = session.createTopic("LolTopik");
}
/*
........................................
*/
connection.close();
}
}
JMS прием
MessageConsumer consumer = session.createConsumer(destination);
String body;
do {
Message msg = consumer.receive();
body = ((TextMessage) msg).getText();
System.out.println("Received = "+body);
}while (!body.equalsIgnoreCase("GoHome!"));
JMS отправка
MessageProducer producer = session.createProducer(destination);
Console c = System.console();
String response;
do {
response = c.readLine("Enter message: ");
TextMessage msg = session.createTextMessage(response);
producer.send(msg);
} while (!response.equalsIgnoreCase("GoHome!"));