I am trying to get messages from kafka and send it to RSocket using Spring. Posting Server side on Spring Java and client side with React
#Configuration
#EnableConfigurationProperties(RsocketConsumerProperties.class)
public class RsocketConsumerConfiguration {
#Bean
public Function<Integer, Mono<Integer>> rsocketConsumer(RSocketRequester.Builder builder,
RsocketConsumerProperties rsocketConsumerProperties) {
RSocketRequester rSocketRequester = builder.websocket(URI.create("ws://localhost:7000/"));
return input -> rSocketRequester.route(rsocketConsumerProperties.getRoute()).data(input).retrieveMono(Integer.class);
}
}
#EnableBinding(Sink.class)
public class Listener {
#Autowired
private Function<Integer, Mono<Integer>> rsocketConsumer;
#StreamListener(Sink.INPUT)
public void fireAndForget(Integer val) {
System.out.println(val);
rsocketConsumer.apply(val).subscribe();
}
}
#Controller
public class ServerController {
#MessageMapping("data")
public Mono<Integer> hello(Integer integer) {
return Mono.just(integer);
}
}
What do i do wrong in server side because my client is connected but not able to get new messages
client.connect().subscribe({
onComplete: socket => {
socket.fireAndForget({
data: { message: "hello from javascript!" },
metadata: null
});
},
onError: error => {
console.log("got error");
console.error(error);
},
onSubscribe: cancel => {
/* call cancel() to abort */
console.log("subscribe!");
console.log(cancel);
// cancel.cancel();
}
});
You do this requester.route("input").data("Welcome to Rsocket").send(); where we have this:
/**
* Perform a {#link RSocket#fireAndForget fireAndForget} sending the
* provided data and metadata.
* #return a completion that indicates if the payload was sent
* successfully or not. Note, however that is a one-way send and there
* is no indication of whether or how the event was handled on the
* remote end.
*/
Mono<Void> send();
You see - Mono? That means that it has to be subscribed to initiate a reactive stream processing. See project Reactor for more info: https://projectreactor.io/
On the other hand it is not clear what is server and what is client in your case...
you do this:
/**
* Build an {#link RSocketRequester} with an
* {#link io.rsocket.core.RSocketClient} that connects over WebSocket to
* the given URL. The requester can be used to make requests
* concurrently. Requests are made over a shared connection that is also
* re-established as needed when further requests are made.
* #param uri the URL to connect to
* #return the created {#code RSocketRequester}
* #since 5.3
*/
RSocketRequester websocket(URI uri);
And I would say it means client in the code you show. The server is on the other side where that 7000 port is opened for ws:// protocol. So, be sure that you understand and configure all the parts properly. For example I don't see why do you need a #RestController in your Listener class...
Related
I have Kinesis Analytics application running with Flink and use external Flink Sink object to transfer data with Apache HttpAsyncClients. The below code is working fine for some time.
override def open(): Unit = {
uuid = randomUUID.toString
client = HttpAsyncClients
.custom()
.setKeepAliveStrategy(
DefaultConnectionKeepAliveStrategy.INSTANCE
)
.build()
client.start()
}
override def invoke(event: String): Unit = {
log.info(s"Received Value to sink: ${event.length} Head: ${event.take(20)}")
val request = createRequest(event)
client.execute(
request,
new FutureCallback[SimpleHttpResponse]() {
override def completed(response: SimpleHttpResponse): Unit = {
val status = StatusLine(response)
if (status.isError) {
log.error(s"Request: $request Status: $status Body: ${response.getBodyText}")
}
}
override def failed(ex: Exception): Unit = {
log.error(s"Request: $request Ex: $ex")
}
override def cancelled(): Unit = {
log.error(s"Request: $request Cancelled")
}
}
)
}
private def createRequest(event: String): SimpleHttpRequest = {
SimpleRequestBuilder
.post()
.setHttpHost(new HttpHost(URIScheme.HTTPS.id, host))
.setPath(path)
.setBody(event, ContentType.APPLICATION_JSON)
.build()
}
override def close(): Unit = {
client.close(CloseMode.GRACEFUL)
}
}
I have around 80k requests every minute and this app works fine for several days, before starting to raise DeadlineTimeoutException. Initially I thought it's related to the throughput, which my server can accept, but I don't see application errors on the backend. Instead, all requests just fail after some time.
I don't really know what is happening, because if socket connection is not closed properly and there is actual timeout I should see some requests dropping off. Instead, at the event of failure I stop to receive any traffic and zero requests successful.
I believe something has to be added for the configuration of the HTTP client, and sockets should not clog. From the documentation it seems like it shouldn't happen, socket connection and leased connection should be terminated. But at this point I have no idea what has to be changed.
I'm trying build a SignalR proof of concept where two applications are involved; one is a web single-page application and the other one is a server-side RESTful web api. The technology/framework being used is ReactJs, ASP.NET Web API 2 (.NET Framework 4.6, NOT .NET Framework Core) and SignalR.
The Web API
This is how I have SignalR wired-up in the server application. When the application starts, I map SignalR to the application pipeline...
public static void ConfigureSignarlR(IAppBuilder app)
{
app.MapSignalR<ChatConnection>("/signalr", new Microsoft.AspNet.SignalR.HubConfiguration
{
EnableDetailedErrors = true
});
}
The ChatConnection class is an implementation of PersistentConnection that does nothing special...
public class ChatConnection : PersistentConnection
{
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
return base.OnReceived(request, connectionId, data);
}
protected override Task OnConnected(IRequest request, string connectionId)
{
return base.OnConnected(request, connectionId);
}
public override Task ProcessRequest(HostContext context)
{
return base.ProcessRequest(context);
}
}
and then I have a very simple hub...
public class ChatHub : Hub
{
public void Send(string name, string message)
{
Clients.All.broadcastMessage(name, message);
}
}
The Client App
For the client application I'm using the #aspnet/signalr-client npm package...this is how I create and start the connection...
initialize = () => {
const hubCon = new HubConnection("http://api.domain/signalr");
hubCon.start()
.then(() => console.log("Connection established..."))
.catch(err => console.log(err))
}
Things to be noticed
Both the API and the client app are hosted on the same local IIS server but with different host names (using host files)
When using the browser to navigate to http://api.domain/signalr/hubs, I get a 400 (Bad Request) response when the message following message Protocol error: Unknown transport.
When attempting to connect from the client app, I get the same error message
The ProcessRequest method is the only one that gets hit when debugging the ChatConnection class
Question(s)
What did I miss here? Or how can I get this PoC to work?
The question is quite broad because I seriously have no clue of what's going on here
After a bit of digging and reading through SignalR documentation I realized that I was doing everything wrong. Basically, SignalR implements two different connection patterns:
Hubs: a high-level API built on top of the Persistent connection API
Persistent connections
A client cannot communicate with a persistent connection endpoint using a Hub proxy (or at least not the way I was doing it). So, what I did was:
Kept the PersistentConnection but overrided the OnReceived method so it can broardcast to all clients
protected override async Task OnReceived(IRequest request, string connectionId, string data)
{
await Connection.BroadCast("message to broadcast");
}
Removed the "signalr\hubs" script reference because it's not needed
Registered the connection on start up (server-side)
app.MapSignalR<ChatConnection>("/chat");
Finnaly, on the client side, initialize the connection and register all necessary callbacks
this.connection = window.$.connection(process.env.REACT_APP_API_BASE_URI + "/chat");
this.connection.logging = true;
this.connection.received((data) => {
console.log("Received some data:")
console.log(data)
});
this.connection.start(() => {
console.log("Connection opened")
console.log("connectionId = " + this.connection.id)
});
We have a SOAP web service that we are migrating from JBoss EAP 5.1 to 6.4.7 and one of the webservices returns absolutely nothing but 200 (in JBoss 5). When we migrated to 6 it still works and returns nothing but returns a 202 instead and that is going to break clients. We have no control over clients. I tried a SOAPHandler at the close method but it does nothing as it is not even called as my guess is that since there is no SOAP message going back there is nothing that triggers the handler.
I also tried to access the context directly in the web method and modif but it did nothing.
MessageContext ctx = wsContext.getMessageContext();
HttpServletResponse response = (HttpServletResponse) ctx.get(MessageContext.SERVLET_RESPONSE);
response.setStatus(HttpServletResponse.SC_OK);
I couldn't find anything in the manual.
Any direction is very much appreciated.
Here is how the port and its implementation look like:
Here is how the port and its implementation head look like:
#WebService(name = "ForecastServicePortType", targetNamespace = "http://www.company.com/forecastservice/wsdl")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
#XmlSeeAlso({
ObjectFactory.class
})
public interface ForecastServicePortType {
/**
*
* #param parameters
* #throws RemoteException
*/
#WebMethod(action = "http://www.company.com/forecast/sendForecast")
public void sendForecast(
#WebParam(name = "SendForecast", targetNamespace = "http://www.company.com/forecastservice", partName = "parameters")
SendForecastType parameters) throws RemoteException;
}
#WebService(name = "ForecastServicePortTypeImpl", serviceName = "ForecastServicePortType", endpointInterface = "com.company.forecastservice.wsdl.ForecastServicePortType", wsdlLocation = "/WEB-INF/wsdl/ForecastServicePortType.wsdl")
#HandlerChain(file = "/META-INF/handlers.xml")
public class ForecastServicePortTypeImpl implements ForecastServicePortType {
...
}
In case anybody will find this useful. Here is the solution;
Apache CXF by default uses async requests and even if the annotation #OneWay is missing it still behaves as it if was there.
So in order to disable that an interceptor needs to be created like below:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import java.util.Arrays;
public class DisableOneWayInterceptor extends AbstractSoapInterceptor {
private static final Log LOG = LogFactory.getLog(DisableOneWayInterceptor.class);
public DisableOneWayInterceptor(){
super(Phase.PRE_LOGICAL);
addBefore(Arrays.asList(org.apache.cxf.interceptor.OneWayProcessorInterceptor.class.getName()));
}
#Override
public void handleMessage(SoapMessage soapMessage) throws Fault {
if(LOG.isDebugEnabled())
LOG.debug("OneWay behavior disabled");
soapMessage.getExchange().setOneWay(false);
}
}
And called in WebService class (annotated with #WebService) as below:
#org.apache.cxf.interceptor.InInterceptors (interceptors = {"com.mycompany.interceptors.DisableOneWayInterceptor" })
The last few weeks we have been experiencing this error message while using the Azure Search SDK (1.1.1 - 1.1.2) and performing searches.
We consume the Search SDK from internal APIs (deployed as Azure Web Apps) that scale up-down based on traffic (so there could be more than 1 instance of the APIs doing the searches).
Our API queries 5 different indexes and maintains an in-memory copy of the SearchIndexClient object that corresponds to each index, a very simple implementation would look like:
public class AzureSearchService
{
private readonly SearchServiceClient _serviceClient;
private Dictionary<string, SearchIndexClient> _clientDictionary;
public AzureSearchService()
{
_serviceClient = new SearchServiceClient("myservicename", new SearchCredentials("myservicekey"));
_clientDictionary = new Dictionary<string, SearchIndexClient>();
}
public SearchIndexClient GetClient(string indexName)
{
try
{
if (!_clientDictionary.ContainsKey(indexName))
{
_clientDictionary.Add(indexName, _serviceClient.Indexes.GetClient(indexName));
}
return _clientDictionary[indexName];
}
catch
{
return null;
}
}
public async Task<SearchResults> SearchIndex(SearchIndexClient client, string text)
{
var parameters = new SearchParameters();
parameters.Top = 10;
parameters.IncludeTotalResultCount = true;
var response = await client.Documents.SearchWithHttpMessagesAsync(text, parameters, null, null);
return response.Body;
}
}
And the API would invoke the service by:
public class SearchController : ApiController
{
private readonly AzureSearchService service;
public SearchController()
{
service = new AzureSearchService();
}
public async Task<HttpResponseMessage> Post(string indexName, [FromBody] string text)
{
var indexClient = service.GetClient(indexName);
var results = await service.SearchIndex(indexClient, text);
return Request.CreateResponse(HttpStatusCode.OK, results, Configuration.Formatters.JsonFormatter);
}
}
We are using SearchWithHttpMessagesAsync due to a requirement to receive custom HTTP headers instead of the SearchAsync method.
This way we avoid opening/closing the client under traffic bursts. Before using this memory cache (and wrapping each client on a using clause) we would get port exhaustion alerts on Azure App Services.
Is this a good pattern? Could we be receiving this error because of the multiple instances running in parallel?
In case it is needed, the stack trace shows:
System.Net.Http.HttpRequestException: Only one usage of each socket address (protocol/network address/port) is normally permitted service.ip.address.hidden:443
[SocketException:Only one usage of each socket address (protocol/network address/port)is normally permitted service.ip.address.hidden:443]
at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure,Socket s4,Socket s6,Socket& socket,IPAddress& address,ConnectSocketState state,IAsyncResult asyncResult,Exception& exception)
[WebException:Unable to connect to the remote server]
at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext& context)
at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
EDIT: We are also receiving this error A connection attempt failed because the connected party did not properly respond after a period of time:
System.Net.Http.HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond service.ip.address.hidden:443
[SocketException:A connection attempt failed because the connected party did not properly respond after a period of time,or established connection failed because connected host has failed to respond service.ip.address.hidden:443]
at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure,Socket s4,Socket s6,Socket& socket,IPAddress& address,ConnectSocketState state,IAsyncResult asyncResult,Exception& exception)
[WebException:Unable to connect to the remote server]
at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext& context)
at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
As implemented in the code in your question, the cache will not prevent port exhaustion. This is because you're instantiating it as a field of the ApiController, which is created once per request. If you want to avoid port exhaustion, the cache must be shared across all requests. To make it concurrency-safe, you should use something like ConcurrentDictionary instead of Dictionary.
The "connection attempt failed" error is likely unrelated.
We use Resteasy to communicate between multiple backend servers & we want to lock this down so not just anyone can attach a client or browser to the restlet server.
We're using Resteasy 3.04 and as our backend services are numerous but very light-weight an embeddded TJWS webserver.
Example Server code:
public class RestEasySSLBasicAuthenticationServer {
static TJWSEmbeddedJaxrsServer webServer;
static class BasicAthenticationSecurityDomain implements SecurityDomain {
#Override
public Principal authenticate(String aUsername, String aPassword) throws SecurityException {
System.out.println("User:" + aUsername + " Password" + aPassword);
if (aPassword.equals("password") == false) {
throw new SecurityException("Access denied to user " + aUsername);
}
return null;
}
#Override
public boolean isUserInRoll(Principal aUsername, String aRole) {
// No role based checks so return true
return true;
}
}
public static void main(String[] args) throws Exception {
// Create embedded TJWS web server
webServer = new TJWSEmbeddedJaxrsServer();
// Set up SSL connections on server
webServer.setSSLPort(8081);
webServer.setSSLKeyStoreFile("K:\\source\\RestEasyTest\\server_localhost.jks");
webServer.setSSLKeyStorePass("krypton");
webServer.setSSLKeyStoreType("JKS");
// Add basic HTTP authentication to the server
webServer.setSecurityDomain( new BasicAthenticationSecurityDomain() );
// Add the restlet resource
webServer.getDeployment().getActualResourceClasses().add(PlayerResource.class);
// Start the web server
webServer.start();
// Run until user presses a key
System.out.print("Web server started. Press a key to stop...");
System.in.read();
// Stop the web server
webServer.stop();
}
}
Example client code:
public class RestEasySSLBasicAuthenticationClient {
public static void main(String[] args) throws Exception {
// Set up the keystore
System.setProperty("javax.net.ssl.keyStore", "K:\\source\\RestEasyTest\\client_localhost.jks");
System.setProperty("javax.net.ssl.keyStoreType", "JKS");
System.setProperty("javax.net.ssl.keyStorePassword", "krypton");
// Create a new Restlet client
Client restletClient = ClientBuilder.newClient();
// *** Even WITHOUT these credentitials we can access the restlet
// restletClient.register(new BasicAuthentication("username", "password"));
// Set up the restlet request target.
WebTarget request = restletClient.target("https://localhost:8081/player/{id}");
request = request.resolveTemplate("id", Long.valueOf(1));
// Build the restlet request
Invocation invocation = request.request("application/xml").buildGet();
// Call the restlet and get returned object
Player result = invocation.invoke( Player.class );
System.out.println(result.toString());
}
}
Using the test client and a registered authentication filter works and as expected I can a 401 access error if I get the password incorrect.
However if no authentication is registered at the client then the server never calls the SecurityDomain check and access is allowed.
How do I enforce a login at the server?
You can ensure all users are authenticated by enabling security on the embedded TJWS web server.
webServer.getDeployment().setSecurityEnabled(true);