跟微軟保持適當距離--Hessian + .net 實現RPC體系的企業應用


同在一個產業鏈園區的XX廠因為5台Window2003服務器收到了律師函並且被迫下了12萬$的采購單,雖然100萬對XXX廠來數不是大數目,但是總有種被打劫的感覺。

在企業ERP應用中服務層一般都是做成遠程調用的,具體Windows平台的技術有WebService,WCF,Remoting等,這里展示的是服務端采用linux 平台下采用Hessian組件實現RPC.

服務端:
Web服務器:JBoss,tomcat (weblogic挺美但是不免費啊)
數據庫:mysql(一般erp都用oracle做數據庫,當然那個啥費用也是不含糊地)
客戶端:
逆天的XP(sp3) 加.net4.0 (NND,這個組合量你也收不了多少錢把!)
VS2010? 我們用180試用版或者那啥notepad

 

Hessian+spring配置
1.web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>HessianTest</display-name>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:conf/ht-core.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>hessionRpc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:conf/ht-rpc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>hessionRpc</servlet-name>
    <url-pattern>/rpc/*</url-pattern>
  </servlet-mapping>

  <servlet>
    <description></description>
    <display-name>TestSpring</display-name>
    <servlet-name>TestSpring</servlet-name>
    <servlet-class>f.studio.web.servlet.TestSpring</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>TestSpring</servlet-name>
    <url-pattern>/TestSpring</url-pattern>
  </servlet-mapping>
  <filter>
    <display-name>HessianCtxFilter</display-name>
    <filter-name>HessianCtxFilter</filter-name>
    <filter-class>f.studio.web.filter.HessianCtxFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>HessianCtxFilter</filter-name>
    <url-pattern>/rpc/*</url-pattern>
  </filter-mapping>
  
   <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>
View Code

2.Service,DAO等spring配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
        <bean name="studentServiceImpl"
            class="f.studio.service.impl.StudentServiceImpl" scope="prototype" />
</beans>
View Code

3.Hessian導出層spring配置,因為一個項目里可能使用strut2,Servlet,cxf等服務提供層,但是他們需要共用Service,DAO等
參考:http://jinnianshilongnian.iteye.com/blog/1602617

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

    <bean name="/studentServiceRpc"
        class="org.springframework.remoting.caucho.HessianServiceExporter">
        <property name="service" ref="studentServiceImpl" />
        <property name="serviceInterface" value="f.studio.service.StudentService" />
    </bean>
    
</beans>
View Code

 

用戶登陸狀態問題
Hessian的C#實現可以自己保存cookie,並且是全局的(應用程序范圍)
服務端嘗試使用Hessian提供的ServiceContext獲取對Session的引用但是結果總為null,所以寫個filter 來自己維護用戶登陸Session供Service實現類使用

package f.studio.web.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import f.studio.util.HessianCtxContainer;

/**
 * Servlet Filter implementation class HessianCtxFilter
 */
public class HessianCtxFilter implements Filter {

    public static final String LOGIN_SESSION_KEY="LOGIN_USER_KEY"; 
    /**
     * Default constructor. 
     */
    public HessianCtxFilter() {
        // TODO Auto-generated constructor stub
    }

    /**
     * @see Filter#destroy()
     */
    public void destroy() {
        // TODO Auto-generated method stub
    }

    /**
     * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpSession session = ((HttpServletRequest) request).getSession(true);
        System.out.println("sessionId:" + session.getId());
        try {
            
            Object user = session.getAttribute(LOGIN_SESSION_KEY);
            HessianCtxContainer.setAttribute(LOGIN_SESSION_KEY, user);
            System.out.println("sessionUser:" + user);
            chain.doFilter(request, response);
            
        } finally {
            //移除掉ThreadLocal中Map中的對象防止益出
            //session具備自動移動除功能
            //不要在環境下(cxf,strut2等)使用HessionCtxContainer,避免線程重用時造成混亂
            Object user = HessianCtxContainer.remove(LOGIN_SESSION_KEY);
            session.setAttribute(LOGIN_SESSION_KEY, user);
        }
        
    }

    /**
     * @see Filter#init(FilterConfig)
     */
    public void init(FilterConfig fConfig) throws ServletException {
        // TODO Auto-generated method stub
    }

}
View Code

服務實現類:

package f.studio.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.omg.PortableInterceptor.USER_EXCEPTION;

import com.caucho.hessian.io.Hessian2Output;
import com.caucho.services.server.ServiceContext;

import f.studio.domain.Klass;
import f.studio.domain.QueryStudentInfo;
import f.studio.domain.StudentInfo;
import f.studio.service.StudentService;
import f.studio.util.HessianCtxContainer;
import f.studio.web.filter.HessianCtxFilter;

public class StudentServiceImpl implements StudentService {

