About neohope

一直在努力,还没想过要放弃...

Oracle与SQL Server同步技术对比

方式 Oracle SQL Server 说明
备份还原 备份还原 备份还原 简单粗暴,无法实时,无法实现增量
日志备份 备库(Dataguard) 数据库镜像(Database Mirroring)
日志传输(Log Shipping)
读写操作受限
集群 RAC(Real Application Clusters) 集群(Database Cluster) RAC配置复杂,SQL Server集群只有单节点工作。实际存储只有一份。
视图 物化视图(Materialized View) 索引视图(Indexed Views) 不可改表结构,如增加字段等。
数据变更捕获 CDC(Change Data Capture) CDC(Change Data Capture) 不够灵活,无法配置只想获取部分事件,数据量很大。
订阅发布 ogg(Oracle Golden Gate)
流复制(Stream Replication)
高级复制(Oracle advanced Replication)
订阅发布(Publish and Subscribe)
数据库复制(Database Replication)
订阅发布(Publish and Subscribe)
最灵活的方式了,但也有限制。如果ogg在源加一列,或订阅发布的快照过期了,就惨了

微软MVC实现REST风格编程

1、总体来说很简单,首先新建一个MVC框架的项目,模板选择WebAPI,这样就搞定80%了。

2、WebApiConfig.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

namespace UrlToPngWebAPI
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.EnableSystemDiagnosticsTracing();
        }
    }
}

3、RouteConfig.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace UrlToPngWebAPI
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

4、请求结构
Web2PNGRequest.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Newtonsoft.Json;

namespace UrlToPngWebAPI.Models
{
    public class Web2PNGRequest
    {
        [JsonProperty]
        public String WebURL { get; set; }
        [JsonProperty]
        public String HeaderPath { get; set; }
        [JsonProperty]
        public String FooterPath { get; set; }
    }
}

5、返回结构
Web2PNGResponse.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json;

namespace UrlToPngWebAPI.Models
{
    public class Web2PNGResponse
    {
        [JsonProperty]
        public int ErrorCode { get; set; }
        [JsonProperty]
        public String ErrorInfo { get; set; }
        [JsonProperty]
        public String PNGPath { get; set; }
    }
}

6、Controller
Url2PNGController.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Helpers;
using System.Web.Http;
using Newtonsoft.Json;
using UrlToPngCsTest;
using UrlToPngWebAPI.Models;
using UrlToPngWebAPI.Pulgins;

namespace UrlToPngWebAPI.Controllers
{
    public class Url2PNGController : ApiController
    {
        // 返回输入参数示例
        public HttpResponseMessage Get()
        {
            Web2PNGRequest req = new Web2PNGRequest();
            req.WebURL = "webURL";
            req.HeaderPath = "headerPath";
            req.FooterPath = "footerPath";

            String jsonString = JsonConvert.SerializeObject(req);
            HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(jsonString, Encoding.GetEncoding("UTF-8"), "application/json") };
            return result;
        }

        // GET
        public HttpResponseMessage Get(String WebURL, String HeaderPath, String FooterPath)
        {
            UrlToPng4Web.InitUrlTOPng4CS();

            Web2PNGRequest req = new Web2PNGRequest();
            req.WebURL = WebURL;
            req.HeaderPath = HeaderPath;
            req.FooterPath = FooterPath;
            Web2PNGResponse rsp = UrlToPng4Web.UrlToPNG(req);
            
            String jsonString = JsonConvert.SerializeObject(rsp);
            HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(jsonString, Encoding.GetEncoding("UTF-8"), "application/json") };
            return result;
        }

        // POST
        public HttpResponseMessage Post(Web2PNGRequest req)
        {
            UrlToPng4Web.InitUrlTOPng4CS();

            Web2PNGResponse rsp = UrlToPng4Web.UrlToPNG(req);

            String jsonString = JsonConvert.SerializeObject(rsp);
            HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(jsonString, Encoding.GetEncoding("UTF-8"), "application/json") };
            return result;
        }
    }
}

Jersey实现REST风格编程

首先是服务端:
1、增加服务类

package com.neohope.jessery.test;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

/**
 * Created by Hansen
*/
@Path("neotest")
@Produces(MediaType.APPLICATION_JSON)
public class NeoTest {
    @GET
    @Path("/Add")
    public String Add(@QueryParam("a") int a,@QueryParam("b") int b) {
        return "{\"c\":" + (a+b) + "}";
    }

    @GET
    @Path("/SayHiTo")
    public String sayHiTo(@QueryParam("name") String name) {
        return "{\"msg\":\"hi " + name + "\"}";
    }
}

2、在web.xml里增加配置

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.neohope.jessery.test</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/Rest/*</url-pattern>
    </servlet-mapping>

3、然后是客户端

package com.neohope.jessery.test;

import org.glassfish.jersey.client.ClientConfig;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;

/**
 * Created by Hansen
 */
public class ClientTest {
    public static void main(String[] args) {
        ClientConfig clientConfig = new ClientConfig();
        Client client = ClientBuilder.newClient(clientConfig);

        WebTarget target = client.target("http://localhost:8080/Rest/neotest/" + "Add");
        Response response = target.queryParam("a", 1).queryParam("b", 2).request().get();
        System.out.println(response.getStatus());
        System.out.println(response.readEntity(String.class));
        response.close();

        WebTarget target1 = client.target("http://localhost:8080/Rest/neotest/" + "SayHiTo");
        Response response1 = target1.queryParam("name", "neohope").request().get();
        System.out.println(response1.getStatus());
        System.out.println(response1.readEntity(String.class));
        response1.close();
    }
}

WCF自托管服务

一般有两种方式实现:

1、通过代码设置直接实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using WcfTest;

namespace WcfHosting
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof (SoapService)))
            {
                host.AddServiceEndpoint(typeof(ISoapService), new WSHttpBinding(), "http://127.0.0.1:1234/neohope");

                if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
                {
                    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                    behavior.HttpGetEnabled = true;
                    behavior.HttpGetUrl = new Uri("http://127.0.0.1:9999/neohope/metadata");
                    host.Description.Behaviors.Add(behavior);
                }

                host.Opened += delegate
                {
                    Console.WriteLine("service started, press enter to exit.");
                };
                host.Open();
                Console.Read();
            }
        }
    }
}

2、通过配置文件实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using WcfTest;

namespace WcfHosting
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof (SoapService)))
            {
                host.Opened += delegate
                {
                    Console.WriteLine("service started, press enter to exit.");
                };
                host.Open();
                Console.Read();
            }
        }
    }
}

app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="metadataBehavior">
          <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:9999/neohope/metadata" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="metadataBehavior" name="WcfTest.SoapService">
        <endpoint address="http://127.0.0.9999/neohope" binding="wsHttpBinding"
                  contract="WcfTest.ISoapService" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

WCF修改SOAP命名空间

有三个地方可以修改SOAP的命名空间:

1、ServiceContract

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Web.Configuration;

namespace WcfTest
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
    [ServiceContract(Name="SoapService",Namespace="http://wcftest.neohope.org")]
    public interface ISoapService
    {
        [OperationContract]
        [WebInvoke(Method = "GET", UriTemplate = "User/Get/{uid}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        NUser GetUser(String uid);

        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string UpdateUser(NUser newuser);


        [OperationContract]
        [WebInvoke(Method = "PUT", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string AddUser(NUser newuser);


        [OperationContract]
        [WebInvoke(Method = "DELETE", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string DeleteUser(String uid);
    }

    [ServiceBehavior(Name = "SoapService", Namespace = "http://wcftest.neohope.org")]
    [DataContract]
    public class NUser
    {
        string uid = "";
        string uname = "";
        string usex = "";

        [DataMember]
        public String Uid
        {
            get { return uid; }
            set { uid = value; }
        }

        [DataMember]
        public string UName
        {
            get { return uname; }
            set { uname = value; }
        }

        [DataMember]
        public string USex
        {
            get { return usex; }
            set { usex = value; }
        }
    }
}

2、服务实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfTest
{
    [ServiceBehavior(Name = "SoapService", Namespace = "http://wcftest.neohope.org")]
   public class SoapService : ISoapService
    {
        static List<NUser> users = new List<NUser>();

        static SoapService()
        {
            NUser aNUser = new NUser();
            aNUser.Uid = "001";
            aNUser.UName = "张三";
            aNUser.USex = "男";
            users.Add(aNUser);
        }

        public NUser GetUser(string uid)
        {
            foreach (NUser nuser in users)
            {
                if (nuser.Uid == uid)
                {
                    return nuser;
                }
            }

            return null;
        }

        public string UpdateUser(NUser newuser)
        {
            if (newuser == null)
            {
                return "nuewuser is null";
            }

            foreach (NUser nuser in users)
            {
                if (nuser.Uid == newuser.Uid)
                {
                    nuser.UName = newuser.UName;
                    newuser.USex = nuser.USex;
                    return "User updated";
                }
            }

            return "User not found";
        }

        public string AddUser(NUser newuser)
        {
            if (newuser == null)
            {
                return "nuewuser is null";
            }

            foreach (NUser nuser in users)
            {
                if (nuser.Uid == newuser.Uid)
                {
                    nuser.UName = newuser.UName;
                    newuser.USex = nuser.USex;
                    return "User updated";
                }
            }

            users.Add(newuser);

            return "User added";
        }

        public string DeleteUser(String uid)
        {
            bool bFound = false;
            for(int i=users.Count-1;i>=0;i--)
            {
                if (users[i].Uid == uid)
                {
                    bFound = true;
                    users.RemoveAt(i);
                }
            }

            if (!bFound)
            {
                return "User Not Found";
            }
            else
            {
                return "User Deleted";
            }
        }
    }
}

3、Web.config

<?xml version="1.0"?>
<configuration>
  
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  
  <system.serviceModel>

    <services>
      <service name="WcfTest.SoapService">
        <endpoint binding="webHttpBinding" contract="WcfTest.ISoapService" bindingNamespace="http://wcftest.neohope.org"/>
      </service>
    </services>
 
    <behaviors>
      <endpointBehaviors>
      </endpointBehaviors>
      
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

WCF实现REST风格编程

1、首先需要一个ServiceContract

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfTest
{
    [ServiceContract]
    public interface IRestService
    {
        [OperationContract]
        [WebInvoke(Method = "GET", UriTemplate = "User/Get/{uid}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        NUser GetUser(String uid);


        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string UpdateUser(NUser newuser);


        [OperationContract]
        [WebInvoke(Method = "PUT", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string AddUser(NUser newuser);


        [OperationContract]
        [WebInvoke(Method = "DELETE", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string DeleteUser(String uid);
    }

    [DataContract]
    public class NUser
    {
        string uid = "";
        string uname = "";
        string usex = "";

        [DataMember]
        public String Uid
        {
            get { return uid; }
            set { uid = value; }
        }

        [DataMember]
        public string UName
        {
            get { return uname; }
            set { uname = value; }
        }

        [DataMember]
        public string USex
        {
            get { return usex; }
            set { usex = value; }
        }
    }
}


2、然后需要实现服务

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;

namespace WcfTest
{
    public class RestService : IRestService
    {
        static List<NUser> users = new List<NUser>();

        static RestService()
        {
            NUser aNUser = new NUser();
            aNUser.Uid = "001";
            aNUser.UName = "张三";
            aNUser.USex = "男";
            users.Add(aNUser);
        }

        public NUser GetUser(string uid)
        {
            foreach (NUser nuser in users)
            {
                if (nuser.Uid == uid)
                {
                    return nuser;
                }
            }

            return null;
        }

        public string UpdateUser(NUser newuser)
        {
            if (newuser == null)
            {
                return "nuewuser is null";
            }

            foreach (NUser nuser in users)
            {
                if (nuser.Uid == newuser.Uid)
                {
                    nuser.UName = newuser.UName;
                    newuser.USex = nuser.USex;
                    return "User updated";
                }
            }

            return "User not found";
        }

        public string AddUser(NUser newuser)
        {
            if (newuser == null)
            {
                return "nuewuser is null";
            }

            foreach (NUser nuser in users)
            {
                if (nuser.Uid == newuser.Uid)
                {
                    nuser.UName = newuser.UName;
                    newuser.USex = nuser.USex;
                    return "User updated";
                }
            }

            users.Add(newuser);

            return "User added";
        }

        public string DeleteUser(String uid)
        {
            bool bFound = false;
            for(int i=users.Count-1;i>=0;i--)
            {
                if (users[i].Uid == uid)
                {
                    bFound = true;
                    users.RemoveAt(i);
                }
            }

            if (!bFound)
            {
                return "User Not Found";
            }
            else
            {
                return "User Deleted";
            }
        }
    }
}

3、修改服务的Markup

<%@ ServiceHost Language="C#" Debug="true" Service="WcfTest.RestService" CodeBehind="RestService.svc.cs" 
    Factory="System.ServiceModel.Activation.WebServiceHostFactory"%>

4、修改服务配置
添加serviceBehaviors、endpointBehaviors、service、endpoint

<?xml version="1.0"?>
<configuration>
  
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="RestSvcBehavior" name="WcfTest.RestService">
        <endpoint binding="webHttpBinding" contract="WcfTest.IRestService" address="" behaviorConfiguration="RestEPBehavior"/>
      </service>
    </services>
 
    <behaviors>
      <endpointBehaviors>
        <behavior name="RestEPBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      
      <serviceBehaviors>
        <behavior name="RestSvcBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

5、访问服务

http://ip:port/RestService.svc/User/Get/001

JBoss EAP MessageBean的调用(Queue)

通讯流程图,其中EJB扮演了Client2的角色
jms-point-to-point-model.png

首先是Server端的开发及设置:
1、增加一个用户:

bin\add-user.bat

用户名密码随便,但要属于guest组

2、启动Server

standalone.bat -server-config=standalone-full.xml 

3、新建Queue

jboss-cli.bat --connect
jms-queue add --queue-address=neoQueue --entries=queue/neoQueue,java:jboss/exported/jms/queue/neoQueue

4、新建java项目
4.1、写一个QueueBean
MessageQueueBean.java

package com.neohope.ejb.test;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.TextMessage;

/**
 * Created by Hansen
 */
@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/neoQueue"),
        @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")
})
public class MessageQueueBean implements javax.jms.MessageListener {
    public MessageQueueBean() {
    }

    @Override
    public void onMessage(Message message) {
        try {
            System.out.println("MessageQueueBean.onMessage invoked");
            if(message instanceof TextMessage)
            {
                TextMessage textMessage = (TextMessage)message;
                System.out.println("Message: " + textMessage.getText());
            }
            else
            {
                System.out.println("Message is not a TextMessage");
            }
        }
        catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

4.2、打jar包,或者自己压缩为TestEJBServer.jar

5、新建一个ear项目
5.1增加application.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd"
             version="6">
    <description>EAR Test</description>
    <display-name>EAR Test</display-name>
    <module>
        <ejb>TestEJBServer.jar</ejb>
    </module>
    <library-directory>lib</library-directory>
</application>

5.2打ear包,或自己压缩为TestEar.ear

5.3TestEar的结构为:

│  TestEJBServer.jar
│
├─lib
└─META-INF
        application.xml

6.通过EAP进行部署

到这里服务端已经完成了。

然后是客户端的设置:
1、通过Queue方式访问

package com.neohope.ejb.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestQueueBean {
    public static void main(String[] args) throws NamingException, JMSException {

        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
        final InitialContext ctx = new InitialContext(jndiProperties);

        QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("jms/RemoteConnectionFactory");
        QueueConnection connection = factory.createQueueConnection("user001", "user001#");
        QueueSession session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);

        Queue queue = (Queue) ctx.lookup("jms/queue/neoQueue");
        TextMessage msg = session.createTextMessage("Queue Test Messagee");
        QueueSender sender = session.createSender(queue);
        sender.send(msg);

        session.close();
        connection.close();
    }
}

2、通过MessageProducer方式访问

package com.neohope.ejb.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestQueueBean {
    public static void main(String[] args) throws NamingException, JMSException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
        final InitialContext ctx = new InitialContext(jndiProperties);

        ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("jms/RemoteConnectionFactory");
        Destination destination = (Destination) ctx.lookup("jms/queue/neoQueue");
        Connection connection = connectionFactory.createConnection("user001", "user001#");
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        MessageProducer producer = session.createProducer(destination);
        connection.start();

        TextMessage msg = session.createTextMessage("Queue Test Messagee");
        producer.send(msg);

        session.close();
        connection.close();
    }
}

JBoss EAP MessageBean的调用(Topic)

通讯流程图,其中EJB扮演了client2的角色
jms-publisher-subscriber-model.png

首先是Server端的开发及设置:
1、增加一个用户:

bin\add-user.bat

用户名密码随便,但要属于guest组

2、启动Server

standalone.bat -server-config=standalone-full.xml 

3、新建Topic

jboss-cli.bat --connect
jms-topic add --topic-address=neoTopic --entries=topic/neoTopic,java:jboss/exported/jms/topic/neoTopic

4、新建java项目
4.1、写一个TopicBean
MessageTopicBean .java

package com.neohope.ejb.test;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.TextMessage;

/**
 * Created by Hansen
 */
@MessageDriven(mappedName = "MessageTopicBean",activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
        @ActivationConfigProperty(propertyName = "destination", propertyValue = "topic/neoTopic"),
        @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")
})
public class MessageTopicBean implements javax.jms.MessageListener {
    public MessageTopicBean() {
    }

    @Override
    public void onMessage(Message message) {
        try {
            System.out.println("MessageTopicBean.onMessage invoked");
            if(message instanceof TextMessage)
            {
                TextMessage textMessage = (TextMessage)message;
                System.out.println("Message: " + textMessage.getText());
            }
            else
            {
                System.out.println("Message is not a TextMessage");
            }
        }
        catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

4.2、打jar包,或者自己压缩为TestEJBServer.jar

5、新建一个ear项目
5.1增加application.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd"
             version="6">
    <description>EAR Test</description>
    <display-name>EAR Test</display-name>
    <module>
        <ejb>TestEJBServer.jar</ejb>
    </module>
    <library-directory>lib</library-directory>
</application>

5.2打ear包,或自己压缩为TestEar.ear

5.3TestEar的结构为:

│  TestEJBServer.jar
│
├─lib
└─META-INF
        application.xml

6.通过EAP进行部署

到这里服务端已经完成了。

然后是客户端的设置:
1、通过Topic方式访问

package com.neohope.ejb.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestTopicBean {
    public static void main(String[] args) throws NamingException, JMSException {

        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
        final InitialContext ctx = new InitialContext(jndiProperties);


        TopicConnectionFactory factory = (TopicConnectionFactory) ctx.lookup("jms/RemoteConnectionFactory");
        TopicConnection connection = factory.createTopicConnection("user001", "user001#");
        TopicSession session = connection.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);

        Topic topic = (Topic) ctx.lookup("jms/topic/neoTopic");
        TextMessage msg = session.createTextMessage("Topic Test Message");
        TopicPublisher publisher = session.createPublisher(topic);
        publisher.publish(msg);

        session.close();
        connection.close();
    }
}

2、通过MessageProducer方式访问

package com.neohope.ejb.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestTopicBean {
    public static void main(String[] args) throws NamingException, JMSException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
        final InitialContext ctx = new InitialContext(jndiProperties);

        ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("jms/RemoteConnectionFactory");
        Destination destination = (Destination) ctx.lookup("jms/topic/neoTopic");
        Connection connection = connectionFactory.createConnection("user001", "user001#");
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        MessageProducer producer = session.createProducer(destination);
        connection.start();

        TextMessage msg = session.createTextMessage("Topic Test Messagee");
        producer.send(msg);

        session.close();
        connection.close();
    }
}

JBoss EAP SessionBean的调用

首先是Server端的开发及设置:
1、增加一个用户:

bin\add-user.bat

用户名密码随便,但要属于guest组

2、启动Server

standalone.bat -server-config=standalone-full.xml 

3、新建java项目
3.1、写SessionBean的接口
ITest.java

package com.neohope.ejb.test;

import javax.ejb.Remote;

/**
 * Created by Hansen
 */

@Remote
public interface ITest{
    /**
     * 返回“Hi XXX"字符串,必须声明抛出RemoteException异常
     * @return 返回“Hi XXX"字符串
     */
    public String sayHiTo(String user);

    /**
     * 加法,必须声明抛出RemoteException异常
     * @param a
     * @parma b
     * @return a+b
     */
    public int add(int a, int b);
}

3.2、写一个无状态的SessionBean
SessionTestBean.java

package com.neohope.ejb.test;

import javax.ejb.Stateless;

/**
 * Created by Hansen
 */
@Stateless(name = "SessionTestEJB")
public class SessionTestBean implements ITest{
    public SessionTestBean() {
    }

    /**
     * 返回“Hi XXX"字符串,必须声明抛出RemoteException异常
     * @return 返回“Hi XXX"字符串
     */
    @Override
    public String sayHiTo(String user) {
        return "Hi " + user;
    }

    /**
     * 加法,必须声明抛出RemoteException异常
     * @param a
     * @parma b
     * @return a+b
     */
    @Override
    public int add(int a, int b) {
        return a+b;
    }
}

3.3、打jar包,或者自己压缩为TestEJBServer.jar

4、新建一个ear项目
4.1增加application.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd"
             version="6">
    <description>EAR Test</description>
    <display-name>EAR Test</display-name>
    <module>
        <ejb>TestEJBServer.jar</ejb>
    </module>
    <library-directory>lib</library-directory>
</application>

4.2打ear包,或自己压缩为TestEar.ear

4.3TestEar的结构为:

│  TestEJBServer.jar
│
├─lib
└─META-INF
        application.xml

5.通过EAP进行部署,部署后,会输出绑定结果

java:global/TestEar/TestEJBServer/SessionTestEJB!com.neohope.ejb.test.ITest
java:app/TestEJBServer/SessionTestEJB!com.neohope.ejb.test.ITest
java:module/SessionTestEJB!com.neohope.ejb.test.ITest
java:jboss/exported/TestEar/TestEJBServer/SessionTestEJB!com.neohope.ejb.test.ITest
java:global/TestEar/TestEJBServer/SessionTestEJB
java:app/TestEJBServer/SessionTestEJB
java:module/SessionTestEJB

到这里服务端已经完成了。

然后是客户端的设置:
1、在scr下新增配置文件jboss-ejb-client.properties

endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
remote.connection.default.port = 4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.default.username=user001
remote.connection.default.password=user001#

2、通过ejb方式访问

package com.neohope.ejb.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestSessionBean{
    public static void main(String[] args) throws NamingException, JMSException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");

        final Context ctx = new InitialContext(jndiProperties);
        final String appName = "TestEar";
        final String moduleName = "TestEJBServer";
        final String distinctName = "";
        final String beanName = "SessionTestEJB";
        final String viewClassName = ITest.class.getName();
        final String namespace = "ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName;
        System.out.println(namespace);
        ITest test =  (ITest)ctx.lookup(namespace);

        System.out.println(test.sayHiTo("neohope"));
        System.out.println(test.add(1,2));
    }
}

3、通过JMS方式访问

package com.neohope.ejb.test;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestSessionBean{
    public static void main(String[] args) throws NamingException, JMSException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
        final InitialContext ctx = new InitialContext(jndiProperties);

        ConnectionFactory factory = (ConnectionFactory) ctx.lookup("jms/RemoteConnectionFactory");
        Connection connection = factory.createConnection("user001", "user001#");
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

        ITest test = (ITest) ctx.lookup("TestEar/TestEJBServer/SessionTestEJB!com.neohope.ejb.test.ITest");

        session.close();
        connection.close();
        System.out.println(test.sayHiTo("neohope"));
        System.out.println(test.add(1,2));
    }
}

导致惨重代价的运维事故01

导致惨重代价的运维事故01

光大证券事件
2013年8月,光大证券在向客户演示时连接了正式数据库,导致股市震荡,被罚款5.2亿。

宁夏银行删库事件
2014年7月,宁夏银行在季末结算业务量较大的情况下,因备份系统异常导致备份存储磁盘读写处理严重延时,备份与主存储数据不一致。工程师在采取中断数据备份录像操作后,造成生产数据库损坏并宕机。造成38小时,700多定点医疗机构和定点零售药无法使用医保支付。

小插曲,2014年5月宁夏银行使用CDP软件进行了一场容灾演练,曾完成800公里的容灾切换。

携程删库事件
2015年5月,携程无法访问。官方反馈是由于运维工程师误操作,误删生产环境,而且重新部署后还是会被删除。经过十几小时努力,最终恢复成功。

小插曲,携程挂掉后,导流给了艺龙,结果艺龙也挂了。

Gitlab删库事件
2017年2月,Gitlab运维人员,在应对前一晚的DDOS攻击后,发现备库复制数据缓慢,并无法解决。最终决定删除备库,重新开始复制。但在十分疲倦的情况下,工程师误删了300G的生产数据,由于备份机制设置不合理,最终导致20多小时系统宕机,707位用户丢失数据,5,037项目丢失,受事故影响的用户基数不到1%。
我们可以看到的问题有:
1、审核和监控全部备份策略:虽然Gitlab号称有五重备份机制:常规备份24小时做一次、自动同步、LVM快照24小时做一次、Azure备份对数据库无效、S3备份。但没有一个可靠地运行或设置,而且备份失败也没有良好的预警机制。最终只能基于LVM的备份(最近6小时以前),还原了6 小时前的备份。
2、积极演练应对重大问题,保证备库是随时可用的,应急时也应该有序进行
3、数据中心之间数据传输要考虑好,本次数据传输也花费了较长时间
4、防止人肉运维,谨防开夜车,脚本工具化自动化。人总归会出错,而且总是在最不该发生的时候出错。
5、Gitlab本次事故发生后,公开透明的处理方式,值得大家借鉴和尊重。

AWS删服务器事件
2017年3月,AWS服务异常,经过4个多小时才恢复正常。
原因为:一名S3工程师根据预先编写的playbook执行一条命令时,输入命令时输错了一个字母,结果删除了一大批本不该删除的服务器。

verelox.com删库事件
2017年6月,荷兰云主机厂商verelox.com,一前任管理员,恶意报复公司,删除全部用户数据,并擦出了多数服务器上面的内容。

链家删库事件
2018年6月4日,链家网财务系统数据被删,这9TB数据,包括了该公司成立以来所有的财务数据。
经过紧急修复,该批数据最终得以恢复。
事件原因:由于管理制度漏洞百出,一名数据库运维人员轻易获得了不应有的高权限。该工程师与公司起了争执,修改MAC地址后,直接删库。

小插曲:
该工程师,只改了MAC地址,但并未变更主机名、IP,被系统日志记录。
修改MAC地址,也用了第三方软件,该软件日志几乎坐实了其攻击行为。
而且删除的数据,很容易就被恢复了,只花了18万。技术方面,及其不专业。
最终,该工程师拒不配合调查,也不认罪,最终被判刑7年。

广西移动扩容事件
2017年9月,华为工程师在进行广西移动扩容时,误将HSS设备的用户数据格式化,导致广西移动损失80万用户数据。

顺丰删库事件
2018年9月,顺丰一个高级工程误删线上库,然后跑路。导致部分服务无法使用并持续近10小时。

郑大附一数据库事件
2018年12月24日,郑大附一的一名工程师,由于操作不当导致HIS库被锁表,让该医院门诊业务停止2小时。
该工程师被判刑5年6个月。

微盟删库事件
2020年2月,微盟删库事件,导致系统6六天无法访问,市值蒸发28亿,预计赔偿金1.5亿,官方反馈是内部员工恶意行为导致。
数据最后在腾讯云的帮助下得以恢复。

小插曲:
小道消息,该工程师欠了网贷无力偿还,而且当天喝了不少酒。
该工程师被判刑6年月。

============================================================
注:本文主要是整理了系统运维导致的惨痛代价,没有记录下面几种情况(设计失败,黑客攻击,病毒爆发)