| 方式 | 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在源加一列,或订阅发布的快照过期了,就惨了 |
Author Archives: neohope
微软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的角色

首先是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的角色

首先是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
光大证券事件
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年月。
============================================================
注:本文主要是整理了系统运维导致的惨痛代价,没有记录下面几种情况(设计失败,黑客攻击,病毒爆发)