    public List<StudentInfo> query(QueryStudentInfo q) {
        
        CheckLogin();
        
        ServletRequest request=  ServiceContext.getContextRequest();
        System.out.println(ServiceContext.getServiceName());
        //HttpSession session= request.getSession(true);
        //session.setAttribute("User", new Date());
        
        System.out.println("ServiceImpHashCode:" + this.hashCode());
        System.out.println(q);
        //if(1==1)throw new RuntimeException("運行錯誤信息啊");
        List<StudentInfo> list=new ArrayList<StudentInfo>();
        Klass klass=new Klass();
        klass.setId(9999);
        klass.setName("張老師");
        klass.setAddTime(new Date());
        
        for(int i=0;i<10;i++){
            StudentInfo s=new StudentInfo();
            
            //===父類
            s.setRecId(88888);
            s.setCreateDate(new Date());
            //==
            s.setId(i);
            s.setName("張思念" + i);
            s.setSex(i % 5 ==0);
            //===添加兩個元素==
            s.getKs().add(klass);
            s.getKs().add(klass);
        
            list.add(s);
        }
        return list;
    }

    public String hello(String name) {
            return "Hi " +name;
    }

    public void Login(String username, String password) {
        if("Admin".equals(username) && "123".equals(password)){
            HessianCtxContainer.setAttribute(HessianCtxFilter.LOGIN_SESSION_KEY , username);
            return;
        }
        throw new RuntimeException("錯誤的用戶名或密碼!");
    }

    public static void CheckLogin(){
        
        if(HessianCtxContainer.getAttribute(HessianCtxFilter.LOGIN_SESSION_KEY)==null){
            throw new RuntimeException("未登錄或登錄超時!");
        }
    }
}
View Code

 

.net客戶端,需要添加對hessianCsharp.dll的引用
調用一次Login后,再執行其他調用

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using hessiancsharp.client;

namespace HessianTest
{
    using f.studio.domain;
    using System.Threading;
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }



        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                CHessianProxyFactory factory = new CHessianProxyFactory("userName", "password");
                string url = "http://localhost/HessianTest/rpc/studentServiceRpc";//修改為你的server端地址
                StudentService test = (StudentService)factory.Create(typeof(StudentService), url);
                string result = test.hello("大白鯊");
                var q = new QueryStudentInfo() { BTime = DateTime.Now, Name = "哈哈", Id = 1888, Sex = false };
                q.Data = new byte[] { 5, 4, 3, 2, 1 };
                q.CreateDate = DateTime.Now;
                q.RecId = 99999;

                var list = test.query(q);
                foreach (var it in list)
                {
                    Console.WriteLine(it);
                }
                Console.WriteLine(result);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        

        }


        private void button5_Click(object sender, EventArgs e)
        {
            try
            {
                CHessianProxyFactory factory = new CHessianProxyFactory("userName", "password");
                string url = "http://localhost/HessianTest/rpc/studentServiceRpc";//修改為你的server端地址
                StudentService test = (StudentService)factory.Create(typeof(StudentService), url);
                test.Login("Admin", "123");
                
                Console.WriteLine("登陸成功");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

    public interface StudentService
    {
         string hello(string name);
         List<StudentInfo> query(QueryStudentInfo q);
         void Login(String usename, String password);
    }




}

namespace f.studio.domain
{
    public class BaseInfo
    {
        private DateTime? createDate;

        public DateTime? CreateDate
        {
            get { return createDate; }
            set { createDate = value; }
        }
        private long? recId;

        public long? RecId
        {
            get { return recId; }
            set { recId = value; }
        }
    }
    /// <summary>
    /// 上傳時用需要保持命名空間與服務器一致
    /// </summary>
    public class QueryStudentInfo :BaseInfo
    {
        private int id;
        private String name;
        private DateTime? btime;
        private Byte[] data;
        private bool sex;

        public int Id
        {
            get { return id; }
            set { id = value; }

        }
        public DateTime? BTime
        {
            get { return btime; }
            set { btime = value; }
        }
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public Byte[] Data
        {
            get { return data; }
            set { data = value; }
        }
        public bool Sex
        {
            get { return sex; }
            set { sex = value; }
        }
    }


    /// <summary>
    /// 不能使用public int Id{get;set;}
    /// private 字段名稱,大小寫需要跟服務端定義一致
    /// [Serializable]標記貌似不是必須的
    /// </summary>
    public class Klass :BaseInfo
    {
        private int id;
        private String name;
        private DateTime? addTime;

        public DateTime? AddTime
        {
            get { return addTime; }
            set { addTime = value; }
        }
        public string Name
        {
            get { return name; }
            set { name = value; }
        }


        public int Id
        {
            get { return id; }
            set { id = value; }
        }

    }


    public class StudentInfo :BaseInfo
    {
        private string name;
        private bool? sex;
        private long id;
        private byte[] fileData;

        public byte[] FileData
        {
            get { return fileData; }
            set { fileData = value; }
        }
        private List<Klass> ks;

        public List<Klass> Ks
        {
            get { return ks; }
            set { ks = value; }
        }


        public string Name
        {
            get { return name; }
            set { name = value; }
        }


        public long Id
        {
            get { return id; }
            set { id = value; }
        }


        public bool? Sex
        {
            get { return sex; }
            set { sex = value; }
        }
        public override string ToString()
        {
            return string.Format("Id:{0},Name:{1},Sex:{2},RecId:{3},CreateDate:{4}", Id, Name, Sex,RecId,CreateDate);
        }

    }
}
View Code


完成服務端代碼下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM